DEV Community

Peter Strøiman
Peter Strøiman

Posted on

Leader keys and mapping keyboard sequences

As always, when I write "vim", the information is valid in both vim and neovim (to the best of my knowledge). When I write about a feature specific to neovim, I specifically write "neovim".

In vim, you can create keyboard mappings, not just to complex modified keys, like ctrl+alt+shift+Q, but sequences of keys. In fact, :help map uses the phrase "Map the key sequence", so the mapping of sequences is integral to vim, and mapping a single key is just a special case of mapping a sequence.

As normal mode, doesn't insert anything you don't need complex modifier combinations, but basic letter combinations can be mapped to a command. Most keys on the keyboard already have a function, so finding good key to start the sequence is tricky. The "leader key", is a special key for this purpose that by convention is used to start a sequence. So when you press the leader key, you tell vim, "hey, listen up, now comes a sequence of keys that should trigger a command".

It is common to structure sequences into groupings, e.g. I use <leader>v for vim related tasks, e.g. edit configuration, <leader>s for search (files, text, symbols), <leader>c for code (refactor, actions, toggle inlay hints).

Plugins can attach keyboard command prefixed with the leader key, making plugins respect your choice of leader key.

By default, the leader key is \1, but you can remap it - depending on your keyboard layout it can be a bit tricky to reach. Many remap it to , - but that also have a function (see :help ,), or they remap it to <space>.

I personally use <space>, as my thumb already rests on it by default, making it the most easy key to press.

The leader is just a variable

The use of <leader> in a mapping basically just looks up the global variable at the time of creating the mapg:mapleader (see :help variable-scope). As an example, I have mapped <leader>ve to edit my vim configuration ([V]im [E]dit configuration).

vim.g.mapleader = " "
vim.keymap.set("n", "<leader>ve", load_init_file)
Enter fullscreen mode Exit fullscreen mode

Run :map to display the keyboard maps, it shows that <Space>, is mapped, not <leader>:

n  <Space>ve   * <Lua 27: ~/.config/nvim/init.lua:29>
Enter fullscreen mode Exit fullscreen mode

So it is important that you set the leader before any other keyboard mapping, or loading plugins that may setup keymaps.

Vim waits for unfinished sequences

When you type in a keys which matches the beginning of a mapped sequence, but you don't complete the sequence, vim will wait to see if the sequence is completed. If it's not completed within one second (default value), the vim will behave as if the keys were typed without the mapping. For example, if I map iii in normal mode:

nnoremap iii :echo "Foo"<cr>
Enter fullscreen mode Exit fullscreen mode

If I type iii, of course, "Foo" is written to the list of messages, (see :help :messages). Buf if I type, ii, after a second, vim will enter insert-mode (the first i), and insert an "i" (the second i)

A real example

In insert mode, I have jk mapped to <esc>, a sequence of keys that is extremely quick to type to exit to normal mode, as the two keys are be default just beneath my left index- and middle finger, and it's a sequence that is never used in real words or code sequences (I've used this mapping for about 8 years now, and the only time it conflicts with a real use case is when I write about my vim configuration)

So when I type j, doesn't write the letter to the buffer yet2. If I wanted to type jump, the moment, I type u, "ju", is written to the buffer as now the sequence no longer matches a mapping. If I had continued to type jk vim would run the command; except if there was an even longer sequence starting with jk (Writing that sentence did result in a hickup because of my keyboard mapping).

For more information on this behaviour see, :help map, :help timeout, and :help timeoutlen. Also :help nowait can modify the behaviour when there are conflicting local and global mappings.

Local and global mapping and localleader

Vim allows you to create global keyboard mappings, and "local" keyboard mappings. A local keyboard mapping is attached only to a single buffer. Often local mappings are setup by a plugin or "autocommand" (consider this an event handler, it's a topic I will explain in another post), resulting in

Vim actually defines two types of leader keys, leader and localleader. Leader is intended for global mappings, and localleader is intended for local mappings.

So an example could be a command to "compile" a source code file. If you load a C or C++ file, you might have mapped <localleader>cc ([C]ode [C]ompile) to run cc, the C-compiler. If you load a Java file, you might map the same sequence to run javac, the Java compiler. By having these as local mappings, you can have the same sequence that semantically means the same thing, compile the current file, but is implemented differently depending on the open file, even in a project that includes both C and Java code.

The two leader keys can be the same or they can be different. By default they are both </kbd>, and I have yet to see a vim/neovim configuration that deliberately sets these

Also, not all plugins even respect the distinction and use leader, where localleader is the "correct" key to use.

unmap

The unmap family of commands, removes a mapping. Bear in mind that the build in shortcuts are not mappings, so to a shortcut, you need to remap it to a no-op. E.g. the buildin <Ctrl+a> is not only useless for me, it has caused me a few bugs, so I remap it to a no-op:

noremap <C-a> <nop>
Enter fullscreen mode Exit fullscreen mode

You can remove all mappings with :mapclear

See also: :help unmap, :help <nop>, :help mapclear.

map vs noremap

All map variants, i.e. imap, nmap, xmap, etc, have a noremap variant, e.g. nnoremap. The difference is that the noremap maps to the build-in keyboard commands, where if a map references a command that you've created a map for itself, you will trigger that command.

E.g. as mentioned previously, I have an alternate shortcut <Esc>. In the beginning, to train my brain to use this, I had mapped escape to a no-op:

inoremap <esc> <nop>
inoremap jk <esc>
Enter fullscreen mode Exit fullscreen mode

The use if inoremap makes the jk map to the original escape function. Had I used imap, it would have mapped to a no-op, not very helpful.

You almost always want to use noremap. I have never used a map in my vim configuration.

When using the lua function vim.keymap.set, it is by default the noremap version used. You can set the option { remap = true } to use the map version.


  1. Actually, that wasn't factually correct. By default there is no leader key, \ is the fallback being used. 

  2. The behaviour I observe in my current configuration is that the "j" is shown on screen but the cursor doesn't move until I type another character, or the timeout has completed. 

Top comments (0)