The following is a section that we left out of this week’s Mac DevCenter article What Is Vim (It’s Easier than You Think) because of length constraints. I hope you find it useful as one more point on your radar screen as you ponder the productivity that Vim may be able to add to your daily workflow.
Save Time With Vim Macros
Do you find yourself frequently whipping out the python interpreter or cooking up regular expressions in Perl just to munge some text? Even if you’re already really good, I bet Vim macros can still make you even better. A Vim macro is simply a set of keystrokes that you can record and use over again to perform repetitious tasks.
But wait a tick. “What’s wrong with regexes,” you ask? We’ll, consider this conventional wisdom:
Some people, when confronted with a problem, think “I know, I’ll use regular expressions.” Now they have two problems. –Jamie Zawinski, in comp.lang.emacs
Although I do commonly use regular expressions in production level code that I write, day-to-day events involving a quick transform of a text file hardly require the effort of cooking up a regex. Read on to see why.
Let’s say you have this text file that contains a list of several Dr. Seuss books and you want to convert it into a numbered HTML list, apply italics to the title only, and remove the initial numbering. Without using a WYSIWYG, what’s the quickest way you could do it?
Open the file in Vim and follow along to see if using a macro to make the changes would take less time than your normal routine.
When writing the macro, we want to leverage the commands available to us in normal mode to apply a generic fix to one line. We’ll save that sequence of keystrokes, and then apply it repeatedly to every other line in the file.
To get started, position your cursor in the upper left corner of the file and follow along. Because HTML tags have angled brackets in them already, I’ll explicitly state when to press enter and escape instead of trying to encode them as <cr> and <esc>.
- Remove the initial numbering
- xxxxx
- Apply the opening HTML tags
- i<li><em>
- Press escape to go back to normal mode
- Find the end of the title
- /(
- Press return to complete the command
- h
- Apply the closing tag around the title
- i</em>
- Press escape to go back to normal mode
- Advance to the end of the line and apply the closing tag
- $a</li>
- Press escape to go back to normal mode
- Advance the cursor to the beginning of the next line
- j^
Once you understand the process, it’s a snap to turn it into a macro. Here’s all you do:
- With your cursor on the beginning of the line you want to modify, press “q” to start the recording process, and then press any other letter to name your macro. Let’s use “w”. At this point, you should see “recording” appear at the bottom of your window
- Follow the process outlined above to modify one of the lines and record the body of your macro
- Press “q” again to stop the recording process
Now you have your macro mapped to the “w” key, and all you need to do to invoke it is to press “@w”. If you want to apply it repeatedly, a shortcut is to press “@@” after you’ve applied it an initial time, or you can even press a number followed by “@w” to apply it a successive number of times. For example, pressing “10@w” would apply the macro 10 times.
Simply finish off this exercise by wrapping the list items in an opening and closing <ol></ol> and you’re all done. While this first macro may have seemed like a bit of work, imagine that you already knew the keystrokes and how to record macros, and then reconsider the effort required; it’s fairly negligible.
I’d love to ramble on all evening about Vim stuff, but I bet you have some good tips of your own. Why not share them below and/or in the article’s Talk Back section?


Ach, the regex in vim is only:
:%s/^[0-9]* -- \(.*\) \(([0-9]*)\)$/<li><em>\1<\/em> \2<\/li>/
It's pretty straightforward. :-) I suppose, in vim style, followed up with :
1GO
<ol>
press escape
0Go
</ol>
press escape
By the way, one thing that would makes long regexes involving forward slashes easier to read (for me, anyway) would be to substitute the forward slashes that separate the actual pieces of the substitute command with something else, like hash characters. i.e. instead of substituting a command with :%s/foo/bar/ you can type :%s#foo#bar Sometimes that ends up saving you some trouble. At any rate, the first time I discovered that (you can do it with Perl too as I recall), I thought it was pretty neat.
I use this recording all the time. One question I have, is there any way to save this recorded macro? Granted, these are usually just throw away scripts, but there are times it'd be nice to save.
A book I recommend to everyone I know is Stephen Covey's Seven Habits of Highly Effective People. I just ran across a document by the primary developer of Vim himself that uses this particular motif. It's a really good read too. I highly recommend reading it. It has some good tips in it:
http://www.moolenaar.net/habits.html
@Mark: Unfortunately, I'm not aware of a way to save the macro itself between sessions. To repeat this sequence of keystrokes, I believe you'd have to write a Vim script that you could call or else map the series of keystrokes with one of the "map" commands. Of course, you could always send in feature request to Bram Moolenaar and ask him to provide this capability for you (or hack at it yourself, if you dare!)
You can insert the contents of the register into a text buffer using the " command. Using the article as an example, you could type (in normal mode)
"wp
and this would insert the contents of the w register (where the macro was stored) at the current cursor location. You can read more about working with registers by typing
:help registers.
@Clint: Awesome tip! that's the thing about Vim...just when you think you're getting good, you find out there's a whole lot you never even knew about!
@Clint: ditto. This is really an amazing program. And just when you think you've learned all you'll need, you can always get plugins to make it do more. A quick and dirty favorite of mine is "Align". If you have a section of code with say "='s" and you want them all lined up (as you should!), you can use Align to quickly neaten things up.Like most things vim, it does much more too.
http://vim.sourceforge.net/scripts/script.php?script_id=294
@Mark: Registers can indeed be saved between sessions. I have the following line in my ~/.vimrc file:
set viminfo=%,'50,\"100,n~/.viminfo
"100 tells vim to save the first 100 lines of each register in the viminfo file. Once I've recorded a macro, it is available in all future vim sessions until I overwrite it.
:help 'viminfo'
:help viminfo
Only problem with this macro, is that you really need 2 or use a small regex.
xxxxx Works for the first 9 lines, then you need xxxxxx otherwise you end up with a leading space.
Still, I have learnt some things here, and it's a bit of a wake up call, to learn some of the more advanced Vim features! :-D
Instead of
xxxxx
you can use
d2f
followed by a space (in normal mode, of course). This will overcome the preceding space problem and can be extended to lists with more than 99 elements.
For anyone who sees this in the future you can also use d2W or 2dW to delete the first 2 WORDS.
see help for word for the difference betweens words and WORDS.
This is great, people. You finally forced me to learn macros. I've escaped regex Hell!
Nice tip! I am a 20 year vi user just being forced onto PC's from Unix and learning VIM. I love it! Do you know if it is possible to save a recorded macro so that it can be used next week or next year? Can it be predefined in the .vimrc file? Thanks.
Instead of "$a" to append at EOL you can use "A"
personally I would do it with three quick regex find and replaces which are quicker than recording the macro or doing the whole search and replace in a single line with \1 captures.
:%s:.*-- ::
:%s: (::
:%s:)$::
stupid comment system ... whats the verbatim tag round here? Theres sposed to be html tags in the comment above ...
You can also use regexp's in a series for doing one-off transformations, you don't have write a big complicated single one.
I do wish that vim supported 'normal' extended regexp's though (so you could do /^.*? -- /); not having that makes it a little harder to do it right:
:%s/^\d\d* --/<li><em>/g
:%s,\( (\d\d*)\),</em>\1</li>,g
And for the macro'ed version I would use:
0d2Wi<li><em> .escape. t(i</em> .escape. A</li> .escape. j
0 - goto start of line
d2W - delete 2 words
i - insert ... escape
t( -- go to the next (
i - insert ... escape
A - append to end of line
j - down a line