├── .babelrc ├── .eslintignore ├── .eslintrc.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── docs ├── options.png └── preferences.png ├── keymaps └── vim.json ├── lib ├── keymap.js ├── keymap.js.map ├── vim.js └── vim.js.map ├── package-lock.json ├── package.json ├── prettier.config.js ├── src ├── keymap.js └── vim.js └── styles └── vim.less /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["@babel/preset-env", { 4 | "targets": { "electron": "12.2.1" } 5 | }] 6 | ], 7 | "plugins": [ 8 | "@babel/plugin-proposal-class-properties" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | lib 2 | src/keymap.js 3 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | root: true 2 | extends: 3 | - prettier 4 | parser: "@babel/eslint-parser" 5 | env: {} 6 | globals: {} 7 | rules: 8 | no-useless-escape: 0 9 | prefer-const: 2 10 | no-unused-vars: 11 | - 2 12 | - 13 | argsIgnorePattern: ^_ 14 | varsIgnorePattern: ^_ 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | npm-debug.log 3 | node_modules 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### 2.2.2 2 | 3 | - fix(option): The seamless focus jump from the editor to the note title bar is now an option and disabled by default 4 | 5 | ### 2.2.1 6 | 7 | - feat(motion): Jump to note title on invoking `vim:move-up` at the first line and char 8 | 9 | ### 2.2.0 10 | 11 | - feat(motion): Support moitons like yG/ygg/dG/dgg (Thanks [jamalmoir](https://github.com/inkdropapp/inkdrop-vim/issues/39)) 12 | 13 | ### 2.1.10 14 | 15 | - fix(buffering): space key should be processed as a character input (Thanks [Chris](https://github.com/inkdropapp/inkdrop-vim/issues/29)) 16 | 17 | ### 2.1.9 18 | 19 | - fix(keymap): Commands should not work while buffering key in visual mode (Thanks [Kazuhiro](https://forum.inkdrop.app/t/minor-issue-in-vim-plugin/2702)) 20 | 21 | ### 2.1.7 22 | 23 | - fix(typo): Number keys not working as expected (Thanks [FORTRAN](https://forum.inkdrop.app/t/vim-plugin/2228/2)) 24 | 25 | ### 2.1.6 26 | 27 | - fix(motion): Ignore numeric keys when a modifier key is pressed (Thanks [Basyura](https://github.com/inkdropapp/inkdrop-vim/pull/25)) 28 | 29 | ### 2.1.5 30 | 31 | - fix(motion): enter/space/arrow keys not working as expected while key buffering (Thanks [rcashie](https://github.com/inkdropapp/inkdrop-vim/issues/24)) 32 | 33 | ### 2.1.4 34 | 35 | - fix(motion): text object manipulation not working for some tokens (Thanks [rcashie](https://github.com/inkdropapp/inkdrop-vim/issues/23)) 36 | 37 | ### 2.1.2 38 | 39 | - fix(keymap): remove keybindings of s h, s k, s l since those conflict with the default vim behavior of `s` (Thanks [oniatsu-san](https://github.com/inkdropapp/inkdrop-vim/issues/19)) 40 | 41 | ### 2.1.1 42 | 43 | - fix(keymap): change keybinding for `vim:move-to-mark` from " to ' (Thanks [oniatsu-san](https://github.com/inkdropapp/inkdrop-vim/issues/18)) 44 | 45 | ### 2.1.0 46 | 47 | - feat(motion): support moving cursor up/down by display lines (g k / g j) (Thanks [jolyon129](https://github.com/inkdropapp/inkdrop-vim/issues/16)) 48 | 49 | ### 2.0.1 50 | 51 | - Fix a bug where `vim:scroll-full-screen-up` and `vim:scroll-full-screen-down` not working (Thanks [@basyura](https://github.com/inkdropapp/inkdrop-vim/issues/13#issuecomment-612326857)) 52 | 53 | ### 2.0.0 54 | 55 | - (Breaking) The command prefix has been changed from `vim-mode:` to `vim:` so that the keybindings are correctly listed in the plugin settings 56 | - Moving focus between panes (sidebar, note list bar, editor, note title) with `s h` / `s j` / `s k` / `s l` 57 | - Select next/prev item in note list bar (`j` / `k`) 58 | - Scroll markdown preview pane ([Thanks @trietphm](https://github.com/inkdropapp/inkdrop-vim/issues/13)) 59 | 60 | ### 1.0.12 61 | 62 | - fix(key-buffering): replace character with "a" does not work ([Thanks seachicken](https://github.com/inkdropapp/inkdrop-vim/issues/11)) 63 | 64 | ### 1.0.11 65 | 66 | - fix(debug): typo in debug code that causes an error 67 | 68 | ### 1.0.10 69 | 70 | - fix(operatormotion): do not start key buffering for "D" and "C" operator motions (Thanks shimizu-san) 71 | - fix(buffering): avoid running command with 0 key while key buffering (Thanks volment) 72 | 73 | ### 1.0.8 74 | 75 | - fix(keymap): handle keystrokes as text input which failed to match binding [#8](https://github.com/inkdropapp/inkdrop-vim/issues/8) (Thanks @rtmoranorg) 76 | 77 | ### 1.0.7 78 | 79 | - fix(keymap): substitute keys not working [#4](https://github.com/inkdropapp/inkdrop-vim/issues/4) (Thanks @gregwebs and @giantsol) 80 | 81 | ### 1.0.6 82 | 83 | - fix(keymap): 'X' in visual mode does not work [#7](https://github.com/inkdropapp/inkdrop-vim/issues/7) (Thanks [@usou](https://github.com/usou)) 84 | 85 | ### 1.0.5 86 | 87 | - Copy deleted text to clipboard 88 | - Fix invalid selectors for `vim-mode:text-object-manipulation*` keymaps again 89 | 90 | ### 1.0.4 91 | 92 | - Fix invalid selectors for `vim-mode:text-object-manipulation*` keymaps 93 | 94 | ### 1.0.3 95 | 96 | - Support some actions for visual mode ([diff](https://github.com/inkdropapp/inkdrop-vim/commit/4536385f6d74c5e7c7247e7c65e593108925b056)) 97 | 98 | ### 1.0.2 99 | 100 | - feat(visual-mode): Support insert-at-start-of-target & insert-at-end-of-target (Thanks [Vikram](https://forum.inkdrop.app/t/vim-inserting-at-beginning-of-line-or-at-target-in-visual-block-mode-doesnt-work/1397/)) 101 | 102 | ### 1.0.1 103 | 104 | - fix(keybuffering): Avoid buffering key after processing command 105 | - fix(keybuffering): Avoid incorrect key buffering 106 | - fix(replace): Replacing with numeric character not working 107 | 108 | ### 1.0.0 109 | 110 | - feat(\*): Support inkdrop 4.x 111 | 112 | ### 0.3.2 113 | 114 | - fix(operator): Fix incorrect handling for operators 115 | 116 | ### 0.3.1 117 | 118 | - fix(keymaps): Support key buffering for keys like 'd' and 'c' 119 | 120 | ### 0.3.0 121 | 122 | - fix(keymaps): Support text object manipulations 123 | 124 | ### 0.2.4 125 | 126 | - Support Inkdrop v3.17.1 127 | 128 | ### 0.2.3 129 | 130 | - Support `ge` and `gE` (Thanks [@kiryph](https://github.com/kiryph)) 131 | 132 | ### 0.1.0 - First Release 133 | 134 | - Every feature added 135 | - Every bug fixed 136 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Takuya Matsuyama 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vim keybindings plugin for Inkdrop 2 | 3 | Provides Vim modal control for Inkdrop, blending the best of Vim and Inkdrop. 4 | 5 | ## Features 6 | 7 | - All common motions and operators, including text objects 8 | - Operator motion orthogonality 9 | - Visual mode - characterwise, linewise, blockwise 10 | - Incremental highlighted search (`/`, `?`, `#`, `*`, `g#`, `g*`) 11 | - Search/replace with confirm (:substitute, :%s) 12 | - Search history 13 | - Sort (`:sort`) 14 | - Marks (`,`) 15 | - Cross-buffer yank/paste 16 | - Select next/prev item in note list bar (`j` / `k`) 17 | - Scroll markdown preview pane 18 | 19 | ## Install 20 | 21 | ```sh 22 | ipm install vim 23 | ``` 24 | 25 | ## Key customizations 26 | 27 | Default vim keymaps are defined [here](https://github.com/inkdropapp/inkdrop-vim/blob/master/keymaps/vim.json) and you can override them in your `keymap.cson` file. 28 | 29 | CSS selectors for each mode: 30 | 31 | - Not insert mode: `.CodeMirror.vim-mode:not(.insert-mode):not(.key-buffering) textarea` 32 | - Normal mode: `.CodeMirror.vim-mode.normal-mode:not(.key-buffering) textarea` 33 | - Insert mode: `.CodeMirror.vim-mode.insert-mode textarea` 34 | - Replace mode: `.CodeMirror.vim-mode.replace-mode textarea` 35 | - Visual mode: `.CodeMirror.vim-mode.visual-mode:not(.key-buffering) textarea` 36 | 37 | You can check current keybindings on the _Keybindings_ pane on preferences window: 38 | 39 | ![Preferences](https://raw.githubusercontent.com/inkdropapp/inkdrop-vim/master/docs/preferences.png) 40 | 41 | ## Ex Commands 42 | 43 | ### `:w`, `:write` 44 | 45 | Saves current note immediately to the disk. 46 | 47 | ### `:next`, `:n` 48 | 49 | Opens next note on the note list. 50 | 51 | ### `:prev` 52 | 53 | Opens previous note on the note list. 54 | 55 | ### `:preview`, `:p` 56 | 57 | Toggles HMTL preview. 58 | 59 | ### `:side-by-side`, `:side` 60 | 61 | Toggles side-by-side mode. 62 | 63 | ### Define custom Ex commands 64 | 65 | You can extend Ex commands by writing [init.js](https://docs.inkdrop.app/manual/the-init-file). 66 | The following example defines `:find` command: 67 | 68 | ```js 69 | inkdrop.onEditorLoad(() => { 70 | var CodeMirror = require('codemirror') 71 | CodeMirror.Vim.defineEx('find', 'f', (cm, event) => { 72 | inkdrop.commands.dispatch(document.body, 'core:find-global') 73 | if (event.argString) 74 | inkdrop.commands.dispatch(document.body, 'core:search-notes', { 75 | keyword: event.argString.trim() 76 | }) 77 | }) 78 | }) 79 | ``` 80 | 81 | ## Options 82 | 83 | ![options](./docs/options.png) 84 | 85 | ### Seamlessly jump to note title 86 | 87 | Whether moving focus seamlessly from the editor to the note title bar by `vim:move-up` command 88 | 89 | ## Changelog 90 | 91 | See the [GitHub releases](https://github.com/inkdropapp/inkdrop-vim/releases) for an overview of what changed in each update. 92 | See [CHANGELOG.md](https://github.com/inkdropapp/inkdrop-vim/blob/master/CHANGELOG.md) for older releases. 93 | -------------------------------------------------------------------------------- /docs/options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkdropapp/inkdrop-vim/eec2aae437599689eba7eced66d08231a57469d4/docs/options.png -------------------------------------------------------------------------------- /docs/preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/inkdropapp/inkdrop-vim/eec2aae437599689eba7eced66d08231a57469d4/docs/preferences.png -------------------------------------------------------------------------------- /keymaps/vim.json: -------------------------------------------------------------------------------- 1 | { 2 | ".CodeMirror.vim-mode": {}, 3 | ".CodeMirror.vim-mode.normal-mode textarea": { 4 | "escape": "vim:reset-normal-mode" 5 | }, 6 | ".CodeMirror.vim-mode:not(.insert-mode):not(.key-buffering) textarea": { 7 | "h": "vim:move-left", 8 | "left": "vim:move-left", 9 | "backspace": "vim:move-left", 10 | "l": "vim:move-right", 11 | "right": "vim:move-right", 12 | "space": "vim:move-right", 13 | "k": "vim:move-up", 14 | "up": "vim:move-up", 15 | "j": "vim:move-down", 16 | "down": "vim:move-down", 17 | "g k": "vim:move-up-by-display-lines", 18 | "g j": "vim:move-down-by-display-lines", 19 | "g v": "vim:reselect-last-selection", 20 | 21 | "w": "vim:move-to-next-word", 22 | "W": "vim:move-to-next-whole-word", 23 | "e": "vim:move-to-end-of-word", 24 | "g e": "vim:move-to-previous-end-of-word", 25 | "E": "vim:move-to-end-of-whole-word", 26 | "g E": "vim:move-to-previous-end-of-whole-word", 27 | "b": "vim:move-to-previous-word", 28 | "B": "vim:move-to-previous-whole-word", 29 | "}": "vim:move-to-next-paragraph", 30 | "{": "vim:move-to-previous-paragraph", 31 | "|": "vim:move-to-beginning-of-line", 32 | "0": "vim:move-to-beginning-of-line-with-zero", 33 | "^": "vim:move-to-first-character-of-line", 34 | "_": "vim:move-to-first-character-of-line-and-down", 35 | "$": "vim:move-to-last-character-of-line", 36 | "g _": "vim:move-to-last-nonblank-character-of-line-and-down", 37 | "-": "vim:move-to-first-character-of-line-up", 38 | "+": "vim:move-to-first-character-of-line-down", 39 | "enter": "vim:move-to-first-character-of-line-down", 40 | "g g": "vim:move-to-start-of-file", 41 | "ctrl-u": "vim:scroll-half-screen-up", 42 | "ctrl-b": "vim:scroll-full-screen-up", 43 | "ctrl-d": "vim:scroll-half-screen-down", 44 | "ctrl-f": "vim:scroll-full-screen-down", 45 | "ctrl-e": "vim:scroll-down", 46 | "ctrl-y": "vim:scroll-up", 47 | "z enter": "vim:scroll-cursor-to-top", 48 | "z t": "vim:scroll-cursor-to-top-leave", 49 | "z .": "vim:scroll-cursor-to-middle", 50 | "z z": "vim:scroll-cursor-to-middle-leave", 51 | "z -": "vim:scroll-cursor-to-bottom", 52 | "z b": "vim:scroll-cursor-to-bottom-leave", 53 | "z s": "vim:scroll-cursor-to-left", 54 | "z e": "vim:scroll-cursor-to-right", 55 | "z M": "editor:fold-all", 56 | "z R": "editor:unfold-all", 57 | "z c": "editor:fold-current-row", 58 | "z o": "editor:unfold-current-row", 59 | "shift-G": "vim:move-to-line", 60 | "H": "vim:move-to-top-of-screen", 61 | "L": "vim:move-to-bottom-of-screen", 62 | "M": "vim:move-to-middle-of-screen", 63 | 64 | "d": "vim:delete", 65 | "D": "vim:delete-to-last-character-of-line", 66 | "c": "vim:change", 67 | "C": "vim:change-to-last-character-of-line", 68 | "r": "vim:replace", 69 | ">": "vim:indent", 70 | "<": "vim:outdent", 71 | "=": "vim:auto-indent", 72 | "J": "vim:join", 73 | 74 | "y": "vim:yank", 75 | "Y": "vim:yank-line", 76 | "P": "vim:put-before", 77 | "p": "vim:put-after", 78 | 79 | "g ~": "vim:toggle-case", 80 | "g U": "vim:upper-case", 81 | "g u": "vim:lower-case", 82 | "~": "vim:toggle-case-now", 83 | 84 | "m": "vim:mark", 85 | "`": "vim:move-to-mark-literal", 86 | "'": "vim:move-to-mark", 87 | 88 | "f": "vim:find", 89 | "F": "vim:find-backwards", 90 | "t": "vim:till", 91 | "T": "vim:till-backwards", 92 | ";": "vim:repeat-find", 93 | ",": "vim:repeat-find-reverse", 94 | 95 | "/": "vim:search", 96 | "?": "vim:reverse-search", 97 | "*": "vim:search-current-word", 98 | "#": "vim:reverse-search-current-word", 99 | "n": "vim:repeat-search", 100 | "N": "vim:repeat-search-backwards", 101 | 102 | "%": "vim:bracket-matching-motion", 103 | 104 | ":": "vim:ex-command" 105 | }, 106 | ".CodeMirror.vim-mode:not(.insert-mode):not(.key-buffering-command) textarea": { 107 | "i": "vim:text-object-manipulation-inner", 108 | "a": "vim:text-object-manipulation" 109 | }, 110 | ".CodeMirror.vim-mode.normal-mode.key-buffering-operator textarea": { 111 | "enter": "vim:move-down", 112 | "space": "vim:move-right", 113 | "up": "vim:move-up", 114 | "down": "vim:move-down", 115 | "right": "vim:move-right", 116 | "left": "vim:move-left" 117 | }, 118 | ".CodeMirror.vim-mode.normal-mode:not(.key-buffering):not(.visual-mode) textarea": { 119 | "i": "vim:activate-insert-mode", 120 | "I": "vim:insert-at-beginning-of-line", 121 | "insert": "vim:activate-insert-mode", 122 | "R": "vim:activate-replace-mode", 123 | "v": "vim:activate-characterwise-visual-mode", 124 | "V": "vim:activate-linewise-visual-mode", 125 | "ctrl-v": "vim:activate-blockwise-visual-mode", 126 | "ctrl-q": "vim:activate-blockwise-visual-mode", 127 | 128 | "u": "vim:undo", 129 | "ctrl-r": "core:redo", 130 | 131 | "O": "vim:insert-above-with-newline", 132 | "o": "vim:insert-below-with-newline", 133 | "a": "vim:insert-after", 134 | "A": "vim:insert-after-end-of-line", 135 | "x": "vim:delete-right", 136 | "X": "vim:delete-left", 137 | "s": "vim:substitute", 138 | "S": "vim:substitute-line", 139 | ".": "vim:repeat", 140 | 141 | "ctrl-a": "vim:increase", 142 | "ctrl-x": "vim:decrease", 143 | 144 | "\"": "vim:register-prefix" 145 | }, 146 | ".CodeMirror.vim-mode.insert-mode textarea": { 147 | "escape": "vim:exit-insert-mode", 148 | "ctrl-w": "vim:delete-to-beginning-of-word" 149 | }, 150 | ".CodeMirror.vim-mode.replace-mode textarea": { 151 | "escape": "vim:exit-insert-mode" 152 | }, 153 | ".CodeMirror.vim-mode.visual-mode textarea": { 154 | "escape": "vim:exit-visual-mode" 155 | }, 156 | ".CodeMirror.vim-mode.visual-mode:not(.key-buffering) textarea": { 157 | "\"": "vim:register-prefix", 158 | "I": "vim:insert-at-start-of-target", 159 | "A": "vim:insert-at-end-of-target", 160 | "v": "vim:activate-characterwise-visual-mode", 161 | "V": "vim:activate-linewise-visual-mode", 162 | "o": "vim:reverse-selections", 163 | "O": "vim:reverse-selections-at-same-line", 164 | "x": "vim:delete-right", 165 | "X": "vim:delete-left", 166 | "s": "vim:substitute-visual", 167 | "S": "vim:substitute-line-visual" 168 | }, 169 | ".note-list-bar": { 170 | "k": "core:open-prev-note", 171 | "j": "core:open-next-note" 172 | }, 173 | ".mde-preview": { 174 | "g g": "vim:move-to-start-of-file", 175 | "ctrl-u": "vim:scroll-half-screen-up", 176 | "ctrl-b": "vim:scroll-full-screen-up", 177 | "ctrl-d": "vim:scroll-half-screen-down", 178 | "ctrl-f": "vim:scroll-full-screen-down", 179 | "k": "vim:scroll-up", 180 | "j": "vim:scroll-down", 181 | "G": "vim:move-to-line", 182 | "H": "vim:move-to-start-of-file" 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /lib/vim.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _inkdrop = require("inkdrop"); 4 | var _keymap = _interopRequireDefault(require("./keymap")); 5 | var _eventKit = require("event-kit"); 6 | var _codemirror = _interopRequireDefault(require("codemirror")); 7 | var _electron = require("electron"); 8 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 9 | function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 10 | function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); } 11 | function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); } 12 | class Plugin { 13 | constructor() { 14 | _defineProperty(this, "config", { 15 | seamlessJumpToTitle: { 16 | title: 'Seamlessly jump to note title', 17 | type: 'boolean', 18 | description: 'Focus jumps from the editor to the note title bar by `vim:move-up` command', 19 | default: false 20 | } 21 | }); 22 | _defineProperty(this, "handleEditorLoad", editor => { 23 | this.activateMode(editor); 24 | }); 25 | _defineProperty(this, "handleVimModeChange", (event, _opt) => { 26 | _inkdrop.logger.debug('vim mode changed:', event); 27 | const { 28 | mode 29 | } = event; 30 | const cm = this.getCodeMirror(); 31 | cm.getWrapperElement().classList.remove('insert-mode'); 32 | cm.getWrapperElement().classList.remove('visual-mode'); 33 | cm.getWrapperElement().classList.remove('normal-mode'); 34 | cm.getWrapperElement().classList.remove('replace-mode'); 35 | switch (mode) { 36 | case 'normal': 37 | cm.getWrapperElement().classList.add('normal-mode'); 38 | break; 39 | case 'visual': 40 | cm.getWrapperElement().classList.add('visual-mode'); 41 | break; 42 | case 'replace': 43 | cm.getWrapperElement().classList.add('replace-mode'); 44 | break; 45 | case 'insert': 46 | cm.getWrapperElement().classList.add('insert-mode'); 47 | break; 48 | } 49 | }); 50 | _defineProperty(this, "handleEditorTextInput", event => { 51 | // only process if the event is fired via EventTarget.dispatchEvent() 52 | if (this.isInsertMode() && !event.isTrusted) { 53 | _inkdrop.logger.debug('handle text input:', event); 54 | const text = event.data; 55 | const cm = this.getCodeMirror(); 56 | cm.replaceSelection(text); 57 | } 58 | }); 59 | _defineProperty(this, "handleEditorKeyDown", event => { 60 | const keyName = this.normalizeKeyName(event.key); 61 | const cm = this.getCodeMirror(); 62 | const vim = this.vim.maybeInitVimState(cm); 63 | const isNumeric = !event.ctrlKey && !event.altKey && !event.metaKey && !event.shiftKey && keyName.match(/^\d$/); 64 | const target = cm.getInputField(); 65 | const currentKeyStroke = [...inkdrop.keymaps.queuedKeystrokes.filter(k => !k.startsWith('^')), keyName]; 66 | const { 67 | partialMatchCandidates, 68 | exactMatchCandidates 69 | } = inkdrop.keymaps.findMatchCandidates(currentKeyStroke); 70 | const partialMatches = inkdrop.keymaps.findPartialMatches(partialMatchCandidates, target); 71 | const exactMatches = inkdrop.keymaps.findExactMatches(exactMatchCandidates, target); 72 | _inkdrop.logger.debug('handleEditorKeyDown: currentKeyStroke:', currentKeyStroke); 73 | _inkdrop.logger.debug('handleEditorKeyDown: exactMatches:', exactMatches); 74 | _inkdrop.logger.debug('handleEditorKeyDown: partialMatches:', partialMatches); 75 | if (this.isBufferingKey()) { 76 | _inkdrop.logger.debug('handleEditorKeyDown: handle key buffering:', keyName, event); 77 | const b = cm.getInputField().webkitMatchesSelector('.CodeMirror.vim-mode:not(.insert-mode) textarea'); 78 | _inkdrop.logger.debug('handleEditorKeyDown: keybinding check:', exactMatches, b); 79 | if (keyName !== 'Ctrl' && keyName !== 'Alt' && keyName !== 'Shift' && keyName !== 'Meta') { 80 | const { 81 | inputState 82 | } = vim; 83 | const hasOperatorOrMotion = inputState.motion || inputState.operator; 84 | if ((event.key.length === 1 || keyName === 'space') && (!isNumeric || !hasOperatorOrMotion)) { 85 | inputState.selectedCharacter = event.key; 86 | inputState.keyBuffer = ''; 87 | if (exactMatches.length === 0 && partialMatches.length === 0) { 88 | const { 89 | pendingCommand 90 | } = this; 91 | this.stopBufferingKey(); 92 | if (typeof pendingCommand === 'function') { 93 | if (pendingCommand(event)) { 94 | event.stopPropagation(); 95 | event.preventDefault(); 96 | } 97 | } 98 | } 99 | } else if (isNumeric) { 100 | vim.inputState.pushRepeatDigit(keyName); 101 | } 102 | } 103 | } else if (!this.isInsertMode()) { 104 | if (isNumeric) { 105 | if (exactMatches.length === 0) { 106 | vim.inputState.pushRepeatDigit(keyName); 107 | } 108 | } else { 109 | // push key buffer to the repeat digit 110 | const keys = vim.inputState.keyBuffer; 111 | vim.inputState.keyBuffer = ''; 112 | const keysMatcher = /^(\d*)(.*)$/.exec(keys); 113 | if (keysMatcher[1] && keysMatcher[1] !== '0') { 114 | vim.inputState.pushRepeatDigit(keysMatcher[1]); 115 | } 116 | } 117 | } 118 | }); 119 | _defineProperty(this, "handleFocusEditor", _event => { 120 | this.yankClipboard(); 121 | }); 122 | } 123 | activate() { 124 | this.vim = (0, _keymap.default)(_codemirror.default); 125 | if (inkdrop.isEditorActive()) { 126 | this.activateMode(inkdrop.getActiveEditor()); 127 | } 128 | inkdrop.onEditorLoad(this.handleEditorLoad); 129 | } 130 | deactivate() { 131 | if (this.disposables) { 132 | this.disposables.dispose(); 133 | } 134 | if (inkdrop.isEditorActive()) { 135 | this.deactivateMode(inkdrop.getActiveEditor()); 136 | } 137 | } 138 | activateMode(editor) { 139 | const { 140 | cm 141 | } = editor; 142 | this.originalKeyMap = cm.getOption('keyMap'); 143 | cm.setOption('keyMap', 'vim'); 144 | cm.on('vim-mode-change', this.handleVimModeChange); 145 | cm.on('focus', this.handleFocusEditor); 146 | const el = cm.getWrapperElement(); 147 | el.classList.add('vim-mode', 'normal-mode'); 148 | this.registerCommands(); 149 | this.registerExCommands(); 150 | } 151 | deactivateMode(editor) { 152 | const { 153 | cm 154 | } = editor; 155 | if (cm && this.originalKeyMap) { 156 | cm.setOption('keyMap', this.originalKeyMap); 157 | cm.off('vim-mode-change', this.handleVimModeChange); 158 | const el = cm.getWrapperElement(); 159 | el.classList.remove('vim-mode'); 160 | } 161 | } 162 | normalizeKeyName(key) { 163 | switch (key) { 164 | case 'Enter': 165 | return 'enter'; 166 | case ' ': 167 | return 'space'; 168 | case 'ArrowRight': 169 | return 'right'; 170 | case 'ArrowLeft': 171 | return 'left'; 172 | case 'ArrowUp': 173 | return 'up'; 174 | case 'ArrowDown': 175 | return 'down'; 176 | default: 177 | if (key.match(/^[A-Z]$/)) { 178 | return `shift-${key}`; 179 | } 180 | return key; 181 | } 182 | } 183 | startBufferingKey(command, customBufferingModeClass) { 184 | const wrapper = this.getCodeMirror().getWrapperElement(); 185 | _inkdrop.logger.debug('Start key buffering'); 186 | wrapper.classList.add('key-buffering'); 187 | if (customBufferingModeClass) { 188 | wrapper.classList.add('key-buffering-' + customBufferingModeClass); 189 | } 190 | this.pendingCommand = command; 191 | } 192 | stopBufferingKey() { 193 | _inkdrop.logger.debug('Stop key buffering'); 194 | const wrapper = this.getCodeMirror().getWrapperElement(); 195 | const classes = Array.prototype.slice.apply(wrapper.classList); 196 | for (const i of classes) { 197 | if (i.startsWith('key-buffering')) { 198 | wrapper.classList.remove(i); 199 | } 200 | } 201 | this.pendingCommand = undefined; 202 | } 203 | isBufferingKey() { 204 | const wrapper = this.getCodeMirror().getWrapperElement(); 205 | return wrapper.classList.contains('key-buffering'); 206 | } 207 | bufferKey(key) { 208 | const cm = this.getCodeMirror(); 209 | const vim = this.vim.maybeInitVimState(cm); 210 | const vimKey = this.vim.cmKeyToVimKey("'" + key + "'"); 211 | vim.inputState.keyBuffer = vim.inputState.keyBuffer + vimKey; 212 | _inkdrop.logger.debug('keyBuffer:', vim.inputState.keyBuffer, vim.inputState); 213 | } 214 | isInsertMode() { 215 | const wrapper = this.getCodeMirror().getWrapperElement(); 216 | return wrapper.classList.contains('insert-mode'); 217 | } 218 | registerCommands() { 219 | const disposables = new _eventKit.CompositeDisposable(); 220 | const editor = inkdrop.getActiveEditor(); 221 | const { 222 | cm 223 | } = editor; 224 | const wrapper = cm.getWrapperElement(); 225 | const doKeyToKey = async command => { 226 | _inkdrop.logger.debug('doKeyToKey:', command); 227 | let keys = command.toKeys; 228 | while (keys) { 229 | // Pull off one command key, which is either a single character 230 | // or a special sequence wrapped in '<' and '>', e.g. ''. 231 | const match = /<\w+-.+?>|<\w+>|./.exec(keys); 232 | let key = match[0]; 233 | _inkdrop.logger.debug('key:', key); 234 | keys = keys.substring(match.index + key.length); 235 | if (this.isBufferingKey()) { 236 | this.handleEditorKeyDown(new KeyboardEvent('keydown', { 237 | key 238 | })); 239 | } else { 240 | if (key.match(/^[A-Z]$/)) { 241 | key = 'shift-' + key; 242 | } 243 | const { 244 | exactMatchCandidates 245 | } = inkdrop.keymaps.findMatchCandidates([key]); 246 | _inkdrop.logger.debug('exactMatchCandidates:', exactMatchCandidates); 247 | const bindings = inkdrop.keymaps.findExactMatches(exactMatchCandidates, document.activeElement); 248 | const b = bindings[0]; 249 | if (b) { 250 | inkdrop.commands.dispatch(document.activeElement, b.command); 251 | } else { 252 | _inkdrop.logger.debug('command not found for key:', key); 253 | } 254 | } 255 | } 256 | }; 257 | 258 | // bind key to command 259 | const h = command => { 260 | return e => { 261 | _inkdrop.logger.debug('command:', command, 'state:', Object.assign({}, cm.state.vim.inputState)); 262 | e.stopPropagation(); 263 | const vim = this.vim.maybeInitVimState(cm); 264 | return cm.operation(() => { 265 | cm.curOp.isVimOp = true; 266 | try { 267 | if (command.type === 'keyToKey') { 268 | doKeyToKey(command); 269 | } else { 270 | this.vim.commandDispatcher.processCommand(cm, vim, command); 271 | } 272 | } catch (e) { 273 | // clear VIM state in case it's in a bad state. 274 | cm.state.vim = undefined; 275 | this.vim.maybeInitVimState(cm); 276 | if (!_codemirror.default.Vim.suppressErrorLogging) { 277 | console.error(e); 278 | } 279 | throw e; 280 | } 281 | return true; 282 | }); 283 | }; 284 | }; 285 | // bind keystroke to command 286 | const b = command => { 287 | return e => { 288 | _inkdrop.logger.debug('buffer command:', command, 'state:', Object.assign({}, cm.state.vim.inputState), e.originalEvent); 289 | this.startBufferingKey(h(command), 'command'); 290 | this.bufferKey(e.originalEvent.key); 291 | }; 292 | }; 293 | // bind keystroke to operator 294 | const p = command => { 295 | return e => { 296 | _inkdrop.logger.debug('operator command:', command, 'state:', Object.assign({}, cm.state.vim.inputState), e.originalEvent, e); 297 | const vim = this.vim.maybeInitVimState(cm); 298 | if (!vim.inputState.operator && !this.isBufferingKey() && !vim.visualMode) { 299 | this.startBufferingKey(e => { 300 | const el = cm.getInputField(); 301 | const keyName = this.normalizeKeyName(e.key); 302 | const keyBinding = inkdrop.keymaps.findKeyBindings({ 303 | keystrokes: keyName, 304 | target: el 305 | }); 306 | if (keyBinding.length > 0) { 307 | inkdrop.commands.dispatch(el, keyBinding[0].command); 308 | return true; 309 | } else { 310 | return false; 311 | } 312 | }, 'operator'); 313 | if (e.originalEvent) { 314 | this.bufferKey(e.originalEvent.key); 315 | } else { 316 | _inkdrop.logger.debug('buffer key:', command.keys); 317 | this.bufferKey(command.keys); 318 | } 319 | } 320 | return h(command)(e); 321 | }; 322 | }; 323 | const handlers = { 324 | 'vim:native!': () => {}, 325 | 'vim:reset-normal-mode': e => { 326 | _codemirror.default.Vim.clearInputState(cm); 327 | this.stopBufferingKey(); 328 | e.stopPropagation(); 329 | }, 330 | 'vim:exit-visual-mode': e => { 331 | _inkdrop.logger.debug('exit-visual-mode'); 332 | _codemirror.default.Vim.clearInputState(cm); 333 | _codemirror.default.Vim.exitVisualMode(cm); 334 | this.stopBufferingKey(); 335 | e.stopPropagation(); 336 | }, 337 | 'vim:exit-insert-mode': e => { 338 | _inkdrop.logger.debug('exit-insert-mode'); 339 | _codemirror.default.Vim.clearInputState(cm); 340 | _codemirror.default.Vim.exitInsertMode(cm); 341 | this.stopBufferingKey(); 342 | e.stopPropagation(); 343 | }, 344 | 'vim:move-left': h({ 345 | keys: 'h', 346 | type: 'motion', 347 | motion: 'moveByCharacters', 348 | motionArgs: { 349 | forward: false 350 | } 351 | }), 352 | 'vim:move-right': h({ 353 | keys: 'l', 354 | type: 'motion', 355 | motion: 'moveByCharacters', 356 | motionArgs: { 357 | forward: true 358 | } 359 | }), 360 | 'vim:move-up': (command => { 361 | return e => { 362 | const seamlessJumpToTitleEnabled = inkdrop.config.get('vim.seamlessJumpToTitle'); 363 | const cur = cm.getCursor(); 364 | if (cur.line === 0 && cur.ch === 0 && seamlessJumpToTitleEnabled) { 365 | e.stopPropagation(); 366 | inkdrop.commands.dispatch(document.body, 'editor:title:focus'); 367 | } else { 368 | command(e); 369 | } 370 | }; 371 | })(h({ 372 | keys: 'k', 373 | type: 'motion', 374 | motion: 'moveByLines', 375 | motionArgs: { 376 | forward: false, 377 | linewise: true 378 | } 379 | })), 380 | 'vim:move-down': h({ 381 | keys: 'j', 382 | type: 'motion', 383 | motion: 'moveByLines', 384 | motionArgs: { 385 | forward: true, 386 | linewise: true 387 | } 388 | }), 389 | 'vim:move-up-by-display-lines': h({ 390 | keys: 'gk', 391 | type: 'motion', 392 | motion: 'moveByDisplayLines', 393 | motionArgs: { 394 | forward: false 395 | } 396 | }), 397 | 'vim:move-down-by-display-lines': h({ 398 | keys: 'gj', 399 | type: 'motion', 400 | motion: 'moveByDisplayLines', 401 | motionArgs: { 402 | forward: true 403 | } 404 | }), 405 | 'vim:move-to-next-word': h({ 406 | keys: 'w', 407 | type: 'motion', 408 | motion: 'moveByWords', 409 | motionArgs: { 410 | forward: true, 411 | wordEnd: false 412 | } 413 | }), 414 | 'vim:move-to-next-whole-word': h({ 415 | keys: 'W', 416 | type: 'motion', 417 | motion: 'moveByWords', 418 | motionArgs: { 419 | forward: true, 420 | wordEnd: false, 421 | bigWord: true 422 | } 423 | }), 424 | 'vim:move-to-end-of-word': h({ 425 | keys: 'e', 426 | type: 'motion', 427 | motion: 'moveByWords', 428 | motionArgs: { 429 | forward: true, 430 | wordEnd: true, 431 | inclusive: true 432 | } 433 | }), 434 | 'vim:move-to-previous-end-of-word': h({ 435 | keys: 'ge', 436 | type: 'motion', 437 | motion: 'moveByWords', 438 | motionArgs: { 439 | forward: false, 440 | wordEnd: true, 441 | inclusive: true 442 | } 443 | }), 444 | 'vim:move-to-end-of-whole-word': h({ 445 | keys: 'E', 446 | type: 'motion', 447 | motion: 'moveByWords', 448 | motionArgs: { 449 | forward: true, 450 | wordEnd: true, 451 | bigWord: true, 452 | inclusive: true 453 | } 454 | }), 455 | 'vim:move-to-previous-end-of-whole-word': h({ 456 | keys: 'gE', 457 | type: 'motion', 458 | motion: 'moveByWords', 459 | motionArgs: { 460 | forward: false, 461 | wordEnd: true, 462 | bigWord: true, 463 | inclusive: true 464 | } 465 | }), 466 | 'vim:move-to-previous-word': h({ 467 | keys: 'b', 468 | type: 'motion', 469 | motion: 'moveByWords', 470 | motionArgs: { 471 | forward: false, 472 | wordEnd: false 473 | } 474 | }), 475 | 'vim:move-to-previous-whole-word': h({ 476 | keys: 'B', 477 | type: 'motion', 478 | motion: 'moveByWords', 479 | motionArgs: { 480 | forward: false, 481 | wordEnd: false, 482 | bigWord: true 483 | } 484 | }), 485 | 'vim:move-to-next-paragraph': h({ 486 | keys: '}', 487 | type: 'motion', 488 | motion: 'moveByParagraph', 489 | motionArgs: { 490 | forward: true, 491 | toJumplist: true 492 | } 493 | }), 494 | 'vim:move-to-previous-paragraph': h({ 495 | keys: '{', 496 | type: 'motion', 497 | motion: 'moveByParagraph', 498 | motionArgs: { 499 | forward: false, 500 | toJumplist: true 501 | } 502 | }), 503 | 'vim:move-to-beginning-of-line-with-zero': (() => { 504 | const handler = h({ 505 | keys: '0', 506 | type: 'motion', 507 | motion: 'moveToStartOfLine' 508 | }); 509 | return e => { 510 | const vim = this.vim.maybeInitVimState(cm); 511 | if (vim.inputState.getRepeat() > 0) { 512 | vim.inputState.pushRepeatDigit('0'); 513 | } else { 514 | return handler(e); 515 | } 516 | }; 517 | })(), 518 | 'vim:move-to-beginning-of-line': h({ 519 | keys: '0', 520 | type: 'motion', 521 | motion: 'moveToStartOfLine' 522 | }), 523 | 'vim:move-to-first-character-of-line': h({ 524 | keys: '^', 525 | type: 'motion', 526 | motion: 'moveToFirstNonWhiteSpaceCharacter' 527 | }), 528 | 'vim:move-to-first-character-of-line-and-down': h({ 529 | keys: '_', 530 | type: 'motion', 531 | motion: 'moveByLines', 532 | motionArgs: { 533 | forward: true, 534 | toFirstChar: true, 535 | repeatOffset: -1 536 | } 537 | }), 538 | 'vim:move-to-last-character-of-line': h({ 539 | keys: '$', 540 | type: 'motion', 541 | motion: 'moveToEol', 542 | motionArgs: { 543 | inclusive: true 544 | } 545 | }), 546 | 'vim:move-to-last-nonblank-character-of-line-and-down': () => { 547 | '???'; 548 | }, 549 | 'vim:move-to-first-character-of-line-up': h({ 550 | keys: '-', 551 | type: 'motion', 552 | motion: 'moveByLines', 553 | motionArgs: { 554 | forward: false, 555 | toFirstChar: true 556 | } 557 | }), 558 | 'vim:move-to-first-character-of-line-down': h({ 559 | keys: '+', 560 | type: 'motion', 561 | motion: 'moveByLines', 562 | motionArgs: { 563 | forward: true, 564 | toFirstChar: true 565 | } 566 | }), 567 | 'vim:move-to-start-of-file': h({ 568 | keys: 'gg', 569 | type: 'motion', 570 | motion: 'moveToLineOrEdgeOfDocument', 571 | motionArgs: { 572 | forward: false, 573 | explicitRepeat: true, 574 | linewise: true, 575 | toJumplist: true 576 | } 577 | }), 578 | 'vim:scroll-half-screen-up': h({ 579 | keys: '', 580 | type: 'motion', 581 | motion: 'moveByScroll', 582 | motionArgs: { 583 | forward: false, 584 | explicitRepeat: true 585 | } 586 | }), 587 | 'vim:scroll-full-screen-up': h({ 588 | keys: '', 589 | type: 'motion', 590 | motion: 'moveByPage', 591 | motionArgs: { 592 | forward: false 593 | } 594 | }), 595 | 'vim:scroll-half-screen-down': h({ 596 | keys: '', 597 | type: 'motion', 598 | motion: 'moveByScroll', 599 | motionArgs: { 600 | forward: true, 601 | explicitRepeat: true 602 | } 603 | }), 604 | 'vim:scroll-full-screen-down': h({ 605 | keys: '', 606 | type: 'motion', 607 | motion: 'moveByPage', 608 | motionArgs: { 609 | forward: true 610 | } 611 | }), 612 | 'vim:scroll-down': h({ 613 | keys: '', 614 | type: 'action', 615 | action: 'scroll', 616 | actionArgs: { 617 | forward: true, 618 | linewise: true 619 | } 620 | }), 621 | 'vim:scroll-up': h({ 622 | keys: '', 623 | type: 'action', 624 | action: 'scroll', 625 | actionArgs: { 626 | forward: false, 627 | linewise: true 628 | } 629 | }), 630 | 'vim:scroll-cursor-to-top': h({ 631 | keys: 'z', 632 | type: 'action', 633 | action: 'scrollToCursor', 634 | actionArgs: { 635 | position: 'top' 636 | }, 637 | motion: 'moveToFirstNonWhiteSpaceCharacter' 638 | }), 639 | 'vim:scroll-cursor-to-top-leave': h({ 640 | keys: 'zt', 641 | type: 'action', 642 | action: 'scrollToCursor', 643 | actionArgs: { 644 | position: 'top' 645 | } 646 | }), 647 | 'vim:scroll-cursor-to-middle': h({ 648 | keys: 'z.', 649 | type: 'action', 650 | action: 'scrollToCursor', 651 | actionArgs: { 652 | position: 'center' 653 | }, 654 | motion: 'moveToFirstNonWhiteSpaceCharacter' 655 | }), 656 | 'vim:scroll-cursor-to-middle-leave': h({ 657 | keys: 'zz', 658 | type: 'action', 659 | action: 'scrollToCursor', 660 | actionArgs: { 661 | position: 'center' 662 | } 663 | }), 664 | 'vim:scroll-cursor-to-bottom': h({ 665 | keys: 'z-', 666 | type: 'action', 667 | action: 'scrollToCursor', 668 | actionArgs: { 669 | position: 'bottom' 670 | } 671 | }), 672 | 'vim:scroll-cursor-to-bottom-leave': h({ 673 | keys: 'zb', 674 | type: 'action', 675 | action: 'scrollToCursor', 676 | actionArgs: { 677 | position: 'bottom' 678 | }, 679 | motion: 'moveToFirstNonWhiteSpaceCharacter' 680 | }), 681 | 'vim:move-to-line': h({ 682 | keys: 'G', 683 | type: 'motion', 684 | motion: 'moveToLineOrEdgeOfDocument', 685 | motionArgs: { 686 | forward: true, 687 | explicitRepeat: true, 688 | linewise: true, 689 | toJumplist: true 690 | } 691 | }), 692 | 'vim:move-to-top-of-screen': h({ 693 | keys: 'H', 694 | type: 'motion', 695 | motion: 'moveToTopLine', 696 | motionArgs: { 697 | linewise: true, 698 | toJumplist: true 699 | } 700 | }), 701 | 'vim:move-to-bottom-of-screen': h({ 702 | keys: 'L', 703 | type: 'motion', 704 | motion: 'moveToBottomLine', 705 | motionArgs: { 706 | linewise: true, 707 | toJumplist: true 708 | } 709 | }), 710 | 'vim:move-to-middle-of-screen': h({ 711 | keys: 'M', 712 | type: 'motion', 713 | motion: 'moveToMiddleLine', 714 | motionArgs: { 715 | linewise: true, 716 | toJumplist: true 717 | } 718 | }), 719 | 'vim:delete': p({ 720 | keys: 'd', 721 | type: 'operator', 722 | operator: 'delete' 723 | }), 724 | 'vim:delete-to-last-character-of-line': h({ 725 | keys: 'D', 726 | type: 'operatorMotion', 727 | operator: 'delete', 728 | motion: 'moveToEol', 729 | motionArgs: { 730 | inclusive: true 731 | }, 732 | context: 'normal' 733 | }), 734 | 'vim:change': p({ 735 | keys: 'c', 736 | type: 'operator', 737 | operator: 'change' 738 | }), 739 | 'vim:change-to-last-character-of-line': h({ 740 | keys: 'C', 741 | type: 'operatorMotion', 742 | operator: 'change', 743 | motion: 'moveToEol', 744 | motionArgs: { 745 | inclusive: true 746 | }, 747 | context: 'normal' 748 | }), 749 | 'vim:substitute-line': h({ 750 | keys: 'S', 751 | type: 'keyToKey', 752 | toKeys: 'cc', 753 | context: 'normal' 754 | }), 755 | 'vim:substitute-line-visual': h({ 756 | keys: 'S', 757 | type: 'keyToKey', 758 | toKeys: 'VdO', 759 | context: 'visual' 760 | }), 761 | 'vim:replace': b({ 762 | keys: 'r', 763 | type: 'action', 764 | action: 'replace', 765 | isEdit: true 766 | }), 767 | 'vim:insert-at-beginning-of-line': h({ 768 | keys: 'I', 769 | type: 'action', 770 | action: 'enterInsertMode', 771 | isEdit: true, 772 | actionArgs: { 773 | insertAt: 'firstNonBlank' 774 | }, 775 | context: 'normal' 776 | }), 777 | 'vim:text-object-manipulation-inner': b({ 778 | keys: 'i', 779 | type: 'motion', 780 | motion: 'textObjectManipulation', 781 | motionArgs: { 782 | textObjectInner: true 783 | } 784 | }), 785 | 'vim:text-object-manipulation': b({ 786 | keys: 'a', 787 | type: 'motion', 788 | motion: 'textObjectManipulation' 789 | }), 790 | 'vim:indent': p({ 791 | keys: '>', 792 | type: 'operator', 793 | operator: 'indent', 794 | operatorArgs: { 795 | indentRight: true 796 | } 797 | }), 798 | 'vim:outdent': p({ 799 | keys: '<', 800 | type: 'operator', 801 | operator: 'indent', 802 | operatorArgs: { 803 | indentRight: false 804 | } 805 | }), 806 | 'vim:auto-indent': () => { 807 | 'not supported'; 808 | }, 809 | 'vim:reselect-last-selection': h({ 810 | keys: 'gv', 811 | type: 'action', 812 | action: 'reselectLastSelection' 813 | }), 814 | 'vim:join': h({ 815 | keys: 'J', 816 | type: 'action', 817 | action: 'joinLines', 818 | isEdit: true 819 | }), 820 | 'vim:yank': p({ 821 | keys: 'y', 822 | type: 'operator', 823 | operator: 'yank' 824 | }), 825 | 'vim:yank-line': h({ 826 | keys: 'Y', 827 | type: 'operatorMotion', 828 | operator: 'yank', 829 | motion: 'expandToLine', 830 | motionArgs: { 831 | linewise: true 832 | }, 833 | context: 'normal' 834 | }), 835 | 'vim:put-before': h({ 836 | keys: 'P', 837 | type: 'action', 838 | action: 'paste', 839 | isEdit: true, 840 | actionArgs: { 841 | after: false, 842 | isEdit: true 843 | } 844 | }), 845 | 'vim:put-after': h({ 846 | keys: 'p', 847 | type: 'action', 848 | action: 'paste', 849 | isEdit: true, 850 | actionArgs: { 851 | after: true, 852 | isEdit: true 853 | } 854 | }), 855 | 'vim:toggle-case': h({ 856 | keys: 'g~', 857 | type: 'operator', 858 | operator: 'changeCase' 859 | }), 860 | 'vim:upper-case': h({ 861 | keys: 'gU', 862 | type: 'operator', 863 | operator: 'changeCase', 864 | operatorArgs: { 865 | toLower: false 866 | }, 867 | isEdit: true 868 | }), 869 | 'vim:lower-case': h({ 870 | keys: 'gu', 871 | type: 'operator', 872 | operator: 'changeCase', 873 | operatorArgs: { 874 | toLower: true 875 | }, 876 | isEdit: true 877 | }), 878 | 'vim:toggle-case-now': h({ 879 | keys: '~', 880 | type: 'operatorMotion', 881 | operator: 'changeCase', 882 | motion: 'moveByCharacters', 883 | motionArgs: { 884 | forward: true 885 | }, 886 | operatorArgs: { 887 | shouldMoveCursor: true 888 | }, 889 | context: 'normal' 890 | }), 891 | 'vim:mark': b({ 892 | keys: 'm', 893 | type: 'action', 894 | action: 'setMark' 895 | }), 896 | 'vim:move-to-mark-literal': b({ 897 | keys: '`', 898 | type: 'motion', 899 | motion: 'goToMark', 900 | motionArgs: { 901 | toJumplist: true 902 | } 903 | }), 904 | 'vim:move-to-mark': b({ 905 | keys: "'", 906 | type: 'motion', 907 | motion: 'goToMark', 908 | motionArgs: { 909 | toJumplist: true, 910 | linewise: true 911 | } 912 | }), 913 | 'vim:find': b({ 914 | keys: 'f', 915 | type: 'motion', 916 | motion: 'moveToCharacter', 917 | motionArgs: { 918 | forward: true, 919 | inclusive: true 920 | } 921 | }), 922 | 'vim:find-backwards': b({ 923 | keys: 'F', 924 | type: 'motion', 925 | motion: 'moveToCharacter', 926 | motionArgs: { 927 | forward: false 928 | } 929 | }), 930 | 'vim:till': b({ 931 | keys: 't', 932 | type: 'motion', 933 | motion: 'moveTillCharacter', 934 | motionArgs: { 935 | forward: true, 936 | inclusive: true 937 | } 938 | }), 939 | 'vim:till-backwards': b({ 940 | keys: 'T', 941 | type: 'motion', 942 | motion: 'moveTillCharacter', 943 | motionArgs: { 944 | forward: false 945 | } 946 | }), 947 | 'vim:repeat-find': h({ 948 | keys: ';', 949 | type: 'motion', 950 | motion: 'repeatLastCharacterSearch', 951 | motionArgs: { 952 | forward: true 953 | } 954 | }), 955 | 'vim:repeat-find-reverse': h({ 956 | keys: ',', 957 | type: 'motion', 958 | motion: 'repeatLastCharacterSearch', 959 | motionArgs: { 960 | forward: false 961 | } 962 | }), 963 | 'vim:search': h({ 964 | keys: '/', 965 | type: 'search', 966 | searchArgs: { 967 | forward: true, 968 | querySrc: 'prompt', 969 | toJumplist: true 970 | } 971 | }), 972 | 'vim:reverse-search': h({ 973 | keys: '?', 974 | type: 'search', 975 | searchArgs: { 976 | forward: false, 977 | querySrc: 'prompt', 978 | toJumplist: true 979 | } 980 | }), 981 | 'vim:search-current-word': h({ 982 | keys: '*', 983 | type: 'search', 984 | searchArgs: { 985 | forward: true, 986 | querySrc: 'wordUnderCursor', 987 | wholeWordOnly: true, 988 | toJumplist: true 989 | } 990 | }), 991 | 'vim:reverse-search-current-word': h({ 992 | keys: '#', 993 | type: 'search', 994 | searchArgs: { 995 | forward: false, 996 | querySrc: 'wordUnderCursor', 997 | wholeWordOnly: true, 998 | toJumplist: true 999 | } 1000 | }), 1001 | 'vim:repeat-search': h({ 1002 | keys: 'n', 1003 | type: 'motion', 1004 | motion: 'findNext', 1005 | motionArgs: { 1006 | forward: true, 1007 | toJumplist: true 1008 | } 1009 | }), 1010 | 'vim:repeat-search-backwards': h({ 1011 | keys: 'N', 1012 | type: 'motion', 1013 | motion: 'findNext', 1014 | motionArgs: { 1015 | forward: false, 1016 | toJumplist: true 1017 | } 1018 | }), 1019 | 'vim:bracket-matching-motion': h({ 1020 | keys: '%', 1021 | type: 'motion', 1022 | motion: 'moveToMatchedSymbol', 1023 | motionArgs: { 1024 | inclusive: true, 1025 | toJumplist: true 1026 | } 1027 | }), 1028 | 'vim:ex-command': h({ 1029 | keys: ':', 1030 | type: 'ex' 1031 | }), 1032 | // normal mode 1033 | 'vim:activate-insert-mode': h({ 1034 | keys: 'i', 1035 | type: 'action', 1036 | action: 'enterInsertMode', 1037 | isEdit: true, 1038 | actionArgs: { 1039 | insertAt: 'inplace' 1040 | }, 1041 | context: 'normal' 1042 | }), 1043 | 'vim:activate-replace-mode': h({ 1044 | keys: 'R', 1045 | type: 'action', 1046 | action: 'enterInsertMode', 1047 | isEdit: true, 1048 | actionArgs: { 1049 | replace: true 1050 | } 1051 | }), 1052 | 'vim:activate-characterwise-visual-mode': h({ 1053 | keys: 'v', 1054 | type: 'action', 1055 | action: 'toggleVisualMode' 1056 | }), 1057 | 'vim:activate-linewise-visual-mode': h({ 1058 | keys: 'V', 1059 | type: 'action', 1060 | action: 'toggleVisualMode', 1061 | actionArgs: { 1062 | linewise: true 1063 | } 1064 | }), 1065 | 'vim:activate-blockwise-visual-mode': h({ 1066 | keys: '', 1067 | type: 'action', 1068 | action: 'toggleVisualMode', 1069 | actionArgs: { 1070 | blockwise: true 1071 | } 1072 | }), 1073 | 'vim:undo': h({ 1074 | keys: 'u', 1075 | type: 'action', 1076 | action: 'undo', 1077 | context: 'normal' 1078 | }), 1079 | 'vim:insert-above-with-newline': h({ 1080 | keys: 'O', 1081 | type: 'action', 1082 | action: 'newLineAndEnterInsertMode', 1083 | isEdit: true, 1084 | interlaceInsertRepeat: true, 1085 | actionArgs: { 1086 | after: false 1087 | }, 1088 | context: 'normal' 1089 | }), 1090 | 'vim:insert-below-with-newline': h({ 1091 | keys: 'o', 1092 | type: 'action', 1093 | action: 'newLineAndEnterInsertMode', 1094 | isEdit: true, 1095 | interlaceInsertRepeat: true, 1096 | actionArgs: { 1097 | after: true 1098 | }, 1099 | context: 'normal' 1100 | }), 1101 | 'vim:insert-after': h({ 1102 | keys: 'a', 1103 | type: 'action', 1104 | action: 'enterInsertMode', 1105 | isEdit: true, 1106 | actionArgs: { 1107 | insertAt: 'charAfter' 1108 | }, 1109 | context: 'normal' 1110 | }), 1111 | 'vim:insert-after-end-of-line': h({ 1112 | keys: 'A', 1113 | type: 'action', 1114 | action: 'enterInsertMode', 1115 | isEdit: true, 1116 | actionArgs: { 1117 | insertAt: 'eol' 1118 | }, 1119 | context: 'normal' 1120 | }), 1121 | 'vim:delete-right': h({ 1122 | keys: 'x', 1123 | type: 'operatorMotion', 1124 | operator: 'delete', 1125 | motion: 'moveByCharacters', 1126 | motionArgs: { 1127 | forward: true 1128 | }, 1129 | operatorMotionArgs: { 1130 | visualLine: false 1131 | } 1132 | }), 1133 | 'vim:delete-left': h({ 1134 | keys: 'X', 1135 | type: 'operatorMotion', 1136 | operator: 'delete', 1137 | motion: 'moveByCharacters', 1138 | motionArgs: { 1139 | forward: false 1140 | }, 1141 | operatorMotionArgs: { 1142 | visualLine: true 1143 | } 1144 | }), 1145 | 'vim:substitute': h({ 1146 | keys: 's', 1147 | type: 'keyToKey', 1148 | toKeys: 'cl', 1149 | context: 'normal' 1150 | }), 1151 | 'vim:substitute-visual': h({ 1152 | keys: 's', 1153 | type: 'keyToKey', 1154 | toKeys: 'c', 1155 | context: 'visual' 1156 | }), 1157 | 'vim:repeat': h({ 1158 | keys: '.', 1159 | type: 'action', 1160 | action: 'repeatLastEdit' 1161 | }), 1162 | 'vim:increase': h({ 1163 | keys: '', 1164 | type: 'action', 1165 | action: 'incrementNumberToken', 1166 | isEdit: true, 1167 | actionArgs: { 1168 | increase: true, 1169 | backtrack: false 1170 | } 1171 | }), 1172 | 'vim:decrease': h({ 1173 | keys: '', 1174 | type: 'action', 1175 | action: 'incrementNumberToken', 1176 | isEdit: true, 1177 | actionArgs: { 1178 | increase: false, 1179 | backtrack: false 1180 | } 1181 | }), 1182 | 'vim:register-prefix': b({ 1183 | keys: '"', 1184 | type: 'action', 1185 | action: 'setRegister' 1186 | }), 1187 | // insert mode 1188 | 'vim:delete-to-beginning-of-word': h({ 1189 | keys: '', 1190 | type: 'operatorMotion', 1191 | operator: 'delete', 1192 | motion: 'moveByWords', 1193 | motionArgs: { 1194 | forward: false, 1195 | wordEnd: false 1196 | }, 1197 | context: 'insert' 1198 | }), 1199 | // visual mode 1200 | 'vim:insert-at-start-of-target': h({ 1201 | keys: 'I', 1202 | type: 'action', 1203 | action: 'enterInsertMode', 1204 | isEdit: true, 1205 | actionArgs: { 1206 | insertAt: 'startOfSelectedArea' 1207 | }, 1208 | context: 'visual' 1209 | }), 1210 | 'vim:insert-at-end-of-target': h({ 1211 | keys: 'A', 1212 | type: 'action', 1213 | action: 'enterInsertMode', 1214 | isEdit: true, 1215 | actionArgs: { 1216 | insertAt: 'endOfSelectedArea' 1217 | }, 1218 | context: 'visual' 1219 | }), 1220 | 'vim:reverse-selections': h({ 1221 | keys: 'o', 1222 | type: 'motion', 1223 | motion: 'moveToOtherHighlightedEnd', 1224 | context: 'visual' 1225 | }), 1226 | 'vim:reverse-selections-at-same-line': h({ 1227 | keys: 'O', 1228 | type: 'motion', 1229 | motion: 'moveToOtherHighlightedEnd', 1230 | motionArgs: { 1231 | sameLine: true 1232 | }, 1233 | context: 'visual' 1234 | }) 1235 | }; 1236 | disposables.add(inkdrop.commands.add(wrapper, handlers)); 1237 | disposables.add(inkdrop.commands.add(document.querySelector('.mde-preview'), { 1238 | 'vim:move-to-start-of-file': ({ 1239 | target 1240 | }) => { 1241 | target.scrollTop = 0; 1242 | }, 1243 | 'vim:scroll-up': ({ 1244 | target 1245 | }) => { 1246 | target.scrollTop -= 30; 1247 | }, 1248 | 'vim:scroll-down': ({ 1249 | target 1250 | }) => { 1251 | target.scrollTop += 30; 1252 | }, 1253 | 'vim:scroll-half-screen-up': ({ 1254 | target 1255 | }) => { 1256 | target.scrollTop -= target.clientHeight / 2; 1257 | }, 1258 | 'vim:scroll-half-screen-down': ({ 1259 | target 1260 | }) => { 1261 | target.scrollTop += target.clientHeight / 2; 1262 | }, 1263 | 'vim:scroll-full-screen-up': ({ 1264 | target 1265 | }) => { 1266 | target.scrollTop -= target.clientHeight; 1267 | }, 1268 | 'vim:scroll-full-screen-down': ({ 1269 | target 1270 | }) => { 1271 | target.scrollTop += target.clientHeight; 1272 | }, 1273 | 'vim:move-to-line': ({ 1274 | target 1275 | }) => { 1276 | target.scrollTop = target.scrollHeight; 1277 | } 1278 | })); 1279 | wrapper.addEventListener('textInput', this.handleEditorTextInput); 1280 | wrapper.addEventListener('keydown', this.handleEditorKeyDown); 1281 | disposables.add(new _eventKit.Disposable(() => wrapper.removeEventListener('keydown', this.handleEditorKeyDown))); 1282 | this.disposables = disposables; 1283 | } 1284 | registerExCommands() { 1285 | const el = this.getCodeMirror().getWrapperElement(); 1286 | this.vim.defineEx('write', 'w', () => { 1287 | inkdrop.commands.dispatch(el, 'core:save-note'); 1288 | }); 1289 | this.vim.defineEx('next', 'n', () => { 1290 | inkdrop.commands.dispatch(el, 'core:open-next-note'); 1291 | }); 1292 | this.vim.defineEx('prev', '', () => { 1293 | inkdrop.commands.dispatch(el, 'core:open-prev-note'); 1294 | }); 1295 | this.vim.defineEx('preview', 'p', () => { 1296 | inkdrop.commands.dispatch(el, 'view:toggle-preview'); 1297 | }); 1298 | this.vim.defineEx('side-by-side', 'side', () => { 1299 | inkdrop.commands.dispatch(el, 'view:toggle-side-by-side'); 1300 | }); 1301 | } 1302 | getCodeMirror() { 1303 | return inkdrop.getActiveEditor().cm; 1304 | } 1305 | yankClipboard() { 1306 | const state = this.vim.getVimGlobalState(); 1307 | const text = _electron.clipboard.readText(); 1308 | const linewise = text.indexOf('\n') >= 0; 1309 | state.registerController.pushText('0', 'yank', text, linewise, true); 1310 | } 1311 | } 1312 | module.exports = new Plugin(); -------------------------------------------------------------------------------- /lib/vim.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vim.js","names":["Plugin","editor","activateMode","event","_opt","logger","debug","mode","cm","getCodeMirror","getWrapperElement","classList","remove","add","isInsertMode","isTrusted","text","data","replaceSelection","keyName","normalizeKeyName","key","vim","maybeInitVimState","isNumeric","ctrlKey","altKey","metaKey","shiftKey","match","target","getInputField","currentKeyStroke","inkdrop","keymaps","queuedKeystrokes","filter","k","startsWith","partialMatchCandidates","exactMatchCandidates","findMatchCandidates","partialMatches","findPartialMatches","exactMatches","findExactMatches","isBufferingKey","b","webkitMatchesSelector","inputState","hasOperatorOrMotion","motion","operator","length","selectedCharacter","keyBuffer","pendingCommand","stopBufferingKey","stopPropagation","preventDefault","pushRepeatDigit","keys","keysMatcher","exec","_event","yankClipboard","activate","CodeMirror","isEditorActive","getActiveEditor","onEditorLoad","handleEditorLoad","deactivate","disposables","dispose","deactivateMode","originalKeyMap","getOption","setOption","on","handleVimModeChange","handleFocusEditor","el","registerCommands","registerExCommands","off","startBufferingKey","command","customBufferingModeClass","wrapper","classes","Array","prototype","slice","apply","i","undefined","contains","bufferKey","vimKey","cmKeyToVimKey","CompositeDisposable","doKeyToKey","toKeys","substring","index","handleEditorKeyDown","KeyboardEvent","bindings","document","activeElement","commands","dispatch","h","e","Object","assign","state","operation","curOp","isVimOp","type","commandDispatcher","processCommand","Vim","suppressErrorLogging","console","error","originalEvent","p","visualMode","keyBinding","findKeyBindings","keystrokes","handlers","clearInputState","exitVisualMode","exitInsertMode","motionArgs","forward","cur","getCursor","line","ch","body","linewise","wordEnd","bigWord","inclusive","toJumplist","handler","getRepeat","toFirstChar","repeatOffset","explicitRepeat","action","actionArgs","position","context","isEdit","insertAt","textObjectInner","operatorArgs","indentRight","after","toLower","shouldMoveCursor","searchArgs","querySrc","wholeWordOnly","replace","blockwise","interlaceInsertRepeat","operatorMotionArgs","visualLine","increase","backtrack","sameLine","querySelector","scrollTop","clientHeight","scrollHeight","addEventListener","handleEditorTextInput","Disposable","removeEventListener","defineEx","getVimGlobalState","clipboard","readText","indexOf","registerController","pushText","module","exports"],"sources":["../src/vim.js"],"sourcesContent":["import { logger } from 'inkdrop'\nimport vimKeymap from './keymap'\nimport { CompositeDisposable, Disposable } from 'event-kit'\nimport CodeMirror from 'codemirror'\nimport { clipboard } from 'electron'\n\nclass Plugin {\n activate() {\n this.vim = vimKeymap(CodeMirror)\n if (inkdrop.isEditorActive()) {\n this.activateMode(inkdrop.getActiveEditor())\n }\n inkdrop.onEditorLoad(this.handleEditorLoad)\n }\n\n deactivate() {\n if (this.disposables) {\n this.disposables.dispose()\n }\n if (inkdrop.isEditorActive()) {\n this.deactivateMode(inkdrop.getActiveEditor())\n }\n }\n\n activateMode(editor) {\n const { cm } = editor\n this.originalKeyMap = cm.getOption('keyMap')\n cm.setOption('keyMap', 'vim')\n cm.on('vim-mode-change', this.handleVimModeChange)\n cm.on('focus', this.handleFocusEditor)\n\n const el = cm.getWrapperElement()\n el.classList.add('vim-mode', 'normal-mode')\n\n this.registerCommands()\n this.registerExCommands()\n }\n\n deactivateMode(editor) {\n const { cm } = editor\n if (cm && this.originalKeyMap) {\n cm.setOption('keyMap', this.originalKeyMap)\n cm.off('vim-mode-change', this.handleVimModeChange)\n const el = cm.getWrapperElement()\n el.classList.remove('vim-mode')\n }\n }\n\n normalizeKeyName(key) {\n switch (key) {\n case 'Enter':\n return 'enter'\n case ' ':\n return 'space'\n case 'ArrowRight':\n return 'right'\n case 'ArrowLeft':\n return 'left'\n case 'ArrowUp':\n return 'up'\n case 'ArrowDown':\n return 'down'\n default:\n if (key.match(/^[A-Z]$/)) {\n return `shift-${key}`\n }\n return key\n }\n }\n\n startBufferingKey(command, customBufferingModeClass) {\n const wrapper = this.getCodeMirror().getWrapperElement()\n logger.debug('Start key buffering')\n wrapper.classList.add('key-buffering')\n if (customBufferingModeClass) {\n wrapper.classList.add('key-buffering-' + customBufferingModeClass)\n }\n this.pendingCommand = command\n }\n\n stopBufferingKey() {\n logger.debug('Stop key buffering')\n const wrapper = this.getCodeMirror().getWrapperElement()\n const classes = Array.prototype.slice.apply(wrapper.classList)\n for (const i of classes) {\n if (i.startsWith('key-buffering')) {\n wrapper.classList.remove(i)\n }\n }\n this.pendingCommand = undefined\n }\n\n isBufferingKey() {\n const wrapper = this.getCodeMirror().getWrapperElement()\n return wrapper.classList.contains('key-buffering')\n }\n\n bufferKey(key) {\n const cm = this.getCodeMirror()\n const vim = this.vim.maybeInitVimState(cm)\n const vimKey = this.vim.cmKeyToVimKey(\"'\" + key + \"'\")\n vim.inputState.keyBuffer = vim.inputState.keyBuffer + vimKey\n logger.debug('keyBuffer:', vim.inputState.keyBuffer, vim.inputState)\n }\n\n isInsertMode() {\n const wrapper = this.getCodeMirror().getWrapperElement()\n return wrapper.classList.contains('insert-mode')\n }\n\n registerCommands() {\n const disposables = new CompositeDisposable()\n const editor = inkdrop.getActiveEditor()\n const { cm } = editor\n const wrapper = cm.getWrapperElement()\n\n const doKeyToKey = async command => {\n logger.debug('doKeyToKey:', command)\n let keys = command.toKeys\n while (keys) {\n // Pull off one command key, which is either a single character\n // or a special sequence wrapped in '<' and '>', e.g. ''.\n const match = /<\\w+-.+?>|<\\w+>|./.exec(keys)\n let key = match[0]\n logger.debug('key:', key)\n keys = keys.substring(match.index + key.length)\n\n if (this.isBufferingKey()) {\n this.handleEditorKeyDown(new KeyboardEvent('keydown', { key }))\n } else {\n if (key.match(/^[A-Z]$/)) {\n key = 'shift-' + key\n }\n const { exactMatchCandidates } = inkdrop.keymaps.findMatchCandidates([\n key\n ])\n logger.debug('exactMatchCandidates:', exactMatchCandidates)\n const bindings = inkdrop.keymaps.findExactMatches(\n exactMatchCandidates,\n document.activeElement\n )\n const b = bindings[0]\n if (b) {\n inkdrop.commands.dispatch(document.activeElement, b.command)\n } else {\n logger.debug('command not found for key:', key)\n }\n }\n }\n }\n\n // bind key to command\n const h = command => {\n return e => {\n logger.debug(\n 'command:',\n command,\n 'state:',\n Object.assign({}, cm.state.vim.inputState)\n )\n e.stopPropagation()\n const vim = this.vim.maybeInitVimState(cm)\n return cm.operation(() => {\n cm.curOp.isVimOp = true\n try {\n if (command.type === 'keyToKey') {\n doKeyToKey(command)\n } else {\n this.vim.commandDispatcher.processCommand(cm, vim, command)\n }\n } catch (e) {\n // clear VIM state in case it's in a bad state.\n cm.state.vim = undefined\n this.vim.maybeInitVimState(cm)\n if (!CodeMirror.Vim.suppressErrorLogging) {\n console.error(e)\n }\n throw e\n }\n return true\n })\n }\n }\n // bind keystroke to command\n const b = command => {\n return e => {\n logger.debug(\n 'buffer command:',\n command,\n 'state:',\n Object.assign({}, cm.state.vim.inputState),\n e.originalEvent\n )\n this.startBufferingKey(h(command), 'command')\n this.bufferKey(e.originalEvent.key)\n }\n }\n // bind keystroke to operator\n const p = command => {\n return e => {\n logger.debug(\n 'operator command:',\n command,\n 'state:',\n Object.assign({}, cm.state.vim.inputState),\n e.originalEvent,\n e\n )\n const vim = this.vim.maybeInitVimState(cm)\n if (\n !vim.inputState.operator &&\n !this.isBufferingKey() &&\n !vim.visualMode\n ) {\n this.startBufferingKey(e => {\n const el = cm.getInputField()\n const keyName = this.normalizeKeyName(e.key)\n const keyBinding = inkdrop.keymaps.findKeyBindings({\n keystrokes: keyName,\n target: el\n })\n if (keyBinding.length > 0) {\n inkdrop.commands.dispatch(el, keyBinding[0].command)\n return true\n } else {\n return false\n }\n }, 'operator')\n if (e.originalEvent) {\n this.bufferKey(e.originalEvent.key)\n } else {\n logger.debug('buffer key:', command.keys)\n this.bufferKey(command.keys)\n }\n }\n return h(command)(e)\n }\n }\n const handlers = {\n 'vim:native!': () => {},\n 'vim:reset-normal-mode': e => {\n CodeMirror.Vim.clearInputState(cm)\n this.stopBufferingKey()\n e.stopPropagation()\n },\n 'vim:exit-visual-mode': e => {\n logger.debug('exit-visual-mode')\n CodeMirror.Vim.clearInputState(cm)\n CodeMirror.Vim.exitVisualMode(cm)\n this.stopBufferingKey()\n e.stopPropagation()\n },\n 'vim:exit-insert-mode': e => {\n logger.debug('exit-insert-mode')\n CodeMirror.Vim.clearInputState(cm)\n CodeMirror.Vim.exitInsertMode(cm)\n this.stopBufferingKey()\n e.stopPropagation()\n },\n 'vim:move-left': h({\n keys: 'h',\n type: 'motion',\n motion: 'moveByCharacters',\n motionArgs: { forward: false }\n }),\n 'vim:move-right': h({\n keys: 'l',\n type: 'motion',\n motion: 'moveByCharacters',\n motionArgs: { forward: true }\n }),\n 'vim:move-up': (command => {\n return e => {\n const cur = cm.getCursor()\n if (cur.line === 0 && cur.ch === 0) {\n e.stopPropagation()\n inkdrop.commands.dispatch(document.body, 'editor:title:focus')\n } else {\n command(e)\n }\n }\n })(\n h({\n keys: 'k',\n type: 'motion',\n motion: 'moveByLines',\n motionArgs: { forward: false, linewise: true }\n })\n ),\n 'vim:move-down': h({\n keys: 'j',\n type: 'motion',\n motion: 'moveByLines',\n motionArgs: { forward: true, linewise: true }\n }),\n 'vim:move-up-by-display-lines': h({\n keys: 'gk',\n type: 'motion',\n motion: 'moveByDisplayLines',\n motionArgs: { forward: false }\n }),\n 'vim:move-down-by-display-lines': h({\n keys: 'gj',\n type: 'motion',\n motion: 'moveByDisplayLines',\n motionArgs: { forward: true }\n }),\n\n 'vim:move-to-next-word': h({\n keys: 'w',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: true, wordEnd: false }\n }),\n 'vim:move-to-next-whole-word': h({\n keys: 'W',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: true, wordEnd: false, bigWord: true }\n }),\n 'vim:move-to-end-of-word': h({\n keys: 'e',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: true, wordEnd: true, inclusive: true }\n }),\n 'vim:move-to-previous-end-of-word': h({\n keys: 'ge',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: false, wordEnd: true, inclusive: true }\n }),\n 'vim:move-to-end-of-whole-word': h({\n keys: 'E',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: {\n forward: true,\n wordEnd: true,\n bigWord: true,\n inclusive: true\n }\n }),\n 'vim:move-to-previous-end-of-whole-word': h({\n keys: 'gE',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: {\n forward: false,\n wordEnd: true,\n bigWord: true,\n inclusive: true\n }\n }),\n 'vim:move-to-previous-word': h({\n keys: 'b',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: false, wordEnd: false }\n }),\n 'vim:move-to-previous-whole-word': h({\n keys: 'B',\n type: 'motion',\n motion: 'moveByWords',\n motionArgs: { forward: false, wordEnd: false, bigWord: true }\n }),\n 'vim:move-to-next-paragraph': h({\n keys: '}',\n type: 'motion',\n motion: 'moveByParagraph',\n motionArgs: { forward: true, toJumplist: true }\n }),\n 'vim:move-to-previous-paragraph': h({\n keys: '{',\n type: 'motion',\n motion: 'moveByParagraph',\n motionArgs: { forward: false, toJumplist: true }\n }),\n 'vim:move-to-beginning-of-line-with-zero': (() => {\n const handler = h({\n keys: '0',\n type: 'motion',\n motion: 'moveToStartOfLine'\n })\n return e => {\n const vim = this.vim.maybeInitVimState(cm)\n if (vim.inputState.getRepeat() > 0) {\n vim.inputState.pushRepeatDigit('0')\n } else {\n return handler(e)\n }\n }\n })(),\n 'vim:move-to-beginning-of-line': h({\n keys: '0',\n type: 'motion',\n motion: 'moveToStartOfLine'\n }),\n 'vim:move-to-first-character-of-line': h({\n keys: '^',\n type: 'motion',\n motion: 'moveToFirstNonWhiteSpaceCharacter'\n }),\n 'vim:move-to-first-character-of-line-and-down': h({\n keys: '_',\n type: 'motion',\n motion: 'moveByLines',\n motionArgs: { forward: true, toFirstChar: true, repeatOffset: -1 }\n }),\n 'vim:move-to-last-character-of-line': h({\n keys: '$',\n type: 'motion',\n motion: 'moveToEol',\n motionArgs: { inclusive: true }\n }),\n 'vim:move-to-last-nonblank-character-of-line-and-down': () => {\n '???'\n },\n 'vim:move-to-first-character-of-line-up': h({\n keys: '-',\n type: 'motion',\n motion: 'moveByLines',\n motionArgs: { forward: false, toFirstChar: true }\n }),\n 'vim:move-to-first-character-of-line-down': h({\n keys: '+',\n type: 'motion',\n motion: 'moveByLines',\n motionArgs: { forward: true, toFirstChar: true }\n }),\n\n 'vim:move-to-start-of-file': h({\n keys: 'gg',\n type: 'motion',\n motion: 'moveToLineOrEdgeOfDocument',\n motionArgs: {\n forward: false,\n explicitRepeat: true,\n linewise: true,\n toJumplist: true\n }\n }),\n 'vim:scroll-half-screen-up': h({\n keys: '',\n type: 'motion',\n motion: 'moveByScroll',\n motionArgs: { forward: false, explicitRepeat: true }\n }),\n 'vim:scroll-full-screen-up': h({\n keys: '',\n type: 'motion',\n motion: 'moveByPage',\n motionArgs: { forward: false }\n }),\n 'vim:scroll-half-screen-down': h({\n keys: '',\n type: 'motion',\n motion: 'moveByScroll',\n motionArgs: { forward: true, explicitRepeat: true }\n }),\n 'vim:scroll-full-screen-down': h({\n keys: '',\n type: 'motion',\n motion: 'moveByPage',\n motionArgs: { forward: true }\n }),\n 'vim:scroll-down': h({\n keys: '',\n type: 'action',\n action: 'scroll',\n actionArgs: { forward: true, linewise: true }\n }),\n 'vim:scroll-up': h({\n keys: '',\n type: 'action',\n action: 'scroll',\n actionArgs: { forward: false, linewise: true }\n }),\n 'vim:scroll-cursor-to-top': h({\n keys: 'z',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'top' },\n motion: 'moveToFirstNonWhiteSpaceCharacter'\n }),\n 'vim:scroll-cursor-to-top-leave': h({\n keys: 'zt',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'top' }\n }),\n 'vim:scroll-cursor-to-middle': h({\n keys: 'z.',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'center' },\n motion: 'moveToFirstNonWhiteSpaceCharacter'\n }),\n 'vim:scroll-cursor-to-middle-leave': h({\n keys: 'zz',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'center' }\n }),\n 'vim:scroll-cursor-to-bottom': h({\n keys: 'z-',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'bottom' }\n }),\n 'vim:scroll-cursor-to-bottom-leave': h({\n keys: 'zb',\n type: 'action',\n action: 'scrollToCursor',\n actionArgs: { position: 'bottom' },\n motion: 'moveToFirstNonWhiteSpaceCharacter'\n }),\n 'vim:move-to-line': h({\n keys: 'G',\n type: 'motion',\n motion: 'moveToLineOrEdgeOfDocument',\n motionArgs: {\n forward: true,\n explicitRepeat: true,\n linewise: true,\n toJumplist: true\n }\n }),\n 'vim:move-to-top-of-screen': h({\n keys: 'H',\n type: 'motion',\n motion: 'moveToTopLine',\n motionArgs: { linewise: true, toJumplist: true }\n }),\n 'vim:move-to-bottom-of-screen': h({\n keys: 'L',\n type: 'motion',\n motion: 'moveToBottomLine',\n motionArgs: { linewise: true, toJumplist: true }\n }),\n 'vim:move-to-middle-of-screen': h({\n keys: 'M',\n type: 'motion',\n motion: 'moveToMiddleLine',\n motionArgs: { linewise: true, toJumplist: true }\n }),\n\n 'vim:delete': p({ keys: 'd', type: 'operator', operator: 'delete' }),\n 'vim:delete-to-last-character-of-line': h({\n keys: 'D',\n type: 'operatorMotion',\n operator: 'delete',\n motion: 'moveToEol',\n motionArgs: { inclusive: true },\n context: 'normal'\n }),\n 'vim:change': p({ keys: 'c', type: 'operator', operator: 'change' }),\n 'vim:change-to-last-character-of-line': h({\n keys: 'C',\n type: 'operatorMotion',\n operator: 'change',\n motion: 'moveToEol',\n motionArgs: { inclusive: true },\n context: 'normal'\n }),\n 'vim:substitute-line': h({\n keys: 'S',\n type: 'keyToKey',\n toKeys: 'cc',\n context: 'normal'\n }),\n 'vim:substitute-line-visual': h({\n keys: 'S',\n type: 'keyToKey',\n toKeys: 'VdO',\n context: 'visual'\n }),\n 'vim:replace': b({\n keys: 'r',\n type: 'action',\n action: 'replace',\n isEdit: true\n }),\n 'vim:insert-at-beginning-of-line': h({\n keys: 'I',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'firstNonBlank' },\n context: 'normal'\n }),\n 'vim:text-object-manipulation-inner': b({\n keys: 'i',\n type: 'motion',\n motion: 'textObjectManipulation',\n motionArgs: { textObjectInner: true }\n }),\n 'vim:text-object-manipulation': b({\n keys: 'a',\n type: 'motion',\n motion: 'textObjectManipulation'\n }),\n 'vim:indent': p({\n keys: '>',\n type: 'operator',\n operator: 'indent',\n operatorArgs: { indentRight: true }\n }),\n 'vim:outdent': p({\n keys: '<',\n type: 'operator',\n operator: 'indent',\n operatorArgs: { indentRight: false }\n }),\n 'vim:auto-indent': () => {\n 'not supported'\n },\n 'vim:reselect-last-selection': h({\n keys: 'gv',\n type: 'action',\n action: 'reselectLastSelection'\n }),\n 'vim:join': h({\n keys: 'J',\n type: 'action',\n action: 'joinLines',\n isEdit: true\n }),\n\n 'vim:yank': p({ keys: 'y', type: 'operator', operator: 'yank' }),\n 'vim:yank-line': h({\n keys: 'Y',\n type: 'operatorMotion',\n operator: 'yank',\n motion: 'expandToLine',\n motionArgs: { linewise: true },\n context: 'normal'\n }),\n 'vim:put-before': h({\n keys: 'P',\n type: 'action',\n action: 'paste',\n isEdit: true,\n actionArgs: { after: false, isEdit: true }\n }),\n 'vim:put-after': h({\n keys: 'p',\n type: 'action',\n action: 'paste',\n isEdit: true,\n actionArgs: { after: true, isEdit: true }\n }),\n\n 'vim:toggle-case': h({\n keys: 'g~',\n type: 'operator',\n operator: 'changeCase'\n }),\n 'vim:upper-case': h({\n keys: 'gU',\n type: 'operator',\n operator: 'changeCase',\n operatorArgs: { toLower: false },\n isEdit: true\n }),\n 'vim:lower-case': h({\n keys: 'gu',\n type: 'operator',\n operator: 'changeCase',\n operatorArgs: { toLower: true },\n isEdit: true\n }),\n 'vim:toggle-case-now': h({\n keys: '~',\n type: 'operatorMotion',\n operator: 'changeCase',\n motion: 'moveByCharacters',\n motionArgs: { forward: true },\n operatorArgs: { shouldMoveCursor: true },\n context: 'normal'\n }),\n\n 'vim:mark': b({\n keys: 'm',\n type: 'action',\n action: 'setMark'\n }),\n 'vim:move-to-mark-literal': b({\n keys: '`',\n type: 'motion',\n motion: 'goToMark',\n motionArgs: { toJumplist: true }\n }),\n 'vim:move-to-mark': b({\n keys: \"'\",\n type: 'motion',\n motion: 'goToMark',\n motionArgs: { toJumplist: true, linewise: true }\n }),\n\n 'vim:find': b({\n keys: 'f',\n type: 'motion',\n motion: 'moveToCharacter',\n motionArgs: { forward: true, inclusive: true }\n }),\n 'vim:find-backwards': b({\n keys: 'F',\n type: 'motion',\n motion: 'moveToCharacter',\n motionArgs: { forward: false }\n }),\n 'vim:till': b({\n keys: 't',\n type: 'motion',\n motion: 'moveTillCharacter',\n motionArgs: { forward: true, inclusive: true }\n }),\n 'vim:till-backwards': b({\n keys: 'T',\n type: 'motion',\n motion: 'moveTillCharacter',\n motionArgs: { forward: false }\n }),\n 'vim:repeat-find': h({\n keys: ';',\n type: 'motion',\n motion: 'repeatLastCharacterSearch',\n motionArgs: { forward: true }\n }),\n 'vim:repeat-find-reverse': h({\n keys: ',',\n type: 'motion',\n motion: 'repeatLastCharacterSearch',\n motionArgs: { forward: false }\n }),\n\n 'vim:search': h({\n keys: '/',\n type: 'search',\n searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true }\n }),\n 'vim:reverse-search': h({\n keys: '?',\n type: 'search',\n searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true }\n }),\n 'vim:search-current-word': h({\n keys: '*',\n type: 'search',\n searchArgs: {\n forward: true,\n querySrc: 'wordUnderCursor',\n wholeWordOnly: true,\n toJumplist: true\n }\n }),\n 'vim:reverse-search-current-word': h({\n keys: '#',\n type: 'search',\n searchArgs: {\n forward: false,\n querySrc: 'wordUnderCursor',\n wholeWordOnly: true,\n toJumplist: true\n }\n }),\n 'vim:repeat-search': h({\n keys: 'n',\n type: 'motion',\n motion: 'findNext',\n motionArgs: { forward: true, toJumplist: true }\n }),\n 'vim:repeat-search-backwards': h({\n keys: 'N',\n type: 'motion',\n motion: 'findNext',\n motionArgs: { forward: false, toJumplist: true }\n }),\n\n 'vim:bracket-matching-motion': h({\n keys: '%',\n type: 'motion',\n motion: 'moveToMatchedSymbol',\n motionArgs: { inclusive: true, toJumplist: true }\n }),\n\n 'vim:ex-command': h({ keys: ':', type: 'ex' }),\n\n // normal mode\n 'vim:activate-insert-mode': h({\n keys: 'i',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'inplace' },\n context: 'normal'\n }),\n 'vim:activate-replace-mode': h({\n keys: 'R',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { replace: true }\n }),\n 'vim:activate-characterwise-visual-mode': h({\n keys: 'v',\n type: 'action',\n action: 'toggleVisualMode'\n }),\n 'vim:activate-linewise-visual-mode': h({\n keys: 'V',\n type: 'action',\n action: 'toggleVisualMode',\n actionArgs: { linewise: true }\n }),\n 'vim:activate-blockwise-visual-mode': h({\n keys: '',\n type: 'action',\n action: 'toggleVisualMode',\n actionArgs: { blockwise: true }\n }),\n\n 'vim:undo': h({\n keys: 'u',\n type: 'action',\n action: 'undo',\n context: 'normal'\n }),\n\n 'vim:insert-above-with-newline': h({\n keys: 'O',\n type: 'action',\n action: 'newLineAndEnterInsertMode',\n isEdit: true,\n interlaceInsertRepeat: true,\n actionArgs: { after: false },\n context: 'normal'\n }),\n 'vim:insert-below-with-newline': h({\n keys: 'o',\n type: 'action',\n action: 'newLineAndEnterInsertMode',\n isEdit: true,\n interlaceInsertRepeat: true,\n actionArgs: { after: true },\n context: 'normal'\n }),\n 'vim:insert-after': h({\n keys: 'a',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'charAfter' },\n context: 'normal'\n }),\n 'vim:insert-after-end-of-line': h({\n keys: 'A',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'eol' },\n context: 'normal'\n }),\n 'vim:delete-right': h({\n keys: 'x',\n type: 'operatorMotion',\n operator: 'delete',\n motion: 'moveByCharacters',\n motionArgs: { forward: true },\n operatorMotionArgs: { visualLine: false }\n }),\n 'vim:delete-left': h({\n keys: 'X',\n type: 'operatorMotion',\n operator: 'delete',\n motion: 'moveByCharacters',\n motionArgs: { forward: false },\n operatorMotionArgs: { visualLine: true }\n }),\n 'vim:substitute': h({\n keys: 's',\n type: 'keyToKey',\n toKeys: 'cl',\n context: 'normal'\n }),\n 'vim:substitute-visual': h({\n keys: 's',\n type: 'keyToKey',\n toKeys: 'c',\n context: 'visual'\n }),\n 'vim:repeat': h({\n keys: '.',\n type: 'action',\n action: 'repeatLastEdit'\n }),\n\n 'vim:increase': h({\n keys: '',\n type: 'action',\n action: 'incrementNumberToken',\n isEdit: true,\n actionArgs: { increase: true, backtrack: false }\n }),\n 'vim:decrease': h({\n keys: '',\n type: 'action',\n action: 'incrementNumberToken',\n isEdit: true,\n actionArgs: { increase: false, backtrack: false }\n }),\n\n 'vim:register-prefix': b({\n keys: '\"',\n type: 'action',\n action: 'setRegister'\n }),\n\n // insert mode\n 'vim:delete-to-beginning-of-word': h({\n keys: '',\n type: 'operatorMotion',\n operator: 'delete',\n motion: 'moveByWords',\n motionArgs: { forward: false, wordEnd: false },\n context: 'insert'\n }),\n\n // visual mode\n 'vim:insert-at-start-of-target': h({\n keys: 'I',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'startOfSelectedArea' },\n context: 'visual'\n }),\n 'vim:insert-at-end-of-target': h({\n keys: 'A',\n type: 'action',\n action: 'enterInsertMode',\n isEdit: true,\n actionArgs: { insertAt: 'endOfSelectedArea' },\n context: 'visual'\n }),\n 'vim:reverse-selections': h({\n keys: 'o',\n type: 'motion',\n motion: 'moveToOtherHighlightedEnd',\n context: 'visual'\n }),\n 'vim:reverse-selections-at-same-line': h({\n keys: 'O',\n type: 'motion',\n motion: 'moveToOtherHighlightedEnd',\n motionArgs: { sameLine: true },\n context: 'visual'\n })\n }\n disposables.add(inkdrop.commands.add(wrapper, handlers))\n disposables.add(\n inkdrop.commands.add(document.querySelector('.mde-preview'), {\n 'vim:move-to-start-of-file': ({ target }) => {\n target.scrollTop = 0\n },\n 'vim:scroll-up': ({ target }) => {\n target.scrollTop -= 30\n },\n 'vim:scroll-down': ({ target }) => {\n target.scrollTop += 30\n },\n 'vim:scroll-half-screen-up': ({ target }) => {\n target.scrollTop -= target.clientHeight / 2\n },\n 'vim:scroll-half-screen-down': ({ target }) => {\n target.scrollTop += target.clientHeight / 2\n },\n 'vim:scroll-full-screen-up': ({ target }) => {\n target.scrollTop -= target.clientHeight\n },\n 'vim:scroll-full-screen-down': ({ target }) => {\n target.scrollTop += target.clientHeight\n },\n 'vim:move-to-line': ({ target }) => {\n target.scrollTop = target.scrollHeight\n }\n })\n )\n wrapper.addEventListener('textInput', this.handleEditorTextInput)\n wrapper.addEventListener('keydown', this.handleEditorKeyDown)\n disposables.add(\n new Disposable(() =>\n wrapper.removeEventListener('keydown', this.handleEditorKeyDown)\n )\n )\n\n this.disposables = disposables\n }\n\n registerExCommands() {\n const el = this.getCodeMirror().getWrapperElement()\n this.vim.defineEx('write', 'w', () => {\n inkdrop.commands.dispatch(el, 'core:save-note')\n })\n this.vim.defineEx('next', 'n', () => {\n inkdrop.commands.dispatch(el, 'core:open-next-note')\n })\n this.vim.defineEx('prev', '', () => {\n inkdrop.commands.dispatch(el, 'core:open-prev-note')\n })\n this.vim.defineEx('preview', 'p', () => {\n inkdrop.commands.dispatch(el, 'view:toggle-preview')\n })\n this.vim.defineEx('side-by-side', 'side', () => {\n inkdrop.commands.dispatch(el, 'view:toggle-side-by-side')\n })\n }\n\n getCodeMirror() {\n return inkdrop.getActiveEditor().cm\n }\n\n yankClipboard() {\n const state = this.vim.getVimGlobalState()\n const text = clipboard.readText()\n const linewise = text.indexOf('\\n') >= 0\n state.registerController.pushText('0', 'yank', text, linewise, true)\n }\n\n handleEditorLoad = editor => {\n this.activateMode(editor)\n }\n\n handleVimModeChange = (event, _opt) => {\n logger.debug('vim mode changed:', event)\n const { mode } = event\n const cm = this.getCodeMirror()\n cm.getWrapperElement().classList.remove('insert-mode')\n cm.getWrapperElement().classList.remove('visual-mode')\n cm.getWrapperElement().classList.remove('normal-mode')\n cm.getWrapperElement().classList.remove('replace-mode')\n switch (mode) {\n case 'normal':\n cm.getWrapperElement().classList.add('normal-mode')\n break\n case 'visual':\n cm.getWrapperElement().classList.add('visual-mode')\n break\n case 'replace':\n cm.getWrapperElement().classList.add('replace-mode')\n break\n case 'insert':\n cm.getWrapperElement().classList.add('insert-mode')\n break\n }\n }\n\n handleEditorTextInput = event => {\n // only process if the event is fired via EventTarget.dispatchEvent()\n if (this.isInsertMode() && !event.isTrusted) {\n logger.debug('handle text input:', event)\n const text = event.data\n const cm = this.getCodeMirror()\n cm.replaceSelection(text)\n }\n }\n\n handleEditorKeyDown = event => {\n const keyName = this.normalizeKeyName(event.key)\n const cm = this.getCodeMirror()\n const vim = this.vim.maybeInitVimState(cm)\n const isNumeric =\n !event.ctrlKey &&\n !event.altKey &&\n !event.metaKey &&\n !event.shiftKey &&\n keyName.match(/^\\d$/)\n\n const target = cm.getInputField()\n const currentKeyStroke = [\n ...inkdrop.keymaps.queuedKeystrokes.filter(k => !k.startsWith('^')),\n keyName\n ]\n const { partialMatchCandidates, exactMatchCandidates } =\n inkdrop.keymaps.findMatchCandidates(currentKeyStroke)\n const partialMatches = inkdrop.keymaps.findPartialMatches(\n partialMatchCandidates,\n target\n )\n const exactMatches = inkdrop.keymaps.findExactMatches(\n exactMatchCandidates,\n target\n )\n logger.debug('handleEditorKeyDown: currentKeyStroke:', currentKeyStroke)\n logger.debug('handleEditorKeyDown: exactMatches:', exactMatches)\n logger.debug('handleEditorKeyDown: partialMatches:', partialMatches)\n\n if (this.isBufferingKey()) {\n logger.debug('handleEditorKeyDown: handle key buffering:', keyName, event)\n const b = cm\n .getInputField()\n .webkitMatchesSelector(\n '.CodeMirror.vim-mode:not(.insert-mode) textarea'\n )\n logger.debug('handleEditorKeyDown: keybinding check:', exactMatches, b)\n\n if (\n keyName !== 'Ctrl' &&\n keyName !== 'Alt' &&\n keyName !== 'Shift' &&\n keyName !== 'Meta'\n ) {\n const { inputState } = vim\n const hasOperatorOrMotion = inputState.motion || inputState.operator\n if (\n (event.key.length === 1 || keyName === 'space') &&\n (!isNumeric || !hasOperatorOrMotion)\n ) {\n inputState.selectedCharacter = event.key\n inputState.keyBuffer = ''\n\n if (exactMatches.length === 0 && partialMatches.length === 0) {\n const { pendingCommand } = this\n this.stopBufferingKey()\n\n if (typeof pendingCommand === 'function') {\n if (pendingCommand(event)) {\n event.stopPropagation()\n event.preventDefault()\n }\n }\n }\n } else if (isNumeric) {\n vim.inputState.pushRepeatDigit(keyName)\n }\n }\n } else if (!this.isInsertMode()) {\n if (isNumeric) {\n if (exactMatches.length === 0) {\n vim.inputState.pushRepeatDigit(keyName)\n }\n } else {\n // push key buffer to the repeat digit\n const keys = vim.inputState.keyBuffer\n vim.inputState.keyBuffer = ''\n const keysMatcher = /^(\\d*)(.*)$/.exec(keys)\n if (keysMatcher[1] && keysMatcher[1] !== '0') {\n vim.inputState.pushRepeatDigit(keysMatcher[1])\n }\n }\n }\n }\n\n handleFocusEditor = _event => {\n this.yankClipboard()\n }\n}\n\nmodule.exports = new Plugin()\n"],"mappings":"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"} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vim", 3 | "main": "./lib/vim", 4 | "version": "2.2.2", 5 | "description": "vim keybindings", 6 | "keywords": [], 7 | "repository": "https://github.com/inkdropapp/inkdrop-vim", 8 | "license": "MIT", 9 | "scripts": { 10 | "build": "babel src/ -d lib/", 11 | "dev": "babel src/ -s -d lib/ --watch" 12 | }, 13 | "engines": { 14 | "inkdrop": "^5.x" 15 | }, 16 | "devDependencies": { 17 | "@babel/cli": "^7.16.0", 18 | "@babel/core": "^7.16.0", 19 | "@babel/eslint-parser": "^7.16.3", 20 | "@babel/plugin-proposal-class-properties": "^7.16.0", 21 | "@babel/preset-env": "^7.16.4", 22 | "@babel/preset-react": "^7.16.0", 23 | "eslint": "^8.3.0", 24 | "eslint-config-prettier": "^8.3.0", 25 | "prettier": "^2.5.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const options = { 2 | arrowParens: 'avoid', 3 | singleQuote: true, 4 | bracketSpacing: true, 5 | endOfLine: 'lf', 6 | semi: false, 7 | tabWidth: 2, 8 | trailingComma: 'none' 9 | } 10 | 11 | module.exports = options 12 | -------------------------------------------------------------------------------- /src/vim.js: -------------------------------------------------------------------------------- 1 | import { logger } from 'inkdrop' 2 | import vimKeymap from './keymap' 3 | import { CompositeDisposable, Disposable } from 'event-kit' 4 | import CodeMirror from 'codemirror' 5 | import { clipboard } from 'electron' 6 | 7 | class Plugin { 8 | config = { 9 | seamlessJumpToTitle: { 10 | title: 'Seamlessly jump to note title', 11 | type: 'boolean', 12 | description: 13 | 'Focus jumps from the editor to the note title bar by `vim:move-up` command', 14 | default: false 15 | } 16 | } 17 | 18 | activate() { 19 | this.vim = vimKeymap(CodeMirror) 20 | if (inkdrop.isEditorActive()) { 21 | this.activateMode(inkdrop.getActiveEditor()) 22 | } 23 | inkdrop.onEditorLoad(this.handleEditorLoad) 24 | } 25 | 26 | deactivate() { 27 | if (this.disposables) { 28 | this.disposables.dispose() 29 | } 30 | if (inkdrop.isEditorActive()) { 31 | this.deactivateMode(inkdrop.getActiveEditor()) 32 | } 33 | } 34 | 35 | activateMode(editor) { 36 | const { cm } = editor 37 | this.originalKeyMap = cm.getOption('keyMap') 38 | cm.setOption('keyMap', 'vim') 39 | cm.on('vim-mode-change', this.handleVimModeChange) 40 | cm.on('focus', this.handleFocusEditor) 41 | 42 | const el = cm.getWrapperElement() 43 | el.classList.add('vim-mode', 'normal-mode') 44 | 45 | this.registerCommands() 46 | this.registerExCommands() 47 | } 48 | 49 | deactivateMode(editor) { 50 | const { cm } = editor 51 | if (cm && this.originalKeyMap) { 52 | cm.setOption('keyMap', this.originalKeyMap) 53 | cm.off('vim-mode-change', this.handleVimModeChange) 54 | const el = cm.getWrapperElement() 55 | el.classList.remove('vim-mode') 56 | } 57 | } 58 | 59 | normalizeKeyName(key) { 60 | switch (key) { 61 | case 'Enter': 62 | return 'enter' 63 | case ' ': 64 | return 'space' 65 | case 'ArrowRight': 66 | return 'right' 67 | case 'ArrowLeft': 68 | return 'left' 69 | case 'ArrowUp': 70 | return 'up' 71 | case 'ArrowDown': 72 | return 'down' 73 | default: 74 | if (key.match(/^[A-Z]$/)) { 75 | return `shift-${key}` 76 | } 77 | return key 78 | } 79 | } 80 | 81 | startBufferingKey(command, customBufferingModeClass) { 82 | const wrapper = this.getCodeMirror().getWrapperElement() 83 | logger.debug('Start key buffering') 84 | wrapper.classList.add('key-buffering') 85 | if (customBufferingModeClass) { 86 | wrapper.classList.add('key-buffering-' + customBufferingModeClass) 87 | } 88 | this.pendingCommand = command 89 | } 90 | 91 | stopBufferingKey() { 92 | logger.debug('Stop key buffering') 93 | const wrapper = this.getCodeMirror().getWrapperElement() 94 | const classes = Array.prototype.slice.apply(wrapper.classList) 95 | for (const i of classes) { 96 | if (i.startsWith('key-buffering')) { 97 | wrapper.classList.remove(i) 98 | } 99 | } 100 | this.pendingCommand = undefined 101 | } 102 | 103 | isBufferingKey() { 104 | const wrapper = this.getCodeMirror().getWrapperElement() 105 | return wrapper.classList.contains('key-buffering') 106 | } 107 | 108 | bufferKey(key) { 109 | const cm = this.getCodeMirror() 110 | const vim = this.vim.maybeInitVimState(cm) 111 | const vimKey = this.vim.cmKeyToVimKey("'" + key + "'") 112 | vim.inputState.keyBuffer = vim.inputState.keyBuffer + vimKey 113 | logger.debug('keyBuffer:', vim.inputState.keyBuffer, vim.inputState) 114 | } 115 | 116 | isInsertMode() { 117 | const wrapper = this.getCodeMirror().getWrapperElement() 118 | return wrapper.classList.contains('insert-mode') 119 | } 120 | 121 | registerCommands() { 122 | const disposables = new CompositeDisposable() 123 | const editor = inkdrop.getActiveEditor() 124 | const { cm } = editor 125 | const wrapper = cm.getWrapperElement() 126 | 127 | const doKeyToKey = async command => { 128 | logger.debug('doKeyToKey:', command) 129 | let keys = command.toKeys 130 | while (keys) { 131 | // Pull off one command key, which is either a single character 132 | // or a special sequence wrapped in '<' and '>', e.g. ''. 133 | const match = /<\w+-.+?>|<\w+>|./.exec(keys) 134 | let key = match[0] 135 | logger.debug('key:', key) 136 | keys = keys.substring(match.index + key.length) 137 | 138 | if (this.isBufferingKey()) { 139 | this.handleEditorKeyDown(new KeyboardEvent('keydown', { key })) 140 | } else { 141 | if (key.match(/^[A-Z]$/)) { 142 | key = 'shift-' + key 143 | } 144 | const { exactMatchCandidates } = inkdrop.keymaps.findMatchCandidates([ 145 | key 146 | ]) 147 | logger.debug('exactMatchCandidates:', exactMatchCandidates) 148 | const bindings = inkdrop.keymaps.findExactMatches( 149 | exactMatchCandidates, 150 | document.activeElement 151 | ) 152 | const b = bindings[0] 153 | if (b) { 154 | inkdrop.commands.dispatch(document.activeElement, b.command) 155 | } else { 156 | logger.debug('command not found for key:', key) 157 | } 158 | } 159 | } 160 | } 161 | 162 | // bind key to command 163 | const h = command => { 164 | return e => { 165 | logger.debug( 166 | 'command:', 167 | command, 168 | 'state:', 169 | Object.assign({}, cm.state.vim.inputState) 170 | ) 171 | e.stopPropagation() 172 | const vim = this.vim.maybeInitVimState(cm) 173 | return cm.operation(() => { 174 | cm.curOp.isVimOp = true 175 | try { 176 | if (command.type === 'keyToKey') { 177 | doKeyToKey(command) 178 | } else { 179 | this.vim.commandDispatcher.processCommand(cm, vim, command) 180 | } 181 | } catch (e) { 182 | // clear VIM state in case it's in a bad state. 183 | cm.state.vim = undefined 184 | this.vim.maybeInitVimState(cm) 185 | if (!CodeMirror.Vim.suppressErrorLogging) { 186 | console.error(e) 187 | } 188 | throw e 189 | } 190 | return true 191 | }) 192 | } 193 | } 194 | // bind keystroke to command 195 | const b = command => { 196 | return e => { 197 | logger.debug( 198 | 'buffer command:', 199 | command, 200 | 'state:', 201 | Object.assign({}, cm.state.vim.inputState), 202 | e.originalEvent 203 | ) 204 | this.startBufferingKey(h(command), 'command') 205 | this.bufferKey(e.originalEvent.key) 206 | } 207 | } 208 | // bind keystroke to operator 209 | const p = command => { 210 | return e => { 211 | logger.debug( 212 | 'operator command:', 213 | command, 214 | 'state:', 215 | Object.assign({}, cm.state.vim.inputState), 216 | e.originalEvent, 217 | e 218 | ) 219 | const vim = this.vim.maybeInitVimState(cm) 220 | if ( 221 | !vim.inputState.operator && 222 | !this.isBufferingKey() && 223 | !vim.visualMode 224 | ) { 225 | this.startBufferingKey(e => { 226 | const el = cm.getInputField() 227 | const keyName = this.normalizeKeyName(e.key) 228 | const keyBinding = inkdrop.keymaps.findKeyBindings({ 229 | keystrokes: keyName, 230 | target: el 231 | }) 232 | if (keyBinding.length > 0) { 233 | inkdrop.commands.dispatch(el, keyBinding[0].command) 234 | return true 235 | } else { 236 | return false 237 | } 238 | }, 'operator') 239 | if (e.originalEvent) { 240 | this.bufferKey(e.originalEvent.key) 241 | } else { 242 | logger.debug('buffer key:', command.keys) 243 | this.bufferKey(command.keys) 244 | } 245 | } 246 | return h(command)(e) 247 | } 248 | } 249 | const handlers = { 250 | 'vim:native!': () => {}, 251 | 'vim:reset-normal-mode': e => { 252 | CodeMirror.Vim.clearInputState(cm) 253 | this.stopBufferingKey() 254 | e.stopPropagation() 255 | }, 256 | 'vim:exit-visual-mode': e => { 257 | logger.debug('exit-visual-mode') 258 | CodeMirror.Vim.clearInputState(cm) 259 | CodeMirror.Vim.exitVisualMode(cm) 260 | this.stopBufferingKey() 261 | e.stopPropagation() 262 | }, 263 | 'vim:exit-insert-mode': e => { 264 | logger.debug('exit-insert-mode') 265 | CodeMirror.Vim.clearInputState(cm) 266 | CodeMirror.Vim.exitInsertMode(cm) 267 | this.stopBufferingKey() 268 | e.stopPropagation() 269 | }, 270 | 'vim:move-left': h({ 271 | keys: 'h', 272 | type: 'motion', 273 | motion: 'moveByCharacters', 274 | motionArgs: { forward: false } 275 | }), 276 | 'vim:move-right': h({ 277 | keys: 'l', 278 | type: 'motion', 279 | motion: 'moveByCharacters', 280 | motionArgs: { forward: true } 281 | }), 282 | 'vim:move-up': (command => { 283 | return e => { 284 | const seamlessJumpToTitleEnabled = inkdrop.config.get('vim.seamlessJumpToTitle') 285 | const cur = cm.getCursor() 286 | if (cur.line === 0 && cur.ch === 0 && seamlessJumpToTitleEnabled) { 287 | e.stopPropagation() 288 | inkdrop.commands.dispatch(document.body, 'editor:title:focus') 289 | } else { 290 | command(e) 291 | } 292 | } 293 | })( 294 | h({ 295 | keys: 'k', 296 | type: 'motion', 297 | motion: 'moveByLines', 298 | motionArgs: { forward: false, linewise: true } 299 | }) 300 | ), 301 | 'vim:move-down': h({ 302 | keys: 'j', 303 | type: 'motion', 304 | motion: 'moveByLines', 305 | motionArgs: { forward: true, linewise: true } 306 | }), 307 | 'vim:move-up-by-display-lines': h({ 308 | keys: 'gk', 309 | type: 'motion', 310 | motion: 'moveByDisplayLines', 311 | motionArgs: { forward: false } 312 | }), 313 | 'vim:move-down-by-display-lines': h({ 314 | keys: 'gj', 315 | type: 'motion', 316 | motion: 'moveByDisplayLines', 317 | motionArgs: { forward: true } 318 | }), 319 | 320 | 'vim:move-to-next-word': h({ 321 | keys: 'w', 322 | type: 'motion', 323 | motion: 'moveByWords', 324 | motionArgs: { forward: true, wordEnd: false } 325 | }), 326 | 'vim:move-to-next-whole-word': h({ 327 | keys: 'W', 328 | type: 'motion', 329 | motion: 'moveByWords', 330 | motionArgs: { forward: true, wordEnd: false, bigWord: true } 331 | }), 332 | 'vim:move-to-end-of-word': h({ 333 | keys: 'e', 334 | type: 'motion', 335 | motion: 'moveByWords', 336 | motionArgs: { forward: true, wordEnd: true, inclusive: true } 337 | }), 338 | 'vim:move-to-previous-end-of-word': h({ 339 | keys: 'ge', 340 | type: 'motion', 341 | motion: 'moveByWords', 342 | motionArgs: { forward: false, wordEnd: true, inclusive: true } 343 | }), 344 | 'vim:move-to-end-of-whole-word': h({ 345 | keys: 'E', 346 | type: 'motion', 347 | motion: 'moveByWords', 348 | motionArgs: { 349 | forward: true, 350 | wordEnd: true, 351 | bigWord: true, 352 | inclusive: true 353 | } 354 | }), 355 | 'vim:move-to-previous-end-of-whole-word': h({ 356 | keys: 'gE', 357 | type: 'motion', 358 | motion: 'moveByWords', 359 | motionArgs: { 360 | forward: false, 361 | wordEnd: true, 362 | bigWord: true, 363 | inclusive: true 364 | } 365 | }), 366 | 'vim:move-to-previous-word': h({ 367 | keys: 'b', 368 | type: 'motion', 369 | motion: 'moveByWords', 370 | motionArgs: { forward: false, wordEnd: false } 371 | }), 372 | 'vim:move-to-previous-whole-word': h({ 373 | keys: 'B', 374 | type: 'motion', 375 | motion: 'moveByWords', 376 | motionArgs: { forward: false, wordEnd: false, bigWord: true } 377 | }), 378 | 'vim:move-to-next-paragraph': h({ 379 | keys: '}', 380 | type: 'motion', 381 | motion: 'moveByParagraph', 382 | motionArgs: { forward: true, toJumplist: true } 383 | }), 384 | 'vim:move-to-previous-paragraph': h({ 385 | keys: '{', 386 | type: 'motion', 387 | motion: 'moveByParagraph', 388 | motionArgs: { forward: false, toJumplist: true } 389 | }), 390 | 'vim:move-to-beginning-of-line-with-zero': (() => { 391 | const handler = h({ 392 | keys: '0', 393 | type: 'motion', 394 | motion: 'moveToStartOfLine' 395 | }) 396 | return e => { 397 | const vim = this.vim.maybeInitVimState(cm) 398 | if (vim.inputState.getRepeat() > 0) { 399 | vim.inputState.pushRepeatDigit('0') 400 | } else { 401 | return handler(e) 402 | } 403 | } 404 | })(), 405 | 'vim:move-to-beginning-of-line': h({ 406 | keys: '0', 407 | type: 'motion', 408 | motion: 'moveToStartOfLine' 409 | }), 410 | 'vim:move-to-first-character-of-line': h({ 411 | keys: '^', 412 | type: 'motion', 413 | motion: 'moveToFirstNonWhiteSpaceCharacter' 414 | }), 415 | 'vim:move-to-first-character-of-line-and-down': h({ 416 | keys: '_', 417 | type: 'motion', 418 | motion: 'moveByLines', 419 | motionArgs: { forward: true, toFirstChar: true, repeatOffset: -1 } 420 | }), 421 | 'vim:move-to-last-character-of-line': h({ 422 | keys: '$', 423 | type: 'motion', 424 | motion: 'moveToEol', 425 | motionArgs: { inclusive: true } 426 | }), 427 | 'vim:move-to-last-nonblank-character-of-line-and-down': () => { 428 | '???' 429 | }, 430 | 'vim:move-to-first-character-of-line-up': h({ 431 | keys: '-', 432 | type: 'motion', 433 | motion: 'moveByLines', 434 | motionArgs: { forward: false, toFirstChar: true } 435 | }), 436 | 'vim:move-to-first-character-of-line-down': h({ 437 | keys: '+', 438 | type: 'motion', 439 | motion: 'moveByLines', 440 | motionArgs: { forward: true, toFirstChar: true } 441 | }), 442 | 443 | 'vim:move-to-start-of-file': h({ 444 | keys: 'gg', 445 | type: 'motion', 446 | motion: 'moveToLineOrEdgeOfDocument', 447 | motionArgs: { 448 | forward: false, 449 | explicitRepeat: true, 450 | linewise: true, 451 | toJumplist: true 452 | } 453 | }), 454 | 'vim:scroll-half-screen-up': h({ 455 | keys: '', 456 | type: 'motion', 457 | motion: 'moveByScroll', 458 | motionArgs: { forward: false, explicitRepeat: true } 459 | }), 460 | 'vim:scroll-full-screen-up': h({ 461 | keys: '', 462 | type: 'motion', 463 | motion: 'moveByPage', 464 | motionArgs: { forward: false } 465 | }), 466 | 'vim:scroll-half-screen-down': h({ 467 | keys: '', 468 | type: 'motion', 469 | motion: 'moveByScroll', 470 | motionArgs: { forward: true, explicitRepeat: true } 471 | }), 472 | 'vim:scroll-full-screen-down': h({ 473 | keys: '', 474 | type: 'motion', 475 | motion: 'moveByPage', 476 | motionArgs: { forward: true } 477 | }), 478 | 'vim:scroll-down': h({ 479 | keys: '', 480 | type: 'action', 481 | action: 'scroll', 482 | actionArgs: { forward: true, linewise: true } 483 | }), 484 | 'vim:scroll-up': h({ 485 | keys: '', 486 | type: 'action', 487 | action: 'scroll', 488 | actionArgs: { forward: false, linewise: true } 489 | }), 490 | 'vim:scroll-cursor-to-top': h({ 491 | keys: 'z', 492 | type: 'action', 493 | action: 'scrollToCursor', 494 | actionArgs: { position: 'top' }, 495 | motion: 'moveToFirstNonWhiteSpaceCharacter' 496 | }), 497 | 'vim:scroll-cursor-to-top-leave': h({ 498 | keys: 'zt', 499 | type: 'action', 500 | action: 'scrollToCursor', 501 | actionArgs: { position: 'top' } 502 | }), 503 | 'vim:scroll-cursor-to-middle': h({ 504 | keys: 'z.', 505 | type: 'action', 506 | action: 'scrollToCursor', 507 | actionArgs: { position: 'center' }, 508 | motion: 'moveToFirstNonWhiteSpaceCharacter' 509 | }), 510 | 'vim:scroll-cursor-to-middle-leave': h({ 511 | keys: 'zz', 512 | type: 'action', 513 | action: 'scrollToCursor', 514 | actionArgs: { position: 'center' } 515 | }), 516 | 'vim:scroll-cursor-to-bottom': h({ 517 | keys: 'z-', 518 | type: 'action', 519 | action: 'scrollToCursor', 520 | actionArgs: { position: 'bottom' } 521 | }), 522 | 'vim:scroll-cursor-to-bottom-leave': h({ 523 | keys: 'zb', 524 | type: 'action', 525 | action: 'scrollToCursor', 526 | actionArgs: { position: 'bottom' }, 527 | motion: 'moveToFirstNonWhiteSpaceCharacter' 528 | }), 529 | 'vim:move-to-line': h({ 530 | keys: 'G', 531 | type: 'motion', 532 | motion: 'moveToLineOrEdgeOfDocument', 533 | motionArgs: { 534 | forward: true, 535 | explicitRepeat: true, 536 | linewise: true, 537 | toJumplist: true 538 | } 539 | }), 540 | 'vim:move-to-top-of-screen': h({ 541 | keys: 'H', 542 | type: 'motion', 543 | motion: 'moveToTopLine', 544 | motionArgs: { linewise: true, toJumplist: true } 545 | }), 546 | 'vim:move-to-bottom-of-screen': h({ 547 | keys: 'L', 548 | type: 'motion', 549 | motion: 'moveToBottomLine', 550 | motionArgs: { linewise: true, toJumplist: true } 551 | }), 552 | 'vim:move-to-middle-of-screen': h({ 553 | keys: 'M', 554 | type: 'motion', 555 | motion: 'moveToMiddleLine', 556 | motionArgs: { linewise: true, toJumplist: true } 557 | }), 558 | 559 | 'vim:delete': p({ keys: 'd', type: 'operator', operator: 'delete' }), 560 | 'vim:delete-to-last-character-of-line': h({ 561 | keys: 'D', 562 | type: 'operatorMotion', 563 | operator: 'delete', 564 | motion: 'moveToEol', 565 | motionArgs: { inclusive: true }, 566 | context: 'normal' 567 | }), 568 | 'vim:change': p({ keys: 'c', type: 'operator', operator: 'change' }), 569 | 'vim:change-to-last-character-of-line': h({ 570 | keys: 'C', 571 | type: 'operatorMotion', 572 | operator: 'change', 573 | motion: 'moveToEol', 574 | motionArgs: { inclusive: true }, 575 | context: 'normal' 576 | }), 577 | 'vim:substitute-line': h({ 578 | keys: 'S', 579 | type: 'keyToKey', 580 | toKeys: 'cc', 581 | context: 'normal' 582 | }), 583 | 'vim:substitute-line-visual': h({ 584 | keys: 'S', 585 | type: 'keyToKey', 586 | toKeys: 'VdO', 587 | context: 'visual' 588 | }), 589 | 'vim:replace': b({ 590 | keys: 'r', 591 | type: 'action', 592 | action: 'replace', 593 | isEdit: true 594 | }), 595 | 'vim:insert-at-beginning-of-line': h({ 596 | keys: 'I', 597 | type: 'action', 598 | action: 'enterInsertMode', 599 | isEdit: true, 600 | actionArgs: { insertAt: 'firstNonBlank' }, 601 | context: 'normal' 602 | }), 603 | 'vim:text-object-manipulation-inner': b({ 604 | keys: 'i', 605 | type: 'motion', 606 | motion: 'textObjectManipulation', 607 | motionArgs: { textObjectInner: true } 608 | }), 609 | 'vim:text-object-manipulation': b({ 610 | keys: 'a', 611 | type: 'motion', 612 | motion: 'textObjectManipulation' 613 | }), 614 | 'vim:indent': p({ 615 | keys: '>', 616 | type: 'operator', 617 | operator: 'indent', 618 | operatorArgs: { indentRight: true } 619 | }), 620 | 'vim:outdent': p({ 621 | keys: '<', 622 | type: 'operator', 623 | operator: 'indent', 624 | operatorArgs: { indentRight: false } 625 | }), 626 | 'vim:auto-indent': () => { 627 | 'not supported' 628 | }, 629 | 'vim:reselect-last-selection': h({ 630 | keys: 'gv', 631 | type: 'action', 632 | action: 'reselectLastSelection' 633 | }), 634 | 'vim:join': h({ 635 | keys: 'J', 636 | type: 'action', 637 | action: 'joinLines', 638 | isEdit: true 639 | }), 640 | 641 | 'vim:yank': p({ keys: 'y', type: 'operator', operator: 'yank' }), 642 | 'vim:yank-line': h({ 643 | keys: 'Y', 644 | type: 'operatorMotion', 645 | operator: 'yank', 646 | motion: 'expandToLine', 647 | motionArgs: { linewise: true }, 648 | context: 'normal' 649 | }), 650 | 'vim:put-before': h({ 651 | keys: 'P', 652 | type: 'action', 653 | action: 'paste', 654 | isEdit: true, 655 | actionArgs: { after: false, isEdit: true } 656 | }), 657 | 'vim:put-after': h({ 658 | keys: 'p', 659 | type: 'action', 660 | action: 'paste', 661 | isEdit: true, 662 | actionArgs: { after: true, isEdit: true } 663 | }), 664 | 665 | 'vim:toggle-case': h({ 666 | keys: 'g~', 667 | type: 'operator', 668 | operator: 'changeCase' 669 | }), 670 | 'vim:upper-case': h({ 671 | keys: 'gU', 672 | type: 'operator', 673 | operator: 'changeCase', 674 | operatorArgs: { toLower: false }, 675 | isEdit: true 676 | }), 677 | 'vim:lower-case': h({ 678 | keys: 'gu', 679 | type: 'operator', 680 | operator: 'changeCase', 681 | operatorArgs: { toLower: true }, 682 | isEdit: true 683 | }), 684 | 'vim:toggle-case-now': h({ 685 | keys: '~', 686 | type: 'operatorMotion', 687 | operator: 'changeCase', 688 | motion: 'moveByCharacters', 689 | motionArgs: { forward: true }, 690 | operatorArgs: { shouldMoveCursor: true }, 691 | context: 'normal' 692 | }), 693 | 694 | 'vim:mark': b({ 695 | keys: 'm', 696 | type: 'action', 697 | action: 'setMark' 698 | }), 699 | 'vim:move-to-mark-literal': b({ 700 | keys: '`', 701 | type: 'motion', 702 | motion: 'goToMark', 703 | motionArgs: { toJumplist: true } 704 | }), 705 | 'vim:move-to-mark': b({ 706 | keys: "'", 707 | type: 'motion', 708 | motion: 'goToMark', 709 | motionArgs: { toJumplist: true, linewise: true } 710 | }), 711 | 712 | 'vim:find': b({ 713 | keys: 'f', 714 | type: 'motion', 715 | motion: 'moveToCharacter', 716 | motionArgs: { forward: true, inclusive: true } 717 | }), 718 | 'vim:find-backwards': b({ 719 | keys: 'F', 720 | type: 'motion', 721 | motion: 'moveToCharacter', 722 | motionArgs: { forward: false } 723 | }), 724 | 'vim:till': b({ 725 | keys: 't', 726 | type: 'motion', 727 | motion: 'moveTillCharacter', 728 | motionArgs: { forward: true, inclusive: true } 729 | }), 730 | 'vim:till-backwards': b({ 731 | keys: 'T', 732 | type: 'motion', 733 | motion: 'moveTillCharacter', 734 | motionArgs: { forward: false } 735 | }), 736 | 'vim:repeat-find': h({ 737 | keys: ';', 738 | type: 'motion', 739 | motion: 'repeatLastCharacterSearch', 740 | motionArgs: { forward: true } 741 | }), 742 | 'vim:repeat-find-reverse': h({ 743 | keys: ',', 744 | type: 'motion', 745 | motion: 'repeatLastCharacterSearch', 746 | motionArgs: { forward: false } 747 | }), 748 | 749 | 'vim:search': h({ 750 | keys: '/', 751 | type: 'search', 752 | searchArgs: { forward: true, querySrc: 'prompt', toJumplist: true } 753 | }), 754 | 'vim:reverse-search': h({ 755 | keys: '?', 756 | type: 'search', 757 | searchArgs: { forward: false, querySrc: 'prompt', toJumplist: true } 758 | }), 759 | 'vim:search-current-word': h({ 760 | keys: '*', 761 | type: 'search', 762 | searchArgs: { 763 | forward: true, 764 | querySrc: 'wordUnderCursor', 765 | wholeWordOnly: true, 766 | toJumplist: true 767 | } 768 | }), 769 | 'vim:reverse-search-current-word': h({ 770 | keys: '#', 771 | type: 'search', 772 | searchArgs: { 773 | forward: false, 774 | querySrc: 'wordUnderCursor', 775 | wholeWordOnly: true, 776 | toJumplist: true 777 | } 778 | }), 779 | 'vim:repeat-search': h({ 780 | keys: 'n', 781 | type: 'motion', 782 | motion: 'findNext', 783 | motionArgs: { forward: true, toJumplist: true } 784 | }), 785 | 'vim:repeat-search-backwards': h({ 786 | keys: 'N', 787 | type: 'motion', 788 | motion: 'findNext', 789 | motionArgs: { forward: false, toJumplist: true } 790 | }), 791 | 792 | 'vim:bracket-matching-motion': h({ 793 | keys: '%', 794 | type: 'motion', 795 | motion: 'moveToMatchedSymbol', 796 | motionArgs: { inclusive: true, toJumplist: true } 797 | }), 798 | 799 | 'vim:ex-command': h({ keys: ':', type: 'ex' }), 800 | 801 | // normal mode 802 | 'vim:activate-insert-mode': h({ 803 | keys: 'i', 804 | type: 'action', 805 | action: 'enterInsertMode', 806 | isEdit: true, 807 | actionArgs: { insertAt: 'inplace' }, 808 | context: 'normal' 809 | }), 810 | 'vim:activate-replace-mode': h({ 811 | keys: 'R', 812 | type: 'action', 813 | action: 'enterInsertMode', 814 | isEdit: true, 815 | actionArgs: { replace: true } 816 | }), 817 | 'vim:activate-characterwise-visual-mode': h({ 818 | keys: 'v', 819 | type: 'action', 820 | action: 'toggleVisualMode' 821 | }), 822 | 'vim:activate-linewise-visual-mode': h({ 823 | keys: 'V', 824 | type: 'action', 825 | action: 'toggleVisualMode', 826 | actionArgs: { linewise: true } 827 | }), 828 | 'vim:activate-blockwise-visual-mode': h({ 829 | keys: '', 830 | type: 'action', 831 | action: 'toggleVisualMode', 832 | actionArgs: { blockwise: true } 833 | }), 834 | 835 | 'vim:undo': h({ 836 | keys: 'u', 837 | type: 'action', 838 | action: 'undo', 839 | context: 'normal' 840 | }), 841 | 842 | 'vim:insert-above-with-newline': h({ 843 | keys: 'O', 844 | type: 'action', 845 | action: 'newLineAndEnterInsertMode', 846 | isEdit: true, 847 | interlaceInsertRepeat: true, 848 | actionArgs: { after: false }, 849 | context: 'normal' 850 | }), 851 | 'vim:insert-below-with-newline': h({ 852 | keys: 'o', 853 | type: 'action', 854 | action: 'newLineAndEnterInsertMode', 855 | isEdit: true, 856 | interlaceInsertRepeat: true, 857 | actionArgs: { after: true }, 858 | context: 'normal' 859 | }), 860 | 'vim:insert-after': h({ 861 | keys: 'a', 862 | type: 'action', 863 | action: 'enterInsertMode', 864 | isEdit: true, 865 | actionArgs: { insertAt: 'charAfter' }, 866 | context: 'normal' 867 | }), 868 | 'vim:insert-after-end-of-line': h({ 869 | keys: 'A', 870 | type: 'action', 871 | action: 'enterInsertMode', 872 | isEdit: true, 873 | actionArgs: { insertAt: 'eol' }, 874 | context: 'normal' 875 | }), 876 | 'vim:delete-right': h({ 877 | keys: 'x', 878 | type: 'operatorMotion', 879 | operator: 'delete', 880 | motion: 'moveByCharacters', 881 | motionArgs: { forward: true }, 882 | operatorMotionArgs: { visualLine: false } 883 | }), 884 | 'vim:delete-left': h({ 885 | keys: 'X', 886 | type: 'operatorMotion', 887 | operator: 'delete', 888 | motion: 'moveByCharacters', 889 | motionArgs: { forward: false }, 890 | operatorMotionArgs: { visualLine: true } 891 | }), 892 | 'vim:substitute': h({ 893 | keys: 's', 894 | type: 'keyToKey', 895 | toKeys: 'cl', 896 | context: 'normal' 897 | }), 898 | 'vim:substitute-visual': h({ 899 | keys: 's', 900 | type: 'keyToKey', 901 | toKeys: 'c', 902 | context: 'visual' 903 | }), 904 | 'vim:repeat': h({ 905 | keys: '.', 906 | type: 'action', 907 | action: 'repeatLastEdit' 908 | }), 909 | 910 | 'vim:increase': h({ 911 | keys: '', 912 | type: 'action', 913 | action: 'incrementNumberToken', 914 | isEdit: true, 915 | actionArgs: { increase: true, backtrack: false } 916 | }), 917 | 'vim:decrease': h({ 918 | keys: '', 919 | type: 'action', 920 | action: 'incrementNumberToken', 921 | isEdit: true, 922 | actionArgs: { increase: false, backtrack: false } 923 | }), 924 | 925 | 'vim:register-prefix': b({ 926 | keys: '"', 927 | type: 'action', 928 | action: 'setRegister' 929 | }), 930 | 931 | // insert mode 932 | 'vim:delete-to-beginning-of-word': h({ 933 | keys: '', 934 | type: 'operatorMotion', 935 | operator: 'delete', 936 | motion: 'moveByWords', 937 | motionArgs: { forward: false, wordEnd: false }, 938 | context: 'insert' 939 | }), 940 | 941 | // visual mode 942 | 'vim:insert-at-start-of-target': h({ 943 | keys: 'I', 944 | type: 'action', 945 | action: 'enterInsertMode', 946 | isEdit: true, 947 | actionArgs: { insertAt: 'startOfSelectedArea' }, 948 | context: 'visual' 949 | }), 950 | 'vim:insert-at-end-of-target': h({ 951 | keys: 'A', 952 | type: 'action', 953 | action: 'enterInsertMode', 954 | isEdit: true, 955 | actionArgs: { insertAt: 'endOfSelectedArea' }, 956 | context: 'visual' 957 | }), 958 | 'vim:reverse-selections': h({ 959 | keys: 'o', 960 | type: 'motion', 961 | motion: 'moveToOtherHighlightedEnd', 962 | context: 'visual' 963 | }), 964 | 'vim:reverse-selections-at-same-line': h({ 965 | keys: 'O', 966 | type: 'motion', 967 | motion: 'moveToOtherHighlightedEnd', 968 | motionArgs: { sameLine: true }, 969 | context: 'visual' 970 | }) 971 | } 972 | disposables.add(inkdrop.commands.add(wrapper, handlers)) 973 | disposables.add( 974 | inkdrop.commands.add(document.querySelector('.mde-preview'), { 975 | 'vim:move-to-start-of-file': ({ target }) => { 976 | target.scrollTop = 0 977 | }, 978 | 'vim:scroll-up': ({ target }) => { 979 | target.scrollTop -= 30 980 | }, 981 | 'vim:scroll-down': ({ target }) => { 982 | target.scrollTop += 30 983 | }, 984 | 'vim:scroll-half-screen-up': ({ target }) => { 985 | target.scrollTop -= target.clientHeight / 2 986 | }, 987 | 'vim:scroll-half-screen-down': ({ target }) => { 988 | target.scrollTop += target.clientHeight / 2 989 | }, 990 | 'vim:scroll-full-screen-up': ({ target }) => { 991 | target.scrollTop -= target.clientHeight 992 | }, 993 | 'vim:scroll-full-screen-down': ({ target }) => { 994 | target.scrollTop += target.clientHeight 995 | }, 996 | 'vim:move-to-line': ({ target }) => { 997 | target.scrollTop = target.scrollHeight 998 | } 999 | }) 1000 | ) 1001 | wrapper.addEventListener('textInput', this.handleEditorTextInput) 1002 | wrapper.addEventListener('keydown', this.handleEditorKeyDown) 1003 | disposables.add( 1004 | new Disposable(() => 1005 | wrapper.removeEventListener('keydown', this.handleEditorKeyDown) 1006 | ) 1007 | ) 1008 | 1009 | this.disposables = disposables 1010 | } 1011 | 1012 | registerExCommands() { 1013 | const el = this.getCodeMirror().getWrapperElement() 1014 | this.vim.defineEx('write', 'w', () => { 1015 | inkdrop.commands.dispatch(el, 'core:save-note') 1016 | }) 1017 | this.vim.defineEx('next', 'n', () => { 1018 | inkdrop.commands.dispatch(el, 'core:open-next-note') 1019 | }) 1020 | this.vim.defineEx('prev', '', () => { 1021 | inkdrop.commands.dispatch(el, 'core:open-prev-note') 1022 | }) 1023 | this.vim.defineEx('preview', 'p', () => { 1024 | inkdrop.commands.dispatch(el, 'view:toggle-preview') 1025 | }) 1026 | this.vim.defineEx('side-by-side', 'side', () => { 1027 | inkdrop.commands.dispatch(el, 'view:toggle-side-by-side') 1028 | }) 1029 | } 1030 | 1031 | getCodeMirror() { 1032 | return inkdrop.getActiveEditor().cm 1033 | } 1034 | 1035 | yankClipboard() { 1036 | const state = this.vim.getVimGlobalState() 1037 | const text = clipboard.readText() 1038 | const linewise = text.indexOf('\n') >= 0 1039 | state.registerController.pushText('0', 'yank', text, linewise, true) 1040 | } 1041 | 1042 | handleEditorLoad = editor => { 1043 | this.activateMode(editor) 1044 | } 1045 | 1046 | handleVimModeChange = (event, _opt) => { 1047 | logger.debug('vim mode changed:', event) 1048 | const { mode } = event 1049 | const cm = this.getCodeMirror() 1050 | cm.getWrapperElement().classList.remove('insert-mode') 1051 | cm.getWrapperElement().classList.remove('visual-mode') 1052 | cm.getWrapperElement().classList.remove('normal-mode') 1053 | cm.getWrapperElement().classList.remove('replace-mode') 1054 | switch (mode) { 1055 | case 'normal': 1056 | cm.getWrapperElement().classList.add('normal-mode') 1057 | break 1058 | case 'visual': 1059 | cm.getWrapperElement().classList.add('visual-mode') 1060 | break 1061 | case 'replace': 1062 | cm.getWrapperElement().classList.add('replace-mode') 1063 | break 1064 | case 'insert': 1065 | cm.getWrapperElement().classList.add('insert-mode') 1066 | break 1067 | } 1068 | } 1069 | 1070 | handleEditorTextInput = event => { 1071 | // only process if the event is fired via EventTarget.dispatchEvent() 1072 | if (this.isInsertMode() && !event.isTrusted) { 1073 | logger.debug('handle text input:', event) 1074 | const text = event.data 1075 | const cm = this.getCodeMirror() 1076 | cm.replaceSelection(text) 1077 | } 1078 | } 1079 | 1080 | handleEditorKeyDown = event => { 1081 | const keyName = this.normalizeKeyName(event.key) 1082 | const cm = this.getCodeMirror() 1083 | const vim = this.vim.maybeInitVimState(cm) 1084 | const isNumeric = 1085 | !event.ctrlKey && 1086 | !event.altKey && 1087 | !event.metaKey && 1088 | !event.shiftKey && 1089 | keyName.match(/^\d$/) 1090 | 1091 | const target = cm.getInputField() 1092 | const currentKeyStroke = [ 1093 | ...inkdrop.keymaps.queuedKeystrokes.filter(k => !k.startsWith('^')), 1094 | keyName 1095 | ] 1096 | const { partialMatchCandidates, exactMatchCandidates } = 1097 | inkdrop.keymaps.findMatchCandidates(currentKeyStroke) 1098 | const partialMatches = inkdrop.keymaps.findPartialMatches( 1099 | partialMatchCandidates, 1100 | target 1101 | ) 1102 | const exactMatches = inkdrop.keymaps.findExactMatches( 1103 | exactMatchCandidates, 1104 | target 1105 | ) 1106 | logger.debug('handleEditorKeyDown: currentKeyStroke:', currentKeyStroke) 1107 | logger.debug('handleEditorKeyDown: exactMatches:', exactMatches) 1108 | logger.debug('handleEditorKeyDown: partialMatches:', partialMatches) 1109 | 1110 | if (this.isBufferingKey()) { 1111 | logger.debug('handleEditorKeyDown: handle key buffering:', keyName, event) 1112 | const b = cm 1113 | .getInputField() 1114 | .webkitMatchesSelector( 1115 | '.CodeMirror.vim-mode:not(.insert-mode) textarea' 1116 | ) 1117 | logger.debug('handleEditorKeyDown: keybinding check:', exactMatches, b) 1118 | 1119 | if ( 1120 | keyName !== 'Ctrl' && 1121 | keyName !== 'Alt' && 1122 | keyName !== 'Shift' && 1123 | keyName !== 'Meta' 1124 | ) { 1125 | const { inputState } = vim 1126 | const hasOperatorOrMotion = inputState.motion || inputState.operator 1127 | if ( 1128 | (event.key.length === 1 || keyName === 'space') && 1129 | (!isNumeric || !hasOperatorOrMotion) 1130 | ) { 1131 | inputState.selectedCharacter = event.key 1132 | inputState.keyBuffer = '' 1133 | 1134 | if (exactMatches.length === 0 && partialMatches.length === 0) { 1135 | const { pendingCommand } = this 1136 | this.stopBufferingKey() 1137 | 1138 | if (typeof pendingCommand === 'function') { 1139 | if (pendingCommand(event)) { 1140 | event.stopPropagation() 1141 | event.preventDefault() 1142 | } 1143 | } 1144 | } 1145 | } else if (isNumeric) { 1146 | vim.inputState.pushRepeatDigit(keyName) 1147 | } 1148 | } 1149 | } else if (!this.isInsertMode()) { 1150 | if (isNumeric) { 1151 | if (exactMatches.length === 0) { 1152 | vim.inputState.pushRepeatDigit(keyName) 1153 | } 1154 | } else { 1155 | // push key buffer to the repeat digit 1156 | const keys = vim.inputState.keyBuffer 1157 | vim.inputState.keyBuffer = '' 1158 | const keysMatcher = /^(\d*)(.*)$/.exec(keys) 1159 | if (keysMatcher[1] && keysMatcher[1] !== '0') { 1160 | vim.inputState.pushRepeatDigit(keysMatcher[1]) 1161 | } 1162 | } 1163 | } 1164 | } 1165 | 1166 | handleFocusEditor = _event => { 1167 | this.yankClipboard() 1168 | } 1169 | } 1170 | 1171 | module.exports = new Plugin() 1172 | -------------------------------------------------------------------------------- /styles/vim.less: -------------------------------------------------------------------------------- 1 | .vim { 2 | } 3 | --------------------------------------------------------------------------------