Tag Navigation
Jump to definitions across files using a tags index.
ctags builds a tags file mapping symbols to file:line. Ctrl-] jumps to the symbol under the cursor; Ctrl-T pops back. The whole jump history forms a stack you can browse with :tags.
Long before LSP, Vim had a 'go to definition.' It still works โ and on big codebases it's faster than any language server because it's just a binary-searchable text index.
Build the index
Run a tags generator (universal-ctags is the canonical one) at the root of your project:
| Command | Effect |
|---|---|
ctags -R . |
Recursively index everything under . into a file called tags |
ctags -R --languages=python,go . |
Limit to specific languages |
ctags -R --exclude=node_modules --exclude=.git . |
Skip noise |
Vim looks for the tags file in the current directory, then walks parents. Configure with :set tags=./tags;/tags to search both adjacent and root tags files.
Jump and return
| Key | Note |
|---|---|
| {key:Ctrl-]} | Jump to the definition of the identifier under the cursor |
| {key:Ctrl-T} | Pop back to where you came from (one level) |
| `:tag``NAME` | Jump to a named tag (Tab-completes) |
| {key:g}{key:]} | Show menu of all matching tags (when the name is ambiguous) |
| {key:Ctrl-W}{key:]} | Open the tag in a new horizontal split |
| {key:Ctrl-W}{key:}} | Show the tag's definition in a preview window without leaving |
The tag stack
| Key | Note |
|---|---|
| `:tags` | Show the full tag stack with current position marked |
| `:tnext` | Next match when the tag was ambiguous |
| `:tprev` | Previous match |
| `:tlast` | Last match |
| `:tfirst` | First match |
See also: The Jump List