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',
};