I make and use recordings in Vim all the time. I cannot imagine my daily work-a-day life without them. The most common reason for using recordings is to perform a complex modification of a semi-formatted dataset: SQL result sets, CSV, TSV, XML, HTML, etc...
As with most things in Vim when you find some new way feature, you find that you probably already had the parts laying before you, but hadn't quite had the 'manual' to put all the parts together. Recently I found a new way to do so conditionally, that I thought I'd share; its one of those methods that has probably been staring me in the face for-ever, and I just didn't see it.
So. What am I talking about? Conditional Macros. Lets break it down:
Macros
Plain and simple. Here is a simple problem that I think requires a macro. Suppose you have some lines of text like the following:
List A: 108 unique colors, 387 total colors
List B: 266 unique colors, 1343 total colors
List C: 361 unique colors, 2554 total colors
List D: 174 unique colors, 1221 total colors
List E: 301 unique colors, 2665 total colors
Suppose you wanted to compute the difference between the total colors and unique colors. So, in the case of the first line this would be 387 minus 108. You can do this once, sure. But suppose there are a couple thousand lines, and you want it all done, and done in Vim. No problem. You would record a macro like so:
- start your macro recording to register m (qm)
- go to the beginning of the line (^)
- move up to the first number (WW)
- store the first number in a register ("iyw)
- go to the second number (f, )
- store the second number in a register("oyw)
- compute the difference between the numbers (:let @s=@o-@i[carriage return])
- print out the result at the end of the line ($a == [control r]s[control c])
- move to the next line (j)
- stop recording (q)
If you were to examine register m, you would see:
^WW"iywf, "oyw:let @s=@o-@i[carriage return]$a == [control r]s[control c]j
And if you executed this macro (@m) for each line you would get something like this:
List A: 108 unique colors, 387 total colors == 279
List B: 266 unique colors, 1343 total colors == 1077
List C: 361 unique colors, 2554 total colors == 2193
List D: 174 unique colors, 1221 total colors == 1047
List E: 301 unique colors, 2665 total colors == 2364
To do several lines you would replay the macro by typing something like 5@m to do it five times, etc.
Conditional
When conditional modification pops to mind, I think of the global command (:g//). Whenever there is a match for the global command, it executes some arbitrary commds. So suppose you had text like the following:
...
...
Uninteresting line
List A: 108 unique colors, 387 total colors
Uninteresting line
Uninteresting line
List B: 266 unique colors, 1343 total colors
Uninteresting line
List C: 361 unique colors, 2554 total colors
Uninteresting line
Uninteresting line
...
...
You could just delete all the uninteresting lines like so:
:g/^Uninteresting line$/delete
Conditional Macros
Now, you could just delete everything not interesting and then run your macro on whatever is left. But a lot of times, you might find that you are interested in 'fixing' some lines but leaving the rest of the text unchanged. You can do this by combining the global command with a macro. So suppose you had:
...
...
Mildly interesting line
List A: 108 unique colors, 387 total colors
Mildly interesting line
Mildly interesting line
List B: 266 unique colors, 1343 total colors
...
...
To execute the macro you created, but only for the interesting lines you can combine the macro with the global command:
:g/^List/norm @m
Which would yield the desired result:
...
...
Mildly interesting line
List A: 108 unique colors, 387 total colors == 279
Mildly interesting line
Mildly interesting line
List B: 266 unique colors, 1343 total colors == 1077
...
...