How to use tags in Vim to jump to definitions quickly
Tags: vim, tags, definition, productivity
Published: 2020-04-28 17:32:33

---
title: How to use tags in Vim to jump to definitions quickly
published: true
description: How to use tags in Vim
tags: vim, tags, definition, productivity
---

*Follow [@learnvim](https://twitter.com/learnvim) for more Vim tips and tricks!*

One useful ability in coding is to jump to any function definitions quickly. It helps you to understand and visualize project codes faster.

Vim uses a tag file to jump to a function definition quickly. In this article, I will show you how tag works and how to manually set up tags. I will also show you how to use a plugin to automate your tag creation.

These are some things I will cover:
- [Tags overview](#overview)
- [Setting up tags](#setup)
- [Tags in action](#action)
- [Tag stack](#stack)
- [Plugins](#plugins)
- [Conclusion](#conclusion)
- [Resources](#resources)

<a name="overview"></a>
# Tags overview
If you are reading a large source code and see a function you don't understand, you can jump to its definition to understand it.

If I saw this code snippet and want to know what `Account` model does:

```
Models::Account.new(
  account_name: from_json(['whatever']
  ...
```

With tags, I can put my cursor on `"Account"`, press `Ctrl-]`, and jump to definition.

Vim jumps to `Account` class inside a file:
```
module MyModule
  module Users
    module Deserializers
      class Account < Iggy::Core::Deserializer

      def initialize()
        ...
```

With this, I can read what is being initialized in a new account. I can see at other methods inside this class and understand more what this class can do.

Tags give you a quick project visualization. The more you know about the code, the more you understand the project.

The way tag system works in Vim, is that Vim relies on a tag generator to generate a tag file (normally called `tags` in root directory) that contains a list of definitions. To see where Vim expects to find a `tags` file, you can run `:set tags?`. Depending on what tags generator you use, the content of your `tags` file may look different. At minimum, a tag file must have either one of these formats:
```
1.  {tagname} {TAB} {tagfile} {TAB} {tagaddress}
2.  {tagname} {TAB} {tagfile} {TAB} {tagaddress} {term} {field} ..
```

I will go over the content later.

<a name="setup"></a>
# Setting up tags

Here is how you can get started creating tags. To use Vim tags, we need a tag generator. There are several options:

```
ctags				    # C only. Available in most unix
exuberant ctags		    # Good, supports many file types
etags 				    # Emacs. Hmm...
JTags				    # Java
ptags.py			    # Python
ptags				    # Perl
gnatxref			    # Ada
```
The popular option is [exuberant ctags](http://ctags.sourceforge.net/). It supports [41 programming languages](http://ctags.sourceforge.net/languages.html). I personally use that. For this tutorial, we will go with exuberant ctags.

If you have mac and homebrew installed, you can run `brew install ctags`. Once installed, you can check it with `ctags --version`.

For this demo, I will use [dev.to](https://github.com/thepracticaldev/dev.to) github project. You can use whatever project you want.

To generate ctags, run:

```
ctags -R .
```

By default, `ctags` generates a `tags` file in your current directory. The `-R` is recursive option (which you probably want to do most of the time). If you noticed, your tag file contains a very long list. Mine has over 170000 entries. This is because ctags generate tags from our `node_modules` too! We need to exclude it. Delete our tag file (`rm tags`) and run this instead:

```
 ctags -R --exclude=node_modules .
```

This time it takes less than a second. I only got around 6900 entries. Much better. 

By the way, you can use exclude option multiple times, for example:
```
ctags -R --exclude=.git--exclude=vendor --exclude=node_modules --exclude=db --exclude=log .
```


<a name="action"></a>
# Vim tags in action
Let me show you an example of a tags file:
```
...

!_TAG_PROGRAM_VERSION	5.8	//
Abstract	config/initializers/sidekiq.rb	/^    module Abstract$/;"	m	class:Rack.Session
Accept	elasticsearch-7.5.2/jdk.app/Contents/Home/include/jdwpTransport.h	/^    jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,$/;"	m	struct:jdwpTransportNativeInterface_
Accept	elasticsearch-7.5.2/jdk.app/Contents/Home/include/jdwpTransport.h	/^    jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {$/;"	f	struct:_jdwpTransportEnv
ActionController	config/initializers/persistent_csrf_token_cookie.rb	/^module ActionController$/;"	m
ActsAsFollowerMigration	db/migrate/20170208152018_acts_as_follower_migration.rb	/^class ActsAsFollowerMigration < ActiveRecord::Migration[4.2]$/;"	c
...
```

This file is generated from [dev.to](https://github.com/thepracticaldev/dev.to) repo. It may look like gibberish, but it isn't that bad. The top lines (that start with `!_TAG_` are metadata. I will not go over that). The important part is the line that looks like this:

```
ActionController	config/initializers/persistent_csrf_token_cookie.rb	/^module ActionController$/;"	m
```

`ActionController` is our **definition**. Think of it like a key in key-value pair. When we press `Ctrl-]` on `"ActionController"` string anywhere in our code, Vim looks at our `tags` file, searches for `"ActionController"`, and looks for the location to jump to, in this case, it is `config/initializers/persistent_csrf_token_cookie.rb`.

Once Vim finds the location, it looks for the location using this pattern: `/^module ActionController$/`. `^` is regex pattern for start of line. `$` is regex for end of line.


# Using tags
You can get good milage using only `Ctrl-]`. But let's learn a few more tricks with tags.

We just learned that we could put our cursor on `"ActionController"` string and jump to definition with `Ctrl-]`. You can do the same with `:tag {name}`.

```
:tag ActionController
```

You can autocomplete `:tag` argument. If you do:

```
:tag A{TAB}
```

Vim lists all tags that starts with `A...`

You can have multiple definitions for one keyword. For example, I have several `"User"` definitions in `tags` file:

```
User	app/models/user.rb	/^class User < ApplicationRecord$/;"	c
User	app/services/data_sync/elasticsearch/user.rb	/^    class User < Base$/;"	c	class:DataSync.Elasticsearch
User	app/services/search/query_builders/user.rb	/^    class User < QueryBase$/;"	c	class:Search.QueryBuilders
User	app/services/search/user.rb	/^  class User < Base$/;"	c	class:Search
```

By default, ctags *always* jumps to first definition. To jump to Nth definition, do:

```
:Ntag User
```

To jump to 3rd `User` definition (the one inside `query_builders/user.rb`), we do `:3tag User`. In normal mode, we can do `3 Ctrl-]` while cursor is on `User`.

You may ask, "This is nice, but how do I know if there are multiple tags for `User`?"

You can use `:tselect` (or `g]` in normal mode) to see all definitions for that string. If you do `:tselect User`, you'll see a list of all tags generated for "User". You also can put cursor on `User` and do `g]`. From there, you can choose where to jump.

<a name="stack"></a>
# Tag stack
Vim keeps a list of all tags we've jumped to in a stack (max 20). You can see the stack with `:tags`. It looks something like this:

```
  # TO tag         FROM line  in file/text
  1  1 banned             64  app/controllers/application_controller.rb
  2  1 submission_template_customized    39  submission_template = @tag.submission_template_customized(@user.name).to_s
>
```
The upper stacks are older stacks. The lower stacks are newer stacks. I started out with "banned", then I searched for "submission_template_customized".

To "pop" the stack, do `Ctrl-T` or `:pop`.

Let's say you're working on a new project and you want to investigate a call graph. You can use tag stack to strategically jump to relevant functions.

```
func0  --> func1a --> func1b
       --> func2a
       --> func3a
```

Starting at `func0`, you observe that it calls `func1a`, `func2a`, and `func3a`. You go to `func1a` with `Ctrl-]`. You see `func1a` calls `func1b`, so you go to `func1b` (`ctrl-]`). Once you understand `func1a` and `func1b`, you return (`Ctrl-T` twice) back at func0. Back at `func0`, jump to `func2a` with `ctrl-]`, see what it does, and "pop" back to `func0`. Finally, jump to `func3a` to complete the call graph.

<a name="plugins"></a>
# Plugins
When your project change, your tags need to change. If you removed `User`'s first definition inside `app/models/user.rb`, the tags still think it is inside `app/models/user.rb`. You have to tell them that it has changed.

You can regenerate tags (`ctasg -R .`), but Vim doesn't do this automatically. Luckily, there are plugins to automate tags generation. I use [vim-gutentags](https://github.com/ludovicchabant/vim-gutentags) and it works right out of the box. Many plugins can do this automatically.

Some alternatives:
- [vim-tags](https://github.com/szw/vim-tags)
- [vim-easytags](https://github.com/xolox/vim-easytags)
- [vim-autotag](https://github.com/craigemery/vim-autotag)


<a name="conclusion"></a>
# Conclusion
I think this is a good place to stop. We learned how tag works. We learned how to set up tags for our project. We learned how to jump, pop back, and view tag stack. Understanding how Vim tags work will boost your productivity. 

Thanks for reading. Happy coding!

<a name="resources"></a>
# Resources
- `:h tags`
- [Exuberant Ctags](http://ctags.sourceforge.net/)
- [How do I create my own tag files with Vim for interconnected non-code text?](https://superuser.com/questions/94039/how-do-i-create-my-own-tag-files-with-vim-for-interconnected-non-code-text)
- [Navigate code like a pro with Ctags](https://ricostacruz.com/til/navigate-code-with-ctags)
- [Browsing programs with tags](https://vim.fandom.com/wiki/Browsing_programs_with_tags)
- [Vim 101: Tags](https://medium.com/usevim/vim-101-tags-bc55bc21e130)
- [How to exclude multiple directories with Exuberant ctags?](https://stackoverflow.com/questions/25819649/how-to-exclude-multiple-directories-with-exuberant-ctags)