mirror of
https://github.com/BoostIo/Boostnote
synced 2025-12-13 01:36:22 +00:00
fixed eslint error & integrated with prettier as well as formatted the whole codebase (#3450)
This commit is contained in:
@@ -6,11 +6,7 @@ import hljs from 'highlight.js'
|
||||
import 'codemirror-mode-elixir'
|
||||
import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement'
|
||||
import convertModeName from 'browser/lib/convertModeName'
|
||||
import {
|
||||
options,
|
||||
TableEditor,
|
||||
Alignment
|
||||
} from '@susisu/mte-kernel'
|
||||
import { options, TableEditor, Alignment } from '@susisu/mte-kernel'
|
||||
import TextEditorInterface from 'browser/lib/TextEditorInterface'
|
||||
import eventEmitter from 'browser/main/lib/eventEmitter'
|
||||
import iconv from 'iconv-lite'
|
||||
@@ -20,11 +16,15 @@ import styles from '../components/CodeEditor.styl'
|
||||
const { ipcRenderer, remote, clipboard } = require('electron')
|
||||
import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily'
|
||||
const spellcheck = require('browser/lib/spellcheck')
|
||||
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu
|
||||
const buildEditorContextMenu = require('browser/lib/contextMenuBuilder')
|
||||
.buildEditorContextMenu
|
||||
import { createTurndownService } from '../lib/turndown'
|
||||
import {languageMaps} from '../lib/CMLanguageList'
|
||||
import { languageMaps } from '../lib/CMLanguageList'
|
||||
import snippetManager from '../lib/SnippetManager'
|
||||
import {generateInEditor, tocExistsInEditor} from 'browser/lib/markdown-toc-generator'
|
||||
import {
|
||||
generateInEditor,
|
||||
tocExistsInEditor
|
||||
} from 'browser/lib/markdown-toc-generator'
|
||||
import markdownlint from 'markdownlint'
|
||||
import Jsonlint from 'jsonlint-mod'
|
||||
import { DEFAULT_CONFIG } from '../main/lib/ConfigManager'
|
||||
@@ -33,28 +33,38 @@ import prettier from 'prettier'
|
||||
CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js'
|
||||
|
||||
const buildCMRulers = (rulers, enableRulers) =>
|
||||
(enableRulers ? rulers.map(ruler => ({
|
||||
column: ruler
|
||||
})) : [])
|
||||
enableRulers
|
||||
? rulers.map(ruler => ({
|
||||
column: ruler
|
||||
}))
|
||||
: []
|
||||
|
||||
function translateHotkey (hotkey) {
|
||||
return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl')
|
||||
function translateHotkey(hotkey) {
|
||||
return hotkey
|
||||
.replace(/\s*\+\s*/g, '-')
|
||||
.replace(/Command/g, 'Cmd')
|
||||
.replace(/Control/g, 'Ctrl')
|
||||
}
|
||||
|
||||
export default class CodeEditor extends React.Component {
|
||||
constructor (props) {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
|
||||
leading: false,
|
||||
trailing: true
|
||||
})
|
||||
this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject)
|
||||
this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject)
|
||||
this.changeHandler = (editor, changeObject) =>
|
||||
this.handleChange(editor, changeObject)
|
||||
this.highlightHandler = (editor, changeObject) =>
|
||||
this.handleHighlight(editor, changeObject)
|
||||
this.focusHandler = () => {
|
||||
ipcRenderer.send('editor:focused', true)
|
||||
}
|
||||
const debouncedDeletionOfAttachments = _.debounce(attachmentManagement.deleteAttachmentsNotPresentInNote, 30000)
|
||||
const debouncedDeletionOfAttachments = _.debounce(
|
||||
attachmentManagement.deleteAttachmentsNotPresentInNote,
|
||||
30000
|
||||
)
|
||||
this.blurHandler = (editor, e) => {
|
||||
ipcRenderer.send('editor:focused', false)
|
||||
if (e == null) return null
|
||||
@@ -66,12 +76,13 @@ export default class CodeEditor extends React.Component {
|
||||
el = el.parentNode
|
||||
}
|
||||
this.props.onBlur != null && this.props.onBlur(e)
|
||||
const {
|
||||
storageKey,
|
||||
noteKey
|
||||
} = this.props
|
||||
const { storageKey, noteKey } = this.props
|
||||
if (this.props.deleteUnusedAttachments === true) {
|
||||
debouncedDeletionOfAttachments(this.editor.getValue(), storageKey, noteKey)
|
||||
debouncedDeletionOfAttachments(
|
||||
this.editor.getValue(),
|
||||
storageKey,
|
||||
noteKey
|
||||
)
|
||||
}
|
||||
}
|
||||
this.pasteHandler = (editor, e) => {
|
||||
@@ -91,7 +102,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.formatTable = () => this.handleFormatTable()
|
||||
|
||||
if (props.switchPreview !== 'RIGHTCLICK') {
|
||||
this.contextMenuHandler = function (editor, event) {
|
||||
this.contextMenuHandler = function(editor, event) {
|
||||
const menu = buildEditorContextMenu(editor, event)
|
||||
if (menu != null) {
|
||||
setTimeout(() => menu.popup(remote.getCurrentWindow()), 30)
|
||||
@@ -104,24 +115,24 @@ export default class CodeEditor extends React.Component {
|
||||
this.turndownService = createTurndownService()
|
||||
}
|
||||
|
||||
handleSearch (msg) {
|
||||
handleSearch(msg) {
|
||||
const cm = this.editor
|
||||
const component = this
|
||||
|
||||
if (component.searchState) cm.removeOverlay(component.searchState)
|
||||
if (msg.length < 1) return
|
||||
|
||||
cm.operation(function () {
|
||||
cm.operation(function() {
|
||||
component.searchState = makeOverlay(msg, 'searching')
|
||||
cm.addOverlay(component.searchState)
|
||||
|
||||
function makeOverlay (query, style) {
|
||||
function makeOverlay(query, style) {
|
||||
query = new RegExp(
|
||||
query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'),
|
||||
'gi'
|
||||
)
|
||||
return {
|
||||
token: function (stream) {
|
||||
token: function(stream) {
|
||||
query.lastIndex = stream.pos
|
||||
var match = query.exec(stream.string)
|
||||
if (match && match.index === stream.pos) {
|
||||
@@ -138,25 +149,27 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleFormatTable () {
|
||||
this.tableEditor.formatAll(options({
|
||||
textWidthOptions: {}
|
||||
}))
|
||||
handleFormatTable() {
|
||||
this.tableEditor.formatAll(
|
||||
options({
|
||||
textWidthOptions: {}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
handleEditorActivity () {
|
||||
handleEditorActivity() {
|
||||
if (!this.textEditorInterface.transaction) {
|
||||
this.updateTableEditorState()
|
||||
}
|
||||
}
|
||||
|
||||
updateDefaultKeyMap () {
|
||||
updateDefaultKeyMap() {
|
||||
const { hotkey } = this.props
|
||||
const self = this
|
||||
const expandSnippet = snippetManager.expandSnippet
|
||||
|
||||
this.defaultKeyMap = CodeMirror.normalizeKeyMap({
|
||||
Tab: function (cm) {
|
||||
Tab: function(cm) {
|
||||
const cursor = cm.getCursor()
|
||||
const line = cm.getLine(cursor.line)
|
||||
const cursorPosition = cursor.ch
|
||||
@@ -198,17 +211,17 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
},
|
||||
'Cmd-Left': function (cm) {
|
||||
'Cmd-Left': function(cm) {
|
||||
cm.execCommand('goLineLeft')
|
||||
},
|
||||
'Cmd-T': function (cm) {
|
||||
'Cmd-T': function(cm) {
|
||||
// Do nothing
|
||||
},
|
||||
[translateHotkey(hotkey.insertDate)]: function (cm) {
|
||||
[translateHotkey(hotkey.insertDate)]: function(cm) {
|
||||
const dateNow = new Date()
|
||||
cm.replaceSelection(dateNow.toLocaleDateString())
|
||||
},
|
||||
[translateHotkey(hotkey.insertDateTime)]: function (cm) {
|
||||
[translateHotkey(hotkey.insertDateTime)]: function(cm) {
|
||||
const dateNow = new Date()
|
||||
cm.replaceSelection(dateNow.toLocaleString())
|
||||
},
|
||||
@@ -231,7 +244,10 @@ export default class CodeEditor extends React.Component {
|
||||
currentConfig.cursorOffset = cm.doc.indexFromPos(cursorPos)
|
||||
|
||||
// Prettify contents of editor
|
||||
const formattedTextDetails = prettier.formatWithCursor(cm.doc.getValue(), currentConfig)
|
||||
const formattedTextDetails = prettier.formatWithCursor(
|
||||
cm.doc.getValue(),
|
||||
currentConfig
|
||||
)
|
||||
|
||||
const formattedText = formattedTextDetails.formatted
|
||||
const formattedCursorPos = formattedTextDetails.cursorOffset
|
||||
@@ -246,7 +262,8 @@ export default class CodeEditor extends React.Component {
|
||||
const appendLineBreak = /\n$/.test(selection)
|
||||
|
||||
const sorted = _.split(selection.trim(), '\n').sort()
|
||||
const sortedString = _.join(sorted, '\n') + (appendLineBreak ? '\n' : '')
|
||||
const sortedString =
|
||||
_.join(sorted, '\n') + (appendLineBreak ? '\n' : '')
|
||||
|
||||
cm.doc.replaceSelection(sortedString)
|
||||
},
|
||||
@@ -256,7 +273,7 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
updateTableEditorState () {
|
||||
updateTableEditorState() {
|
||||
const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions)
|
||||
if (active) {
|
||||
if (this.extraKeysMode !== 'editor') {
|
||||
@@ -272,7 +289,7 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
componentDidMount() {
|
||||
const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props
|
||||
eventEmitter.on('line:jump', this.scrollToLineHandeler)
|
||||
|
||||
@@ -298,7 +315,11 @@ export default class CodeEditor extends React.Component {
|
||||
rtlMoveVisually: RTL,
|
||||
foldGutter: true,
|
||||
lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false,
|
||||
gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
|
||||
gutters: [
|
||||
'CodeMirror-linenumbers',
|
||||
'CodeMirror-foldgutter',
|
||||
'CodeMirror-lint-markers'
|
||||
],
|
||||
autoCloseBrackets: {
|
||||
pairs: this.props.matchingPairs,
|
||||
triples: this.props.matchingTriples,
|
||||
@@ -309,7 +330,9 @@ export default class CodeEditor extends React.Component {
|
||||
prettierConfig: this.props.prettierConfig
|
||||
})
|
||||
|
||||
document.querySelector('.CodeMirror-lint-markers').style.display = enableMarkdownLint ? 'inline-block' : 'none'
|
||||
document.querySelector(
|
||||
'.CodeMirror-lint-markers'
|
||||
).style.display = enableMarkdownLint ? 'inline-block' : 'none'
|
||||
|
||||
if (!this.props.mode && this.props.value && this.props.autoDetect) {
|
||||
this.autoDetectLanguage(this.props.value)
|
||||
@@ -342,7 +365,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.textEditorInterface = new TextEditorInterface(this.editor)
|
||||
this.tableEditor = new TableEditor(this.textEditorInterface)
|
||||
if (this.props.spellCheck) {
|
||||
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
|
||||
this.editor.addPanel(this.createSpellCheckPanel(), { position: 'bottom' })
|
||||
}
|
||||
|
||||
eventEmitter.on('code:format-table', this.formatTable)
|
||||
@@ -352,13 +375,13 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
|
||||
this.editorKeyMap = CodeMirror.normalizeKeyMap({
|
||||
'Tab': () => {
|
||||
Tab: () => {
|
||||
this.tableEditor.nextCell(this.tableEditorOptions)
|
||||
},
|
||||
'Shift-Tab': () => {
|
||||
this.tableEditor.previousCell(this.tableEditorOptions)
|
||||
},
|
||||
'Enter': () => {
|
||||
Enter: () => {
|
||||
this.tableEditor.nextRow(this.tableEditorOptions)
|
||||
},
|
||||
'Ctrl-Enter': () => {
|
||||
@@ -477,7 +500,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.initialHighlighting()
|
||||
}
|
||||
|
||||
getWordBeforeCursor (line, lineNumber, cursorPosition) {
|
||||
getWordBeforeCursor(line, lineNumber, cursorPosition) {
|
||||
let wordBeforeCursor = ''
|
||||
const originCursorPosition = cursorPosition
|
||||
const emptyChars = /\t|\s|\r|\n|\$/
|
||||
@@ -514,11 +537,11 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
quitEditor () {
|
||||
quitEditor() {
|
||||
document.querySelector('textarea').blur()
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
componentWillUnmount() {
|
||||
this.editor.off('focus', this.focusHandler)
|
||||
this.editor.off('blur', this.blurHandler)
|
||||
this.editor.off('change', this.changeHandler)
|
||||
@@ -533,7 +556,7 @@ export default class CodeEditor extends React.Component {
|
||||
eventEmitter.off('code:format-table', this.formatTable)
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps, prevState) {
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
let needRefresh = false
|
||||
const {
|
||||
rulers,
|
||||
@@ -561,13 +584,18 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr')
|
||||
this.editor.setOption('rtlMoveVisually', this.props.RTL)
|
||||
}
|
||||
if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) {
|
||||
if (
|
||||
prevProps.enableMarkdownLint !== enableMarkdownLint ||
|
||||
prevProps.customMarkdownLintConfig !== customMarkdownLintConfig
|
||||
) {
|
||||
if (!enableMarkdownLint) {
|
||||
this.editor.setOption('lint', {default: false})
|
||||
document.querySelector('.CodeMirror-lint-markers').style.display = 'none'
|
||||
this.editor.setOption('lint', { default: false })
|
||||
document.querySelector('.CodeMirror-lint-markers').style.display =
|
||||
'none'
|
||||
} else {
|
||||
this.editor.setOption('lint', this.getCodeEditorLintConfig())
|
||||
document.querySelector('.CodeMirror-lint-markers').style.display = 'inline-block'
|
||||
document.querySelector('.CodeMirror-lint-markers').style.display =
|
||||
'inline-block'
|
||||
}
|
||||
needRefresh = true
|
||||
}
|
||||
@@ -599,9 +627,11 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd)
|
||||
}
|
||||
|
||||
if (prevProps.matchingPairs !== this.props.matchingPairs ||
|
||||
if (
|
||||
prevProps.matchingPairs !== this.props.matchingPairs ||
|
||||
prevProps.matchingTriples !== this.props.matchingTriples ||
|
||||
prevProps.explodingPairs !== this.props.explodingPairs) {
|
||||
prevProps.explodingPairs !== this.props.explodingPairs
|
||||
) {
|
||||
const bracketObject = {
|
||||
pairs: this.props.matchingPairs,
|
||||
triples: this.props.matchingTriples,
|
||||
@@ -646,11 +676,18 @@ export default class CodeEditor extends React.Component {
|
||||
const elem = document.getElementById('editor-bottom-panel')
|
||||
elem.parentNode.removeChild(elem)
|
||||
} else {
|
||||
this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'})
|
||||
this.editor.addPanel(this.createSpellCheckPanel(), {
|
||||
position: 'bottom'
|
||||
})
|
||||
}
|
||||
}
|
||||
if (prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments) {
|
||||
this.editor.setOption('deleteUnusedAttachments', this.props.deleteUnusedAttachments)
|
||||
if (
|
||||
prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments
|
||||
) {
|
||||
this.editor.setOption(
|
||||
'deleteUnusedAttachments',
|
||||
this.props.deleteUnusedAttachments
|
||||
)
|
||||
}
|
||||
|
||||
if (needRefresh) {
|
||||
@@ -658,17 +695,19 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getCodeEditorLintConfig () {
|
||||
getCodeEditorLintConfig() {
|
||||
const { mode } = this.props
|
||||
const checkMarkdownNoteIsOpen = mode === 'Boost Flavored Markdown'
|
||||
|
||||
return checkMarkdownNoteIsOpen ? {
|
||||
getAnnotations: this.validatorOfMarkdown,
|
||||
async: true
|
||||
} : false
|
||||
return checkMarkdownNoteIsOpen
|
||||
? {
|
||||
getAnnotations: this.validatorOfMarkdown,
|
||||
async: true
|
||||
}
|
||||
: false
|
||||
}
|
||||
|
||||
validatorOfMarkdown (text, updateLinting) {
|
||||
validatorOfMarkdown(text, updateLinting) {
|
||||
const { customMarkdownLintConfig } = this.props
|
||||
let lintConfigJson
|
||||
try {
|
||||
@@ -693,7 +732,7 @@ export default class CodeEditor extends React.Component {
|
||||
let ruleNames = ''
|
||||
item.ruleNames.map((ruleName, index) => {
|
||||
ruleNames += ruleName
|
||||
ruleNames += (index === item.ruleNames.length - 1) ? ': ' : '/'
|
||||
ruleNames += index === item.ruleNames.length - 1 ? ': ' : '/'
|
||||
})
|
||||
const lineNumber = item.lineNumber - 1
|
||||
foundIssues.push({
|
||||
@@ -708,7 +747,7 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
setMode (mode) {
|
||||
setMode(mode) {
|
||||
let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text'))
|
||||
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
|
||||
|
||||
@@ -716,7 +755,7 @@ export default class CodeEditor extends React.Component {
|
||||
CodeMirror.autoLoadMode(this.editor, syntax.mode)
|
||||
}
|
||||
|
||||
handleChange (editor, changeObject) {
|
||||
handleChange(editor, changeObject) {
|
||||
spellcheck.handleChange(editor, changeObject)
|
||||
|
||||
// The current note contains an toc. We'll check for changes on headlines.
|
||||
@@ -726,7 +765,11 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
// Check if one of the changed lines contains a headline
|
||||
for (let line = 0; line < changeObject.text.length; line++) {
|
||||
if (this.linePossibleContainsHeadline(editor.getLine(changeObject.from.line + line))) {
|
||||
if (
|
||||
this.linePossibleContainsHeadline(
|
||||
editor.getLine(changeObject.from.line + line)
|
||||
)
|
||||
) {
|
||||
requireTocUpdate = true
|
||||
break
|
||||
}
|
||||
@@ -755,13 +798,13 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
linePossibleContainsHeadline (currentLine) {
|
||||
linePossibleContainsHeadline(currentLine) {
|
||||
// We can't check if the line start with # because when some write text before
|
||||
// the # we also need to update the toc
|
||||
return currentLine.includes('# ')
|
||||
}
|
||||
|
||||
incrementLines (start, linesAdded, linesRemoved, editor) {
|
||||
incrementLines(start, linesAdded, linesRemoved, editor) {
|
||||
const highlightedLines = editor.options.linesHighlighted
|
||||
|
||||
const totalHighlightedLines = highlightedLines.length
|
||||
@@ -782,7 +825,7 @@ export default class CodeEditor extends React.Component {
|
||||
highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1)
|
||||
|
||||
// Lines that need to be relocated
|
||||
if (lineNumber >= (start + linesRemoved)) {
|
||||
if (lineNumber >= start + linesRemoved) {
|
||||
newLines.push(lineNumber + offset)
|
||||
}
|
||||
}
|
||||
@@ -796,22 +839,30 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleHighlight (editor, changeObject) {
|
||||
handleHighlight(editor, changeObject) {
|
||||
const lines = editor.options.linesHighlighted
|
||||
|
||||
if (!lines.includes(changeObject)) {
|
||||
lines.push(changeObject)
|
||||
editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background')
|
||||
editor.addLineClass(
|
||||
changeObject,
|
||||
'text',
|
||||
'CodeMirror-activeline-background'
|
||||
)
|
||||
} else {
|
||||
lines.splice(lines.indexOf(changeObject), 1)
|
||||
editor.removeLineClass(changeObject, 'text', 'CodeMirror-activeline-background')
|
||||
editor.removeLineClass(
|
||||
changeObject,
|
||||
'text',
|
||||
'CodeMirror-activeline-background'
|
||||
)
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
this.props.onChange(editor)
|
||||
}
|
||||
}
|
||||
|
||||
updateHighlight (editor, changeObject) {
|
||||
updateHighlight(editor, changeObject) {
|
||||
const linesAdded = changeObject.text.length - 1
|
||||
const linesRemoved = changeObject.removed.length - 1
|
||||
|
||||
@@ -842,28 +893,28 @@ export default class CodeEditor extends React.Component {
|
||||
this.incrementLines(start, linesAdded, linesRemoved, editor)
|
||||
}
|
||||
|
||||
moveCursorTo (row, col) {}
|
||||
moveCursorTo(row, col) {}
|
||||
|
||||
scrollToLine (event, num) {
|
||||
scrollToLine(event, num) {
|
||||
const cursor = {
|
||||
line: num,
|
||||
ch: 1
|
||||
}
|
||||
this.editor.setCursor(cursor)
|
||||
const top = this.editor.charCoords({line: num, ch: 0}, 'local').top
|
||||
const top = this.editor.charCoords({ line: num, ch: 0 }, 'local').top
|
||||
const middleHeight = this.editor.getScrollerElement().offsetHeight / 2
|
||||
this.editor.scrollTo(null, top - middleHeight - 5)
|
||||
}
|
||||
|
||||
focus () {
|
||||
focus() {
|
||||
this.editor.focus()
|
||||
}
|
||||
|
||||
blur () {
|
||||
blur() {
|
||||
this.editor.blur()
|
||||
}
|
||||
|
||||
reload () {
|
||||
reload() {
|
||||
// Change event shouldn't be fired when switch note
|
||||
this.editor.off('change', this.changeHandler)
|
||||
this.value = this.props.value
|
||||
@@ -874,7 +925,7 @@ export default class CodeEditor extends React.Component {
|
||||
this.editor.refresh()
|
||||
}
|
||||
|
||||
setValue (value) {
|
||||
setValue(value) {
|
||||
const cursor = this.editor.getCursor()
|
||||
this.editor.setValue(value)
|
||||
this.editor.setCursor(cursor)
|
||||
@@ -885,18 +936,19 @@ export default class CodeEditor extends React.Component {
|
||||
* @param {Number} lineNumber
|
||||
* @param {String} content
|
||||
*/
|
||||
setLineContent (lineNumber, content) {
|
||||
setLineContent(lineNumber, content) {
|
||||
const prevContent = this.editor.getLine(lineNumber)
|
||||
const prevContentLength = prevContent ? prevContent.length : 0
|
||||
this.editor.replaceRange(content, { line: lineNumber, ch: 0 }, { line: lineNumber, ch: prevContentLength })
|
||||
this.editor.replaceRange(
|
||||
content,
|
||||
{ line: lineNumber, ch: 0 },
|
||||
{ line: lineNumber, ch: prevContentLength }
|
||||
)
|
||||
}
|
||||
|
||||
handleDropImage (dropEvent) {
|
||||
handleDropImage(dropEvent) {
|
||||
dropEvent.preventDefault()
|
||||
const {
|
||||
storageKey,
|
||||
noteKey
|
||||
} = this.props
|
||||
const { storageKey, noteKey } = this.props
|
||||
attachmentManagement.handleAttachmentDrop(
|
||||
this,
|
||||
storageKey,
|
||||
@@ -905,37 +957,44 @@ export default class CodeEditor extends React.Component {
|
||||
)
|
||||
}
|
||||
|
||||
insertAttachmentMd (imageMd) {
|
||||
insertAttachmentMd(imageMd) {
|
||||
this.editor.replaceSelection(imageMd)
|
||||
}
|
||||
|
||||
autoDetectLanguage (content) {
|
||||
autoDetectLanguage(content) {
|
||||
const res = hljs.highlightAuto(content, Object.keys(languageMaps))
|
||||
this.setMode(languageMaps[res.language])
|
||||
}
|
||||
|
||||
handlePaste (editor, forceSmartPaste) {
|
||||
handlePaste(editor, forceSmartPaste) {
|
||||
const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props
|
||||
|
||||
const isURL = str => /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str)
|
||||
const isURL = str =>
|
||||
/(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str)
|
||||
|
||||
const isInLinkTag = editor => {
|
||||
const startCursor = editor.getCursor('start')
|
||||
const prevChar = editor.getRange({
|
||||
line: startCursor.line,
|
||||
ch: startCursor.ch - 2
|
||||
}, {
|
||||
line: startCursor.line,
|
||||
ch: startCursor.ch
|
||||
})
|
||||
const prevChar = editor.getRange(
|
||||
{
|
||||
line: startCursor.line,
|
||||
ch: startCursor.ch - 2
|
||||
},
|
||||
{
|
||||
line: startCursor.line,
|
||||
ch: startCursor.ch
|
||||
}
|
||||
)
|
||||
const endCursor = editor.getCursor('end')
|
||||
const nextChar = editor.getRange({
|
||||
line: endCursor.line,
|
||||
ch: endCursor.ch
|
||||
}, {
|
||||
line: endCursor.line,
|
||||
ch: endCursor.ch + 1
|
||||
})
|
||||
const nextChar = editor.getRange(
|
||||
{
|
||||
line: endCursor.line,
|
||||
ch: endCursor.ch
|
||||
},
|
||||
{
|
||||
line: endCursor.line,
|
||||
ch: endCursor.ch + 1
|
||||
}
|
||||
)
|
||||
return prevChar === '](' && nextChar === ')'
|
||||
}
|
||||
|
||||
@@ -947,7 +1006,7 @@ export default class CodeEditor extends React.Component {
|
||||
return true
|
||||
}
|
||||
|
||||
let line = line = cursor.line - 1
|
||||
let line = (line = cursor.line - 1)
|
||||
while (line >= 0) {
|
||||
token = editor.getTokenAt({
|
||||
ch: 3,
|
||||
@@ -979,7 +1038,11 @@ export default class CodeEditor extends React.Component {
|
||||
|
||||
if (isInFencedCodeBlock(editor)) {
|
||||
this.handlePasteText(editor, pastedTxt)
|
||||
} else if (fetchUrlTitle && isMarkdownTitleURL(pastedTxt) && !isInLinkTag(editor)) {
|
||||
} else if (
|
||||
fetchUrlTitle &&
|
||||
isMarkdownTitleURL(pastedTxt) &&
|
||||
!isInLinkTag(editor)
|
||||
) {
|
||||
this.handlePasteUrl(editor, pastedTxt)
|
||||
} else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) {
|
||||
this.handlePasteUrl(editor, pastedTxt)
|
||||
@@ -1015,13 +1078,13 @@ export default class CodeEditor extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleScroll (e) {
|
||||
handleScroll(e) {
|
||||
if (this.props.onScroll) {
|
||||
this.props.onScroll(e)
|
||||
}
|
||||
}
|
||||
|
||||
handlePasteUrl (editor, pastedTxt) {
|
||||
handlePasteUrl(editor, pastedTxt) {
|
||||
let taggedUrl = `<${pastedTxt}>`
|
||||
let urlToFetch = pastedTxt
|
||||
let titleMark = ''
|
||||
@@ -1071,16 +1134,16 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handlePasteHtml (editor, pastedHtml) {
|
||||
handlePasteHtml(editor, pastedHtml) {
|
||||
const markdown = this.turndownService.turndown(pastedHtml)
|
||||
editor.replaceSelection(markdown)
|
||||
}
|
||||
|
||||
handlePasteText (editor, pastedTxt) {
|
||||
handlePasteText(editor, pastedTxt) {
|
||||
editor.replaceSelection(pastedTxt)
|
||||
}
|
||||
|
||||
mapNormalResponse (response, pastedTxt) {
|
||||
mapNormalResponse(response, pastedTxt) {
|
||||
return this.decodeResponse(response).then(body => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
@@ -1088,10 +1151,12 @@ export default class CodeEditor extends React.Component {
|
||||
body,
|
||||
'text/html'
|
||||
)
|
||||
const escapePipe = (str) => {
|
||||
const escapePipe = str => {
|
||||
return str.replace('|', '\\|')
|
||||
}
|
||||
const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})`
|
||||
const linkWithTitle = `[${escapePipe(
|
||||
parsedBody.title
|
||||
)}](${pastedTxt})`
|
||||
resolve(linkWithTitle)
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
@@ -1100,7 +1165,7 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
initialHighlighting () {
|
||||
initialHighlighting() {
|
||||
if (this.editor.options.linesHighlighted == null) {
|
||||
return
|
||||
}
|
||||
@@ -1114,16 +1179,20 @@ export default class CodeEditor extends React.Component {
|
||||
// make sure that we skip the invalid lines althrough this case should not be happened.
|
||||
continue
|
||||
}
|
||||
this.editor.addLineClass(lineNumber, 'text', 'CodeMirror-activeline-background')
|
||||
this.editor.addLineClass(
|
||||
lineNumber,
|
||||
'text',
|
||||
'CodeMirror-activeline-background'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
restartHighlighting () {
|
||||
restartHighlighting() {
|
||||
this.editor.options.linesHighlighted = this.props.linesHighlighted
|
||||
this.initialHighlighting()
|
||||
}
|
||||
|
||||
mapImageResponse (response, pastedTxt) {
|
||||
mapImageResponse(response, pastedTxt) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const url = response.url
|
||||
@@ -1136,7 +1205,7 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
decodeResponse (response) {
|
||||
decodeResponse(response) {
|
||||
const headers = response.headers
|
||||
const _charset = headers.has('content-type')
|
||||
? this.extractContentTypeCharset(headers.get('content-type'))
|
||||
@@ -1144,10 +1213,10 @@ export default class CodeEditor extends React.Component {
|
||||
return response.arrayBuffer().then(buff => {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const charset = _charset !== undefined &&
|
||||
iconv.encodingExists(_charset)
|
||||
? _charset
|
||||
: 'utf-8'
|
||||
const charset =
|
||||
_charset !== undefined && iconv.encodingExists(_charset)
|
||||
? _charset
|
||||
: 'utf-8'
|
||||
resolve(iconv.decode(Buffer.from(buff), charset).toString())
|
||||
} catch (e) {
|
||||
reject(e)
|
||||
@@ -1156,50 +1225,49 @@ export default class CodeEditor extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
extractContentTypeCharset (contentType) {
|
||||
extractContentTypeCharset(contentType) {
|
||||
return contentType
|
||||
.split(';')
|
||||
.filter(str => {
|
||||
return str.trim().toLowerCase().startsWith('charset')
|
||||
return str
|
||||
.trim()
|
||||
.toLowerCase()
|
||||
.startsWith('charset')
|
||||
})
|
||||
.map(str => {
|
||||
return str.replace(/['"]/g, '').split('=')[1]
|
||||
})[0]
|
||||
}
|
||||
|
||||
render () {
|
||||
const {
|
||||
className,
|
||||
fontSize
|
||||
} = this.props
|
||||
render() {
|
||||
const { className, fontSize } = this.props
|
||||
const fontFamily = normalizeEditorFontFamily(this.props.fontFamily)
|
||||
const width = this.props.width
|
||||
return (<
|
||||
div className={
|
||||
className == null ? 'CodeEditor' : `CodeEditor ${className}`
|
||||
}
|
||||
ref='root'
|
||||
tabIndex='-1'
|
||||
style={{
|
||||
fontFamily,
|
||||
fontSize: fontSize,
|
||||
width: width
|
||||
}}
|
||||
onDrop={
|
||||
e => this.handleDropImage(e)
|
||||
}
|
||||
return (
|
||||
<div
|
||||
className={className == null ? 'CodeEditor' : `CodeEditor ${className}`}
|
||||
ref='root'
|
||||
tabIndex='-1'
|
||||
style={{
|
||||
fontFamily,
|
||||
fontSize: fontSize,
|
||||
width: width
|
||||
}}
|
||||
onDrop={e => this.handleDropImage(e)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
createSpellCheckPanel () {
|
||||
createSpellCheckPanel() {
|
||||
const panel = document.createElement('div')
|
||||
panel.className = 'panel bottom'
|
||||
panel.id = 'editor-bottom-panel'
|
||||
const dropdown = document.createElement('select')
|
||||
dropdown.title = 'Spellcheck'
|
||||
dropdown.className = styles['spellcheck-select']
|
||||
dropdown.addEventListener('change', (e) => spellcheck.setLanguage(this.editor, dropdown.value))
|
||||
dropdown.addEventListener('change', e =>
|
||||
spellcheck.setLanguage(this.editor, dropdown.value)
|
||||
)
|
||||
const options = spellcheck.getAvailableDictionaries()
|
||||
for (const op of options) {
|
||||
const option = document.createElement('option')
|
||||
|
||||
Reference in New Issue
Block a user