Applying DRY to Editing

Editing text can be tedious and mechanical; you are often required repeat the same sequence of commands several times. Keyboard macros are one way in which you can break out of the repetition.

Suppose you have a lisp file with a lot of duplicated function composition. You want to turn this:

(def my-constant (foo (bar '(1 2 3))))
(def your-constant (foo (bar '(1 (2 3 4) 5 6))))
(defn kitchen [x]
  (let [table (foo (bar x))]
    (foo bar table)))
(def mundane (foo (bar (foo (bar '())))))

Into this:

(defn combo [x]
  (foo (bar x)))
(def my-constant (combo '(1 2 3)))
(def your-constant (combo '(1 (2 3 4) 5 6)))
(defn kitchen [x]
  (let [table (combo x)]
    (combo table)))
(def mundane (combo (combo '())))

How quickly could you convert the first piece of text into the second? Scratch that. How much fun could you have doing it? How much could you make editing feel like programming?

If you are a regular user of Vim, you can use a macro to accomplish this task without ever feeling like you stopped programming!

How would you create and execute such a macro?

One Approach To Recording A Macro:

If you have a change that you want to make many times, you can record a macro using the following steps:

  • Navigate to the location of the first change you want to make
  • Tell Vim to start recording a macro
  • Use the best Vim-fu you can to make your change
  • Get into position to make the next change
  • Tell vim to stop recording

Let's expand these steps for the lisp example above:

  • Navigate to the location of the first change you want to make
    • Search for the first occurrence of duplication, using:
      /(foo (bar
  • Tell Vim to start recording a macro
    • Get in command mode and hit:
      qm
      . This tells Vim to record your keystrokes into a macro named 'm'.
  • Use the best Vim-fu you can to make your change:
    • Delete the call to
      bar
      , using:
      /bar<ENTER>dw
    • Delete the parentheses surrounding
      bar
      , using:
      hmm%dl`mdl
    • Change
      foo
      to
      combo
      , using:
      bcwcombo
  • Get into position to make the next change:
    • Search for the next occurrence of duplication, using:
      /(foo (bar
  • Tell vim to stop recording:
    • Get in command mode and hit "q"

Putting it all together, we wind up with the following macro:

/bar<ENTER>dwhmm%dl`mdlbcwcombo

Applying the macro

Each application of the macro processes a single instance of duplication. We can process the entire file by invoking the macro five times[1], with the command:

5@m

Why You Should Care

Editing text usually is not fun. It is mundane and it makes your job boring. You should try to counteract this phenomenon.

As pragmatic programmers, we try to automate as much as we can; we are constantly writing tools to make our lives easier. Somehow, however, most of us fail to apply this mindset to text editing.

Every minute that you spend repeating a mundane edit is boring and it is wasted. Every minute that you spend thinking of a clever regular expression is also wasted. Stop wasting time! Automate your editing and be a happier, less bored, more productive programmer.

Macros are fundamentally easier than regular expressions

Writing a macro is easy because it basically requires no extra work. You begin recording, do the thing you were already going to do, and then stop recording. In other words, the language you use to describe your task is the same language you use to perform your task. Conversely, each regular expression you write requires you to think; it requires you to articulate your task in a new language.

Parting Tip

The macros you record don't have to be perfect. Don't be afraid to write a macro which leaves you with some whitespace to clean up or some text to delete. If a simpler, less complete macro is easier to write, then you should write it. Leverage the editor to clean up afterwards.

[1] You actually want to invoke the macro four times, because you already made one of the five edits during the initial recording

Wai Lee Chin Feman is a software craftsman, enthusiastic about mathematics, functional programming, and philosophy.