Setting Up Evil-Mode Friendly Ctags in Emacs
Despite my failed experiment with evil-mode back in January, I wound up switching over to evil-powered Emacs full time earlier this summer. I love having modal editing combined with truly deep extensibility, and I’ve been focusing my tweaking on changes that let Emacs help me explore and understand code faster.
Ctags is a program that can parse code in many different language and generate an index of defined symbols—stuff like class names, method names, constants and so on. Text editors can be configured to use this index to do things like jump to where a particular symbol is defined (instead of just grepping for where the symbol occurs).
Here’s how I set up Ctags in Emacs.
OS X ships with an older version of Ctags that can’t generate a tags index in the format that Emacs expects. I installed the latest version via Homebrew:
Tags can be generated on the command line pretty easily—
ctags -Re will recursively parse and index all files below the current directory, in Emacs format. However, it’s handy to be able to regenerate them from within Emacs, especially if you’re switching git branches and want to quickly purge stale tags. The following lets me that do for the current projectile project with
Rather than re-running this on the entire project whenever a file changes, I used the
ctags-update package to do so incrementally on save. I’m just enabling it for when I’m in
enhanced-ruby-mode right now.
Now that we can generate and update a tags file, we can start using it to explore our code faster. I’ve found the following keys in normal mode:
gfgoes to the first match for the tag under the cursor. If I’ve got the symbol
Userunder the cursor, I can hit
gfand go to where the
Userclass or module is defined.
gngoes to the next match—if there are multiple matching tags (e.g. I’ve got multiple
Userclasses in the same project), this cycles to the next one.
gbpops me back to where I was before jumping.
Using a fuzzy matcher to search through the tags index is a great way to zip around a codebase. I use Helm for as-you-type filtering for all sorts of things—emacs functions, project files, and now, tags! I bound Helm’s built-in tag filter to
The default behavior of the filtering left something to be desired, however. Matching tag candidate were sorted alphabetically, which mean that if I was looking for the module Searchable, the results would look like this (with the top item selected):
What I really wanted was the closest item to my search to be on top. As it turns out, the shortest string that still matches the filtering text winds up being a decent hueristic for this. I did a bit of poking around in the helm-etags source, and found an empty function that’s meant to allow users to define their own custom behavior for Helm sources. Perfect!
Now, when I run the above search, the module I was looking for is on top and selected by default:
There’s still more to tweak—the Emacs motto, I think—but I’m pretty pleased with my tags setup so far.