How to Use Tags in Vim to Jump to Definitions Quickly

April 27, 2020

Follow @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 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:
  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.

# 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                # Python
ptags                   # Perl
gnatxref                # Ada

The popular option is exuberant ctags. It supports 41 programming languages. 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 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 .
# 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/ /^ jdwpTransportError (JNICALL Accept)(jdwpTransportEnv env,$/;" m struct:jdwpTransportNativeInterface Accept elasticsearch-7.5.2/ /^ 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 []( 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(

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.

# 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 and it works right out of the box. Many plugins can do this automatically.

Some alternatives:

# 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!

# Resources - `:h tags` - [Exuberant Ctags]( - [How do I create my own tag files with Vim for interconnected non-code text?]( - [Navigate code like a pro with Ctags]( - [Browsing programs with tags]( - [Vim 101: Tags]( - [How to exclude multiple directories with Exuberant ctags?](
© Copyright 2021 Igor Irianto