03 Mar 2026
This post is about using tags and buffers in nvi.
Let's start with the tags. If you're currently working on files containing code, you can leverage the ctags command to create a tags file. The tags file is an index of defined objects in your code. Combined with vi this tags file can be used to start editing at a specific tag name in a file. For example, if you want to inspect the source code of the cat programm:
ctags cat.c
tail -n +39 tags | head -n 3
MAXIMUM cat.c /^#define MAXIMUM(/;" d file:
bflag cat.c /^int bflag, eflag, [...];$/;" v typeref:typename:int
cat_file cat.c /^cat_file(const char *path)$/;" f typeref:typename:void
vi -t cat_file
This would leave you on line 112 at the function void cat_file(...) of the cat.c source code. This works because inside a tags file are 5 columns of information to an entry seperated by tabs. The first is the tag name. The second is the file name and location (if using recursion with ctags -R). The third column is an ex command that can be used to locate the specific line. The next column specifies the type of the entry. There are different letters for each type that can be indexed by ctags. The letters d, v and f are used for macro definitions, variables and functions. The last column is used for extra information.
Every field and abbreviation is explained inside the metainformation at the beginning of the tags file (the first 38 lines in this example).
There are two major keybindings used for working with tags inside vi. The description of the keybindings and commands below were copied from the OpenBSD manual. will return to the most recent tag context, and will push a tag reference to the tag stack. To further clarify it: In vim, both keybindings can be used to navigate different links (tags) while browsing the help documentation pages of a full vim version.
Other useful commands are:
:display tags (di t)
:tag (ta) # edit file containing string
:tagnext (tagn) # edit file containing the next context for the current tag
:tagpop (tagp) # pop the specified tag in the stack
:tagprev (tagpr) # edit file containing the previous context for the current tag
:tagtop (tagt) # pop to the least recent tag on the stack, clearing it
:set taglength (tl) # sets number of significant characters in tag names
:set tags (tag) # sets the list of tags files
The first six commands are used for displaying and navigating tags (the tag stack) inside a vi session and are not supported in newer vim versions. The most important key takeaway of tags are the two keybindings that will assist in navigating tagged documents like the internal help documentation of vim.
Moving on to buffers, it's important to recognize that there are two modes of buffers. Character based and line based. Thus, in vi buffers are used for storing text. Different commands are used for creating buffers or working with them (see man page). All buffers are named with a single lowercase character preceded by a double quote. This must be entered before the "count specification" of a vi command. Uppercase letters can be used to append text to the buffer. Buffers with numbers from 1 to 9 are used as a queue storage (FIFO) when making changes to the text with the c or d commands in vi. Content is automatically stored and changed in these buffers. Aside from manipulating buffers, the following two commands can be used to take meta actions on buffers.
@ or * # execute a named buffer as vi commands
display buffers (di b) # display buffers
Especially important is the @ command. With this command it's possible to create macros like in newer vim versions. If you're working on the following text for example:
| ID | Name | Category |
|----------------------------|
| 1 | blue | color |
| 2 | orange | color, fruit |
| 3 | minute | time period |
And the goal is to append a zero before each ID in every row the following string needs to be first added to a new line:
/\d^Mdwi0^[p^
| ID | Name | Category |
...
Then the contents can be moved to a named buffer of d with "ddd and applied with @d to the selected rows. It automatically jumps to the next digit, after processing the first. Highlighted parts are special keys like enter or escape created with <control-v>+<key>. Also it automatically moves to the next line since the whole line was copied into the buffer (including the newline at the end).
The next and final part of this series summarizes everything and provides a good basic configuration.