Jul 7 2008

VIM Bliss

I was originally planning to post a big fat entry on VIM, how swank I think it is, and some example configs and such that I've accumulated over the years. (It dawned on me that I've been using this editor for about 12 years now, and every year I feel like I "level up" again, and realize it can do stuff I had no idea about.)

I've come to the conclusion that there is simply too much info to possibly try and coalesce into a single blog entry. My .vimrc is 260 lines, for crissake. I think this will just end up being my top 10 (20?) tips and tricks, instead.

My last "level up" was from watching some buddies use TextMate. Dude. That's one sexy editor. Which of course, just made me wonder how much of that could be done in Vim. I'd actually use TextMate primarily, but:

So, here are some general purpose tips that I've found to be extremely helpful. Hope you do too.

Split your .vimrc and .gvimrc

GVim inherits its settings from Vim. Put all of your default settings into .vimrc for day-to-day editing, then put anything different from that (for programming, etc) into your .gvimrc.

In this fashion, you can use vim for stuff like email editing (spell checking, spaces expansion, etc) and fire up GVim for "real work."

Automate everything

Find yourself typing a path in all the time? Make it a variable. I like to keep my sessions all in a directory of their own, but my fingers hate the typing to load them up. Now, my sessions directory is just referenced as $S.

let $S = $HOME . '/.vim/sessions'  (in your .vimrc)
:so $S/sessionname                 (from the command prompt in vim)

Tab complete even works after you've typed $S/. So much nicer.

Vim also supplies you with some fancy ways to do dynamic replacing of text as you type it. Most people like to use this feature to automatically fix common misspellings. I tend to use it as more of a "mini macro" expression.

iabbrev {{DATE}} <C-R>=strftime("%A, %B %e %Y @ %r")<CR>
iabbrev {{EPOCH}} <C-R>=strftime("%s")<CR>
iabbrev {{UUID}} <ESC>!!uuidgen<CR>guu

This allows me to just type {{DATE}}, and it will expand automatically to Monday, July 7 2008 @ 07:22:02 AM.

Another nice automation tool is the autocmd command. With it, you can dynamically change your keybindings, perform actions, or do essentially anything based on what kind of file you are currently editing.

" Script syntax checking
autocmd BufEnter *.pl map <F11> :w<CR>:!perl -c %<CR>
autocmd BufEnter *.rb map <F11> :w<CR>:!ruby -c %<CR>

" Automatically expand folds when opening new files
autocmd BufEnter * if has('folding') | execute 'normal zR' | endif

autocmd can actually be used as a poor man's event system. The above examples all use the BufEnter event. Here's an example that auto-saves perl, ruby, and plain text files as soon as you leave insert mode (but only if there are changes to save!)

autocmd InsertLeave *.{txt,pl,rb} :up

Use sessions

While we're talking about session automation, it took me awhile before appreciating the beauty of this feature. A session is like a marshalled editing environment. Source it, and everything you were working on previously comes right back up, right down to your cursor position.

What makes this even nicer is a quick shortcut like so:

map <F8> :execute 'mksession! ' . v:this_session<CR>

If you're inside a session, hitting F8 will re-save any environmental changes you've made out to the same session file.

Use external programs when appropriate

A good example of this is with replies to email. Vim has a great built in reformatting function (gq), but with multiple levels of quoting and wonky whitespace/wrapping, it starts to fall short.

Bind a key sequence to call out for some par love:

map ^^ {!}par w72qrg<CR>
vmap ^^ !/par w72qrg<CR>

And everyone else's crappy email formatting is instantaneously prettified, ready for inline reply.

Reduce keystrokes when moving around

I've found that opening new buffers in split (usually horizontal, but vertical too) windows works the best with how my brain functions. I like seeing at least the statusline for all opened files. If it gets too clunky having 10 files open, it's time to complete what I was doing in some of them and close them out. ;)

With that said, I've moving between them constantly, and the default bindings can get tedious.

map <C-H> <C-W>h<C-W><BAR>
map <C-L> <C-W>l<C-W><BAR>
map <C-J> <C-W>j<C-W>_
map <C-K> <C-W>k<C-W>_

These new bindings let you switch and maximize in a single keystroke, in whichever direction the buffer you're targeting is.

Use auto-complete copiously

It's hard to get into the habit, but once you do, you won't go back. Start typing something, then hit <C-N>. If you're using Vim 7.x, you'll get an inline popup menu with all available completions -- populated with words found elsewhere in the file. Different programming modes toy with this feature as well, making potential method calls on an object jump to the top of the list.

You can manipulate this list, as well.

set complete+=k
set dictionary=$HOME.'/.vim/spell/en.utf-8.add',/usr/share/dict/words

This adds all the words in the OSX/Unix "dictionary" file to autocomplete, along with your local spell check 'whitelist' -- another great one while composing email.

Get friendly with text 'objects'

Even more shortcuts for selecting and modifying "blocks" of text. Given the current location of your cursor, you can do things like:

Do a :help text-objects for more info on this super useful bit.

Use path=**

As part of my normal work routine, I use :chdir to jump into the top level project directory. (This gets carried along with the session when saving, so I only have to do this once.)

autocmd BufEnter * set path=**

As long as you are in the project current working directory, you can now use :find as a synonym for :e, and :sf as a synonym for :split. It doesn't matter where in your directory structure the file you want lives, Vim will happily locate it for you, without the need to type explicit paths.

You can do other interesting things with the 'starstar' notation, too.

:10vimgrep /frazzles/igj **/*.rb | :cw

This says, "search through all the ruby files in the current directory for the phrase 'frazzles', and limit results to the first 10 matches. It puts results into a separate window, where you can hit return to open each file (and it jumps within the file straight to the match!)

Plugins!

My favorites, in no particular order:

Tabs, not spaces

I know, this is a holy war, but I'll mention it here cause I actually converted. Michael made the very good point that by using tabs, you allow other people with decent editors to display your file in whatever form they please. The roving gangs of Rails dudes and their horrific (to me) 2 space indent doesn't matter a bit, if I can open and view it with a 4 space. Everyone gets their way. What helps considerably with this is modelines.

With a good modeline, you're instructing your editor to display a file a certain way, regardless of your local settings. Others can modify to taste or ignore outright. To make this easier to get in the habit, the Modeliner plugin (mentioned above) writes a modeline based on your current settings.

Using spaces instead of tabs is basically saying "fuck you" to the readers of your code. Tabs are polite. I didn't care before. Now I do. Be polite.

Use 'Go to'

Another programmer's lifesaver, once you realize it exists. There are three different types of contextual 'goto' while programming within Vim. The first is scoped within your current file. Put the cursor over a variable or method, and type gd in command mode. This is saying Go to Declaration, and you'll jump to where the variable or method is originally defined.

The second is gf, or Go to File. If what you're hovering over is a path to a file on disk, you'll jump directly into that file. This works for a lot of syntax modes, too -- for example, calling gf on a ruby 'require' line will jump into the required file. Wicked.

The third uses an external utility, called ctags. Ctags creates an index across your entire project, defining declaration locations for everything. From any file, you can hit C-] while your cursor is over what you want to lookup. You'll jump directly to the declaration, even if it is in a different file. Super slick. If there are multiple matches, I also like to add a keybinding to this:

map <C-\> :tselect /<C-R><C-W>

Which presents you with a list of matching destinations that you can choose from.

VimDiff

Finally, VimDiff. All your differencing needs, without leaving Vim. Similar to sdiff, if you've used that. Original on the left, new file on the right. Cherry pick changes if you so desire back and forth. Colorized output for quick and discernible changes. All in all, neat.

To compare your current buffer with another arbitrary file on disk:

:vert diffsplit /path/to/file

Once in VimDiff mode, you can type do to 'get' the changes from the other buffer, and dp to put your changes there.

Whew, okay. I think that's all I've got for now. The two things that I feel would make Vim perfect that aren't quite there yet... visual block character substitutions, and a "real" shell buffer. I want to run Mutt inside of Vim, dammit! As for the visual character stuff... try selecting a box in the middle of a chunk of text, and doing a substitution. It applies to the lines in their entirety, instead of just what you've selected. Not the end of the world, but not intuitive either. I may have to finally donate to Bram's Uganda cause, and vote these up.