Garrett L. Ward

On Vim

Vim is a complicated beast. Although the recent trend has been away from megalithic IDEs in favour of simple text editors such as Sublime Text, the classic “simple” text editors (emacs and Vim) don’t seem to get much love–mostly because their arcane nature scares off any new users long before they begin to appreciate their true power. But, those brave (or foolhardy) enough to tackle the asymptotic learning curve become gurus of text manipulation, able to fling any ASCII text around with a mere handful of screenshots.

I am not one of those gurus.

What I am is a little bit crazy, and a little bit nostalgic for an era of computing that was largely over before I ever even learned to speak, much less type. I won’t try to convince anyone that Vim is awesome, or that they should chose to learn it over a more modern text editor–the days of Vi being the only thing available on remote systems are long over, so there’s really no justification for even learning basic Vi motions. What I offer here is just a glimpse into the mind of someone who consciously chose to learn a text editor that was already archaic, and a helping hand along the way should you choose that as well.

Get a drink ready. You’ll need it.

Aside: Required Reading

Before we start, I’m going to point you to a few articles about the basics of Vim. I’m not going to teach you those; by the time you need this article you should know how to move with hjkl, and the basics of Vim text objects. If you’re not comfortable with Vim, you should check these out first:

Also, run through the “vimtutor” command, which will get you up-to-speed on the basics of Vim motions.

Getting Started

A fair word of warning: I use OSX, and as such this article is geared towards other people running OSX. Although most of the tips presented here will work on any Unix-like OS (Windows users are on their own here), I make no promises.

Getting Vim

First, we need to install a proper version of Vim. Yes, OSX ships with Vim, but it’s outdated and isn’t built with some useful features. The best way to get a more up-to-date build without much headache is the MacVim project, which provides a native Cocoa interface as well as shipping a more up-to-date Vim binary (7.3.754 as of this writing, whereas the OSX Vim is 7.3 with no patches). It comes with a MacVim.app, which should be installed as normal, and a script called “mvim”, which should be placed in /usr/local/bin.

Console vs GUI

You’ll need to decide if you want to run Vim as a proper OSX app or if you want to run it in a terminal window. There’s a whole other post about that one waiting to be written, but I’ll break down what I use pretty simply:

  • GUI for writing prose (like this)
  • Console for everything else

You’ll see why shortly, but the short version is that running Vim in a console for programming is much better because you can background Vim (or use tmux) and build/test your code, then jump back to Vim to continue editing. It’s a much less jarring shift than switching between apps. If you want to use the version of Vim that ships with MacVim in the terminal, instead of the OSX version, you’ll need to symlink “vim” to the mvim script in /usr/local/bin:

1
2
cd /usr/local/bin
ln -s mvim vim

Test it by running vim –version and ensuring that it shows “Included patches: 1-xxx”.

Configuring Vim

The biggest barrier to using Vim is that, out of the box, it’s pretty user unfriendly. There’s two main focus points for Vim customization:

  • .vimrc, the vim configuration file, and
  • Vim plugins.

The .vimrc file

You can find the entirety of my .vimrc file on github, but I’ll highlight the important parts here.

First, the basic things you need to make Vim sort-of-sane:

1
2
3
4
5
6
7
8
9
10
runtime bundle/vim-pathogen/autoload/pathogen.vim

" VI compatability is overrated
set nocompatible

" Load all the bundles first
syntax on
filetype off
call pathogen#infect()
filetype plugin indent on

These lines set up the pathogen plugin management system, which load all the plugins we’ll talk about later. Additionally, the “set nocompatible” option disables “compatible” mode, wherein Vim will do it’s damnedest to cripple itself to behave like Vi, which was originally written in 1976. Needless to say, we don’t really want Vi compatiblity. We’ll also turn on syntax highlighting and filetype autodetection, which allows Vim to detect what kind of file we’re editing and apply any syntax highlighting, indentation guides, and filetype-specific plugins as needed.

We still need some more basic settings to drag Vim kicking and screaming into the 21st century:

1
2
3
4
5
6
7
8
9
10
11
12
13
" Disable welcome message
set shortmess+=I
" Set default encoding
set encoding=utf-8
" Hide buffers on switching instead of abandoning them
set hidden
" Autoload hidden buffers on change
set autoread
" j and k go by actual line on the screen and not the entire wrapped line
nnoremap j gj
nnoremap k gk
" Make backspace work
set backspace=indent,eol,start

These settings disable the annoying welcome message, set vim to default to utf-8 (because you really should use utf-8 everywhere), and set it to silently send files to the background when editing multiple files, because it’s 2013 and sometimes people work on more than one file at a time. Go figure. Since we’re going to train ourselves to use hjkl instead of the arrow keys, it’ll help a lot to make sure that j and k behave like we expect them to instead of by some bizarre method which made perfect sense when running vi on a 300 baud TTY.

On that note, disable the arrow keys:

1
2
3
4
5
6
7
8
9
" Use the right goddamn keys
inoremap  <Up>     <NOP>
inoremap  <Down>   <NOP>
inoremap  <Left>   <NOP>
inoremap  <Right>  <NOP>
noremap   <Up>     <NOP>
noremap   <Down>   <NOP>
noremap   <Left>   <NOP>
noremap   <Right>  <NOP>

You’ll get used to hjkl (and, hopefully, the more powerful Vim motions) rather quickly.

We also set up tab settings. This is more a matter of personal preference, but mine are:

1
2
3
4
5
6
7
8
" Tabs are four (spaces) wide
set shiftwidth=4
set tabstop=4
set softtabstop=4
" Tabs insert spaces, not a tab character
set expandtab
" Round to multiples of shiftwidth when < or >ing
set shiftround

This sets tabs to 4 spaces, and always inserts spaces instead of actual tab characters. If you’re a fan of tabs over spaces, change “set expandtab” to “set noexpandtab”.

It’s nice to set up some sane-r defaults for searching, too:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
" Search options
" Ignore case when searching
set ignorecase
" Unless the search term has capital letters
set smartcase
" Global search and replace by default; use s/a/b/g to override
set gdefault
" Start searching as soon as text is typed
set incsearch
" Jump back to matching bracket briefly when closing one is inserted. 
set showmatch
" Highlight search results. Can be annoying, so
set hlsearch
" leader+space will clear it. 
nnoremap <leader><space> :noh<cr>

The last mapping uses Vim’s leader key, which is a handy way to add quick keyboard shortcuts in normal mode. I remapped mine from ‘' to ‘,’:

1
let mapleader = ","

So, the above mapping to clear highlights would be “Hit the , key, followed by the spacebar”. You’ll see more examples of leader commands below, but suffice to say it’s one of the more useful features of Vim.

Finally, let’s set up a status bar to show useful information about the current file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
" Always show a status line. 
set laststatus=2
set statusline=%F%m%r%h                     " file, modified, ro, help tags
if v:version >= 703
    set statusline+=%q                      " quickfix tag
endif
set statusline+=\ [%{&ff}]%y                " file format (dos/unix) and type
set statusline+=\ %{fugitive#statusline()}  " git status (branch)
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*                          "*
set statusline+=\ %=#%n                     " start right-align. buffer number
set statusline+=\ %l/%L,%c                  " lines/total, column
set statusline+=\ [%P]                      " percentage in file

Some of these are provided by the plugins we’ll discuss below, but the end result is a status bar telling us:

  • The file name and whether it’s been modified
  • The format (line-ending style) and filetype
  • The current git branch, if any
  • The number of errors in the current file
  • How many lines there are in the file
  • How far down the file we currently are.

There are some other useful settings, but they’re a bit more advanced so we’ll leave them for you to play with later

Keymappings

I do define a few useful keymappings (mostly using the leader key):

First, we disable the F1 key, which by default brings up the help menu. And is very easy to hit when you meant to hit Esc.

1
2
3
4
" Stop accidentally triggering help
inoremap <F1> <ESC>
nnoremap <F1> <ESC>
vnoremap <F1> <ESC>

When writing, it’s useful to write without wrapping and clean up later; these two are easier to remember than the actual Vim macros:

1
2
3
4
" Un-wrap hard-wrapped text. 
nnoremap <leader>q vipJ
" And the opposite
nnoremap <leader>Q gqap

Sometimes, especially when dealing with older files, nuking the indentations and starting over is the only sane choice:

1
2
" Re-indent the whole document.
nnoremap <leader>re G=gg

Finally, it’s nice to be able to quickly change Vim’s working directory to the one containing our open file:

1
2
" cd to the directory of the currently open file
nmap <silent> <leader>cd :lcd %:h<CR>

Quicker Escaping

If you’ve done the required reading, you probably came across this idea already:

1
inoremap jj <Esc>

This remaps the chord “jj” to act as Escape when in insert mode. You can also use jk, or similar, for equal effect.

Filetype-specific settings

Using Vim au commands, you can set or change settings only for specific filetypes. For example, I have the following in my .vimrc:

1
au FileType make   setlocal noexpandtab

Which ensures that any make file uses real tabs instead of spaces.

You can also map files to Vim filetypes, like so:

1
au BufRead,BufNewFile {Gemfile,Rakefile,Vagrantfile,Thorfile,Procfile,config.ru,*.rake} set ft=ruby "*

Which says that rakefiles and Vagrantfiles are actually just ruby files and should be treated as such. There’s lots of options here, should you need custom configuration for a specific filetype.

Vim Plugins

The true power of Vim is its extensibility. Many, many plugins exist for Vim to do all kinds of horrible things. Below are the ones I use most frequently, and that I would consider (near) essential.

Pathogen

First, just install pathogen already:

1
2
cd .vim/bundle
git clone git@github.com:tpope/vim-pathogen.git

This allows you to install Vim plugins to subfolders of ~/.vim/bundle, which makes plugin management much, much, much easier.

Other plugins

The list below is every plugin I have installed, in no particular order. I’d recommend most of them, but some of them are obviously only useful in certain edge cases. If nothing else, get Syntastic and CtrlP. The rest are really dependent on your language(s) and VCS(es) of choice.

I’ve tried to note where plugins depend on certain Vim features, such as Python or Ruby support. I’ll also note any .vimrc settings I use to make them play nice, if needed.

YouCompleteMe

YouCompleteMe does intelligent code completion with semantic support for C-like languages (courtesy of libclang) and anything else supported by Vim’s omnicomplete engine. It also does matching based on substring matching for any other filetypes.

I only have a few custom settings here:

1
2
3
4
5
6
7
8
9
let g:ycm_key_detailed_diagnostics = '<localleader>d'   " Don't confict with my ,d mapping
let g:ycm_confirm_extra_conf = 0
" Disable YCM for LaTeX
let g:ycm_filetype_blacklist = {
      \ 'notes' : 1,
      \ 'markdown' : 1,
      \ 'text' : 1,
      \ 'tex' : 1,
      \}

The import ones are to move YCM’s diagnostics keybind to use localleader (which is still ‘') so it doesn’t interfere with my ,d binding for CtrlP, and to make YCM not run for LaTeX files.

YCM requires python bindings and, if using libclang, building a shared library. It also needs a very recent version of Vim (> 7.3.684, at a minimum), so if you’re on Linux you’ll most certainly end up having to build your own Vim from source. Good luck!

Syntastic

Syntastic provides syntax checking and/or lint checking for many languages. It checks automatically on write and uses the vim gutter to visually indicate lines with errors. If you’re using YCM, YCM will replace the Syntastic checker for C/C++ and provide automatic syntax checking as you code, which is quite nice.

Syntastic usually depends on either having a compiler installed or on having a lint checker (eg, pyflakes) for the language you’re using.

CtrlP

CtrlP is a fuzzy file finder, written in pure VimScript (so no ruby dependency like Command-T). The idea is that you can invoke it and start typing any part of the name of a file in or anywhere below the current working directory, and choose any that match. There’s configurable keybinds for opening a file in the current window, in a new split, or in a new tab. Additionally, it can search open buffers as well, which is what I use it for more often than anything else.

I have a keybind set up to invoke it:

1
2
" Bring up the CtrlP buffer list. 
nnoremap <leader>d :CtrlPBuffer<CR>

I also tweak some settings to make the default window slightly easier to use:

1
2
3
let g:ctrlp_regexp_search = 1       " Regex search by default
let g:ctrlp_max_height = 15         " Show more results at once
let g:ctrlp_switch_buffer = 0       " Don't try to use the buffer if it's already open

NERDCommenter

NERDCommenter allows you to quickly comment/uncomment arbitrary text objects in Vim. It supports several comment modes, but the only one I ever use is toggle comments on/off, which I map to “,/”:

1
map <leader>/ <plug>NERDCommenterToggle<CR>

LaTeX-Box

LaTeX-Box is a plugin for using LaTeX with Vim. It adds basic text objects (e.g., select environment block) and some omnicomplete stuff, as well as macros which, in combination with latexmk, can (continuously) compile LaTeX documents as you edit. It has a lot of functionality I don’t use much, or at least that I just don’t know about; I mostly use it for the compile (\ll) and view (\lv) shortcuts. For OSX, you need to tell it to use open to open the PDF correctly:

1
2
let g:LatexBox_latexmk_options = '-pvc -pdflatex="pdflatex -shell-escape"'
let g:LatexBox_viewer = 'open'

I also tell it to pass the “-shell-escape” option to pdflatex, which is necessary for minted to work correctly on documents which have source code blocks embedded.

UltiSnips

ultisnips is a snippet management tool. It supports using python to define snippets (but also needs python 2.7+ in Vim to work). Unlike snipmate, it supports local snippets in addition to packaged snippets, so it doesn’t require dealing with merging from upstream when updating. This is mostly useful for generating boilerplate in languages (A proper main() function in C, or environments in LaTeX, for example). I rebind the expansion key to Ctrl-J to avoid conflicts with YCM, and also because that’s the default key for jumping between variable spots in more complex snippets:

1
let g:UltiSnipsExpandTrigger = '<c-j>'

Fugitive

Fugitive is a power set of tools for working with git from within Vim, including staging and committing, using git blame, and a statusbar object with information about the current git branch. A very powerful tool, and I really don’t even use 3/4ths of the features. I mostly use it to stage and perform commits without dropping to shell, though it can do a lot more. I’ll get around to learning more about it one of these days.

Other plugins

Those are the main plugins I use, but there are some others I have installed that don’t quite warrant a section of their own

Syntax/Filetype support

  • vim-git - Syntax files for various git text files, such as commits.

  • ample.vim - Syntax highlighting for Mentor Graphics’ AMPLE language, a TCL-esque language used to script the entirety of the IC Station/etc GUI. Installed because I wrote helper functions during EGRE633, namely one that turned 30+ minutes of manually adding polygons into a 2 second script run. Go me.

  • vim-markdown - Syntax highlighting for markdown files.

Miscellaneous

  • gundo - Visually see the undo history of your file. Much more useful in Vim 7.3+ where persistent undo files are a thing.

  • nerdtree - Browse the current working directory in a sidebar.

  • vim-easytags - Automatically generate tags files during cursorhold times (e.g., when not typing). Also generates highlights such that text which is also a tag is highlighted (which makes function names and the like jump out). Useful in combination with

  • tagbar - Using ctags information, display an outline of the current file (methods, classes, etc). Jump to and from tags. Only useful for larger files or overly-object-oriented code.

  • vim-distraction-free - An iaWriter inspired mode for Vim, for use when writing prose. Modified to not use its Markdown theme (which looks horrible with dark backgrounds) and to not force guioptions to +r on restore. Only really works with MacVim, so writing is more or less the only time I use the GUI version of Vim over the console version.

  • vim-gitgutter - Uses git diff to show unstaged and staged diffs from the last git commit. Useful for at-a-glance overview of what’s changed (and as a visual reminder of uncommited changes!)

  • vim-indent-guides - Visual representation of indent levels. This is probably useful, but I keep forgetting that I have it installed…

  • vim-surround - Adds new features for surrounding objects with characters or markup (e.g., HTML code).

  • vim_ack - Use ack to search in the current working directory and place all results in the quickfix window. Adds shortcuts to quickly jump to any results.

  • vimux - Interact with tmux from within Vim. Requires ruby support.

Closing

Vim isn’t for everyone. If you just want to bang out text with some useful features on top, go use Sublime Text, or TextMate 2, or even gedit. But if you spend the time learning Vim properly, you’ll soon find yourself unable to work effectively in anything without Vi-like keybindings, because you know that what takes 5 mouse clicks and 2 dialog boxes in Sublime takes 4 keystrokes in Vim.

I’d tell you to run, but if you’ve made it through this article it’s probably already too late. God help you. And welcome to the club.