Working with large Rails projects means dealing with a lot of files. Being able to navigate these files quickly is a huge productivity boost. Combining Vim and ctags can help by offering tag-based navigation.
Ctags is a tool for indexing source code. It scans a bunch of files and creates a single list of the locations of classes, methods, constants or whatever else you tell it to index. Having an index like that gives you a nice way to search for important identifiers in your code base.
The de facto standard these days however is not ctags itself but a project called exuberant ctags, which supports more programming languages and features and integrates nicely with Vim.
You can install exuberant ctags through your regular package manager of choice; in the Mac OS X world you would use Homebrew:
% brew install ctags
To make sure you have the right version installed, check the version information for Exuberant Ctags:
% ctags --version Exuberant Ctags 5.8, Copyright (C) 1996-2009 Darren Hiebert Compiled: Jul 25 2014, 20:54:25 Addresses: <firstname.lastname@example.org>, http://ctags.sourceforge.net Optional compiled features: +wildcards, +regex
Generating a tags file
If you have a project with a bunch of source files – say, a Rails project –
you can run
ctags from your project root to index it:
% ctags --recurse .
This will give you a nice
tags file. Now you may want to include more than
just your project’s tags in your tags file. In a Ruby project, it might be
helpful to include tags for all your gems, too. You can give
locations to index, so you could feed it a bunch of paths from Bundler:
% ctags --recurse . `bundle show --paths`
This will significantly increase the size of your index (and the time it takes to generate it) but makes it a lot easier to quickly inspect library code.
ctags can take quite a few options, but the most important I use are
--recurseto index everything in this directory and all directories below it;
--exclude=to skip certain directories from indexing, specifying it once for at least
--appendto have new definitions added to, rather than replacing the old, collection tags;
Specifying all these options at the command line is cumbersome so Ctags supports a configuration file in your home directory where you can list your default options:
My ctags file is available online in my dotfiles repository.
Navigating tags in Vim
Once we do have a Ctags index generated, we can use it for navigation in Vim.
Vim by default looks for a
tags file in your current directory and in the
directory of the current file:
:set tags? ./tags,tags
…which is usually fine. You can use the
tags option to store it somewhere
else, such as in your
There are several different ways you can navigate your code base using tags:
- Use the
:tagcommand to jump to a tag by name. This comes with tab completion and positions your cursor straight on the line of the tag definition (such as where a constant is defined).
- Using the
CTRL-]command you can jump to a tag under your cursor. This is nice when you come across a class name in your code and want to jump to it. Position your cursor on the name, press
CTRL-]and there you go.
- Some plugins integrate with tags, such as CtrlP. Apart from fuzzy finding files, it can also fuzzy-find tags. This is particularly nice if you are not quite sure on the exact spelling of the tag you are looking for.
- Using the tag stack, which is a list of tag-based locations you have
visited. After following some tags, you can trace back through your history
CTRL-Tand forward with
:tag(each take an optional count). To see your history, you can use
:tags. This works much like Vim’s regular
CTRL-Ocommands for the jump list, but the tag stack only contains, well, tags.
There are some more options are details you may be interested in, which are
documented nicely in Vim’s documentation. See
:help tags for more information.
Keeping your tags file up to date
The trick to using Ctags is keeping your index up to date. When you install new
dependencies or pull in new commits, your index is out of date. You could run
ctags every time, but we all know how that’ll work out. So there are some
tricks to automatically keep your index updated.
First, you can use a Git hook to re-generate your index whenever your working
tree is changed. The
.git/hooks/post-checkout file is a good place to stick
such a hook. Tim Pope has written an excellent explanation of how to install a
Git hook to generate tags.
Second, when dealing with Ruby projects, you can hook into Rubygems and automatically generate ctags for your gems on installation. Tim Pope (again) has written a neat plugin gem-ctags to do just that. Having Tim’s vim-bundler plugin installed handles picking up the generated indices for you.
Standard Library tags
Finally, there’s tags for the Ruby standard library. If you use Rbenv – and why wouldn’t you? – you can use Tim Pope’s (gasp!) rbenv-ctags plugin to automatically generate indices when you install a new Ruby. Having vim-ruby and vim-rbenv installed will make sure Vim will pick these up, too. And while you’re at it, use rbenv-default-gems to automatically install gem-ctags when you install new Rubies.
You can navigate your projects using meaningful names rather than files using tags. You can browse back and forth through your code using Vim’s tag stack and there are various plugins available to make sure your tags file always stays up to date. Go forth and navigate!