(Neo)Vim Menu that maps keys to actions inspired by Spacemacs.
You can use your favorite vim plugin manager. An example with vim-plug:
Plug 'nathanielsimard/vmenu'
Vim needs to be in nocompatible
mode to use the filetype keybindings.
set nocompatible
We are building a graph that can be navigated with keybindings. First, we need to define what keybinding we will use to show the vmenu.
nnoremap <silent> <Space> :call vmenu#show()<CR>
There is two things that we can do, add a category to show more keybindings or add directly a keybinding that executes a command. We can start be defining some categories.
let g:vmenu_buffer = vmenu#category('b', 'Buffer')
let g:vmenu_window = vmenu#category('w', 'Window')
let g:vmenu_help = vmenu#category('h', 'Help')
The first argument is the key that is used to open the menu and the second argument is the description.
It is possible to add a menu into another menu to create submenu by adding the parent
option.
let g:vmenu_help_doc = vmenu#category('d', 'Documentation', { 'parent': g:vmenu_help})
Now, we probably want to define some commands.
call vmenu#commands([
\['Q', 'Quit', 'qa'],
\['s', 'Save File', 'w'],
\['S', 'Save All Files', 'wa'],
\["'", 'Terminal', 'terminal']
\])
Each keybinding is defined by a list of 3 items: the key, the description and the command in order. But we also want to add some keybindings into the categories we defined above.
call vmenu#commands([
\['q', 'Quit Window', 'q'],
\['L', 'Add Vertical Space', 'vertical resize +5'],
\['H', 'Remove Vertical Space', 'vertical resize -5'],
\['J', 'Add Horizontal Space', 'res +5'],
\['K', 'Remove Horizontal Space', 'res -5'],
\['h', 'Focus Left', 'wincmd h'],
\['j', 'Focus Down', 'wincmd j'],
\['k', 'Focus Top', 'wincmd k'],
\['l', 'Focus Right', 'wincmd l'],
\['v', 'Split Vertical', 'call VerticalSplit()'],
\['b', 'Split Horizontal', 'call HorizontalSplit()']
\], {
\'parent': g:vmenu_window
\})
The keybindings without the parent
option will be available at the root of the menu.
It is also possible to add some keybindings that will only be active for a specific filetype.
call vmenu#commands([
\['g', 'Generate Doc', 'Pydocstring']
\], {
\'parent': g:vmenu_help_doc,
\'filetype': 'python'
\})
It is possible to directly use the keybinding objects to build the menu. First, we can define the root and the native vim keybinding to show the vmenu.
let g:menu = vmenu#new() " Everything is an object, even the menu.
let g:keybindings_root = vmenu#keybinding#category#new('no important', 'Keybinding Menu')
nnoremap <silent> <Space> :call g:keybindings_root.execute(g:menu)<CR>
But there is no keybindings to show, we need to add some. For example, we may want to add a keybinding to show all open buffers.
call g:keybindings_root.add(vmenu#keybinding#command#new('b', 'List All Buffers', 'buffers'))
But what if we want to have another menu that list all the actions available on buffers with b
.
We can create another category keybinding object and then add the List All Buffers
keybinding.
let g:keybindings_buffer = vmenu#keybinding#category#new('b', 'Buffer')
call g:keybindings_buffer.add(vmenu#keybinding#command#new('b', 'List All Buffers', 'buffers'))
call g:keybindings_buffer.add(vmenu#keybinding#command#new('n', 'Next Buffer', 'bn'))
...
Also, we must not forget to add the new category keybinding to the root.
call g:keybindings_root.add(g:keybindings_buffer)
It is possible to have category keybindings that are aware of the current filetype to only show the appropriate keybindings.
let g:keybindings_refactor = vmenu#keybinding#filetype#new('r', 'Refactoring')
call g:keybindings_refactor.add(vmenu#keybinding#command#new('a', 'Alway visible', 'AlwaysVisibleCommand'))
call g:keybindings_refactor.add_filetype('python', vmenu#keybinding#command#new('f', 'Format File', 'PythonFormaterCommand'))
call g:keybindings_refactor.add_filetype('c', vmenu#keybinding#command#new('f', 'Format File', 'CFormaterCommand'))
...
In this example, the Format File
option will only be displayed on python
and c
files and use the appropriate formater for each filetype.
In order to have zero side-effect with other keybindings, the implementation does not use map
, nmap
, etc.
For now, it means that only one caracter key can be set for each menu entry,
Also, keys like <S-a>
or <S-2>
are not supported, just use the corresponding caracter A
or @
.