1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-11-27 08:21:03 +02:00

Desktop: Extend functionality of codemirror vim (#3823)

add swapLine(Up/Down)
have `o` use the more complex list indent
enable sync initializing from vim (and maybe emacs)
split keymap stuff into it's own file
This commit is contained in:
Caleb John 2020-09-28 11:57:17 -06:00 committed by GitHub
parent 7fb52b8b0e
commit a7fa119041
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 130 additions and 83 deletions

View File

@ -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

1
.gitignore vendored
View File

@ -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

View File

@ -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 () => {};

View File

@ -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('<A-j>', 'action', 'swapLineDown', {}, { context: 'normal', isEdit: true });
CodeMirror.Vim.defineAction('swapLineUp', CodeMirror.commands.swapLineUp);
CodeMirror.Vim.mapCommand('<A-k>', '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();
}, []);
}

View File

@ -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;