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:
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.
# Setting up tagsHere 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. 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 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 .
# 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:
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.
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](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)