Vim Keybindings I Use Every Day

And I mean custom mappings, not built-in stuff. I mean, you could easily fill a blog post with the built-in bindings you use regularly, since, in vim, there's a dozen ways to do the same thing. Some people prefer D and others prefer d$. But no, this post will include some of the custom bindings defined in my vimrc that I use pretty much every day.

If you're a big vim user already, feel free to grab these and throw them in your vimrc (if you're not a big vim user, that's usually ~/.vimrc). If you just want to experiment, you can type ":" and then any of the commands below to see how they work.

First, it's worth noting that I have my leader key set to space (which feels way more comfortable than the default backslash). You can configure your leader to use space like this:

let mapleader = "\<Space>"  

The following commands use the map and noremap base commands (and their modal equivalents). noremap is a non-recursive mapping, meaning any non-default mappings that occur in another mapping will not be expanded, whereas map is recursive. For example, given these bindings

:map j gg
:map Q j
:noremap W j

pressing j will invoke the default gg binding (jump to top of file). Pressing Q will also jump to the top of the file because map is recursive, so the j is recursively mapped to the gg mapping above it. However, the third binding will simply move your cursor down a line (the default binding for j) since it is a non-recursive map and the j is not expanded.

In addition to these commands, there are modal equivalents for creating bindings that only work in particular modes, such as nmap and nnoremap, imap and inoremap, and vmap and vnoremap for normal, insert, and visual mode recursive and non-recursive mappings respectively. If you are new to vim and that sentence was gibberish to you, 1) don't worry about it and just copy the mappings below. They work. I promise. 2) Don't worry about it. Vim takes time. I've been using it exclusively for over two years now and I still learn new things. There's probably still more that I don't know than I do.

Toggle highlighting

Vim can highlight matches when you search if you set the hlsearch or hls flag (i.e. :set hls). This is a nice feature when you're in a text heavy piece of code, like HTML or markdown, or if you're just looking for a quick overview of where a variable is used, but I find it distracting to have on all the time. Here's a couple bindings you can add to quickly toggle between highlight and no higlight.

" Toggle on highlighting
nnoremap <silent> <leader><CR> :set hls<CR>  
" Toggle off highlighting
nnoremap <silent> <leader><Esc> :set nohlsearch<Bar>:echo<CR>  

The first binding let's you turn on search highlighting by pressing "space enter" (depending on what you're leader key is). The second let's you turn it back off with "space esc."

Tab Navigation

It only makes sense that your tab key should switch between tabs. They have the same name after all. The tab key is popular in many Vim plugins, such as YouCompleteMe and Snipmate, but most plugins don't use the normal mode mapping, so add the following to make Tab and Shift-Tab cycle tabs forward and backward.

" Go to next tab
nnoremap <Tab> gt  
" Go to previous tab
nnoremap <S-Tab> gT  

Smarting backspacing

First, backspacing is not smart in vim to being with, so you should definitely add

set backspace=indent,eol,start  

to your vimrc to make it work more like you'd expect. An additional mapping I like to add is one that deletes the previous character while putting you into insert mode.

nnoremap <BS> i<BS>  

It's important to map it this way, as opposed to some of the other possible ways, like hs. That achieves the same thing, but also writes the character you delete to your default register, blowing away anything there, which I find annoying.

Remap annoying things

I use to hit F1 frequently when trying to exit insert mode, so I finally added these first two bindings to eliminate this problem. The third is just for consistency, since D deletes to the end of the line and C changes to the end of the line, but Y by default yanks the whole line (which yy also does).

map <F1> <Esc>  
imap <F1> <Esc>  
nnoremap Y y$  

I guess I don't technically know that I use this binding every day. The whole point of it is that I don't know when I'm using it. But I definitely did hit F1 somewhat frequently before I added it.

Toggle wrapping

Just like highlighting, text wrapping can be helpful sometimes (again, often in html) but is also annoying to leave on all the time. These bindings quickly toggle between wrapped and unwrapped text.

" Turn on wrapping
nnoremap <leader>wr :set wrap<CR>  
" Turn off wrapping
nnoremap <leader>nw :set nowrap<CR>  

Note the mnemonic wr for wrap and nw for no wrap. On a side note, I also have this configuration in my vimrc

set showbreak=>>\ \ \ \  

which makes it easier to see which lines are wrapped lines.

Always navigate linewise

By default, vim treats a line as a line, even if it wraps across multiple lines, but you can override that with the following bindings so that j and k function more as you'd expect when wrapping is turned on.

nnoremap j gj  
nnoremap k gk  
vnoremap j gj  
vnoremap k gk  

Toggle test exclusivity

These bindings are extremely specific and therefore might not help you, but I spend a lot of time writing jasmine (with jasmine-only) and mocha tests for node, so I frequently add .only to a describe or context to run a test exclusively. This is tedious, so I wrote these little helpers to do it for me.

" Top-only: Add .only to top-most describe
nnoremap <silent> <leader>to moG/describe<CR>ea.only<Esc>`o:delmarks o<CR>

" Describe-only: Add .only to nearest describe
nnoremap <silent> <leader>do mo/describe<CR><S-N>ea.only<Esc>`o:delmarks o<CR>

" Context-only: Add .only to nearest context
nnoremap <silent> <leader>co mo/context<CR><S-N>ea.only<Esc>`o:delmarks o<CR>

" It-only: Add .only to nearest it
nnoremap <silent> <leader>io mo/it '<CR><S-N>ea.only<Esc>`o:delmarks o<CR>

" Remove-only: Remove all occurrences of .only
nnoremap <silent> <leader>ro mo:%s/\.only//g<CR>`o:delmarks o<CR>  

The mnemonics here are as follows:

  • to = top only
  • do = describe only
  • co = context only
  • io = it only
  • ro = remove only

A sort of tl;dr explanation of what's happening is that we mark the current position, find the function (describe, context, etc.) to annotate, annotate it, then jump back to the mark we set.

Copy and paste

Copying and pasting from the system register is much easier on Linux than on Mac, but nonetheless, it feels more intuitive using these mapping.

vnoremap <leader>y "+y  
nnoremap <leader>p "+p  

On Mac, it's similar:

vnoremap <leader>y "*y  
nnoremap <leader>p "*p  

Bonus points

And on the topic of different commands for Linux vs. Mac, here's a little variable you can set to determine where you're running.

let g:osName = substitute(system('uname'), "\n", "", "")  

You can then use this variable like this

if g:osName == 'Darwin'  
  " Add some Mac specific bindings
else  
  " Add Linux specific bindings
endif  

Conclusion

I believe this is the part of the show where I am supposed to tell you how vim will change your life and how all other editors are inferior. But I'm not going to do that. I do believe it improves my productivity immensely, and it probably would for most people if they take the time to learn it, but many don't want to take that time and that's fine. Vim is not for everyone. But hopefully, if you're in the process of learning or you just have an interest in doing so, these bindings will help you become more productive quicker. Happy typing!

comments powered by Disqus