├── Flavorfile.lock ├── Gemfile ├── Gemfile.lock ├── README.md ├── doc ├── shortcut.txt └── tags ├── plugin └── shortcut.vim └── t ├── shortcut_test.vim └── shortcut_test ├── resolve_caller_SID_from_scriptnames └── resolve_caller_SID_from_stacktrace /Flavorfile.lock: -------------------------------------------------------------------------------- 1 | kana/vim-vspec (1.9.0) 2 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'vim-flavor', '~> 4.0' 3 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | parslet (2.0.0) 5 | pastel (0.8.0) 6 | tty-color (~> 0.5) 7 | thor (1.2.1) 8 | tty-color (0.6.0) 9 | vim-flavor (4.0.2) 10 | parslet (>= 1.8, < 3.0) 11 | pastel (~> 0.7) 12 | thor (>= 0.20, < 2.0) 13 | 14 | PLATFORMS 15 | ruby 16 | 17 | DEPENDENCIES 18 | vim-flavor (~> 4.0) 19 | 20 | BUNDLED WITH 21 | 2.2.17 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shortcut.vim 2 | 3 | This plugin provides a _discoverable_ shortcut system for Vim that is inspired 4 | by [Spacemacs] and powered by [fzf.vim]. It displays a searchable menu of 5 | shortcuts when you pause partway while typing a shortcut, say, because you 6 | forgot the rest of it or because you just want to see the shortcut menu again 7 | to discover what else is available. You can interactively filter the menu by 8 | typing more shortcut keys or parts of shortcut descriptions shown in the menu. 9 | 10 | [![asciicast](https://asciinema.org/a/104572.png)](https://asciinema.org/a/104572?autoplay=1) 11 | 12 | ## Requirements 13 | 14 | * [fzf.vim] plugin. 15 | 16 | ## Installation 17 | 18 | 1. Clone this Git repository as follows, or [download and extract its 19 | contents]( https://github.com/sunaku/vim-shortcut/archive/master.zip ). 20 | 21 | git clone https://github.com/sunaku/vim-shortcut.git ~/vim-shortcut 22 | 23 | 2. Run the following commands in Vim to start using this plugin immediately, 24 | or add them to your *vimrc* file to automate this whenever you start Vim. 25 | 26 | :set runtimepath+=~/vim-shortcut 27 | :runtime plugin/shortcut.vim 28 | 29 | 3. Run the following command inside Vim to learn more about using this plugin. 30 | 31 | :help shortcut.vim 32 | 33 | ## Usage 34 | 35 | * Use the `Shortcut!` prefix (with a bang) to describe existing shortcuts. 36 | 37 | * Use the `Shortcut` prefix (without a bang) to define brand new shortcuts. 38 | 39 | * Use the `:Shortcuts` command to display a searchable menu of shortcuts. 40 | 41 | * Use the `g:shortcuts` variable to access shortcuts keys and descriptions. 42 | 43 | * Use the `g:shortcuts_overwrite_warning` variable to detect any conflicts. 44 | 45 | ### Discovery & fallback shortcuts 46 | 47 | I recommend that you define these two shortcuts for discovery and fallback 48 | (feel free to change the `` key to your own commonly used prefix): 49 | 50 | ```vim 51 | Shortcut show shortcut menu and run chosen shortcut 52 | \ noremap :Shortcuts 53 | 54 | Shortcut fallback to shortcut menu on partial entry 55 | \ noremap :Shortcuts 56 | ``` 57 | 58 | The fallback shortcut's keys should represent the common prefix used by most 59 | of your shortcuts so that it can automatically launch the shortcut menu for 60 | you when you pause partway while typing a shortcut, say, because you forgot 61 | the rest of it or because you just want to see the shortcut menu again to 62 | discover what else is available. However, this is not a strict requirement 63 | because you might find it useful to map shortcuts with uncommon prefixes when 64 | you know them by heart and you thereby feel that a fallback is unnecessary. 65 | As a result, you can map any keys to any shortcut, regardless of the prefix! 66 | Furthermore, you can set up multiple fallback shortcuts too, one per prefix. 67 | 68 | ### Describing existing shortcuts 69 | 70 | Use `Shortcut!` with a bang to describe shortcuts that are already defined: 71 | 72 | ```vim 73 | Shortcut! keys description 74 | ``` 75 | 76 | For more examples, [see my vimrc]( 77 | https://github.com/sunaku/.vim/blob/qwerty/bundle/motion/unimpaired.vim 78 | ): 79 | 80 | ```vim 81 | Shortcut! [f go to previous file in current file's directory 82 | Shortcut! ]f go to next file in current file's directory 83 | ``` 84 | 85 | Any extra whitespace is ignored. 86 | 87 | ### Defining new shortcuts 88 | 89 | Simply prefix any existing `map` command with `Shortcut` and a description. 90 | 91 | For example, take this mapping: 92 | 93 | ```vim 94 | map definition 95 | ``` 96 | 97 | Add `Shortcut` and description: 98 | 99 | ```vim 100 | Shortcut description map definition 101 | ``` 102 | 103 | You can use multiple lines too: 104 | 105 | ```vim 106 | Shortcut description 107 | \ map definition 108 | ``` 109 | 110 | For more examples, [see my vimrc]( 111 | https://github.com/sunaku/.vim/blob/qwerty/plugin/format.vim 112 | ): 113 | 114 | ```vim 115 | Shortcut duplicate before cursor and then comment-out 116 | \ map cP NERDCommenterYank`[P 117 | ``` 118 | 119 | ```vim 120 | Shortcut fzf files in directory and go to chosen file 121 | \ nnoremap ef :Files 122 | ``` 123 | 124 | ```vim 125 | Shortcut save file as... 126 | \ nnoremap yf :call feedkeys(":saveas %\t", "t") 127 | ``` 128 | 129 | ```vim 130 | for i in range(1,9) 131 | execute 'Shortcut go to tab number '. i .' ' 132 | \ 'nnoremap '. i .'t :tabfirst'. i .'tabnext' 133 | endfor 134 | ``` 135 | 136 | ```vim 137 | Shortcut comment-out using FIGlet ASCII art decoration 138 | \ nnoremap c@ V:call CommentUsingFIGlet() 139 | \|vnoremap c@ :call CommentUsingFIGlet() 140 | 141 | function! CommentUsingFIGlet() 142 | " ... 143 | endfunction 144 | ``` 145 | 146 | Any extra whitespace is ignored. 147 | 148 | ## Documentation 149 | 150 | Run `:help shortcut.vim` or see the `doc/shortcut.txt` file. 151 | 152 | ## Testing 153 | 154 | Developers can run the [vim-vspec]( https://github.com/kana/vim-vspec ) tests: 155 | 156 | ```sh 157 | gem install bundler # first time 158 | bundle install # first time 159 | bundle exec vim-flavor test # every time 160 | ``` 161 | 162 | ## License 163 | 164 | [Spare A Life]: https://sunaku.github.io/vegan-for-life.html 165 | > Like my work? 👍 Please [spare a life] today as thanks! 🐄🐖🐑🐔🐣🐟✨🙊✌ 166 | > Why? For 💕 ethics, the 🌎 environment, and 💪 health; see link above. 🙇 167 | 168 | Copyright 2015 Suraj N. Kurapati 169 | 170 | Distributed under [the same terms as Vim itself][LICENSE]. 171 | 172 | [LICENSE]: http://vimdoc.sourceforge.net/htmldoc/uganda.html#license 173 | [Spacemacs]: http://spacemacs.org 174 | [fzf.vim]: https://github.com/junegunn/fzf.vim 175 | -------------------------------------------------------------------------------- /doc/shortcut.txt: -------------------------------------------------------------------------------- 1 | *shortcut.vim* Discoverable shortcut system for Vim, inspired by Spacemacs. 2 | 3 | Author: Suraj N. Kurapati 4 | License: Same terms as Vim itself (see |license|) 5 | Homepage: https://github.com/sunaku/vim-shortcut 6 | 7 | ------------------------------------------------------------------------------ 8 | INTRODUCTION *shortcut-intro* 9 | ------------------------------------------------------------------------------ 10 | 11 | This plugin provides a discoverable shortcut system for Vim that is inspired 12 | by Emacs' Spacemacs http://spacemacs.org plugin and powered by Vim's |fzf-vim| 13 | https://github.com/junegunn/fzf.vim plugin. It displays a searchable menu of 14 | shortcuts when you pause partway while typing a shortcut, say, because you 15 | forgot the rest of it or because you just want to see the shortcut menu again 16 | to discover what else is available. You can interactively filter the menu by 17 | typing more shortcut keys or parts of shortcut descriptions shown in the menu. 18 | 19 | 20 | ------------------------------------------------------------------------------ 21 | INSTALLATION *shortcut-install* 22 | ------------------------------------------------------------------------------ 23 | 24 | 1. Clone this plugin's Git repository as follows, or download and extract its 25 | contents from . 26 | > 27 | git clone https://github.com/sunaku/vim-shortcut.git ~/vim-shortcut 28 | < 29 | 30 | 2. Run the following commands in Vim to start using this plugin immediately, 31 | or add them to your |vimrc| file to automate this whenever you start Vim. 32 | > 33 | :set runtimepath+=~/vim-shortcut 34 | :runtime plugin/shortcut.vim 35 | < 36 | 37 | 3. Run the following command inside Vim to learn more about using this plugin. 38 | > 39 | :help shortcut.vim 40 | < 41 | 42 | ------------------------------------------------------------------------------ 43 | USAGE *shortcut-usage* 44 | ------------------------------------------------------------------------------ 45 | 46 | * Use the |:Shortcut!| prefix (with a bang) to describe existing shortcuts. 47 | 48 | * Use the |:Shortcut| prefix (without a bang) to define brand new shortcuts. 49 | 50 | * Use the |:Shortcuts| command to display a searchable menu of shortcuts. 51 | 52 | * Use the |g:shortcuts| variable to access shortcuts keys and descriptions. 53 | 54 | * Use the |g:shortcuts_overwrite_warning| variable to detect any conflicts. 55 | 56 | 57 | ------------------------------------------------------------------------------ 58 | SETUP *shortcut-setup* 59 | ------------------------------------------------------------------------------ 60 | 61 | I recommend that you define these two shortcuts for discovery and fallback 62 | (feel free to change the "" key to your own commonly used prefix): 63 | > 64 | Shortcut show shortcut menu and run chosen shortcut 65 | \ noremap :Shortcuts 66 | 67 | Shortcut fallback to shortcut menu on partial entry 68 | \ noremap :Shortcuts 69 | < 70 | 71 | The fallback shortcut's keys should represent the common prefix used by most 72 | of your shortcuts so that it can automatically launch the shortcut menu for 73 | you when you pause partway while typing a shortcut, say, because you forgot 74 | the rest of it or because you just want to see the shortcut menu again to 75 | discover what else is available. However, this is not a strict requirement 76 | because you might find it useful to map shortcuts with uncommon prefixes when 77 | you know them by heart and you thereby feel that a fallback is unnecessary. 78 | As a result, you can map any keys to any shortcut, regardless of the prefix! 79 | Furthermore, you can set up multiple fallback shortcuts too, one per prefix. 80 | 81 | 82 | ------------------------------------------------------------------------------ 83 | VARIABLES *shortcut-var* 84 | ------------------------------------------------------------------------------ 85 | 86 | g:shortcuts *g:shortcuts* 87 | Dictionary that maps a shortcut's {keys} to its {description}. 88 | 89 | g:shortcuts_overwrite_warning *g:shortcuts_overwrite_warning* 90 | Displays a warning when a shortcut description is overwritten. 91 | This is disabled by default. Assign 1 to enable this feature. 92 | > 93 | let g:shortcuts_overwrite_warning = 1 " enable this warning 94 | < 95 | 96 | ------------------------------------------------------------------------------ 97 | COMMANDS *shortcut-cmd* 98 | ------------------------------------------------------------------------------ 99 | 100 | :Shortcuts! *:Shortcuts!* 101 | Same as |:Shortcuts| except that the menu is shown fullscreen. 102 | 103 | :Shortcuts *:Shortcuts* 104 | Display a |fzf-vim| menu of shortcuts described in |g:shortcuts|. 105 | When an item is chosen from the menu, its shortcut {keys} are 106 | synthetically replayed (as if you typed them) via |feedkeys()|. 107 | 108 | The cursor will be moved to the beginning of the line before 109 | the menu is shown. To avoid this, use |:ShortcutsRangeless|. 110 | 111 | :ShortcutsRangeless! *:ShortcutsRangeless!* 112 | Same as |:ShortcutsRangeless| except that menu is fullscreen. 113 | 114 | :ShortcutsRangeless *:ShortcutsRangeless* 115 | Same as |:Shortcuts| except that |[range]| argument is ignored. 116 | 117 | This prevents the cursor from moving to the beginning of line 118 | before the shortcut menu is shown, but it comes at the cost of 119 | preventing your chosen shortcut from operating on a visual 120 | selection (note: repetition |[count]| will continue to work). 121 | 122 | To avoid this limitation, just trigger the shortcut directly 123 | (without choosing through a menu) or use |:Shortcuts| instead. 124 | 125 | :Shortcut! {keys} {description} *:Shortcut!* 126 | Associate an existing shortcut's {keys} to its {description} 127 | in the |g:shortcuts| dictionary. Extra whitespace is ignored. 128 | 129 | For real-life examples, take a look at this file in my |vimrc|: 130 | (a portion of this file is shown below, for your convenience) 131 | https://github.com/sunaku/.vim/blob/qwerty/bundle/motion/unimpaired.vim 132 | > 133 | Shortcut! [f go to previous file in current file's directory 134 | Shortcut! ]f go to next file in current file's directory 135 | < 136 | 137 | :Shortcut {description} {|:map-commands|} *:Shortcut* 138 | Define a new shortcut by first executing {|:map-commands|} and 139 | then associating the resulting shortcut to its {description} 140 | in the |g:shortcuts| dictionary. Extra whitespace is ignored. 141 | 142 | For example, consider any |key-mapping| that takes this form: 143 | > 144 | map definition 145 | < 146 | Now simply prefix the |:Shortcut| command and a {description}: 147 | > 148 | Shortcut description map definition 149 | < 150 | Note that you can split this construct onto multiple lines: 151 | > 152 | Shortcut description 153 | \ map definition 154 | < 155 | For real-life examples, take a look at this file in my |vimrc|: 156 | (a portion of this file is shown below, for your convenience) 157 | https://github.com/sunaku/.vim/blob/qwerty/plugin/format.vim 158 | > 159 | Shortcut duplicate before cursor and then comment-out 160 | \ map cP NERDCommenterYank`[P 161 | 162 | Shortcut fzf files in directory and go to chosen file 163 | \ nnoremap ef :Files 164 | 165 | Shortcut save file as... 166 | \ nnoremap yf :call feedkeys(":saveas %\t", "t") 167 | 168 | for i in range(1,9) 169 | execute 'Shortcut go to tab number '. i .' ' 170 | \ 'nnoremap '. i .'t :tabfirst'. i .'tabnext' 171 | endfor 172 | 173 | Shortcut comment-out using FIGlet ASCII art decoration 174 | \ nnoremap c@ V:call CommentUsingFIGlet() 175 | \|vnoremap c@ :call CommentUsingFIGlet() 176 | 177 | function! CommentUsingFIGlet() 178 | " ... 179 | endfunction 180 | < 181 | 182 | vim:tw=78:ts=8:ft=help:norl: 183 | -------------------------------------------------------------------------------- /doc/tags: -------------------------------------------------------------------------------- 1 | :Shortcut shortcut.txt /*:Shortcut* 2 | :Shortcut! shortcut.txt /*:Shortcut!* 3 | :Shortcuts shortcut.txt /*:Shortcuts* 4 | :Shortcuts! shortcut.txt /*:Shortcuts!* 5 | :ShortcutsRangeless shortcut.txt /*:ShortcutsRangeless* 6 | :ShortcutsRangeless! shortcut.txt /*:ShortcutsRangeless!* 7 | g:shortcuts shortcut.txt /*g:shortcuts* 8 | g:shortcuts_overwrite_warning shortcut.txt /*g:shortcuts_overwrite_warning* 9 | shortcut-cmd shortcut.txt /*shortcut-cmd* 10 | shortcut-install shortcut.txt /*shortcut-install* 11 | shortcut-intro shortcut.txt /*shortcut-intro* 12 | shortcut-setup shortcut.txt /*shortcut-setup* 13 | shortcut-usage shortcut.txt /*shortcut-usage* 14 | shortcut-var shortcut.txt /*shortcut-var* 15 | shortcut.vim shortcut.txt /*shortcut.vim* 16 | -------------------------------------------------------------------------------- /plugin/shortcut.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_shortcut') 2 | finish 3 | endif 4 | let g:loaded_shortcut = 1 5 | 6 | if !exists('g:shortcuts') 7 | let g:shortcuts = {} 8 | endif 9 | 10 | command! -range -bang Shortcuts ,call s:shortcut_menu_command(0) 11 | command! -range -bang ShortcutsRangeless call s:shortcut_menu_command(0) 12 | 13 | function! s:shortcut_menu_command(fullscreen) range abort 14 | let s:is_from_visual = a:firstline == line("'<") && a:lastline == line("'>") 15 | let s:cursor_position = winsaveview() 16 | call fzf#run(fzf#wrap('Shortcuts', s:shortcut_menu_options({ 17 | \ 'source': s:shortcut_menu_items(), 18 | \ 'sink': function('s:shortcut_menu_item_action'), 19 | \ 'options': '--tiebreak=begin' 20 | \ }), a:fullscreen)) 21 | endfunction 22 | 23 | function! s:shortcut_menu_items() abort 24 | let labels = map(copy(g:shortcuts), 'ShortcutLeaderKeys(v:key)') 25 | let width = max(map(values(labels), 'len(v:val)')) + 4 26 | return values(map(labels, "printf('%-".width."S%s', v:val, g:shortcuts[v:key])")) 27 | endfunction 28 | 29 | function! s:shortcut_menu_item_action(choice) abort 30 | " dismiss the shortcut menu and leave insert mode 31 | " 32 | " NOTE: without this, the feedkeys() calls below _sometimes_ behave as if 33 | " the shortcut keys were _partially_ typed out in insert mode, which then 34 | " triggers the shortcut menu again, thereby creating an endless menu loop! 35 | " 36 | call feedkeys("\", 'n') 37 | 38 | " restore cursor to original position before menu 39 | call winrestview({ 40 | \ 'lnum': s:cursor_position.lnum, 41 | \ 'col': s:cursor_position.curswant 42 | \ }) 43 | 44 | " restore visual selection or numbered repetition 45 | if v:count 46 | call feedkeys(v:count, 'n') 47 | elseif s:is_from_visual 48 | call feedkeys('gv', 'n') 49 | endif 50 | 51 | " trigger shortcut by typing keys on user's behalf 52 | let shortcut = matchstr(a:choice, '^\S\+') 53 | let keystrokes = ShortcutKeystrokes(shortcut) 54 | call feedkeys(keystrokes) 55 | endfunction 56 | 57 | function! s:shortcut_menu_options(options) abort 58 | if !has('nvim') 59 | " Vim does not automatically propagate unmatched 60 | " typeahead characters the user might have typed 61 | " after the fallback shortcut has been triggered 62 | " so this is a workaround to grab that typeahead 63 | " and propagate it into FZF as user's keystrokes 64 | " https://github.com/junegunn/fzf.vim/issues/307 65 | let typeahead = ShortcutTypeaheadInput() 66 | let a:options['options'] .= ' --query=' . shellescape(typeahead) 67 | endif 68 | return a:options 69 | endfunction 70 | 71 | " Returns any unmatched typeahead the user typed. 72 | " https://github.com/junegunn/fzf.vim/issues/307 73 | " by Junegunn Choi 74 | function! ShortcutTypeaheadInput() 75 | let chars = '' 76 | while 1 77 | let c = getchar(0) 78 | if c == 0 79 | break 80 | endif 81 | let chars .= nr2char(c) 82 | endwhile 83 | return chars 84 | endfunction 85 | 86 | function! ShortcutLeaderKeys(input) abort 87 | let result = a:input 88 | 89 | let leader = get(g:, 'mapleader', '\') 90 | let result = substitute(result, '\c', leader, 'g') 91 | 92 | let localleader = get(g:, 'maplocalleader', '\') 93 | let result = substitute(result, '\c', localleader, 'g') 94 | 95 | return result 96 | endfunction 97 | 98 | function! ShortcutKeystrokes(input) abort 99 | let leadered = ShortcutLeaderKeys(a:input) 100 | let escaped = substitute(leadered, '\ze[\<"]', '\', 'g') 101 | execute 'return "'. escaped .'"' 102 | endfunction 103 | 104 | command! -bang -nargs=+ Shortcut call s:shortcut_command(, 0, expand('')) 105 | 106 | function! s:shortcut_command(qargs, bang, caller) abort 107 | if a:bang 108 | call s:handle_describe_command(a:qargs) 109 | else 110 | call s:handle_define_command(a:qargs, a:caller) 111 | endif 112 | endfunction 113 | 114 | function! s:handle_describe_command(qargs) abort 115 | let [shortcut, description] = ShortcutParseDescribeCommand(a:qargs) 116 | call s:describe_shortcut(shortcut, description) 117 | endfunction 118 | 119 | function! ShortcutParseDescribeCommand(input) abort 120 | let words = split(a:input) 121 | if len(words) < 2 122 | throw 'expected " " but got ' . string(a:input) 123 | endif 124 | let [shortcut; rest] = words 125 | let description = join(rest) 126 | return [shortcut, description] 127 | endfunction 128 | 129 | function! s:handle_define_command(qargs, caller) abort 130 | let [shortcut, description, definition] = ShortcutParseDefineCommand(a:qargs) 131 | call s:define_shortcut(shortcut, description, definition, a:caller) 132 | endfunction 133 | 134 | function! s:define_shortcut(shortcut, description, definition, caller) abort 135 | execute s:resolve_caller_SIDs_in_definition(a:definition, a:caller) 136 | call s:describe_shortcut(a:shortcut, a:description) 137 | endfunction 138 | 139 | function! s:resolve_caller_SIDs_in_definition(definition, caller) abort 140 | let caller_SID = s:resolve_caller_SID(a:caller) 141 | return substitute(a:definition, '', caller_SID, 'g') 142 | endfunction 143 | 144 | function! s:resolve_caller_SID(caller) abort 145 | return ''. s:resolve_caller_SNR(a:caller) .'_' 146 | endfunction 147 | 148 | function! s:resolve_caller_SNR(caller) abort 149 | let caller_SNR = s:resolve_caller_SNR_from_stacktrace(a:caller) 150 | if empty(caller_SNR) 151 | let caller_SNR = s:resolve_caller_SNR_from_scriptnames(a:caller) 152 | endif 153 | return caller_SNR 154 | endfunction 155 | 156 | function! s:resolve_caller_SNR_from_stacktrace(caller) abort 157 | " See :help 158 | return matchstr(a:caller, '.*\zs\d\+\ze_') 159 | endfunction 160 | 161 | function! s:resolve_caller_SNR_from_scriptnames(caller) abort 162 | " See :help scriptnames-dictionary 163 | redir => output 164 | silent scriptnames 165 | redir END 166 | let caller_relative_path = fnamemodify(a:caller, ':~') 167 | return matchstr(output, '\d\+\ze: \V'. caller_relative_path) 168 | endfunction 169 | 170 | function! s:describe_shortcut(shortcut, description) abort 171 | if get(g:, 'shortcuts_overwrite_warning', 0) 172 | \ && has_key(g:shortcuts, a:shortcut) 173 | \ && a:description != g:shortcuts[a:shortcut] 174 | echomsg 'shortcut.vim: overwriting '. string(a:shortcut) .' description' 175 | \ .' from '. string(g:shortcuts[a:shortcut]) 176 | \ .' to '. string(a:description) 177 | endif 178 | let g:shortcuts[a:shortcut] = a:description 179 | endfunction 180 | 181 | function! ShortcutParseDefineCommand(input) abort 182 | let [description, definition] = s:split_description_and_definition(a:input) 183 | let shortcut = s:parse_shortcut_from_definition(definition) 184 | return [shortcut, description, definition] 185 | endfunction 186 | 187 | function! s:split_description_and_definition(input) abort 188 | let parts = split(a:input, '\s*\ze\<[nvxsoilct]\?\%(nore\)\?map\>') 189 | if len(parts) < 2 190 | throw 'expected " " but got ' . string(a:input) 191 | endif 192 | let [description; rest] = parts 193 | let definition = join(rest, '') 194 | return [description, definition] 195 | endfunction 196 | 197 | function! s:parse_shortcut_from_definition(definition) abort 198 | let [directive; arguments] = split(a:definition) 199 | call s:remove_special_arguments_for_map_command(arguments) 200 | if len(arguments) < 2 201 | throw 'expected "'. directive .' " but got ' . string(a:definition) 202 | endif 203 | return arguments[0] 204 | endfunction 205 | 206 | function! s:remove_special_arguments_for_map_command(list) abort 207 | while !empty(a:list) && a:list[0] =~# 208 | \ '\v||||