Suppose that you would like to add anchors to a blog post which allow readers to link to specific sections. This is a tedious task and therefore a candidate for automation. However, automation would require defining a mapping between arbitrary header strings and urls; a task much simpler for the post author than a program. So the goal is to automate the easy things (moving around the document and inserting common characters).

vim logo As an example we will look at one of my previous posts, Tool Sharpening. The raw content which I will be working with is available on GitHub. I plan to add an id attribute corresponding to the section title to each heading (<h1> - <h6>)tag in the post. After the tag is found and the common text is inserted we want to leave the cursor in the correct position to manually enter the desired link.

For this task we are going to use my favorite text editor, vim. Vim has many commands which make it a powerful text editor and macros specifically address the problem at hand. Macros provide a way to repeat a sequence of commands thus avoiding tedious editing tasks. The basic workflow for using macros has two steps: record and playback.

Record

To begin recording a macro press qR where R is a lowercase Latin character, or if you prefer regular expressions [a-z]. Once recording has started every keystroke is saved into the chosen register (R). To stop recording return to normal mode and press q.

For this example we will use register a. Let's start with the simplest case, a single word title which is on line 31. Move to line 31 (31gg) and make sure to be in normal mode. Begin the recording, qa. First step is to move to a common position - the start of the line - 0. Next we want to insert the text id="#" inside of the h2 tag. So move to the first > - f>. Then enter insert mode (i), type the desired text (id="#"), and return to normal mode (ESC). Finally we want to position the cursor in position to prepare for inserting the desired link (T#). Now we can finish recording by pressing q.

Playback

Now that you have recorded a macro it can be played back with @R. Vim will read through each character in the register and interpret it as if you typed it.

Editing

Up until now we have used macros without any visibility. Suppose that by playing the macro on a line where the header tag is not the first tag. Our macro would fail since it looks for the first >. What we are really looking for is the first > which ends a header tag.

We know that the rest of the macro correctly edits the line. The only error is finding the initial starting point. Based on the techniques discussed so far the only option is to re-record the macro with our desired change. This works, but is inefficient and introduces the opportunity to make a different mistake. A better option would be to edit the macro directly.

This will have three steps: paste, edit, and yank.

In order to paste the contents of the register we can use p. The syntax in vim for accessing a register is "R. So for our example we can use the following to paste the macro into the current buffer ("ap). Let's see what our register contains - 0f>i id="#"^[T#.

It may look like hieroglyphics, but if you look closely those are the exact keys we pressed while recording the macro. Now that we have the register contents we can edit it with all the same commands as an ordinary file. It is just a string of characters after all.

The edit is up to you. For our example we can insert /<h^M in between 0 and f.

Once the command is satisfactory we need to store it back into the register. 0"Ry$

Now we can retry the macro and verify that it now works as originally intended.

Commentary

Vim macros trigger the same circuits in my brain as metaprogramming. They also have the same costs and benefits - simple to inject functionality not originally designed but difficult to interpret and anticipate all of the consequences. While macros are not always the correct solution (see :h filter and a recent vimcast), they are often an effective tool.

More Information

For more information see :help complex-repeat