diff --git a/.eslintignore b/.eslintignore index fe0098417..9c4619b3a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -119,6 +119,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js +ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js diff --git a/.gitignore b/.gitignore index 499227ffe..da8b9f5b6 100644 --- a/.gitignore +++ b/.gitignore @@ -112,6 +112,7 @@ ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/types.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useCursorUtils.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useEditorSearch.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useJoplinMode.js +ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useLineSorting.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.js ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useScrollUtils.js diff --git a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.tsx b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.tsx index d96cdd00f..61b8bc640 100644 --- a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.tsx +++ b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/Editor.tsx @@ -17,13 +17,13 @@ import useCursorUtils from './utils/useCursorUtils'; import useLineSorting from './utils/useLineSorting'; import useEditorSearch from './utils/useEditorSearch'; import useJoplinMode from './utils/useJoplinMode'; +import useKeymap from './utils/useKeymap'; import 'codemirror/keymap/emacs'; import 'codemirror/keymap/vim'; import 'codemirror/keymap/sublime'; // Used for swapLineUp and swapLineDown import 'codemirror/mode/meta'; -const { shim } = require('lib/shim.js'); const { reg } = require('lib/registry.js'); @@ -50,7 +50,7 @@ const topLanguages = [ 'haskell', 'pascal', 'css', - + // Additional languages, not in the PYPL list 'xml', // For HTML too 'markdown', @@ -99,6 +99,7 @@ function Editor(props: EditorProps, ref: any) { useLineSorting(CodeMirror); useEditorSearch(CodeMirror); useJoplinMode(CodeMirror); + useKeymap(CodeMirror); useImperativeHandle(ref, () => { return editor; @@ -141,87 +142,6 @@ function Editor(props: EditorProps, ref: any) { } }, []); - useEffect(() => { - CodeMirror.keyMap.basic = { - 'Left': 'goCharLeft', - 'Right': 'goCharRight', - 'Up': 'goLineUp', - 'Down': 'goLineDown', - 'End': 'goLineRight', - 'Home': 'goLineLeftSmart', - 'PageUp': 'goPageUp', - 'PageDown': 'goPageDown', - 'Delete': 'delCharAfter', - 'Backspace': 'delCharBefore', - 'Shift-Backspace': 'delCharBefore', - 'Tab': 'smartListIndent', - 'Shift-Tab': 'smartListUnindent', - 'Enter': 'insertListElement', - 'Insert': 'toggleOverwrite', - 'Esc': 'singleSelection', - }; - // Add some of the Joplin smart list handling to emacs mode - CodeMirror.keyMap.emacs['Tab'] = 'smartListIndent'; - CodeMirror.keyMap.emacs['Enter'] = 'insertListElement'; - CodeMirror.keyMap.emacs['Shift-Tab'] = 'smartListUnindent'; - - if (shim.isMac()) { - CodeMirror.keyMap.default = { - // MacOS - 'Cmd-A': 'selectAll', - 'Cmd-D': 'deleteLine', - 'Cmd-Z': 'undo', - 'Shift-Cmd-Z': 'redo', - 'Cmd-Y': 'redo', - 'Cmd-Home': 'goDocStart', - 'Cmd-Up': 'goDocStart', - 'Cmd-End': 'goDocEnd', - 'Cmd-Down': 'goDocEnd', - 'Cmd-Left': 'goLineLeft', - 'Cmd-Right': 'goLineRight', - 'Alt-Left': 'goGroupLeft', - 'Alt-Right': 'goGroupRight', - 'Alt-Backspace': 'delGroupBefore', - 'Alt-Delete': 'delGroupAfter', - 'Cmd-[': 'indentLess', - 'Cmd-]': 'indentMore', - 'Cmd-/': 'toggleComment', - 'Cmd-Opt-S': 'sortSelectedLines', - 'Opt-Up': 'swapLineUp', - 'Opt-Down': 'swapLineDown', - - 'fallthrough': 'basic', - }; - } else { - CodeMirror.keyMap.default = { - // Windows/linux - 'Ctrl-A': 'selectAll', - 'Ctrl-D': 'deleteLine', - 'Ctrl-Z': 'undo', - 'Shift-Ctrl-Z': 'redo', - 'Ctrl-Y': 'redo', - 'Ctrl-Home': 'goDocStart', - 'Ctrl-End': 'goDocEnd', - 'Ctrl-Up': 'goLineUp', - 'Ctrl-Down': 'goLineDown', - 'Ctrl-Left': 'goGroupLeft', - 'Ctrl-Right': 'goGroupRight', - 'Alt-Left': 'goLineStart', - 'Alt-Right': 'goLineEnd', - 'Ctrl-Backspace': 'delGroupBefore', - 'Ctrl-Delete': 'delGroupAfter', - 'Ctrl-[': 'indentLess', - 'Ctrl-]': 'indentMore', - 'Ctrl-/': 'toggleComment', - 'Ctrl-Alt-S': 'sortSelectedLines', - 'Alt-Up': 'swapLineUp', - 'Alt-Down': 'swapLineDown', - - 'fallthrough': 'basic', - }; - } - }, []); - useEffect(() => { if (!editorParent.current) return () => {}; diff --git a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.ts b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.ts new file mode 100644 index 000000000..291e84b82 --- /dev/null +++ b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useKeymap.ts @@ -0,0 +1,107 @@ +import { useEffect } from 'react'; +import CommandService from 'lib/services/CommandService'; +const { shim } = require('lib/shim.js'); + +export default function useKeymap(CodeMirror: any) { + + function save() { + CommandService.instance().execute('synchronize'); + } + + function setupEmacs() { + CodeMirror.keyMap.emacs['Tab'] = 'smartListIndent'; + CodeMirror.keyMap.emacs['Enter'] = 'insertListElement'; + CodeMirror.keyMap.emacs['Shift-Tab'] = 'smartListUnindent'; + } + + function setupVim() { + CodeMirror.Vim.defineAction('swapLineDown', CodeMirror.commands.swapLineDown); + CodeMirror.Vim.mapCommand('', 'action', 'swapLineDown', {}, { context: 'normal', isEdit: true }); + CodeMirror.Vim.defineAction('swapLineUp', CodeMirror.commands.swapLineUp); + CodeMirror.Vim.mapCommand('', 'action', 'swapLineUp', {}, { context: 'normal', isEdit: true }); + CodeMirror.Vim.defineAction('insertListElement', CodeMirror.commands.vimInsertListElement); + CodeMirror.Vim.mapCommand('o', 'action', 'insertListElement', { after: true }, { context: 'normal', isEdit: true, interlaceInsertRepeat: true }); + } + + useEffect(() => { + // This enables the special modes (emacs and vim) to initiate sync by the save action + CodeMirror.commands.save = save; + + CodeMirror.keyMap.basic = { + 'Left': 'goCharLeft', + 'Right': 'goCharRight', + 'Up': 'goLineUp', + 'Down': 'goLineDown', + 'End': 'goLineRight', + 'Home': 'goLineLeftSmart', + 'PageUp': 'goPageUp', + 'PageDown': 'goPageDown', + 'Delete': 'delCharAfter', + 'Backspace': 'delCharBefore', + 'Shift-Backspace': 'delCharBefore', + 'Tab': 'smartListIndent', + 'Shift-Tab': 'smartListUnindent', + 'Enter': 'insertListElement', + 'Insert': 'toggleOverwrite', + 'Esc': 'singleSelection', + }; + + if (shim.isMac()) { + CodeMirror.keyMap.default = { + // MacOS + 'Cmd-A': 'selectAll', + 'Cmd-D': 'deleteLine', + 'Cmd-Z': 'undo', + 'Shift-Cmd-Z': 'redo', + 'Cmd-Y': 'redo', + 'Cmd-Home': 'goDocStart', + 'Cmd-Up': 'goDocStart', + 'Cmd-End': 'goDocEnd', + 'Cmd-Down': 'goDocEnd', + 'Cmd-Left': 'goLineLeft', + 'Cmd-Right': 'goLineRight', + 'Alt-Left': 'goGroupLeft', + 'Alt-Right': 'goGroupRight', + 'Alt-Backspace': 'delGroupBefore', + 'Alt-Delete': 'delGroupAfter', + 'Cmd-[': 'indentLess', + 'Cmd-]': 'indentMore', + 'Cmd-/': 'toggleComment', + 'Cmd-Opt-S': 'sortSelectedLines', + 'Opt-Up': 'swapLineUp', + 'Opt-Down': 'swapLineDown', + + 'fallthrough': 'basic', + }; + } else { + CodeMirror.keyMap.default = { + // Windows/linux + 'Ctrl-A': 'selectAll', + 'Ctrl-D': 'deleteLine', + 'Ctrl-Z': 'undo', + 'Shift-Ctrl-Z': 'redo', + 'Ctrl-Y': 'redo', + 'Ctrl-Home': 'goDocStart', + 'Ctrl-End': 'goDocEnd', + 'Ctrl-Up': 'goLineUp', + 'Ctrl-Down': 'goLineDown', + 'Ctrl-Left': 'goGroupLeft', + 'Ctrl-Right': 'goGroupRight', + 'Alt-Left': 'goLineStart', + 'Alt-Right': 'goLineEnd', + 'Ctrl-Backspace': 'delGroupBefore', + 'Ctrl-Delete': 'delGroupAfter', + 'Ctrl-[': 'indentLess', + 'Ctrl-]': 'indentMore', + 'Ctrl-/': 'toggleComment', + 'Ctrl-Alt-S': 'sortSelectedLines', + 'Alt-Up': 'swapLineUp', + 'Alt-Down': 'swapLineDown', + + 'fallthrough': 'basic', + }; + } + setupEmacs(); + setupVim(); + }, []); +} diff --git a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.ts b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.ts index afabc6099..45b75b270 100644 --- a/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.ts +++ b/ElectronClient/gui/NoteEditor/NoteBody/CodeMirror/utils/useListIdent.ts @@ -126,6 +126,24 @@ export default function useListIdent(CodeMirror: any) { }); }; + // This is a special case of insertList element because it happens when + // vim is in normal mode and input is disabled and the cursor is not + // necessarily at the end of line (but it should pretend it is + CodeMirror.commands.vimInsertListElement = function(cm: any) { + cm.setOption('disableInput', false); + + const ranges = cm.listSelections(); + if (ranges.length === 0) return; + const { anchor } = ranges[0]; + + // Need to move the cursor to end of line as this is the vim behavior + const line = cm.getLine(anchor.line); + cm.setCursor({ line: anchor.line, ch: line.length }); + + cm.execCommand('insertListElement'); + cm.setOption('disableInput', true); + }; + CodeMirror.commands.insertListElement = function(cm: any) { if (cm.getOption('disableInput')) return CodeMirror.Pass; diff --git a/ReactNativeClient/lib/services/searchengine/SearchEngine.js b/ReactNativeClient/lib/services/searchengine/SearchEngine.js index 84f4195bf..ebdd948e7 100644 --- a/ReactNativeClient/lib/services/searchengine/SearchEngine.js +++ b/ReactNativeClient/lib/services/searchengine/SearchEngine.js @@ -460,6 +460,15 @@ class SearchEngine { const fuzzyTitle = await this.fuzzifier(titleTerms.filter(x => !x.wildcard).map(x => trimQuotes(x.value))); const fuzzyBody = await this.fuzzifier(bodyTerms.filter(x => !x.wildcard).map(x => trimQuotes(x.value))); + // Floor the fuzzy scores to 0, 1 and 2. + const floorFuzzyScore = (matches) => { + for (let i = 0; i < matches.length; i++) matches[i].score = i; + }; + + fuzzyText.forEach(floorFuzzyScore); + fuzzyTitle.forEach(floorFuzzyScore); + fuzzyBody.forEach(floorFuzzyScore); + const phraseTextSearch = textTerms.filter(x => x.quoted); const wildCardSearch = textTerms.concat(titleTerms).concat(bodyTerms).filter(x => x.wildcard);