Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use VIM to combine the power of macros and regex

Tags:

vim

I find my myself frequently with a problems that need to combine regular expressions, some kind of movement, and copying and pasting. I'm sure there is some way to do this in vim but I haven't seen anything bringing it all together. As an example right now the problem I am trying to solve is like this.

  1. Find a line that is /^Description "(.*)"/
  2. Save the captured text
  3. Go to the line that is /^TEXT "(.*)-/
  4. Replace the captured text above with the captured text from step 2
  5. Repeat as necessary moving forward through the file

Does anyone know of a way to quickly automate this type of task in VIM?

like image 340
z4ce Avatar asked May 29 '12 20:05

z4ce


Video Answer


2 Answers

I haven't learned all the new-fangled vim-only features; I learned vi before vim and this solution will work in any vi-ish editor. There might be an even better solution using vim features, perhaps.

What I would usually do in this case is use the :map command to bind several keys, each one doing part of the above. You need to pick some keys you can live without for a while; I often use g and v for this. @Neil Forrester suggested using function keys, which is a great idea.

Now, you showed regular expression patterns with parens indicating a match group. (In vi or vim, you actually need to put a backslash before each paren to make it "magic"; see the documentation.) For this solution, however, I am instead going to use the f command, which (f)inds a specified character; and/or the t command, which jumps un(t)il a character. f jumps up to a character, where t jumps just before a character. So, with f" we can jump to the first double-quote of a string, and then using t" we can jump until just before the second double quote. So, the sequence f"lyt" would find the first double-quote, move one char to the right, then yank everything until the next double quote. But, let's store the yanked text into one of the 26 named buffers; let's just use buffer "a": f"l"ayt" This is a little bit confusing, because we must use "a to refer to named buffer "a" but we have lots of other " characters that we are looking for.

Also, within a "map" you may need to record a keystroke for the Enter key. The way you do that is to hit Ctrl+V, then hit the Enter key. This will display as ^M. In my code below, if you see ^M it is not intended to mean an actual ^ followed by an actual M but rather a single key that represents the Enter key.

So now, we can make our two key mappings. Let's bind v to do steps 1 and 2, and g to do steps 3 and 4.

:map v /^Description "/^Mf"l"ayt"

:map g /^TEXT "/^Mf"ldt""aP

Don't forget, use Ctrl+V and Enter rather than actually typing ^M, so you can't just copy/paste the above without editing it.

Now, use the v key to do steps 1 and 2, and the g key to do steps 3 and 4. By alternately hitting the two keys you can do a lot pretty quick.

There might also be a way to do this by using scripting in vim, but on the other hand, you might just want to write a short Python script (or your favorite language) if you want to script this. The two key macros, above, really do provide a fast way to do this sort of thing in vim.

vim has some sort of feature for recording keys as you type them, which I think can be used to quickly create this sort of macro.

like image 159
steveha Avatar answered Nov 14 '22 20:11

steveha


You can map the following sequence to an unused key (such as F2), and then press it as many times as you want.

:map <F2> /^Description "(.*)"<CR>y//e/^TEXT "(.*)-<CR>Pd//e

Optionally, you could add <F2> to the end, so that it will run recursively until the match fails.

:map <F2> /^Description "(.*)"<CR>y//e/^TEXT "(.*)-<CR>Pd//e<F2>

Let's break this down to see what's happening. This moves to the beginning of your first search:

/^Description "(.*)"<CR>

This yanks until the end of your first search.

y//e

This is your second search:

/^TEXT "(.*)-<CR>

This puts what you yanked just before the cursor, moves one character to the right, and then deletes until the end of your second search.

Pld//e
like image 43
Neil Forrester Avatar answered Nov 14 '22 21:11

Neil Forrester