Vim After 11 Years

by Ian Langworth - 19 February 2013

TL;DR

main

Introduction

At some point over a decade ago I received my first real Unix account on Northeastern CCS’s computing infrastructure. I realized that my primary method of development — editing files in BBEdit and uploading them via FTP — wouldn’t scale for college-level projects, so I decided to learn how to efficiently edit files on a remote host. I used Pico for a while but became annoyed at its lack of syntax highlighting, so I used the only other editor I remembered bumping into: Vim.

Fast forward eleven years. I’ve written many different languages and used Eclipse, IntelliJ IDEA, TextMate, and Flash Builder. I even used Emacs for a year just to see what I was missing. But when it was time to start a company I knew that an early priority would be to bang out code quickly, so I chose the best tool for the job.

Recently, someone noticed a plugin I was using and said, “I had no idea Vim could do that. You should teach me all of these tricks someday.” A fine suggestion, indeed, and that day is today.

Customizing Vim: A Preamble

Customizing Vim is two parts: editing rc files and installing plugins. You should already have a .vim/ directory with a vimrc and possibly a gvimrc. For plugins, the first thing to look at is Pathogen, a neat little plugin that lets you keep plugins in subdirectories of .vim/bundle/ instead of merging everything into the big .vim/ tree. It’s easy to install and makes it a breeze to try out new plugins.

It’s also useful to keep the .vim/ directory in version control because you can test a plugin or setting and, if you don’t like it, put everything back the way used to be with a git revert.

Addtionally, I use an update.sh script I wrote to install plugins as well as keep everything up date. Keeping Vim plugins up to date is advantageous; I’ve found that Vim plugins are often updated to fix bugs but not change functionality significantly.

Regarding mappings, I use the backslash key as my utility prefix since backslash isn’t bound to anything by default. For example, \o toggles paste mode and \l toggles line numbers:

:nmap \l :setlocal number!<CR>
:nmap \o :set paste!<CR>

Rudimentary Essentials

Vim’s defaults are pretty smart, but there are a few small rough spots which need ironing out.

The Esc key is used to return to Normal mode, but on most keyboards the Esc key is pretty far from the home row. Ctrl-\[ produces the same keycode but involves two hands. Both might have problems on latent terminals which support Esc as an alternative to Meta. Alternatively, use Ctrl-C — it’s easy to type, instant, and you’re already used to jamming on it from when you cancel commands. The only caveat is that you still need to use Esc for operations in visual block mode (more on this later).

Moving around text is part of what makes using Vim feel so efficient. The one single thing I could never stand about Vim’s default movements, however, is how j and k move around wrapped lines. By default they move you one line down and ups but on a linewise basis, which is annoying. If I hit j I would expect the cursor to move down a single row on the screen, just like every other text editing area in the world. The following does just that:

:nmap j gj
:nmap k gk

Regarding Vim’s command line, its defaults make it behave very unlike a modern command line. If you’re used to Emacs-style movement keys on your Bash or Zsh command line (using Ctrl-A and Ctrl-E and the like) the you might want to make Vim act the same way:

:cnoremap <C-a>  <Home>
:cnoremap <C-b>  <Left>
:cnoremap <C-f>  <Right>
:cnoremap <C-d>  <Delete>
:cnoremap <M-b>  <S-Left>
:cnoremap <M-f>  <S-Right>
:cnoremap <M-d>  <S-right><Delete>
:cnoremap <Esc>b <S-Left>
:cnoremap <Esc>f <S-Right>
:cnoremap <Esc>d <S-right><Delete>
:cnoremap <C-g>  <C-c>

A common operation is to search for text, so it makes sense to have some sane defaults. The incsearch option highlights as you type an expression (a.k.a. “Emacs style”), and ignorecase plus smartcase make searches case-insensitive except when you include upper-case characters (so /foo matches FOO and fOo, but /FOO only matches the former). hlsearch is a useful option which highlights the current search, but the highlight can become annoying so it makes sense to have a key to clear the highlight when you no longer need it:

:set incsearch
:set ignorecase
:set smartcase
:set hlsearch
:nmap \q :nohlsearch<CR>

Vim, the Terminal, Buffers, and You.

There are two forms of Vim: Vim in the console and the native gVim application. The advantages of gVim are better OS integration for dialogs, native printing support, and a wider range of color themes. A modern terminal gives you everything else such as mouse support and mostly-decent color.

It was decided early in its development that Vim isn’t designed to replace a terminal, so Vim is and forever will be bad at emulating terminals. Using the :shell command in gVim is a path to madness. One plugin, Conque, comes close to adding terminal support, but I found that it caused Vim to hang, and its integration felt clunky. Thus, if you do a lot of work from the command line while you edit, the best way to run Vim is from the command line.

One of Vim’s strengths is that it starts lightning fast, so starting Vim from the terminal is trivial. With a modern, 256-color terminal like iTerm2 or Gnome Terminal, it will even look like gVim. But the best part is that you can drop into the command line at any time with Ctrl-Z, which suspends Vim, and your working directory is where you left off.

If you have trouble remembering which terminal has Vim suspended, try adding the number of background jobs to your shell prompt. If you have many Vims in the background, however, you should start using buffers instead.

Vim has the powerful ability to keep multiple files open in the background, and there are many ways to navigate between them. This is useful for performing work on a project or for editing multiple files as part of an operation.

For example, say your current working directory is a Django project and you want to edit the shopping cart request handler, so you run vim cart/views.py. Once Vim is open you realize you need to change a setting so you type :e settings.py, which opens up the file settings.py in the current window. You can get back to the other file by running :b views (:b is short for :buffer), which performs a substring match across all buffers, or use :b#, which opens the previously viewed buffer. Buffers become more powerful once you start using CtrlP, which I’ll get to below.

A lot of times you’ll want to do some work somewhere other than the file you’re editing and return afterward, in which case :b# is a godsend. (Or :e#, I’m not sure if there’s a difference.) It’s so useful that it deserves a more natural keybinding, like Ctrl-E. (The default binding is Ctrl-Shift-6, which you’ll never remember, and nobody knows what Ctrl-E does anyway.)

:nmap <C-e> :e#<CR>

Vim automatically creates a buffer for each file on the command line. This is useful from the command line, such as with vim *.js, or combined with grep/ack: vim `grep -l foo *.js`. I use this pattern so often that I bound two keys to cycle between all open buffers:

:nmap <C-n> :bnext<CR>
:nmap <C-p> :bprev<CR>

Yes, My Editor Does That

Vim has error highlighting through the plugin Syntastic, which uses external compilers and linters to show errors inline with your code. This is absolutely essential, and it works so well that the shortness of my description doesn’t do it justice.

syntastic

Syntastic uses the current file’s filetype and runs an appropriate linter or compiler after every save ­ super useful for finicky syntaxes of JSON, CSS, and SASS, not to mention C.

TextMate raised the bar when it introduced its “Go To File…” command which lets you quickly jump to any file using a fuzzy text search. The best Vim equivalent is Ctrl-P, which not only has a fuzzy file search, but a fuzzy buffer search as well:

ctrlp

If you use Vim buffers, Ctrl-P’s ability to quickly go to the buffer you want is life-changing. It’s so useful that I’ve bound it to ; (and nobody remembers what ; does anyway). Ctrl-P’s file search combined with buffer search is magnificent – use the file search to open files related to the task at hand, then use buffer search to flip in between them.

:nmap ; :CtrlPBuffer<CR>

It’s worth showing a few settings for Ctrl-P. The following are the settings I use which map it to ,t, put it at the top of the screen, hide unnecessary files from the search results, and a few more things. Run :help ctrlp-options to read more about them.

:let g:ctrlp_map = '<Leader>t'
:let g:ctrlp_match_window_bottom = 0
:let g:ctrlp_match_window_reversed = 0
:let g:ctrlp_custom_ignore = '\v\~$|\.(o|swp|pyc|wav|mp3|ogg|blend)$|(^|[/\\])\.(hg|git|bzr)($|[/\\])|__init__\.py'
:let g:ctrlp_working_path_mode = 0
:let g:ctrlp_dotfiles = 0
:let g:ctrlp_switch_buffer = 0

A collapsible directory tree is a great tool to explore a project structure when you don’t know what you’re looking for or to help keep the project’s organization in your head. The best file browser plugin is NERD Tree, which has easy-to-remember keyboard navigation (hit ? in the window for help), mouse support, and uses little Unicode arrows next to folders.

nerdtree

It’s also useful to have a key which toggles the visibility of the tree:

:nmap \e :NERDTreeToggle<CR>

Finally, everybody has a color scheme they’re comfortable with. Modern terminals support 256 colors, but sometimes you need to kick Vim to recognize this:

if $TERM == "xterm-256color" || $TERM == "screen-256color" || $COLORTERM == "gnome-terminal"
  set t_Co=256
endif

You can’t, unfortunately, start using any gVim theme immediately, though there’s a plugin that tries to make that happen. There exists a plugin called vim-colorschemes which has bundled hundreds of themes with 256-color support, such as twilight256 and wombat256. It also includes the popular Zenburn theme, but its copy isn’t as good as the independently-maintained zenburn.vim. If you want to see what all of the themes look like, check out vimcolorscheme page which contains samples of each theme.

twilight256

Finally, in terms of visuals, Vim’s default status line is pretty lacking. A popular plugin is Powerline, which displays lots of helpful things in the status bar including your current git branch. It also uses colors to cue you into the current mode as well as when paste is enabled:

(Note: As of this writing it seems that the authors of Powerline decided to port the plugin to Python, which unfortunately requires a Vim build with Python support and complicates its installation. The pure vimscript version is still availabe.)

Other People’s Code

If everyone could write code like you, wouldn’t the world be a great place? Unfortunately, there are still some jerks who use or don’t use tabs, or maybe they indent with four spaces instead of two, or vice versa, or whatever it is that those jerks do. You still need to read their code and it helps to be able to quickly switch between popular (and unpopular) tab modes:

:nmap \t :set expandtab tabstop=4 shiftwidth=4 softtabstop=4<CR>
:nmap \T :set expandtab tabstop=8 shiftwidth=8 softtabstop=4<CR>
:nmap \M :set noexpandtab tabstop=8 softtabstop=4 shiftwidth=4<CR>
:nmap \m :set expandtab tabstop=2 shiftwidth=2 softtabstop=2<CR>

Those authors might wrap or not wrap lines at 80 or 100 columns or whatever it is you like, so being able to quickly toggle wrap mode helps:

:nmap \w :setlocal wrap!<CR>:setlocal wrap?<CR>

Things Which Friends Found Interesting

All text cut and copy operations are saved into registers. If you have cut text and then cut something else, you haven’t lost the first cut – just type :reg (short for :registers) to see all of the registers. You can then type the name of the register and then p, such as "3p, to paste whatever you cut three or four operations prior.

It’s a good habit to use registers for longer-term copies and pastes. If there’s anything I want to save “in the clipboard” for a while, I cut or copy it to register a , such as with "ay (copy) or "ad (cut). That way, no matter how many operations later, I’m still able to paste what I wanted with "ap.

Note that macros are saved to registers, too. (You know about macros, right?) If you record a macro into register a (using the q key to start and stop it), you can type :reg to see the contents of the macro.

registers

Another interesting, seldom-used feature is visual block selection mode. If you type Ctrl-v in normal mode instead of Shift-v, your selection will be a rectangular block instead of a linewise selection. You can then perform cut and insert operations with c or Shift-i and, as long as you finish the operation with Esc and not Ctrl-c, the operation will be repeated for every line in the block.

visualblock

Finally, Vim has built in tag support for motions. This means that, if you’re used to operating on paragraphs using, say, dap (delete a paragraph) or ci( (delete and insert in parentheses), you can do the same with tags: dat deletes the tag that the cursor is in, and dit deletes only the contents of the tag. And if you’re writing HTML or XML, make sure you’ve got closetag.vim installed so that Vim automatically inserts a closing tag when you type </.

Conclusion

There are still features I miss from Emacs and other editors. For example, Emacs has a useful mode which highlights hexidecimal colors in CSS and SASS with the color represented by the text. It’s smart enough to display light text when the color is dark and vice-versa.

The biggest hole, however, is the lack of refactoring and smart completion. The downside of working with dynamic languages such as JavaScript make writing refactoring engines hard, but even non-language-specific editors like Sublime Text make it easy to rename a variable everywhere within a function. For Python, there’s a thing called ropevim which adds some refactoring commands, but I’ve found it to be clunky and unreliable.

Nonetheless, Vim is a great tool, and I hope the above has been useful.

blog comments powered by Disqus