Combining vim with ctags yields a powerful combination for working withlarge or unfamiliar codebases.
Ctags is a tool that will sift through your code, indexing methods, classes,variables, and other identifiers, storing the index in a tags
file. The tagsfile contains a single tag per line. Depending on command line arguments and thelanguage ctags is run against, a lot of information can be obtained from thisindex.
Ctags currently supports 41 programming languages, and it’s relatively easyto add definitions for more.
Ctags makes it much easier to navigate a larger project, particularly if thecode you’re working with is unfamiliar. If you’re unsure of what a method doesor how it’s supposed to be called, you can jump straight to it’s definition. Ifyou’re in the downward spiral of a 500+ line Perl script and want to know wherea variable was defined three hours ago, you can jump right back to it. Andafterwards, you can jump right back to where you were working.
You can install ctags using a package manager.1 If you’re on OS X, usehomebrew:
brew install ctags
OS X comes with a ctags
executable, but it’s not exuberant-ctags, and ismissing most of the useful features.
If you’re currently sitting in the directory you want to index, just run:
ctags -R .
Ctags will walk through the directory recursively, tagging all source files itencounters. For very large projects, this might take a while, but normally it’spretty fast.
I normally don’t like having a tags
file in plain sight in the sourcedirectory, so I keep it a little bit more hidden, in the .git
folder:
ctags -R -f ./.git/tags .
This can be a pain in the ass to run regularly, so you might like to bind it toa vim keyboard shortcut so you can run it every so often, or add some githooks to run ctags every time you check out, commit, or fetch with git.
If you’ve already run ctags in the current project folder, vim willautomatically pick up your tags file (even in the .git
directory), and you canstart using it right away. Take this bit of Ruby code, for example:
class TestCase def assert_equal(expected, value) # do_stuff_with_args endendclass ModelTest < TestCase assert_equal true, model_functionend
If you put your cursor over ModelTest
’s assert_true
call in normal mode andpress <C-]>
, vim will jump to assert_true
’s definition in the TestCase
class,even if they’re in different files. You can continue down this rabbit hole, ifyou choose, and when you’re done, press <C-t>
to climb back up the tree.2
You can also go directly to a tag’s definition by entering one of the followingin vim’s command mode:
:tag function_name:ta function_name
These commands will also accept regular expressions, so, for example, :tag/^asserts_*
would find all tags that start with ‘asserts_‘. By default vim willjump to the first result, but a number of commands can be used to sort throughthe list of tags:
:ts
or :tselect
shows the list:tn
or :tnext
goes to the next tag in that list:tp
or :tprev
goes to the previous tag in that list:tf
or :tfirst
goes to the first tag of the list:tl
or :tlast
goes to the last tag of the listTo show the tags you’ve traversed since you opened vim, run :tags
.
If you’re using the Ctrlp plugin for vim, running :CtrlPTag
will let yousearch through your tags
file and jump to where tags are defined. Very usefulwhen you need to jump around a project in a hurry.
It’s also handy to bind this to a keyboard shortcut. I use this in my ~/.vimrc
:
nnoremap <leader>. :CtrlPTag<cr>
Tagbar is another useful vim plugin for working with a tags
file. Install it,and map a key to it (I use ,b
):
nnoremap <silent> <Leader>b :TagbarToggle<CR>
When the tagbar is toggled, it will pop up on the right side of the vim windowand show the tags picked up by ctags
for the current file, organized by tagtype, e.g. function, variable, class, etc. This effectively gives youa birds-eye view of the code you’re working on.
You can put extra configuration for ctags in a ~/.ctags
file, and ctags willuse the contents as arguments every time it’s run. You can use this to establishbasic options, define regular expressions for additional languages, and setoptions for specific languages.
For example, here’s my ~/.ctags
file:
# Basic options--recurse=yes--tag-relative=yes--exclude=.git# Regex for Clojure--langdef=Clojure--langmap=Clojure:.clj--regex-clojure=/\([ \t]*create-ns[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/n,namespace/--regex-clojure=/\([ \t]*def[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/d,definition/--regex-clojure=/\([ \t]*defn-?[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/f,function/--regex-clojure=/\([ \t]*defmacro[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/m,macro/--regex-clojure=/\([ \t]*definline[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/i,inline/--regex-clojure=/\([ \t]*defmulti[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/a,multimethod definition/--regex-clojure=/\([ \t]*defmethod[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/b,multimethod instance/--regex-clojure=/\([ \t]*defonce[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/c,definition (once)/--regex-clojure=/\([ \t]*defstruct[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/s,struct/--regex-clojure=/\([ \t]*intern[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/v,intern/--regex-clojure=/\([ \t]*ns[ \t]+([-[:alnum:]*+!_:\/.?]+)/\1/n,namespace/# PHP--langmap=php:.engine.inc.module.theme.install.php --PHP-kinds=+cf-v
联系客服