Old Guy New Trick

An old guys journey to learn how to code.

Macro VimOnomics


Author: John on October 27, 2015

Today I would like to share a productivity tip that I learned from my pair/lead developer partner.  Vim continues to surprise me on what seems to be a daily basis.  I know that with my neophyte knowledge with vim, that I'm barely scratching the surface.  Ok, let's get down with some Macro Econom..., I mean VimOnomics.

Recently I was tasked with changing a handful of our fixture \*.csv files.  In case you have not yet worked with these types of files, they are a simple text file which use a delimiter, commonly the comma.  For example:

phone_prefix,phone_num,city,state,postal_code
914,2251976,Carmel,New York,10134

Now imagine a file in the above format, but with hundreds of rows and that we need to change the phone_prefix from 914 to 944.  Also, imagine that we have a dozen or so fixture files that require this update.  Using the power of macros in vim, this can be a relatively easy task.

[Open all of our fixture files]
vim spec/fixtures/*/*.csv

[Record the macro]
qq
:1,$s/^914,/944,/
:w
:n
q

To review what the above steps are doing, first we tell vim to start recording using the command qq.  Next, we define our replace command telling vim that we want to replace 914, with 944, and then we write the file using :w to save our changes.  The :n command tells vim to move to the next file and q stops the recording.

With the macro ready to go, we can use @q to launch the macro.  This should bring the next file in the buffer into focus and perform the steps outlined in the macro.  Repeat the @q command until all of your files have been updated.  After feeling the pain of doing manual updates for a while, this tip was much welcomed.

However, we are are not done yet.  We need to address this change of 914 to 944 in our related feature file(s). Continuing onwards with our example, we will assume that we have an existing feature file with some steps similar to the following (picture dozens if not hundreds of lines):

  And   I download the csv file report "Phone-Records" as admin
  Then  the contents of the downloaded "Phone-Records" report contain:
    | text                                          |
    | phone_prefix,phone_num,city,state,postal_code |
    | 914,2251976,Carmel,New York,10134             |
    | 914,2251981,Carmel,New York,10134             |
    | 914,2251376,Carmel,New York,10134             |
    | 914,2252946,Carmel,New York,10134             |
    | 914,2253976,Carmel,New York,10134             |
    | 914,2251376,Carmel,New York,10134             |
    | 914,2254576,Carmel,New York,10134             |
    | 914,2258986,Carmel,New York,10134             |
    | 914,2259176,Carmel,New York,10134             |
    | 914,2257326,Carmel,New York,10134             |
    | 914,2251974,Carmel,New York,10134             |

For our example, assume we have our new data in the file new_phone_prefix.csv:

phone_prefix,phone_num,city,state,postal_code
944,2251976,Carmel,New York,10134
944,2251981,Carmel,New York,10134
944,2251376,Carmel,New York,10134
944,2252946,Carmel,New York,10134
944,2253976,Carmel,New York,10134
944,2251376,Carmel,New York,10134
944,2254576,Carmel,New York,10134
944,2258986,Carmel,New York,10134
944,2259176,Carmel,New York,10134
944,2257326,Carmel,New York,10134
944,2251974,Carmel,New York,10134

And we want to replace our data with new data, or perhaps we are creating the feature steps from scratch.  For our first step, let's create/edit our feature file to have the basics:

  And   I download the csv file report "Phone-Records" as admin
  Then  the contents of the downloaded "Phone-Records" report contain:
    | text                                          |
    | phone_prefix,phone_num,city,state,postal_code |

Next, we will yank (y) the new data from the new_phone_prefix.csv file and place (p) them under what we just did:

  And   I download the csv file report "Phone-Records" as admin
  Then  the contents of the downloaded "Phone-Records" report contain:
    | text                                          |
    | phone_prefix,phone_num,city,state,postal_code |

944,2251976,Carmel,New York,10134
944,2251981,Carmel,New York,10134
944,2251376,Carmel,New York,10134
944,2252946,Carmel,New York,10134
944,2253976,Carmel,New York,10134
944,2251376,Carmel,New York,10134
944,2254576,Carmel,New York,10134
944,2258986,Carmel,New York,10134
944,2259176,Carmel,New York,10134
944,2257326,Carmel,New York,10134
944,2251974,Carmel,New York,10134

Now we need to move the data to line up with the left hand pipe (|) character.  I will place my cursor over the 9 on the first row of data, then use Cntrl+v to visual select a column, and press j until I have all the records covered.  Then I'll use shift+i to insert and press my space bar 4 times then the | key.  Lastly, remove that line break between our column headers and the data.  You should have something similar to:

  And   I download the csv file report "Phone-Records" as admin
  Then  the contents of the downloaded "Phone-Records" report contain:
    | text                                          |
    | phone_prefix,phone_num,city,state,postal_code |
    |944,2251976,Carmel,New York,10134
    |944,2251981,Carmel,New York,10134
    |944,2251376,Carmel,New York,10134
    |944,2252946,Carmel,New York,10134
    |944,2253976,Carmel,New York,10134
    |944,2251376,Carmel,New York,10134
    |944,2254576,Carmel,New York,10134
    |944,2258986,Carmel,New York,10134
    |944,2259176,Carmel,New York,10134
    |944,2257326,Carmel,New York,10134
    |944,2251974,Carmel,New York,10134

Hang in there, we are almost done.  To wrap this up, we return to using a macro.  I will position my cursor on the first line of data, build a macro, then execute it as follow:

qq
shift+a
|
esc
j
q
10 @q

Assuming you were successful, we should have something similar to:

  And   I download the csv file report "Phone-Records" as admin
  Then  the contents of the downloaded "Phone-Records" report contain:
    | text                                          |
    | phone_prefix,phone_num,city,state,postal_code |
    | 944,2251976,Carmel,New York,10134             |
    | 944,2251981,Carmel,New York,10134             |
    | 944,2251376,Carmel,New York,10134             |
    | 944,2252946,Carmel,New York,10134             |
    | 944,2253976,Carmel,New York,10134             |
    | 944,2251376,Carmel,New York,10134             |
    | 944,2254576,Carmel,New York,10134             |
    | 944,2258986,Carmel,New York,10134             |
    | 944,2259176,Carmel,New York,10134             |
    | 944,2257326,Carmel,New York,10134             |
    | 944,2251974,Carmel,New York,10134             |

The clever eye may have noticed that we executed our macro in a slightly different manner this time - 10 @q.  After recording the macro for our first row of data, we tell vim to execute the macro ten times by using:  10 @q.

I use this second example, of editing a feature file, more frequently that the first example.  After updating the feature files manually for too many times that I'd like to admit to, I finally asked my pair if he knew a better way.  Of course he did, what a silly question.  He showed me the above techniques which saves me a lot of cycles - time which can be better spent on actual code.

Learn Something New Every Day

Last Edited by: John on November 10, 2015