Trace Source Code with Vim and Ctags

Ctags is a source code indexing tool. With ctags, we can easily find the definitions of the classes, functions, and variables. According to my experiences, ctags can significantly reduce the time to browse the source code. In this post, I would like to give a brief introduction to ctags and vim.

Installation

First, we have to install exuberant-ctags package for ctags. On Ubuntu or Debian, we can install exuberant-ctags with:

$ sudo apt-get install exuberant-ctags

Second, I would suggest to tweak the .vimrc as well. By default, vim will only load the tags file from:

  1. the current working directory, or
  2. the directory containing the opened source file.

For example, if you have opened the file foo/bar.c, then vim will only check the tags file named after tags, TAGS, foo/tags, or foo/TAGS. However, the tags file in the parent directories, such as ../../tags won't be considered.

To ask vim to search for tags file in the parent directories, we should add an extra semicolon ; after ./tags and ./TAGS. For example,

set tags=./tags;,./TAGS;,tags,TAGS

Third, if you wish to put the generated tags file in the .git directory so that the tags file won't be added to git repository, then you may wish to add the following snippets to the .vimrc as well:

" Check .git/tags for ctags file.
fun! FindTagsFileInGitDir(file)
  let path = fnamemodify(a:file, ':p:h')
  while path != '/'
    let fname = path . '/.git/tags'
    if filereadable(fname)
      silent! exec 'set tags+=' . fname
    endif
    let path = fnamemodify(path, ':h')
  endwhile
endfun

augroup CtagsGroup
  autocmd!
  autocmd BufRead * call FindTagsFileInGitDir(expand("<afile>"))
augroup END

This snippet allows vim to search for the tags file in the .git directory of the parent directories of the source file, such as ../../.git/tags.

Usage

After installation, we have to generate the tags file as the index. We can generate the tags file with the following commands:

$ ctags -R [dir]
$ ctags -R -f [tags-file] [dir]
$ ctags --tag-relative=yes -R -f [tags-file] [dir]

Usually, I prefer to run this command in the git repository:

$ ctags --tag-relative=yes -R -f .git/tags .

It is highly recommended to add --tag-relative=yes to the ctags command. Without this option, vim won't be able to open the correct source file under certain circumstances.

Sometimes, there might be some special naming convention in your project. To index those files, we can change the programming language map with --langmap option. For example, to treat the .inc file extension as C++ source code:

$ ctags --langmap=c++:+*.inc ...

This options adds .inc to the C++ file extensions (in addition to the default file extensions such as .c, .cpp, and etc.)

Vim Keys

After generating the tags file, we can browse the source code with vim and ctags. Vim has excellent built-in support for ctags. We can simply jump to or preview the definition with a little key stroke. Here are some combinations for your reference.

Basic Usage

First, we can jump to the matching patterns with the :ta command. If we are not satisfied by the first match, we can jump to the next match with :tn. Conversely, we can jump to the previous match with :tN. We can also list all matches with :ts.

Command Usage
:ta [pattern] Jump to the best matching tag
:ta /[regex] Jump to the best matching tag
:ts [pattern] List the tags and jump to the selected one
:ts /[regex] List the tags and jump to the selected one
:tn Jump to next matching tag
:tN Jump to previous matching tag

Here are some tips to search for the keyword pointed by the cursor:

  • To show the best matching in the new window, press Ctrl+W then ]. (This is my favorite!)
  • To list all matchings and show the selected one in the new window, press Ctrl+W then g].
  • To jump to the best matching directly, press Ctrl+].
  • To list all matchings and jump to selected one, press g].
  • To search a pattern by selection, press v to enter visual mode, select the text, and press either Ctrl+] or g].

Preview Window

The preview window is a separated window that will pop up on the top for quick reference. Unlike the "Ctrl-W then ]" command mentioned above, the cursor won't be moved to the preview window.

To preview the keywords pointed by the cursor, press Ctrl-W and }. Alternatively, we can open the preview window by the :pt command. To close the preview window, use the :pc command.

Command Usage
:pt [pattern] Preview the best matching tag
:ptn Preview the next matching tag
:ptN Preview the previous matching tag
:pc Close the preview window

Tag Stack

If we jump to a matching tag with :ta or g], we may wish to go back and forth. When we jump to a new tag, the current position will be pushed to the tag stack. To go back to previous position (i.e. pop the tag stack), we can use the :po command. To go forward, we can use the :ta command.

Command Usage
:tags List the tag stack
:po Pop the tag stack (go up)
:[n]po Pop n items from the tag stack (go up)
:ta Push the tag stack (go down)
:[n]ta Push n items to the tag stack (go down)

Conclusion

In this post, we gave a brief introduction to the basic usages of ctags. We have mentioned some handy .vimrc tweaks and some important ctags option. Besides, we have walked through several useful vim commands for code browsing and navigation. This should would be sufficient for most use cases. You can find more materials from the references.

Last, but not the least, I would like to say that ctags is an amazing source code indexing tool. I found my programming productivity has been significantly improved by ctags. Hope you enjoy it too. Happy coding!

Reference