diff --git a/.eslintignore b/.eslintignore index 41d9111b444..53f0c3eb9ca 100644 --- a/.eslintignore +++ b/.eslintignore @@ -104,12 +104,6 @@ ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js ElectronClient/gui/NoteEditor/commands/showLocalSearch.js ElectronClient/gui/NoteEditor/commands/showRevisions.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js diff --git a/.gitignore b/.gitignore index fbfd2af92c9..ed7e1dc4918 100644 --- a/.gitignore +++ b/.gitignore @@ -97,12 +97,6 @@ ElectronClient/gui/NoteEditor/commands/focusElementNoteBody.js ElectronClient/gui/NoteEditor/commands/focusElementNoteTitle.js ElectronClient/gui/NoteEditor/commands/showLocalSearch.js ElectronClient/gui/NoteEditor/commands/showRevisions.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.js -ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/styles/index.js diff --git a/ElectronClient/app.js b/ElectronClient/app.js index fcb61da843a..a8996e6b9c9 100644 --- a/ElectronClient/app.js +++ b/ElectronClient/app.js @@ -1042,11 +1042,9 @@ class Application extends BaseApplication { // https://github.com/laurent22/joplin/issues/155 const css = `.CodeMirror * { font-family: ${fontFamilies.join(', ')} !important; }`; - const ace_css = `.ace_editor * { font-family: ${fontFamilies.join(', ')} !important; }`; const styleTag = document.createElement('style'); styleTag.type = 'text/css'; styleTag.appendChild(document.createTextNode(css)); - styleTag.appendChild(document.createTextNode(ace_css)); document.head.appendChild(styleTag); } diff --git a/ElectronClient/gui/MainScreen/MainScreen.jsx b/ElectronClient/gui/MainScreen/MainScreen.jsx index 860a1de2f47..e1f9fa4d876 100644 --- a/ElectronClient/gui/MainScreen/MainScreen.jsx +++ b/ElectronClient/gui/MainScreen/MainScreen.jsx @@ -434,7 +434,7 @@ class MainScreenComponent extends React.Component { // A bit of a hack, but for now don't allow changing code view // while a note is being saved as it will cause a problem with // TinyMCE because it won't have time to send its content before - // being switch to Ace Editor. + // being switch to the Code Editor. if (this.props.hasNotesBeingSaved) return; Setting.toggle('editor.codeView'); }, @@ -468,8 +468,7 @@ class MainScreenComponent extends React.Component { const noteContentPropertiesDialogOptions = this.state.noteContentPropertiesDialogOptions; const shareNoteDialogOptions = this.state.shareNoteDialogOptions; - const codeEditor = Setting.value('editor.betaCodeMirror') ? 'CodeMirror' : 'AceEditor'; - const bodyEditor = this.props.settingEditorCodeView ? codeEditor : 'TinyMCE'; + const bodyEditor = this.props.settingEditorCodeView ? 'CodeMirror' : 'TinyMCE'; return (
diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.tsx b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.tsx deleted file mode 100644 index a4696e235cd..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/AceEditor.tsx +++ /dev/null @@ -1,654 +0,0 @@ -import * as React from 'react'; -import { useState, useEffect, useRef, forwardRef, useCallback, useImperativeHandle, useMemo } from 'react'; - -// eslint-disable-next-line no-unused-vars -import { EditorCommand, NoteBodyEditorProps } from '../../utils/types'; -import { commandAttachFileToBody, handlePasteEvent } from '../../utils/resourceHandling'; -import { ScrollOptions, ScrollOptionTypes } from '../../utils/types'; -import { textOffsetToCursorPosition, useScrollHandler, useRootWidth, usePrevious, lineLeftSpaces, selectionRange, selectionRangeCurrentLine, selectionRangePreviousLine, currentTextOffset, textOffsetSelection, selectedText } from './utils'; -import useListIdent from './utils/useListIdent'; -import Toolbar from './Toolbar'; -import styles_ from './styles'; -import { RenderedBody, defaultRenderedBody } from './utils/types'; - -const AceEditorReact = require('react-ace').default; -const { bridge } = require('electron').remote.require('./bridge'); -const Note = require('lib/models/Note.js'); -const { clipboard } = require('electron'); -const Setting = require('lib/models/Setting.js'); -const NoteTextViewer = require('../../../NoteTextViewer.min'); -const shared = require('lib/components/shared/note-screen-shared.js'); -const Menu = bridge().Menu; -const MenuItem = bridge().MenuItem; -const markdownUtils = require('lib/markdownUtils'); -const { _ } = require('lib/locale'); -const { reg } = require('lib/registry.js'); -const dialogs = require('../../../dialogs'); - -require('brace/mode/markdown'); -// https://ace.c9.io/build/kitchen-sink.html -// https://highlightjs.org/static/demo/ -require('brace/theme/chrome'); -require('brace/theme/solarized_light'); -require('brace/theme/solarized_dark'); -require('brace/theme/twilight'); -require('brace/theme/dracula'); -require('brace/theme/chaos'); -require('brace/theme/tomorrow'); -require('brace/keybinding/vim'); -require('brace/keybinding/emacs'); -require('brace/theme/terminal'); - -// TODO: Could not get below code to work - -// @ts-ignore Ace global variable -// const aceGlobal = (ace as any); - -// class CustomHighlightRules extends aceGlobal.acequire( -// 'ace/mode/markdown_highlight_rules' -// ).MarkdownHighlightRules { -// constructor() { -// super(); -// if (Setting.value('markdown.plugin.mark')) { -// this.$rules.start.push({ -// // This is actually a highlight `mark`, but Ace has no token name for -// // this so we made up our own. Reference for common tokens here: -// // https://github.com/ajaxorg/ace/wiki/Creating-or-Extending-an-Edit-Mode#common-tokens -// token: 'highlight_mark', -// regex: '==[^ ](?:.*?[^ ])?==', -// }); -// } -// } -// } - -// /* eslint-disable-next-line no-undef */ -// class CustomMdMode extends aceGlobal.acequire('ace/mode/markdown').Mode { -// constructor() { -// super(); -// this.HighlightRules = CustomHighlightRules; -// } -// } - -function markupRenderOptions(override: any = null) { - return { ...override }; -} - -function AceEditor(props: NoteBodyEditorProps, ref: any) { - const styles = styles_(props); - - const [renderedBody, setRenderedBody] = useState(defaultRenderedBody()); // Viewer content - const [editor, setEditor] = useState(null); - const [webviewReady, setWebviewReady] = useState(false); - - const previousRenderedBody = usePrevious(renderedBody); - const previousSearchMarkers = usePrevious(props.searchMarkers); - const previousContentKey = usePrevious(props.contentKey); - - const editorRef = useRef(null); - editorRef.current = editor; - const rootRef = useRef(null); - const webviewRef = useRef(null); - const props_onChangeRef = useRef(null); - props_onChangeRef.current = props.onChange; - const contentKeyHasChangedRef = useRef(false); - contentKeyHasChangedRef.current = previousContentKey !== props.contentKey; - - const rootWidth = useRootWidth({ rootRef }); - - const { resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll } = useScrollHandler(editor, webviewRef, props.onScroll); - - useListIdent({ editor }); - - const aceEditor_change = useCallback((newBody: string) => { - // Throw an error early to know what part of the code set the body to the - // wrong value. Otherwise it will trigger an error somewhere deep in React-Ace - // which will be hard to debug. - if (typeof newBody !== 'string') throw new Error('Body is not a string'); - props_onChangeRef.current({ changeId: null, content: newBody }); - }, []); - - const wrapSelectionWithStrings = useCallback((string1: string, string2 = '', defaultText = '', replacementText: string = null, byLine = false) => { - if (!editor) return; - - const selection = textOffsetSelection(selectionRange(editor), props.content); - - let newBody = props.content; - - if (selection && selection.start !== selection.end) { - const selectedLines = replacementText !== null ? replacementText : props.content.substr(selection.start, selection.end - selection.start); - const selectedStrings = byLine ? selectedLines.split(/\r?\n/) : [selectedLines]; - - newBody = props.content.substr(0, selection.start); - - let startCursorPos, endCursorPos; - - for (let i = 0; i < selectedStrings.length; i++) { - if (byLine == false) { - const start = selectedStrings[i].search(/[^\s]/); - const end = selectedStrings[i].search(/[^\s](?=[\s]*$)/); - newBody += selectedStrings[i].substr(0, start) + string1 + selectedStrings[i].substr(start, end - start + 1) + string2 + selectedStrings[i].substr(end + 1); - // Getting position for correcting offset in highlighted text when surrounded by white spaces - startCursorPos = textOffsetToCursorPosition(selection.start + start, newBody); - endCursorPos = textOffsetToCursorPosition(selection.start + end + 1, newBody); - - } else { newBody += string1 + selectedStrings[i] + string2; } - - } - - newBody += props.content.substr(selection.end); - - const r = selectionRange(editor); - - // Because some insertion strings will have newlines, we'll need to account for them - const str1Split = string1.split(/\r?\n/); - - // Add the number of newlines to the row - // and add the length of the final line to the column (for strings with no newlines this is the string length) - - let newRange: any = {}; - if (!byLine) { - // Correcting offset in Highlighted text when surrounded by white spaces - newRange = { - start: { - row: startCursorPos.row, - column: startCursorPos.column + string1.length, - }, - end: { - row: endCursorPos.row, - column: endCursorPos.column + string1.length, - }, - }; - } else { - newRange = { - start: { - row: r.start.row + str1Split.length - 1, - column: r.start.column + str1Split[str1Split.length - 1].length, - }, - end: { - row: r.end.row + str1Split.length - 1, - column: r.end.column + str1Split[str1Split.length - 1].length, - }, - }; - } - - if (replacementText !== null) { - const diff = replacementText.length - (selection.end - selection.start); - newRange.end.column += diff; - } - - setTimeout(() => { - const range = selectionRange(editor); - range.setStart(newRange.start.row, newRange.start.column); - range.setEnd(newRange.end.row, newRange.end.column); - editor.getSession().getSelection().setSelectionRange(range, false); - editor.focus(); - }, 10); - } else { - const middleText = replacementText !== null ? replacementText : defaultText; - const textOffset = currentTextOffset(editor, props.content); - const s1 = props.content.substr(0, textOffset); - const s2 = props.content.substr(textOffset); - newBody = s1 + string1 + middleText + string2 + s2; - - const p = textOffsetToCursorPosition(textOffset + string1.length, newBody); - const newRange = { - start: { row: p.row, column: p.column }, - end: { row: p.row, column: p.column + middleText.length }, - }; - - // BUG!! If replacementText contains newline characters, the logic - // to select the new text will not work. - - setTimeout(() => { - if (middleText && newRange) { - const range = selectionRange(editor); - range.setStart(newRange.start.row, newRange.start.column); - range.setEnd(newRange.end.row, newRange.end.column); - editor.getSession().getSelection().setSelectionRange(range, false); - } else { - for (let i = 0; i < string1.length; i++) { - editor.getSession().getSelection().moveCursorRight(); - } - } - editor.focus(); - }, 10); - } - - aceEditor_change(newBody); - }, [editor, props.content, aceEditor_change]); - - const addListItem = useCallback((string1, string2 = '', defaultText = '', byLine = false) => { - let newLine = '\n'; - const range = selectionRange(editor); - if (!range || (range.start.row === range.end.row && !selectionRangeCurrentLine(range, props.content))) { - newLine = ''; - } - wrapSelectionWithStrings(newLine + string1, string2, defaultText, null, byLine); - }, [wrapSelectionWithStrings, props.content, editor]); - - useImperativeHandle(ref, () => { - return { - content: () => props.content, - resetScroll: () => { - resetScroll(); - }, - scrollTo: (options:ScrollOptions) => { - if (options.type === ScrollOptionTypes.Hash) { - if (!webviewRef.current) return; - webviewRef.current.wrappedInstance.send('scrollToHash', options.value as string); - } else if (options.type === ScrollOptionTypes.Percent) { - const p = options.value as number; - setEditorPercentScroll(p); - setViewerPercentScroll(p); - } else { - throw new Error(`Unsupported scroll options: ${options.type}`); - } - }, - supportsCommand: (/* name:string*/) => { - // TODO: not implemented, currently only used for "search" command - // which is not directly supported by Ace Editor. - return false; - }, - execCommand: async (cmd: EditorCommand) => { - if (!editor) return false; - - reg.logger().debug('AceEditor: execCommand', cmd); - - let commandProcessed = true; - - if (cmd.name === 'dropItems') { - if (cmd.value.type === 'notes') { - wrapSelectionWithStrings('', '', '', cmd.value.markdownTags.join('\n')); - } else if (cmd.value.type === 'files') { - const newBody = await commandAttachFileToBody(props.content, cmd.value.paths, { createFileURL: !!cmd.value.createFileURL }); - if (newBody) aceEditor_change(newBody); - } else { - reg.logger().warn('AceEditor: unsupported drop item: ', cmd); - } - } else if (cmd.name === 'focus') { - editor.focus(); - } else { - commandProcessed = false; - } - - if (!commandProcessed) { - const commands: any = { - textBold: () => wrapSelectionWithStrings('**', '**', _('strong text')), - textItalic: () => wrapSelectionWithStrings('*', '*', _('emphasised text')), - textLink: async () => { - const url = await dialogs.prompt(_('Insert Hyperlink')); - if (url) wrapSelectionWithStrings('[', `](${url})`); - }, - textCode: () => { - const selection = textOffsetSelection(selectionRange(editor), props.content); - const string = props.content.substr(selection.start, selection.end - selection.start); - - // Look for newlines - const match = string.match(/\r?\n/); - - if (match && match.length > 0) { - if (string.startsWith('```') && string.endsWith('```')) { - wrapSelectionWithStrings('', '', '', string.substr(4, selection.end - selection.start - 8)); - } else { - wrapSelectionWithStrings(`\`\`\`${match[0]}`, `${match[0]}\`\`\``); - } - } else { - wrapSelectionWithStrings('`', '`', ''); - } - }, - insertText: (value: any) => wrapSelectionWithStrings(value), - attachFile: async () => { - const selection = textOffsetSelection(selectionRange(editor), props.content); - const newBody = await commandAttachFileToBody(props.content, null, { position: selection ? selection.start : 0 }); - if (newBody) aceEditor_change(newBody); - }, - textNumberedList: () => { - const selection = selectionRange(editor); - let bulletNumber = markdownUtils.olLineNumber(selectionRangeCurrentLine(selection, props.content)); - if (!bulletNumber) bulletNumber = markdownUtils.olLineNumber(selectionRangePreviousLine(selection, props.content)); - if (!bulletNumber) bulletNumber = 0; - addListItem(`${bulletNumber + 1}. `, '', _('List item'), true); - }, - textBulletedList: () => addListItem('- ', '', _('List item'), true), - textCheckbox: () => addListItem('- [ ] ', '', _('List item'), true), - textHeading: () => addListItem('## ','','', true), - textHorizontalRule: () => addListItem('* * *'), - }; - - if (commands[cmd.name]) { - commands[cmd.name](cmd.value); - } else { - reg.logger().warn('AceEditor: unsupported Joplin command: ', cmd); - return false; - } - } - - return true; - }, - }; - }, [editor, props.content, addListItem, wrapSelectionWithStrings, selectionRangeCurrentLine, aceEditor_change, setEditorPercentScroll, setViewerPercentScroll, resetScroll, renderedBody]); - - const onEditorPaste = useCallback(async (event: any = null) => { - const resourceMds = await handlePasteEvent(event); - if (!resourceMds.length) return; - wrapSelectionWithStrings('', '', resourceMds.join('\n')); - }, [wrapSelectionWithStrings]); - - const editorCutText = useCallback(() => { - const text = selectedText(selectionRange(editor), props.content); - if (!text) return; - - clipboard.writeText(text); - - const s = textOffsetSelection(selectionRange(editor), props.content); - if (!s || s.start === s.end) return; - - const s1 = props.content.substr(0, s.start); - const s2 = props.content.substr(s.end); - - aceEditor_change(s1 + s2); - - setTimeout(() => { - const range = selectionRange(editor); - range.setStart(range.start.row, range.start.column); - range.setEnd(range.start.row, range.start.column); - editor.getSession().getSelection().setSelectionRange(range, false); - editor.focus(); - }, 10); - }, [props.content, editor, aceEditor_change]); - - function clipboardText() { - return clipboard.readText() ? clipboard.readText() : clipboard.readHTML(); - } - - const editorCopyText = useCallback(() => { - const text = selectedText(selectionRange(editor), props.content); - clipboard.writeText(text); - }, [props.content, editor]); - - const editorPasteText = useCallback(() => { - wrapSelectionWithStrings(clipboardText(), '', '', ''); - }, [wrapSelectionWithStrings]); - - const onEditorContextMenu = useCallback(() => { - const menu = new Menu(); - - const hasSelectedText = !!selectedText(selectionRange(editor), props.content); - const currentClipboardText = clipboardText(); - - menu.append( - new MenuItem({ - label: _('Cut'), - enabled: hasSelectedText, - click: async () => { - editorCutText(); - }, - }) - ); - - menu.append( - new MenuItem({ - label: _('Copy'), - enabled: hasSelectedText, - click: async () => { - editorCopyText(); - }, - }) - ); - - menu.append( - new MenuItem({ - label: _('Paste'), - enabled: true, - click: async () => { - if (currentClipboardText) { - editorPasteText(); - } else { - // To handle pasting images - onEditorPaste(); - } - }, - }) - ); - - menu.popup(bridge().window()); - }, [props.content, editorCutText, editorPasteText, editorCopyText, onEditorPaste, editor]); - - function aceEditor_load(editor: any) { - setEditor(editor); - } - - useEffect(() => { - if (!editor) return () => {}; - - const cancelledKeys = []; - const letters = ['F', 'T', 'P', 'Q', 'L', ',', 'G', 'K']; - for (let i = 0; i < letters.length; i++) { - const l = letters[i]; - cancelledKeys.push(`Ctrl+${l}`); - cancelledKeys.push(`Command+${l}`); - } - cancelledKeys.push('Alt+E'); - cancelledKeys.push('Command+Shift+L'); - cancelledKeys.push('Ctrl+Shift+L'); - - for (let i = 0; i < cancelledKeys.length; i++) { - const k = cancelledKeys[i]; - editor.commands.bindKey(k, () => { - // HACK: Ace doesn't seem to provide a way to override its shortcuts, but throwing - // an exception from this undocumented function seems to cancel it without any - // side effect. - // https://stackoverflow.com/questions/36075846 - throw new Error(`HACK: Overriding Ace Editor shortcut: ${k}`); - }); - } - - document.querySelector('#note-editor').addEventListener('paste', onEditorPaste, true); - document.querySelector('#note-editor').addEventListener('contextmenu', onEditorContextMenu); - - // Disable Markdown auto-completion (eg. auto-adding a dash after a line with a dash. - // https://github.com/ajaxorg/ace/issues/2754 - // @ts-ignore: Keep the function signature as-is despite unusued arguments - editor.getSession().getMode().getNextLineIndent = function(state: any, line: string) { - const leftSpaces = lineLeftSpaces(line); - const lineNoLeftSpaces = line.trimLeft(); - - if (lineNoLeftSpaces.indexOf('- [ ] ') === 0 || lineNoLeftSpaces.indexOf('- [x] ') === 0 || lineNoLeftSpaces.indexOf('- [X] ') === 0) return `${leftSpaces}- [ ] `; - if (lineNoLeftSpaces.indexOf('- ') === 0) return `${leftSpaces}- `; - if (lineNoLeftSpaces.indexOf('* ') === 0 && line.trim() !== '* * *') return `${leftSpaces}* `; - - const bulletNumber = markdownUtils.olLineNumber(lineNoLeftSpaces); - if (bulletNumber) return `${leftSpaces + (bulletNumber + 1)}. `; - - return this.$getIndent(line); - }; - - return () => { - document.querySelector('#note-editor').removeEventListener('paste', onEditorPaste, true); - document.querySelector('#note-editor').removeEventListener('contextmenu', onEditorContextMenu); - }; - }, [editor, onEditorPaste, onEditorContextMenu]); - - const webview_domReady = useCallback(() => { - setWebviewReady(true); - }, []); - - const webview_ipcMessage = useCallback((event: any) => { - const msg = event.channel ? event.channel : ''; - const args = event.args; - const arg0 = args && args.length >= 1 ? args[0] : null; - - if (msg.indexOf('checkboxclick:') === 0) { - const newBody = shared.toggleCheckbox(msg, props.content); - aceEditor_change(newBody); - } else if (msg === 'percentScroll') { - setEditorPercentScroll(arg0); - } else { - props.onMessage(event); - } - }, [props.onMessage, props.content, aceEditor_change, setEditorPercentScroll]); - - useEffect(() => { - let cancelled = false; - - const interval = contentKeyHasChangedRef.current ? 0 : 500; - - const timeoutId = setTimeout(async () => { - let bodyToRender = props.content; - - if (!bodyToRender.trim() && props.visiblePanes.indexOf('viewer') >= 0 && props.visiblePanes.indexOf('editor') < 0) { - // Fixes https://github.com/laurent22/joplin/issues/217 - bodyToRender = `${_('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout'))}`; - } - - const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({ resourceInfos: props.resourceInfos })); - if (cancelled) return; - setRenderedBody(result); - }, interval); - - return () => { - cancelled = true; - clearTimeout(timeoutId); - }; - }, [props.content, props.contentMarkupLanguage, props.visiblePanes, props.resourceInfos, props.markupToHtml]); - - useEffect(() => { - if (!editor) return; - - if (contentKeyHasChangedRef.current) { - // editor.getSession().setMode(new CustomMdMode()); - const undoManager = editor.getSession().getUndoManager(); - undoManager.reset(); - editor.getSession().setUndoManager(undoManager); - } - }, [props.content, editor]); - - useEffect(() => { - if (!webviewReady) return; - - const options: any = { - pluginAssets: renderedBody.pluginAssets, - downloadResources: Setting.value('sync.resourceDownloadMode'), - }; - webviewRef.current.wrappedInstance.send('setHtml', renderedBody.html, options); - }, [renderedBody, webviewReady]); - - useEffect(() => { - if (props.searchMarkers !== previousSearchMarkers || renderedBody !== previousRenderedBody) { - webviewRef.current.wrappedInstance.send('setMarkers', props.searchMarkers.keywords, props.searchMarkers.options); - } - }, [props.searchMarkers, renderedBody]); - - const cellEditorStyle = useMemo(() => { - const output = { ...styles.cellEditor }; - if (!props.visiblePanes.includes('editor')) { - // Note: Ideally we'd set the display to "none" to take the editor out - // of the DOM but if we do that, certain things won't work, in particular - // things related to scroll, which are based on the editor. - - // Note that the below hack doesn't work and causes a bug in this case: - // - Put Ace Editor in viewer-only mode - // - Go to WYSIWYG editor - // - Create new to-do - set title only - // - Switch to Code View - // - Switch layout and type something - // => Text editor layout is broken and text is off-screen - - output.display = 'none'; // Seems to work fine since the refactoring - } - - return output; - }, [styles.cellEditor, props.visiblePanes]); - - const cellViewerStyle = useMemo(() => { - const output = { ...styles.cellViewer }; - if (!props.visiblePanes.includes('viewer')) { - // Note: setting webview.display to "none" is currently not supported due - // to this bug: https://github.com/electron/electron/issues/8277 - // So instead setting the width 0. - output.width = 1; - output.maxWidth = 1; - } else if (!props.visiblePanes.includes('editor')) { - output.borderLeftStyle = 'none'; - } - return output; - }, [styles.cellViewer, props.visiblePanes]); - - const editorReadOnly = props.visiblePanes.indexOf('editor') < 0; - - function renderEditor() { - // Need to hard-code the editor width, otherwise various bugs pops up - let width = 0; - if (props.visiblePanes.includes('editor')) { - width = !props.visiblePanes.includes('viewer') ? rootWidth : Math.floor(rootWidth / 2); - } - - return ( -
- -
- ); - } - - function renderViewer() { - return ( -
- -
- ); - } - - return ( -
-
- - {props.noteToolbar} -
-
- {renderEditor()} - {renderViewer()} -
-
- ); -} - -export default forwardRef(AceEditor); - diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.tsx b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.tsx deleted file mode 100644 index 79d113d1fcb..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/Toolbar.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import * as React from 'react'; -import CommandService from '../../../../lib/services/CommandService'; - -const ToolbarBase = require('../../../Toolbar.min.js'); -const { buildStyle, themeStyle } = require('lib/theme'); - -interface ToolbarProps { - theme: number, - dispatch: Function, - disabled: boolean, -} - -function styles_(props:ToolbarProps) { - return buildStyle('AceEditorToolbar', props.theme, (/* theme:any*/) => { - const theme = themeStyle(props.theme); - return { - root: { - flex: 1, - marginBottom: 0, - borderTop: `1px solid ${theme.dividerColor}`, - }, - }; - }); -} - -export default function Toolbar(props:ToolbarProps) { - const styles = styles_(props); - - const cmdService = CommandService.instance(); - - const toolbarItems = [ - cmdService.commandToToolbarButton('textBold'), - cmdService.commandToToolbarButton('textItalic'), - { type: 'separator' }, - cmdService.commandToToolbarButton('textLink'), - cmdService.commandToToolbarButton('textCode'), - cmdService.commandToToolbarButton('attachFile'), - { type: 'separator' }, - cmdService.commandToToolbarButton('textNumberedList'), - cmdService.commandToToolbarButton('textBulletedList'), - cmdService.commandToToolbarButton('textCheckbox'), - cmdService.commandToToolbarButton('textHeading'), - cmdService.commandToToolbarButton('textHorizontalRule'), - cmdService.commandToToolbarButton('insertDateTime'), - ]; - - return ; -} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.ts b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.ts deleted file mode 100644 index 1f2e7ee8b71..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/styles/index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { NoteBodyEditorProps } from '../../../utils/types'; -const { buildStyle } = require('lib/theme'); - -export default function styles(props: NoteBodyEditorProps) { - return buildStyle('AceEditor', props.theme, (theme: any) => { - return { - root: { - position: 'relative', - display: 'flex', - flexDirection: 'column', - ...props.style, - }, - rowToolbar: { - position: 'relative', - display: 'flex', - flexDirection: 'row', - }, - rowEditorViewer: { - position: 'relative', - display: 'flex', - flexDirection: 'row', - flex: 1, - paddingTop: 10, - }, - cellEditor: { - position: 'relative', - display: 'flex', - flex: 1, - }, - cellViewer: { - position: 'relative', - display: 'flex', - flex: 1, - borderLeftWidth: 1, - borderLeftColor: theme.dividerColor, - borderLeftStyle: 'solid', - }, - viewer: { - display: 'flex', - overflow: 'hidden', - verticalAlign: 'top', - boxSizing: 'border-box', - width: '100%', - }, - editor: { - display: 'flex', - width: 'auto', - height: 'auto', - flex: 1, - overflowY: 'hidden', - paddingTop: 0, - lineHeight: `${theme.textAreaLineHeight}px`, - fontSize: `${theme.editorFontSize}px`, - color: theme.color, - backgroundColor: theme.backgroundColor, - aceEditorTheme: theme.aceEditorTheme, // Defined in theme.js - }, - }; - }); -} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.ts b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.ts deleted file mode 100644 index 64844e97ce2..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/index.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { useState, useEffect, useCallback, useRef } from 'react'; - -export function cursorPositionToTextOffset(cursorPos: any, body: string) { - if (!body) return 0; - - const noteLines = body.split('\n'); - - let pos = 0; - for (let i = 0; i < noteLines.length; i++) { - if (i > 0) pos++; // Need to add the newline that's been removed in the split() call above - - if (i === cursorPos.row) { - pos += cursorPos.column; - break; - } else { - pos += noteLines[i].length; - } - } - - return pos; -} - -export function currentTextOffset(editor: any, body: string) { - return cursorPositionToTextOffset(editor.getCursorPosition(), body); -} - -export function rangeToTextOffsets(range: any, body: string) { - return { - start: cursorPositionToTextOffset(range.start, body), - end: cursorPositionToTextOffset(range.end, body), - }; -} - -export function textOffsetSelection(selectionRange: any, body: string) { - return selectionRange && body ? rangeToTextOffsets(selectionRange, body) : null; -} - -export function selectedText(selectionRange: any, body: string) { - const selection = textOffsetSelection(selectionRange, body); - if (!selection || selection.start === selection.end) return ''; - - return body.substr(selection.start, selection.end - selection.start); -} - -export function selectionRange(editor:any) { - const ranges = editor.getSelection().getAllRanges(); - return ranges && ranges.length ? ranges[0] : null; -} - -export function textOffsetToCursorPosition(offset: number, body: string) { - const lines = body.split('\n'); - let row = 0; - let currentOffset = 0; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (currentOffset + line.length >= offset) { - return { - row: row, - column: offset - currentOffset, - }; - } - - row++; - currentOffset += line.length + 1; - } - - return null; -} - -function lineAtRow(body: string, row: number) { - if (!body) return ''; - const lines = body.split('\n'); - if (row < 0 || row >= lines.length) return ''; - return lines[row]; -} - -export function selectionRangeCurrentLine(selectionRange: any, body: string) { - if (!selectionRange) return ''; - return lineAtRow(body, selectionRange.start.row); -} - -export function selectionRangePreviousLine(selectionRange: any, body: string) { - if (!selectionRange) return ''; - return lineAtRow(body, selectionRange.start.row - 1); -} - -export function lineLeftSpaces(line: string) { - let output = ''; - for (let i = 0; i < line.length; i++) { - if ([' ', '\t'].indexOf(line[i]) >= 0) { - output += line[i]; - } else { - break; - } - } - return output; -} - -export function usePrevious(value: any): any { - const ref = useRef(); - useEffect(() => { - ref.current = value; - }); - return ref.current; -} - -export function useScrollHandler(editor: any, webviewRef: any, onScroll: Function) { - const editorMaxScrollTop_ = useRef(0); - const restoreScrollTop_ = useRef(null); - const ignoreNextEditorScrollEvent_ = useRef(false); - const scrollTimeoutId_ = useRef(null); - - // TODO: Below is not needed anymore???? - // - // this.editorMaxScrollTop_ = 0; - // // HACK: To go around a bug in Ace editor, we first set the scroll position to 1 - // // and then (in the renderer callback) to the value we actually need. The first - // // operation helps clear the scroll position cache. See: - // // - // this.editorSetScrollTop(1); - // this.restoreScrollTop_ = 0; - - const editorSetScrollTop = useCallback((v) => { - if (!editor) return; - editor.getSession().setScrollTop(v); - }, [editor]); - - // Complicated but reliable method to get editor content height - // https://github.com/ajaxorg/ace/issues/2046 - const onAfterEditorRender = useCallback(() => { - const r = editor.renderer; - editorMaxScrollTop_.current = Math.max(0, r.layerConfig.maxHeight - r.$size.scrollerHeight); - - if (restoreScrollTop_.current !== null) { - editorSetScrollTop(restoreScrollTop_.current); - restoreScrollTop_.current = null; - } - }, [editor, editorSetScrollTop]); - - const scheduleOnScroll = useCallback((event: any) => { - if (scrollTimeoutId_.current) { - clearTimeout(scrollTimeoutId_.current); - scrollTimeoutId_.current = null; - } - - scrollTimeoutId_.current = setTimeout(() => { - scrollTimeoutId_.current = null; - onScroll(event); - }, 10); - }, [onScroll]); - - const setEditorPercentScroll = useCallback((p: number) => { - ignoreNextEditorScrollEvent_.current = true; - editorSetScrollTop(p * editorMaxScrollTop_.current); - scheduleOnScroll({ percent: p }); - }, [editorSetScrollTop, scheduleOnScroll]); - - const setViewerPercentScroll = useCallback((p: number) => { - if (webviewRef.current) { - webviewRef.current.wrappedInstance.send('setPercentScroll', p); - scheduleOnScroll({ percent: p }); - } - }, [scheduleOnScroll]); - - const editor_scroll = useCallback(() => { - if (ignoreNextEditorScrollEvent_.current) { - ignoreNextEditorScrollEvent_.current = false; - return; - } - - const m = editorMaxScrollTop_.current; - const percent = m ? editor.getSession().getScrollTop() / m : 0; - - setViewerPercentScroll(percent); - }, [editor, setViewerPercentScroll]); - - const resetScroll = useCallback(() => { - if (!editor) return; - - // Ace Editor caches scroll values, which makes - // it hard to reset the scroll position, so we - // need to use this hack. - // https://github.com/ajaxorg/ace/issues/2195 - editor.session.$scrollTop = -1; - editor.session.$scrollLeft = -1; - editor.renderer.scrollTop = -1; - editor.renderer.scrollLeft = -1; - editor.renderer.scrollBarV.scrollTop = -1; - editor.renderer.scrollBarH.scrollLeft = -1; - editor.session.setScrollTop(0); - editor.session.setScrollLeft(0); - }, [editorSetScrollTop, editor]); - - useEffect(() => { - if (!editor) return () => {}; - - editor.renderer.on('afterRender', onAfterEditorRender); - - return () => { - editor.renderer.off('afterRender', onAfterEditorRender); - }; - }, [editor]); - - return { resetScroll, setEditorPercentScroll, setViewerPercentScroll, editor_scroll }; -} - -export function useRootWidth(dependencies:any) { - const { rootRef } = dependencies; - - const [rootWidth, setRootWidth] = useState(0); - - useEffect(() => { - if (!rootRef.current) return; - - if (rootWidth !== rootRef.current.offsetWidth) setRootWidth(rootRef.current.offsetWidth); - }); - - return rootWidth; -} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.ts b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.ts deleted file mode 100644 index 79c6bb8e3e5..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export interface RenderedBody { - html: string; - pluginAssets: any[]; -} - -export function defaultRenderedBody(): RenderedBody { - return { - html: '', - pluginAssets: [], - }; -} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.ts b/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.ts deleted file mode 100644 index a0496f7d36c..00000000000 --- a/ElectronClient/gui/NoteEditor/NoteBody/AceEditor/utils/useListIdent.ts +++ /dev/null @@ -1,178 +0,0 @@ -import { useEffect } from 'react'; -import { selectionRange } from './index'; -const markdownUtils = require('lib/markdownUtils'); - -// The line that contains only `- ` is -// recognized as a heading in Ace. -function hyphenEmptyListItem(tokens: any[]) { - return ( - tokens.length === 2 && - tokens[0].type === 'markup.heading.2' && - tokens[0].value === '-' && - tokens[1].type === 'text.xml' && - tokens[1].value === ' ' - ); -} - -// Returns tokens of the line if it starts with a 'markup.list' token. -function listTokens(editor: any, row: number) { - const tokens = editor.session.getTokens(row); - if ( - !(tokens.length > 0 && tokens[0].type === 'markup.list') && - !hyphenEmptyListItem(tokens) - ) { - return []; - } - return tokens; -} - -function countIndent(line: string): number { - return line.match(/\t| {4}/g)?.length || 0; -} - -// Finds the list item with indent level `prevIndent`. -function findPrevListNum(editor: any, row: number, indent: number) { - while (row > 0) { - row--; - const line = editor.session.getLine(row); - - if (countIndent(line) === indent) { - const num = markdownUtils.olLineNumber(line.trimLeft()); - if (num) { - return num; - } - } - } - return 0; -} - -interface HookDependencies { - editor: any; -} - -export default function useListIdent(dependencies: HookDependencies) { - const { editor } = dependencies; - - useEffect(() => { - if (!editor) return () => {}; - - // Markdown list indentation. (https://github.com/laurent22/joplin/pull/2713) - // If the current line starts with `markup.list` token, - // hitting `Tab` key indents the line instead of inserting tab at cursor. - const originalEditorIndent = editor.indent; - - editor.indent = function() { - const range = selectionRange(editor); - if (range.isEmpty()) { - const row = range.start.row; - const tokens = listTokens(this, row); - - if (tokens.length > 0) { - if (tokens[0].value.search(/\d+\./) != -1) { - const line = this.session.getLine(row); - const n = findPrevListNum(this, row, countIndent(line) + 1) + 1; - this.session.replace( - { - start: { row, column: 0 }, - end: { row, column: tokens[0].value.length }, - }, - tokens[0].value.replace(/\d+\./, `${n}.`) - ); - } - - this.session.indentRows(row, row, '\t'); - return; - } - } - - if (originalEditorIndent) originalEditorIndent.call(this); - }; - - // Correct the number of numbered list item when outdenting. - editor.commands.addCommand({ - name: 'markdownOutdent', - bindKey: { win: 'Shift+Tab', mac: 'Shift+Tab' }, - multiSelectAction: 'forEachLine', - exec: function(editor: any) { - const range = selectionRange(editor); - - if (range.isEmpty()) { - const row = range.start.row; - - const tokens = editor.session.getTokens(row); - if (tokens.length && tokens[0].type === 'markup.list') { - const matches = tokens[0].value.match(/^(\t+)\d+\./); - if (matches && matches.length) { - const indent = countIndent(matches[1]); - const n = findPrevListNum(editor, row, indent - 1) + 1; - console.log(n); - editor.session.replace( - { - start: { row, column: 0 }, - end: { row, column: tokens[0].value.length }, - }, - tokens[0].value.replace(/\d+\./, `${n}.`) - ); - } - } - } - - editor.blockOutdent(); - }, - readonly: false, - }); - - // Delete a list markup (e.g. `- `) from an empty list item on hitting Enter. - // (https://github.com/laurent22/joplin/pull/2772) - editor.commands.addCommand({ - name: 'markdownEnter', - bindKey: 'Enter', - multiSelectAction: 'forEach', - exec: function(editor: any) { - const range = editor.getSelectionRange(); - const tokens = listTokens(editor, range.start.row); - - const emptyListItem = - tokens.length === 1 || hyphenEmptyListItem(tokens); - const emptyCheckboxItem = - tokens.length === 3 && - ['[ ]', '[x]'].includes(tokens[1].value) && - tokens[2].value === ' '; - - if (!range.isEmpty() || !(emptyListItem || emptyCheckboxItem)) { - editor.insert('\n'); - // Cursor can go out of the view after inserting '\n'. - editor.renderer.scrollCursorIntoView(); - return; - } - - const row = range.start.row; - const line = editor.session.getLine(row); - let indent = editor - .getSession() - .getMode() - .getNextLineIndent(null, line); - if (indent.startsWith('\t')) { - indent = indent.slice(1); - } else { - indent = ''; - } - - editor.session.replace( - { - start: { row, column: 0 }, - end: { row, column: line.length }, - }, - indent - ); - }, - readOnly: false, - }); - - return () => { - editor.indent = originalEditorIndent; - editor.commands.removeCommand('markdownOutdent'); - editor.commands.removeCommand('markdownEnter'); - }; - }, [editor]); -} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx index 5f18bdf48b8..e649f49f88c 100644 --- a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx +++ b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx @@ -113,7 +113,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) { }, supportsCommand: (/* name:string*/) => { // TODO: not implemented, currently only used for "search" command - // which is not directly supported by Ace Editor. + // which is not directly supported by this Editor. return false; }, execCommand: async (cmd: EditorCommand) => { @@ -479,36 +479,12 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) { useEffect(() => { if (props.searchMarkers !== previousSearchMarkers || renderedBody !== previousRenderedBody) { - // SEARCHHACK - // TODO: remove this options hack when aceeditor is removed - // Currently the webviewRef will send out an ipcMessage to set the results count - // Also setting it here will start an infinite loop of repeating the search - // Unfortunately we can't remove the function in the webview setMarkers - // until the aceeditor is remove. - // The below search is more accurate than the webview based one as it searches - // the text and not rendered html (rendered html fails if there is a match - // in a katex block) - // Once AceEditor is removed the options definition below can be removed and - // props.searchMarkers.options can be directly passed to as the 3rd argument below - // (replacing options) - let options = { notFromAce: true }; - if (props.searchMarkers.options) { - options = Object.assign({}, props.searchMarkers.options, options); - } - webviewRef.current.wrappedInstance.send('setMarkers', props.searchMarkers.keywords, options); - // SEARCHHACK + webviewRef.current.wrappedInstance.send('setMarkers', props.searchMarkers.keywords, props.searchMarkers.options); + if (editorRef.current) { const matches = editorRef.current.setMarkers(props.searchMarkers.keywords, props.searchMarkers.options); - // SEARCHHACK - // TODO: when aceeditor is removed then this check will be performed in the NoteSearchbar - // End the if statement can be removed in favor of simply returning matches - if (props.visiblePanes.includes('editor')) { - props.setLocalSearchResultCount(matches); - } else { - props.setLocalSearchResultCount(-1); - } - // end SEARCHHACK + props.setLocalSearchResultCount(matches); } } }, [props.searchMarkers, props.setLocalSearchResultCount, renderedBody]); diff --git a/ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx b/ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx index 0536b5dbbca..1a7c8449c8c 100644 --- a/ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx +++ b/ElectronClient/gui/NoteEditor/NoteBody/TinyMCE/TinyMCE.tsx @@ -267,7 +267,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => { } else if (cmd.value.type === 'files') { insertResourcesIntoContentRef.current(cmd.value.paths, { createFileURL: !!cmd.value.createFileURL }); } else { - reg.logger().warn('AceEditor: unsupported drop item: ', cmd); + reg.logger().warn('TinyMCE: unsupported drop item: ', cmd); } } else { commandProcessed = false; diff --git a/ElectronClient/gui/NoteEditor/NoteEditor.tsx b/ElectronClient/gui/NoteEditor/NoteEditor.tsx index ba9c950d486..daef549e6f9 100644 --- a/ElectronClient/gui/NoteEditor/NoteEditor.tsx +++ b/ElectronClient/gui/NoteEditor/NoteEditor.tsx @@ -2,7 +2,6 @@ import * as React from 'react'; import { useState, useEffect, useCallback, useRef } from 'react'; // eslint-disable-next-line no-unused-vars import TinyMCE from './NoteBody/TinyMCE/TinyMCE'; -import AceEditor from './NoteBody/AceEditor/AceEditor'; import CodeMirror from './NoteBody/CodeMirror/CodeMirror'; import { connect } from 'react-redux'; import MultiNoteActions from '../MultiNoteActions'; @@ -398,8 +397,6 @@ function NoteEditor(props: NoteEditorProps) { if (props.bodyEditor === 'TinyMCE') { editor = ; - } else if (props.bodyEditor === 'AceEditor') { - editor = ; } else if (props.bodyEditor === 'CodeMirror') { editor = ; } else { @@ -466,6 +463,7 @@ function NoteEditor(props: NoteEditorProps) { onNext={localSearch_next} onPrevious={localSearch_previous} onClose={localSearch_close} + visiblePanes={props.noteVisiblePanes} /> ); } diff --git a/ElectronClient/gui/NoteSearchBar.jsx b/ElectronClient/gui/NoteSearchBar.jsx index fcdc59206b0..16e340e5946 100644 --- a/ElectronClient/gui/NoteSearchBar.jsx +++ b/ElectronClient/gui/NoteSearchBar.jsx @@ -148,14 +148,7 @@ class NoteSearchBarComponent extends React.Component {
) : null; - // Currently searching in the viewer does not support jumping between matches - // So we explicitly disable those commands when only the viewer is open (this is - // currently signaled by results count being set to -1, but once Ace editor is removed - // we can observe the visible panes directly). - // SEARCHHACK - // TODO: remove the props.resultCount check here and replace it by checking visible panes directly - const allowScrolling = this.props.resultCount !== -1; - // end SEARCHHACK + const allowScrolling = this.props.visiblePanes.indexOf('editor') >= 0; const viewerWarning = (
diff --git a/ElectronClient/gui/note-viewer/index.html b/ElectronClient/gui/note-viewer/index.html index 2fa22cbdb99..a99fd76e840 100644 --- a/ElectronClient/gui/note-viewer/index.html +++ b/ElectronClient/gui/note-viewer/index.html @@ -277,25 +277,7 @@ let selectedElement = null; let elementIndex = 0; - const onEachElement = (element) => { - // SEARCHHACK - // TODO: remove notFromAce hack when removing aceeditor - // when removing just remove the 'notFromAce' part and leave the rest alone - if (!('selectedIndex' in options) || 'notFromAce' in options) return; - // SEARCHHACK - - if (('selectedIndex' in options) && elementIndex === options.selectedIndex) { - markSelectedElement_ = element; - element.classList.add('mark-selected'); - selectedElement = element; - } - - elementIndex++; - } - - const markKeywordOptions = { - each: onEachElement, - }; + const markKeywordOptions = {}; if ('separateWordSearch' in options) markKeywordOptions.separateWordSearch = options.separateWordSearch; @@ -307,22 +289,6 @@ replaceRegexDiacritics: replaceRegexDiacritics, }, markKeywordOptions); } - - // SEARCHHACK - // TODO: Remove this block (until the other SEARCHHACK marker) when removing Ace - // HACK: Aceeditor uses this view to handle all the searching - // The newer editor wont and this needs to be disabled in order to - // prevent an infinite loop - if (!('notFromAce' in options)) { - ipcProxySendToHost('setMarkerCount', elementIndex); - - // We only scroll the element into view if the search just happened. So when the user type the search - // or select the next/previous result, we scroll into view. However for other actions that trigger a - // re-render, we don't scroll as this is normally not wanted. - // This is to go around this issue: https://github.com/laurent22/joplin/issues/1833 - if (selectedElement && Date.now() - options.searchTimestamp <= 1000) selectedElement.scrollIntoView(); - } - // SEARCHHACK } let markLoader_ = { state: 'idle', whenDone: null }; diff --git a/ElectronClient/package.json b/ElectronClient/package.json index 7b8d5c5f915..07cf88b74a5 100644 --- a/ElectronClient/package.json +++ b/ElectronClient/package.json @@ -178,7 +178,6 @@ "promise": "^8.0.1", "query-string": "^5.1.1", "react": "^16.9.0", - "react-ace": "^6.1.4", "react-datetime": "^2.14.0", "react-dom": "^16.9.0", "react-redux": "^5.0.7", diff --git a/ElectronClient/style.css b/ElectronClient/style.css index dc768f52389..2930182420f 100644 --- a/ElectronClient/style.css +++ b/ElectronClient/style.css @@ -63,13 +63,6 @@ a { transition: 0.3s; opacity: 1; } -/* By default, the Ice Editor displays invalid characters, such as non-breaking spaces - as red boxes, but since those are actually valid characters and common in imported - Evernote data, we hide them here. */ -.ace-chrome .ace_invisible_space { - background-color: transparent !important; - opacity: 0; -} .note-list .list-item-container:hover { background-color: rgba(0,160,255,0.1) !important; diff --git a/ReactNativeClient/lib/models/Setting.js b/ReactNativeClient/lib/models/Setting.js index 24cb29a283c..3326be3da98 100644 --- a/ReactNativeClient/lib/models/Setting.js +++ b/ReactNativeClient/lib/models/Setting.js @@ -392,14 +392,6 @@ class Setting extends BaseModel { appTypes: ['desktop'], label: () => _('Auto-pair braces, parenthesis, quotations, etc.'), }, - 'editor.betaCodeMirror': { - value: false, - type: Setting.TYPE_BOOL, - public: true, - section: 'note', - appTypes: ['desktop'], - label: () => _('Use CodeMirror as the code editor (WARNING: BETA).'), - }, 'notes.sortOrder.reverse': { value: true, type: Setting.TYPE_BOOL, section: 'note', public: true, label: () => _('Reverse sort order'), appTypes: ['cli'] }, 'folders.sortOrder.field': { value: 'title', diff --git a/ReactNativeClient/lib/themes/aritimDark.js b/ReactNativeClient/lib/themes/aritimDark.js index 72d54e85d8b..e2550905b4b 100644 --- a/ReactNativeClient/lib/themes/aritimDark.js +++ b/ReactNativeClient/lib/themes/aritimDark.js @@ -28,7 +28,6 @@ const aritimStyle = { codeBorderColor: '#141a21', // Single line code border, and tables codeColor: '#005b47', // Single line code text - aceEditorTheme: 'chaos', codeMirrorTheme: 'monokai', codeThemeCss: 'atom-one-dark-reasonable.css', diff --git a/ReactNativeClient/lib/themes/dark.js b/ReactNativeClient/lib/themes/dark.js index ab7fc32d45d..0f4fd18d90f 100644 --- a/ReactNativeClient/lib/themes/dark.js +++ b/ReactNativeClient/lib/themes/dark.js @@ -28,7 +28,6 @@ const darkStyle = { codeBackgroundColor: 'rgb(47, 48, 49)', codeBorderColor: 'rgb(70, 70, 70)', - aceEditorTheme: 'twilight', codeMirrorTheme: 'material-darker', codeThemeCss: 'atom-one-dark-reasonable.css', diff --git a/ReactNativeClient/lib/themes/dracula.js b/ReactNativeClient/lib/themes/dracula.js index 69f3a3e9c8e..0059a4cca7b 100644 --- a/ReactNativeClient/lib/themes/dracula.js +++ b/ReactNativeClient/lib/themes/dracula.js @@ -28,7 +28,6 @@ const draculaStyle = { codeBorderColor: '#f8f8f2', codeColor: '#50fa7b', - aceEditorTheme: 'dracula', codeMirrorTheme: 'dracula', codeThemeCss: 'atom-one-dark-reasonable.css', }; diff --git a/ReactNativeClient/lib/themes/light.js b/ReactNativeClient/lib/themes/light.js index a8b09d085b3..b1c39123072 100644 --- a/ReactNativeClient/lib/themes/light.js +++ b/ReactNativeClient/lib/themes/light.js @@ -31,7 +31,6 @@ const lightStyle = { codeBorderColor: 'rgb(220, 220, 220)', codeColor: 'rgb(0,0,0)', - aceEditorTheme: 'chrome', codeMirrorTheme: 'default', codeThemeCss: 'atom-one-light.css', }; diff --git a/ReactNativeClient/lib/themes/nord.js b/ReactNativeClient/lib/themes/nord.js index c041331055b..37eac0c040d 100644 --- a/ReactNativeClient/lib/themes/nord.js +++ b/ReactNativeClient/lib/themes/nord.js @@ -74,7 +74,6 @@ const nordStyle = { codeBorderColor: nord[2], codeColor: nord[13], - aceEditorTheme: 'terminal', codeMirrorTheme: 'nord', codeThemeCss: 'atom-one-dark-reasonable.css', }; diff --git a/ReactNativeClient/lib/themes/solarizedDark.js b/ReactNativeClient/lib/themes/solarizedDark.js index d35f24475da..43c06b5c426 100644 --- a/ReactNativeClient/lib/themes/solarizedDark.js +++ b/ReactNativeClient/lib/themes/solarizedDark.js @@ -28,7 +28,6 @@ const solarizedDarkStyle = { codeBorderColor: '#696969', codeColor: '#fdf6e3', - aceEditorTheme: 'twilight', codeMirrorTheme: 'solarized dark', codeThemeCss: 'atom-one-dark-reasonable.css', }; diff --git a/ReactNativeClient/lib/themes/solarizedLight.js b/ReactNativeClient/lib/themes/solarizedLight.js index a1d6c1867e7..c5c792a3966 100644 --- a/ReactNativeClient/lib/themes/solarizedLight.js +++ b/ReactNativeClient/lib/themes/solarizedLight.js @@ -28,7 +28,6 @@ const solarizedLightStyle = { codeBorderColor: '#eee8d5', codeColor: '#002b36', - aceEditorTheme: 'tomorrow', codeMirrorTheme: 'solarized light', codeThemeCss: 'atom-one-light.css', };