Leveraging Emacs macros to make powerful changes to your code

Today I want to talk a little about a really nice feature in Emacs.
That feature is keyboard macros. The work by recording your actions on
the keyboard and then replaying those actions. These macros can then be
combined with C-u to run the following command, in this case the
just recorded macro, num number of times.

The most recent task I used macros for is while extending some functionality
in Erlang. I needed to add a lot of function clauses for a set of some fifteen
new patterns, and use those to translate, from the pattern to a one value, and vice versa.

There was already functionality for this. All I had to do was to replicate
the two functions for all the new patterns.

Lets say that we start out with the following list of defined items.

-define(THING_ONE, 1).
-define(THING_TWO, 2).
-define(THING_THREE, 3).
-define(THING_FOUR, 4).
-define(THING_FIVE, 5).
-define(THING_SIX, 6).
-define(THING_SEVEN, 7).
-define(THING_EIGHT, 8).
-define(THING_NINE, 9).
-define(THING_TEN, 10).
-define(THING_ELEVEN, 11).
-define(THING_TWELVE, 12).

I then needed to transform that into the following form:

to_value(?THING_ONE) -> 1111;
to_value(?THING_TWO) -> 2222;
...
to_value(?THING_TWELVE) -> 1212.

As well as this format:

to_macro(1111) -> ?THING_ONE;
to_macro(2222) -> ?THING_TWO;
...
to_macro(1212) -> ?THING_TWELVE.

This is really straight forward by combining rectangles and keyboard
macros.

First use put your cursor right after the first -define( and then press
C-x SPC to enable rectangle mode. Mark all of the definitions so that your
selection looks like this:

THING_ONE, 1
THING_TWO, 2
THING_THREE,
THING_FOUR,
THING_FIVE,
THING_SIX, 6
THING_SEVEN,
THING_EIGHT,
THING_NINE,
THING_TEN, 1
THING_ELEVEN
THING_TWELVE

Copy the region with M-w and paste it where you want to define your to_value function.

To get rid of the trailing commas and numbers you select the region again and run M-x replace-regex with ,.* as your pattern. Voila! You now have a pristine region to work with.

Do a rectangle select on the first column of the values and then use M-x string-insert-rectangle to insert to_value(? at the start of all the lines.

You rectangle select again to mark the last column in your partial function
definition. Now do the same M-x string-insert-rectangle and insert ) -> ;.
Now you need to do some manual work to insert all the values for the function and you’re done with the first function.

Now for the exciting part where we leverage keyboard macros.

Copy the whole to_value function and paste it. Create a region for it and
then to M-x % to replace to_value with to_macro

to_macro(?THING_ONE ) -> 1111;
to_macro(?THING_TWO ) -> 2222;
...
to_macro(?THING_TWELVE) -> 1212.

To get rid of all the extra white space we’ll mark the entire function and do M-x replace-regexp with the pattern \b\s-*) and replace it with ).

Now we’ll record a macro:

  • Start with your cursor on the first row. Press C-x ( to start to record the macro.
  • C-s and search to the first ? press back arrow so that the cursor is on top of the ?. Then we press C-SPC to create a region for the parameter by using C-s again to search to the first ). Remember to use back arrow again to get the selection just right. C-w to kill/cut it.
  • Use C-s to search for >, and press right arrow. Paste item we just cut by using C-y.
  • Create a region and press C-e to select to the end of the line. Press left arrow to deselect the semi-colon. Cut the region with C-w.
  • Press C-r to search backward and search for ) press RET and C-y to paste. Now press down, C-a, and stop recording with C-x ). Now we can use C-x e to execute our macro. Did you notice that it did the exact same thing that you did and ended up with the cursor on the third function clause? Now we can replace the rest by using C-u 10 C-x e. Emacs will execute the macro another ten times.

#Emacs