DEV Community

Igor Irianto
Igor Irianto

Posted on • Updated on

Basic Vim Mapping

Follow @learnvim for more Vim tips and tricks!

One way you can customize vim is by creating your own key mappings to make Vim yours. This article will show you basics of map. This is not 100% comprehensive, but should be enough to get you started.

Basic Mapping

If you want to map x to dd (delete line), you can:

:map x dd
Enter fullscreen mode Exit fullscreen mode

Now whenever you type x, it executes dd instead.

Unmapping

To undo a particular mapping (ie, our x earlier), do:

:unmap x
Enter fullscreen mode Exit fullscreen mode

When you type x, it will no longer do dd. It is back to its original function.

To remove all mapping, you can do

:mapclear
Enter fullscreen mode Exit fullscreen mode

This will wipe out all maps (don't worry, just exit and reopen vim to get your vimrc mappings back).

Avoiding recursion

Suppose instead of mapping x to dd, you want to map x to delete 5 characters at a time (5x), so you do:

:map x 5x
Enter fullscreen mode Exit fullscreen mode

When you type x, your screen freezes. What happened?

:map x 5x
     ^  ^-- x is recursive
Enter fullscreen mode Exit fullscreen mode

What we are really hoping to do is to execute original x function (delete character under cursor). But Vim interprets it as if we are calling x map, which calls 5x, which calls our map, and so on, recursively - causing your display to freeze (C-c to free yourself).

To tell vim to use original x functionality, we need to use *NO RE*cursive mapping. In Vim, we add nore-:

:noremap x 5x
Enter fullscreen mode Exit fullscreen mode

This time it should work.

Steve Losh, author of learn Vimscript the Hard Way, recommends using nonrecursive all the time.

I agree.

Even if we don't need it, it is a good habit to start mapping using non-recursive variants.

Different mode mapping

What if we want to create a mapping that works only on visual mode, or mapping that works only on normal mode, or mapping that works only on insert mode? We can use modal mapping.

If you check :h map.txt, you'll see many different map options:

     COMMANDS                    MODES ~
:map   :noremap  :unmap     Normal, Visual, Select, Operator-pending
:nmap  :nnoremap :nunmap    Normal
:vmap  :vnoremap :vunmap    Visual and Select
:smap  :snoremap :sunmap    Select
:xmap  :xnoremap :xunmap    Visual
:omap  :onoremap :ounmap    Operator-pending
:map!  :noremap! :unmap!    Insert and Command-line
:imap  :inoremap :iunmap    Insert
:lmap  :lnoremap :lunmap    Insert, Command-line, Lang-Arg
:cmap  :cnoremap :cunmap    Command-line
:tmap  :tnoremap :tunmap    Terminal-Job
Enter fullscreen mode Exit fullscreen mode

I will not go through all, but the documentation provide excellent resource. To learn more, nothing beats Vim's own documentation. I will go through 3 of them to get you started:

  • normal
  • insert
  • operator pending

Let's go through each.

Normal

If we use :map, it actually applies to 4 modes: normal, visual, select, and operator-pending. If we want to apply our mapping to just normal mode, we need to use normal-mode only map, nmap:

:nmap x dd
Enter fullscreen mode Exit fullscreen mode

This will map x to dd in normal mode. Using the nonrecursive mapping variant, we have:

:nnoremap x dd
Enter fullscreen mode Exit fullscreen mode

I will use the non-recursive variant of all mapping from this point on.

Insert

We can also create mapping for when we are in insert mode. One popular insert mode map is to escape to normal mode. This can be very useful if you are typing with keyboard that does not have <escape> key equivalent (like some ipad keyboards). I mapped <esc> to "jk" using:

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

Typing "jk" will now exits insert mode.

When mapping for insert mode, don't forget that Vim takes your mapping literally. Let's say whenever you type Ctrl-X in insert mode, you want it to delete 5 characters under cursor (5x).

You might try:

:inoremap <C-X> 5x
Enter fullscreen mode Exit fullscreen mode

If you tried that, you'd see that it didn't work. Instead it printed "5x". You need to first escape, then perform 5x, then back to insert mode.

:inoremap <C-X> <esc>5xa
Enter fullscreen mode Exit fullscreen mode

Operator Pending

If you are not familiar with "Operator Pending", let me briefly explain. In Vim, whenever you type an operator (ex: d, y, and c), you need to give it a motion or text object. This mapping is for the motion/text object that comes after you type an operator.

For example, to delete (d) inner word (iw), you can do diw. To create a mapping of dw so it does diw:

:onoremap w iw
Enter fullscreen mode Exit fullscreen mode

Whenever we do dw, it will instead do diw. Not much, but it saves one keystroke!

More resources on operation pending mapping:

Special arguments

We can give our mapping special arguments (:h :map-arguments). There are 7 special arguments:

<buffer>
<nowait>
<silent>
<special>
<script>
<expr>
<unique>
Enter fullscreen mode Exit fullscreen mode

The basic syntax is:

map <special-argument> key-trigger key-sequence
Enter fullscreen mode Exit fullscreen mode

I will go over silent and buffer because they are quite useful. You should be able to look for the rest.

Silent

Adding <silent> prevents stdout in Vim when a command runs. Sometimes when you execute a command call in Vim, it gets echoed. Adding <silent> removes the echo. It does not remove error message though.

For example, I use fzf.vim:Buffers a lot and it echoes "Buffers" every time I run it.

This is the mapping that I initially have:

nnoremap <Leader>bb :Buffers<CR>
Enter fullscreen mode Exit fullscreen mode

To silence it, I need to add <silent> in the map:

nnoremap <silent> <Leader>bb :Buffers<CR>
Enter fullscreen mode Exit fullscreen mode

Now when I type <Leader>bb, I no longer see "Buffer" echoed.

Buffer

Using <buffer> will make the mapping unique only to current buffer (if you're not sure what buffers are, this is an excellent article).

For example, if I have 2 files opened: fileA and fileB.

In file1 buffer, I want to map x to be 5x:

:noremap <buffer> x 5x
Enter fullscreen mode Exit fullscreen mode

In file2 buffer, I want to map x to do dd:

:noremap <buffer> x dd
Enter fullscreen mode Exit fullscreen mode
  • When I type x in file1, it does 5x.
  • When I type x in file2, it does dd.
  • When I type x in new file, file3, it does neither 5x nor dd.

Leader Mapping

Vim allows us to use <Leader> key for further customization. For example, I have several mappings for buffers that use Leader key

nnoremap <Leader>bs :ls<CR>:sbuffer<Space>           " 1
nnoremap <Leader>bv :ls<CR>:vertical sbuffer<Space>  " 2
nnoremap <Leader>bp :let @+=expand("%:p")<CR>        " 3
Enter fullscreen mode Exit fullscreen mode
  1. split buffer horizontally
  2. split buffer vertically
  3. copy current buffer's absolute path.

Vim's default leader is \ (to view your current leader, you can do :echo mapleader).

I remapped my leader key to <space> because it is the biggest key in my keyboard. To do that, in my vimrc, I did:

let mapleader = "\<space>"
Enter fullscreen mode Exit fullscreen mode

Conclusion

During your Vim journey, you will want to start mapping commonly used actions. I suggest creating your own mapping (and not copy-paste other's vimrc) because other people may have different workflow than you.

I hope this article helps you understand Vim mapping better. Happy coding!

Happy vimming!

Resources:

Btw, when I was reading the docs, :h map.txt, I was initially confused by {lhs} and {rhs}. After researching, they are pretty simple:

  • lhs is the map trigger.
  • rhs is key sequence if mapping is triggered.

If we have :map x dd, x is {lhs} and dd is {rhs}.

Oldest comments (0)