Skip to content

Instantly share code, notes, and snippets.

@cabrinha
Last active August 29, 2015 14:25
Show Gist options
  • Save cabrinha/828b27f860770e44228e to your computer and use it in GitHub Desktop.
Save cabrinha/828b27f860770e44228e to your computer and use it in GitHub Desktop.

#Vim beginner's customization guide

This article is for those who've been using Vim for a little while and have started to grasp modal editing and the powers of Vim, and now want to start customizing it. If you're completely new to Vim, there are many resources on the internet for learning it, so I'm not going to bother with that here, I'm just gonna point you here and here.

If you already have someone else's vimrc or are using a distribution like Janus or spf13 then I suggest you delete it. You likely have no idea what it does and it means nothing to you. I'm not going to explain how to use someone else's config, I'm going to attempt to walk you through the process of creating your own, which is much more rewarding, although do keep in my mind I'm really only going to give you my own opinions on the process and attempt to save you from some of the boring legwork of testing things out (which admittedly can also be a rewarding process).

As this is not an introduction to writing Vimscript, and targeted at beginners, a lot of this is suggesting plugins. Some people don't like plugins, and want to write their own Vimscript to do similar things as existing plugins, or just want to use vanilla Vim. I'm not a fan of vanilla Vim (and I imagine neither are you if you're reading this), and I'm not a fan of reinventing the wheel, and I have no problem with using other people's plugins. If you don't like plugins you might as well stop reading now.

This is written under the assumption you're running a Linux distro, but much of this will work on Mac, and some of it on Windows too. Your mileage may vary.

Initial configuration

At this point I'm assuming you have no ~/.vim directory and no ~/.vimrc file. Let's make a directory skeleton to use in your vimrc later with this command:

mkdir -vp ~/.vim/{autoload,bundle,cache,undo,backups,swaps}

Then, let's install a plugin manager called vim-plug with this command:

curl -fLo ~/.vim/autoload/plug.vim https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim

(Aside: There are definitely many choices of plugin managers, but to me, vim-plug is the best of them; the syntax is very clean and it does parallel processing which makes updates so much faster than others. The other biggest contender would be NeoBundle which offers more fine-grained control, but I find the extra control is rarely needed, and vim-plug will do just about all of that anyway and with cleaner syntax. There's also Vundle but it doesn't have delayed loading of plugins which is a really great feature not to be missed out on.)

Now, open your ~/.vimrc with Vim, and let's use vim-plug to install a minimal, sensible, set of default options by adding the following lines:

call plug#begin('~/.vim/bundle')
Plug 'tpope/vim-sensible'
Plug 'shougo/vimproc', {'do': 'make'}
call plug#end()

There's an extra plugin there called vimproc that you'll want to let Vim do things asynchronously, or non-blocking-ly, so that things can run in the background without making Vim freeze. The second half of that tells Vim-plug to run the command make whenever it's installed or updated.

Now let's save this file and install the plugin with this command: :w | PlugInstall. You're hopefully familiar with :ex commands but you may not be aware you can chain them together with |. This is equivalent to running :w and then running :PlugInstall. At this point I would recommend taking a look at the options Vim-sensible defines.

As you're working/reading through this article, try to add comments to help you remember what things do later on (comments are started with " but I prefer to use two of them).

More sensible configurations

Vim's default nature when handling buffers is to not allow opening a new one or changing to a new buffer without saving change to the current one. Anyone who's spent any time in another editor will likely find this incredibly annoying (at least I do), and want Vim to just remember their changes for the moment and focus on a new buffer without prompting to save first. This line will do that:

set hidden

I should have pointed this out earlier, but any options you set in Vim are not actually read by the running instance until you run :source ~/.vimrc. You can write an autocmd to do this automatically whenever you save it, but if you make a typo and save it, you're gonna have a bad time. See the bottom if you want this functionality.


I quite like having line numbers in a text editor, and in recent versions there's also relative line numbering which is really great to have in Vim, since you're most often moving around relative to your position rather than to an absolute line (which of course can still be done). The following lines will add line numbers and relative line numbers (or not if you're using a version that doesn't support it):

set number
if exists('+rnu') | set relativenumber | endif

If you prefer, you can split that into multiple lines where the | bars are.


I'm going to suggest yet plugin (did I mention this article will be filled with lots of them?) from Tim Pope called unimpaired that adds a bunch of keybinds that, I think, should be added by default. Make sure all your Plugs go inside the call block we added at the top.

Plug 'tpope/vim-unimpaired'

With this plugin you can now toggle line numbers with con and relative numbers with cor. My most used features of this plugin are changing buffers with [b and ]b and pasting with yo/yO. You should read the list of all these keybinds by running :vert help unimpaired. I cannot stress the importance of this: you should really, really, read the help file of every plugin you add to your vimrc. You can get a list of all help files for your plugins by running :help local-additions.


Another one of TPope's great plugins is surround. This "provides mappings to easily delete, change and add such surroundings in pairs." The README and help file does a great job of explaining how to use it so I won't bother with that here.

Plug 'tpope/vim-surround'

Before I forget, and while I'm still talking about TPope, there's yet another plugin he's written that expands on the power of the . command which repeats the last action. . doesn't always work with some plugins, so this plugin attempts to fix that.

Plug 'tpope/vim-repeat'

Vim has a very powerful undo tree which is different from the linear undo lists most editors use. Having this tree be persistent even after quitting is one of my favorite features of Vim, and so is having your :ex command history be persistent. The following lines will accomplish this:

if version >= 703
    if exists("&undodir")
        set undodir=~/.vim/undo//
    endif
    set undofile
    set undoreload=10000
endif
set undolevels=10000
if exists("&backupdir")
    set backupdir=~/.vim/backups//
endif
if exists("&directory")
    set directory=~/.vim/swaps//
endif

There's also a plugin called gundo that I recommend adding to graphically navigate the undo tree. Vim-plug also has the ability to not load this plugin until you actually use it.

Plug 'sjl/gundo.vim', {'on': 'GundoToggle'}

If you want to bind opening this to a key, you can do that with

nnoremap <silent> <F5> <Esc>:<C-u>GundoToggle<CR>

But you can, of course, replace <F5> with something else like <leader>u if you prefer. Make sure to read the help file for gundo, there's several settings in there you can tweak.


This is probably a good time to explain mappings and why I chose that particular string of characters. map is the most primitive way in Vim to make a key map to a certain command or function, but map is recursive and this is usually an undesired effect, so noremap, as in "no recursive" map is usually used. nnoremap (notice the extra n at the beginning) is a non-recursive map in normal mode, so if you want the map to work in all modes you can just use noremap. I used <silent> here because I don't prefer to have the command print in the command prompt. <Esc>:<C-u> is a way to make sure the :ex command is entered exactly as intended and there weren't any previously entered characters messing it up. It's also worth specifically pointing out that <C-u> (read as Control+u) will clear all characters entered into the command prompt. Finally, <CR> is 'carriage return', which is kind of another way of saying push enter and actually execute the command.


Vim-sensible enables incsearch which highlights the first match as you type, which is great and all, but there's a pretty awesome plugin that expands on this power to not just highlight the first match, but all visible matches on the screen, which is extremely handy for writing regex. (Again, make sure to check the help file!)

Plug 'haya14busa/incsearch.vim'

The settings I use for this are:

map /  <Plug>(incsearch-forward)
map ?  <Plug>(incsearch-backward)
map g/ <Plug>(incsearch-stay)
map n  <Plug>(incsearch-nohl-n)
map N  <Plug>(incsearch-nohl-N)
map *  <Plug>(incsearch-nohl-*)
map #  <Plug>(incsearch-nohl-#)
map g* <Plug>(incsearch-nohl-g*)
map g# <Plug>(incsearch-nohl-g#)
let g:incsearch#consistent_n_direction = 1
let g:incsearch#auto_nohlsearch = 1
let g:incsearch#magic = '\v'

The last line enables very magic in your / and ? searches, which makes vim behave a bit more like other regex engines, where you don't have to escape all the "magic" characters. These can go anywhere outside of your plugin definitions


One of my new favorite plugins is vim-sneak and I can't believe how long I went without it. This overrides the s command (which is really just a shortcut for xi) to provide a motion that lets you jump to the next two characters you give it. If you've seen easymotion (which I strongly disklike), it's similar to that but faster and more lightweight, and actually provides a motion for use with operators (instead of just moving your cursor). It can also extend f/F/t/T to work across lines which is killer. Be sure to read the :help file for it (have I said this enough yet?), there's some settings you'll definitely want to play with.

Plug 'justinmk/vim-sneak'

The settings I use for this are:

let g:sneak#prompt = '(sneak)» '
map <silent> f <Plug>Sneak_f
map <silent> F <Plug>Sneak_F
map <silent> t <Plug>Sneak_t
map <silent> T <Plug>Sneak_T
map <silent> ; <Plug>SneakNext
map <silent> , <Plug>SneakPrevious

Again, those go anywhere outside of the plugin definitions.


There is perhaps one more plugin that I consider "necessary", and that's targets.vim. This plugin provides some extra modifiers and text-objects to operate on. Vim provides you with ci) to "change inside parenthesis", but this really only works if your cursor is inside the parenthesis. Targets (among many other things) provides you with cin) to "change inside next parenthesis". You'll have to read the help file for all of the features it provides. Hopefully by now I don't have to explain how to install plugins.

Fancy suggestions

Determining whether a file is using tabs or spaces consistently isn't always easily done visually, but Vim has a few features for handling non-visible chars. You'll want to check the helpfiles for more information.

set list listchars=tab:\›\ ,trail:★,extends:»,precedes:«,nbsp:•
"" set listchars+=eol:↵  "" uncomment this line if you want to show end of line chars
set fillchars=stl:\ ,stlnc:\ ,vert:│,fold:\ ,diff:-

As far as looks go in Vim, compared to more modern editors, it's not exactly nice to look at. You may like the minimal interface, but I don't. There are a dozens plugins that fix this, I have found Airline very attractive to look at and very functional while not being too heavy on resources. On top of having a nice statusline at the bottom telling you which mode you're in (and changing colors accordingly), it will also list all of your open buffers or tabs in the tabline.

If that's too fancy for you, you can play with the ruler, which by default only shows the current line number and column, but you can put many things in the ruler:

set rulerformat=%30(%=\:b%n%y%m%r%w\ %l,%c%V\ %P%)

If you want to learn how all the syntax works, check out :h statusline.

Code specific plugins

If you're like me and you spend a lot of time using Vim to write code you'll probably want to make it behave a bit like an IDE, and get some syntax checking and/or code completion added to Vim.

Syntastic is by far the best and almost de-facto syntax linter for Vim. 'Nuff said.

Next I recommend using snippits. Snippits are shortcuts so you can for instance type function<C-k> and have Vim move your cursor around while you fill in the blanks. There are two major choices: ultisnips and neosnippit. Honestly I never tried ultisnips, mostly because neosnippits ties in nicely together with the completion engine I use. Keep in mind both of those are just the snippit engine, there are separate plugins for the actual snippit library (check the READMEs).


When it comes to code-completion, there are many choices, and some of them have drawbacks compared to others, so you'll really want to spend a little time playing with each and see which you prefer most. I've narrowed it down to two:

  • YouCompleteMe - Perhaps the most popular, this is really good at completion for C-languages and also works for Python and has support for a few other languages. It's pretty heavy (as in slow) though and requires clang to be installed.
  • NeoComplete - This is the one I use, mostly because I don't work with C-languages and seems to function and work exactly how I'd like. This requires a somewhat newer version of Vim that's been built with lua support. NeoComplCache is similar but doesn't have the same dependencies.

It's also possible to use YouCompleteMe if clang is available, or attempt NeoComplete if it can, and fallback to NeoComplCache if all else fails, so I wrote a gist for this idea.

If you do webdev work then you'll really want emmett.vim which is more of a text expander: it lets you write css-style selections and then expands them in to the markup it requires. For example ul>li*2 expands to <ul><li></li><li></li></ul> (but with linebreaks and indention).


There are several linters for all kinds of languages, a quick search should bring up at least one for any language so I won't get to far into those, but I do have a few more for you:

  • delimitMate - this will add matching parenthesis, quotes, braces, brackets as soon as you type the first one. I cry whenever I use Vim and this isn't available.
  • endwise - provides a few closing lines for things like if, for, function, etc in several languages
  • fugitive - another one of tpope's great contributions, it may very well be the best Git wrapper of all time.
  • git-gutter - a handy companion to fugitive that shows diff signs from to the current commit beside line numbers
  • gist - speaking of Git, it's often really handy to send a file or just a selection of a file to gist to share with people. This plugin does that very nicely
  • browserlink - another handy one for web developers, this will update your browser whenever you save html/css/js files and doesn't always require a full page refresh.
  • vinegar - if you've come from IDE-land you may be tempted to try and find some sort of file browser for Vim, but Vim already has one. This plugin from tpope is an enhancement over the built-in netrw that lets you navigate for a file and open it in Vim. There's NerdTree but that uses splits and can be a huge pain, and again, there's already a built-in one, no need to add another.

Even more plugins

A very popular choice for many users is CtrlP, which lets you fuzzy filter through files, buffer, tags, etc. It's pretty simple and lightweight, and many users love it, but I opt for:

Unite.vim, which attempts to "Unite" plugins in Vim by providing a common interface. Like CtrlP, it can filter through files and buffers, and also your register list, yank history, and more (there's plugins for this plugin). Out of the box it provides no mappings, you will have to define your own (I personally start them all off with <leader>u, check out the link at the bottom for my vimrc if you like). It will take some time configuring (and possibly/probably a bit of head scratching), but it has improved my Vim experience a whole lot.

I've never used Sublime Text, but I found a plugin that tries to implement it's multiple cursors feature, which is useful for all kinds of things, especially variable renaming.

If you use tmux you might want some integration with it and REPLs in other panes. There are lots of these plugins that do things slightly differently so you should try a few and find one that does what you like. I use tmuxify because it was the first I found that worked how I expected.

Personal tweaks

If you have a lot of <leader> key mappings, you might find that the default \ key can be a bit hard to reach. Well, the space bar doesn't really do much besides advance the cursor one char, just like l does, so I think it's a great candidate for being mapleader.

let g:mapleader = "\<Space>"

The default behavior of Y is to yank the entire line (which can be done with yy), but I like Y to behave more like C and D, which work from the cursor to the end of the line.

nnoremap Y y$

When serching with n and N I often find it difficult to see exactly where the next search was, so I use zt to put that line at the top of the scrolling area (or zz to center the line) Sometimes the search is inside a fold that I have to open with zv to see it. At some point I realized I do this so often that I should really just make it my default behavior:

nnoremap n nzvzt
nnoremap N Nzvzt

note that if you're using incsearch to handle all of your searching you'll have to add the zvzt to the end of that plug mapping


If you've used vim with wrapped text, you may have noticed that j and k don't quite behave as expected, as they jump literal lines and not visible lines, which is done with gj and gk. I thought it would be handy to have a function to swap them around as needed, so here it is:

function! Togglegjgk()
    if !exists("g:togglegjgk") || g:togglegjgk==0
        let g:togglegjgk=1
        nnoremap j gj
        nnoremap k gk
        nnoremap gk k
        nnoremap gj j
        echo 'j/k swapped with gj/gk'
    else
        let g:togglegjgk=0
        nunmap j
        nunmap k
        nunmap gk
        nunmap gj
        echo 'normal j/k'
    endif
endfunction
nnoremap <silent> <leader>tgj <Esc>:call Togglegjgk()<CR>

Early in the text I mentioned autocmd, which is a very powerful feature in Vim. They do have some caveats to be aware of, and the syntax is slightly strange at first, so check out :h autocmd. Any time your vimrc is re-sourced Vim will re-add your autocmds to it's list, so it's good to wrap them in either an augroup or a condition to make them not re-load if already done so (I opt for the former). Here's a quick sample of how to :source your vimrc whenever it's saved, and, if you're using Airline, fix a strange quirk it has when re-sourcing:

augroup VIM
    autocmd!
    autocmd BufWritePost ~/.vimrc source ~/.vimrc | if exists(':AirlineRefresh') | AirlineRefresh | endif
augroup END

Because help files are so helpful in Vim, I find myself reading them a lot (and hopefully you have been too), but by default most of the help splits are horizontal (which makes no sense to me), so I have a couple autocmds for dealing with them. The first is to try and make all help splits vertical on the far right side and resized to 80 columns, and the other is to have K, which by default runs man on the word your cursor is in, run :vert help on the word your cursor is in if you're editing a Vim file. Again, this would go inside the augroup above:

autocmd FileType help wincmd L | vert resize 80
autocmd FileType Vim nnore <silent><buffer> K <Esc>:help <C-R><C-W><CR>

You can also use an autocmd to load your last known cursor position whenever a file is read. Put this inside the block above.

autocmd BufReadPost *
\	if line("'\"") > 0 && line("'\"") <= line("$") |
\		exe 'normal! g`"zvzz' |
\	endif

If you want your Vim settings to be more portable, it's possible to use your .vimrc to recreate your whole ~/.vim dir. This block will create missing directories and install vim-plug and the rest of your missing plugins: (works best at the top of the file)

if empty(glob('~/.vim'))
    silent !mkdir -vp ~/.vim/{autoload,bundle,cache,undo,backups,swaps}
    silent !curl -fLo ~/.vim/autoload/plug.vim
        \ https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim
    source ~/.vim/autoload/plug.vim
    autocmd VimEnter * PlugInstall
endif

Closing a buffer and removing it from memory is usually done with :bd, which is an often used command but is a bit cumbersome to type all the time. The Q key starts you in :ex mode which is more often used on accident, and you can still get to this by typing gQ in normal mode, so I wrote this little function to override it and close buffers by typing QY:

function! PromptQuit()
    echo 'close current buffer?'
    if nr2char(getchar()) =~ 'Y'
        bd
    endif
    silent! redraw!
endfunction
nnoremap <silent> Q <Esc>:call PromptQuit()<CR>

My last suggestion, which is not a tweak for Vim per-se, but is extremely beneficial for Vim users, is to swap your caps-lock and escape keys around. I mean, who actually makes consistent use of caps lock to want it on the home row within pinky's reach? That space would be much better suited for the escape key, and is actually how Bill Joy, the original author of vi expected the keyboard layout to be. Xmodmap will swap them out very easily and at a low enough level that no other programs are any wiser. You'll want to put this in a file called ~/.xmodmap:

remove Lock = Caps_Lock
keysym Caps_Lock = Escape
keysym Escape = Caps_Lock
add Lock = Caps_Lock

Then, to use this configuration, run xmodmap ~/.xmodmap. If you use an ~/.xinitrc you'll probably want to add that to it. You may even want to look into xcape, which can make your caps-lock key function as escape when pressed by itself, or act as control when used as a modifer with another key.

//TODO:

this is still a work in progress

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment