How to Use Custom Key Mappings in Vim

2 minutes

I find Vim to be very useful in my daily development activities. I was delighted to learn that you can have your own key mappings in Vim. It is documented very well in the vim help section. But if you want a quick gist of what is available this post is for you.

Posted in these interests:
h/vim23 guides
h/code69 guides

I am picking a simple example from the vim help documentation itself so you can quickly understand what is going on. Suppose you want a quick way of inserting today's date below your current cursor position. EG:

cursor was here| "Now hitting leader followed by 2 d keys will print the line below
Date: Tue Mar 17 16:11:47 IST 2015

Here is the mapping you can define in your ~/.vimrc

map <leader>dd oDate: <Esc>:read !date<CR>kJ

I will try to break down what the above key mapping does does in the steps below. Note Leader is mapped to ** by default. You can optionally change it to any other key. Spacebar** is a good option which can be achieved with the following mapping in my ~/.vimrc file

let mapleader = " "

You can define custom mappings in ~/.vimrc so that they are permanent across your vim sessions. To define mappings for your current session you can type

:map <leader>dd oDate: <Esc>:read !date<CR>kJ

which will save your mapping for the current session. If you define new mapping in your ~/.vimrc, you may need to run

source ~/.vimrc

in your current shell for your new mappings to reflect.

In the above example we trigger our key mapping by typing <leader>dd in normal mode. Key mappings can be defined for different modes as listed below

:map       Normal, Visual and Operator-pending
:vmap      Visual
:nmap      Normal
:omap      Operator-pending (Eg: dw where d is operator character and w is motion character)
:map!      Insert and Command-line
:imap      Insert
:cmap      Command-line

Once we trigger our custom key binding we ask it to perform some commands. In this case

oDate: <Esc>:read !date<CR>kJ

The break down

o -> Insert a line below current cursor position and switch to insert mode
Date -> Type the text "Date"
<Esc> -> Bring us out of insert mode and into normal mode
: -> This character takes vim to the command line where you can execute commands
read ->  Vim command to Insert output below the cursor(typically from a file) (:help :read)
!date -> ! is a way to execute a shell command in vim. eg: try !ls
<CR> -> This is like hitting enter.  (:help key-notation)

What the above commands does is print the following output below the current cursor position

cursor was here
2 Date:
Tue Mar 17 16:11:47 IST 2015

Now the kJ do the following

k -> Move the cursor up by one line
J -> Join the lines together

Final output:

cursor was here
2 Date: Tue Mar 17 16:11:47 IST 2015

You can get a similar shortcut in insert mode with the following mappings.

imap <leader>dd <CR>Date: <Esc>:read !date<CR>kJA

Slight difference here

<CR>Date: -> Since already in insert mode type enter to go to next line and then type Date.
<Esc> -> Bring us out of insert mode and read the date as before

To bring us back to insert mode we finish with A in the end. Note Based on feedback, I learnt that mapping in insert mode has a drawback. For example if you type

This is today's date <leader>dd
Date: Wed Mar 18 11:23:09 IST 2015

This prints the date below. But if you hit undo u it deletes the entire text inxluding This is today's date unless you switched to normal mode first. So good to remember that.

Based on some feedback, I learnt that there is a key difference between map and noremap noremap is an option that prevents mapping recursively. Consider these mappings

map a $
map T a
noremap M a

Here typing a will take you to the end of the line. T will do the same. However M will behave as a and let you append from current cursor position. So it is always recommended to use non recursive versions of mappings to not get caught in unexpected recursive maps. So generally user

nnoremap, xnoremap, cnoremap, inoremap, etc.

unless know you know what you are doing.

I often need to insert logging statements in my code for debugging purposes. The following mapping will help me easily do that, then get my cursor ready to start typing my debug statements.

noremap <leader>db ologging.debug("")<Esc>hi
inoremap <leader>db <CR>logging.debug("")<Esc>hi

So hitting

<leader>db
will print

logging.debug("|")

with my cursor ready to type between the debug quotes.

Sometimes you have some plugins with your vim setup that already define a ton of mappings. You can use

:map
:noremap

and

:imap
:inoremap

to list all your command and insert mode mappings. Similarly you can list your other mode mappings with corresponding commands.

Ensure you are not overlapping with existing mappings or existing vim commands so that your current functionality does not break. Example mapping my command to dd instead of <leader>dd would mess up my delete line functionality. Also ensure you have your key mappings in the mode that you want them in.

You can find more details about key mapping in vim documentation itself.

:help key-mappings
:help 40.1
:help map-overview

Let me know if you have any comments or suggestions in the comments below and of any cool ways you use custom key mappings. Hope you learnt something new today.

Once you learn to think in text objects, you'll never be the same
Dayne Dayne (57)
5 minutes

Any vim user knows that there are underlying concepts worth understanding. While learning the specific keystrokes is useful, I find that knowing the why is crucial to actually adopting new features.