Article summary
Vim macros let us transform code like no other editor. Here’s how they work:
- Pick a register to record into. (A Vim register is like a little slot where the macro data will be stored. We usually want to record into one of the named
a-z
lowercase letter registers.) - Begin recording with the
q
command. - Edit however you want.
- End recording with the
q
command. - Replay your macro wherever you want with the
@
command.
A Simple Example
Let’s say we need a macro that takes three lines and moves them down three lines. We can accomplish this with the sequence d3djjp
. (Cut 3 lines, move down two lines, then paste below the current line.) To record this as a macro into, say, register a
, we’d type qad3djjpq
. Then, wherever we wanted we could replay this sequence with @a
.
Unleashing the Full Power of Vim Macros
The "
quote in Vim is especially useful when working with macros. The "
lets us specify what register we want to store text into when we’re yanking/deleting. This is needed, for instance, when we have multiple items to copy/paste at once.
Let’s create a macro to generate a Java getter method from a field declaration.
Here’s how we’ll do it. I’ll explain what each step does at a high level and then include the keys that accomplish it:
- Go to the beginning of the current line. (This is a good general practice that allows us to replay macros from anywhere on a line.)
0
- Advance to the second word of the line and yank the Java type into register ’t’.
w"tyw
- Advance to the next word of the line and yank the field name into register ‘y’.
w"yyw
- Begin writing the method signature. (
^[
is how the escape character is displayed.)jopublic ^[
- Paste the Java type.
"tp
- Paste the field name.
"yp
- Go to the beginning of the field name, uppercase the first letter, and add ‘get’.
b~higet^[
- Finish the rest of the method, including one more paste of the field name. (
^M
is a newline.)A() {^Mreturn this.^["ypA;^M}^M^[
When we put it all together we get:
qa0w"tyww"yywjopublic ^["tp"ypb~higet^[A() {^Mreturn this.^["ypA;^M}^M^[q
It looks pretty complicated, but we don’t usually see the contents of the register. Instead, we can focus on what our cursor is doing while we’re interacting with it.
What situations have Vim macros helped you out with?
This post was originally posted in 2014 and was updated in 2024.
Cool, emacs users do the same thing. Finally something in common! Is it easy/common to write macros in vimscript too?
The trick about anchoring a macro by moving to the beginning of the line is good advice. Whenever I write keyboard macros I try to anchor each step. I do things like search forward for a unique token, or jump to the end of a line and then move backwards so I don’t have to worry about different word counts in the middle of a line.
In your example, I’d begin by copying the entire line, then change the copy into the getter method. In emacs it’s common to have a macro work on an active region or even a temporary buffer so that edits don’t accidentally mess up the file. This is probably how I’d do the macro since Java types can be a lot more complicated than a word:
duplicate line, make the line the active region, delete “private|protected”, jump to beginning of line, skip spaces, insert “public”, jump to end of line, delete spaces, delete semicolon, move back a word, copy word, insert “get”, jump to end of line, insert “() { return “, paste, insert “; }”.
Hey Ken, thanks for commenting.
I’d say no, it’s not common to write Vimscript, unfortunately. The other day, some of us were talking about Tim Pope, who wrote many of the best vim plugins. We were joking that he’s one of the only people out there who actually knows how to use Vimscript.
Most of my experience comes from writing one-off macros, when I find myself needing to make a bunch of repetitive edits. It’s usually a race against the clock for me, trying to come with something just robust enough to get the job done, without being an overall loss of time.
I tend to move forward and backward by word a lot, and like you said, other Java keywords like
static
would mess my example up. I like the idea of either jumping to the end of the line or searching to make the macro more robust.