Vim Macros and You

Josh Clayton

Ever get the urge to update a ton of files? I know I do. For example, I recently changed multiple hundred coffeescript files from the syntax of

MyGreatClass = Backbone.Model.extend(
  defaults:
    awesome: true
)

to

class @MyGreatClass extends Backbone.Model
  defaults:
    awesome: true

How long did it take me? A couple of minutes. Here’s how.

Ack (or Grep)

I like ack. To find all the files I need to edit, I’d write something like this:

ack '^[^\s].*\=.*\.extend\($' app/assets/javascripts -l

This finds everything that doesn’t start with a space, has an equals sign, and has .extend( at the end of the line.

Opening a list of files in vim

After looking over the results of ack and ensuring that everything that matched is what I want to edit, I’ll open those files in vim.

vim $(ack '^[^\s].*\=.*\.extend\($' app/assets/javascripts -l)

Vim Macros

If you’ve been using vim and haven’t taken advantage of macros (especially if you’re editing a lot of files in a similar fashion), you’re missing out. Open up vim and type :help q to get the nitty-gritty; I’ll summarize here.

To start recording a macro, press (in normal mode) q and then a letter or number. This will record a macro to whatever register you chose (via the letter or number).

Once you’re recording a macro, anything you type will be recorded to that macro so that it can be replayed. What I would type to change these files to the new format would be:

qqgg0iclass @<esc>f=cwextends<esc>2f.DGdd:wnq

Whoa, brain overload. Let’s break it down:

qq             # records the macro to the q buffer
gg             # first line in file
0              # first character in line
iclass @<esc>  # inserts class @ at the cursor and returns to normal mode
f=             # finds the first equal after the cursor
cwextends<esc> # changes the word (=) and moves to insert mode, adds extends, and returns to normal mode
2f.            # finds the second period after the cursor
D              # deletes the remainder of the line
G              # moves to the end of the file
dd             # deletes the line
:wn            # writes the file and moves to the next file in the list
q              # stops recording

This should be fairly straightforward; the only thing I really want to point out is the :wn. The n in that command moves to the next file in the list of files you opened with vim. This is one half of what makes editing all these files really fast.

Replaying a Macro

Now that you have your macro, it’s time to replay it. To replay a macro, press (in normal mode) @q (assuming you stored your macro into the q register). If you were to run that macro, it’ll run it against the current file, write the file, and move to the next. Since vim supports prefixing many commands with a number (for the number of times to repeat the command), running 100@q will run that macro on the first one hundred open files that I’ve opened with vim. Typically, this should be all you need to batch-edit, but if there are more files, just run that command again (or start with a higher number). If there are no more files to edit, vim will let you know.

Ben also mentioned recursive macros (my mind was blown) by adding @q right before the last q (which will run the q macro before stopping recording). Just make sure your q register is empty! This would allow you to run your macro once, without specifying the number of times to run it, because vim will run the macro until it’s out of files. Fancy!

Vim for fun and profit

Want to kick ass at vim? Pick up a copy of Vim for Rails Developers and become blazing-fast! If you want to hang out with fellow vim users to swap awesome tips like this, be sure to head to the Boston Vim Meetup!