Vim Tips Wiki
(→‎Comments: another example of the method where it makes more sense to use it)
(Change <tt> to <code>, perhaps also minor tweak.)
(15 intermediate revisions by 8 users not shown)
Line 1: Line 1:
  +
{{TipNew
{{TipProposed
 
|id=0
+
|id=1577
|previous=0
+
|previous=1576
|next=0
+
|next=1578
|created=November 7, 2008
+
|created=2008
 
|complexity=basic
 
|complexity=basic
 
|author=Hongleong
 
|author=Hongleong
 
|version=7.0
 
|version=7.0
 
|subpage=/200811
 
|subpage=/200811
|category1=
+
|category1=Usage
 
|category2=
 
|category2=
 
}}
 
}}
Line 39: Line 39:
 
</pre>
 
</pre>
   
Yes, this can probably be done via <tt>:substitute</tt> with back references, but that can be intimidating for some. An easier, more flexible and often quicker method to accomplish the same task is to use "complex repeats", which really aren't too complex.
+
Yes, this can be done via <code>:substitute</code> with back references (Try: <code>%s/\v(.+r)(.+)\s(\d{5})/\1\3\2/</code>), but that can be intimidating for some. An easier, more flexible and often quicker method to accomplish the same task is to use "complex repeats", which really aren't too complex.
   
 
Here's how:
 
Here's how:
 
#Place the cursor on the first character of the first line of the file.
 
#Place the cursor on the first character of the first line of the file.
 
#Hit the following keys to record the repeat-sequence:
 
#Hit the following keys to record the repeat-sequence:
#*<tt>qqq</tt> : Start and immediately end recording for register <tt>q</tt> -- this essentially empties register <tt>q</tt>
+
#*<code>qqq</code> : Start and immediately end recording for register <code>q</code> -- this essentially empties register <code>q</code>
#*<tt>qq</tt> : Start recording actions into register <tt>q</tt>
+
#*<code>qq</code> : Start recording actions into register <code>q</code>
#*<tt>f2</tt> : Find the "2" character to the right on this line (cursor will jump there)
+
#*<code>f2</code> : Find the "2" character to the right on this line (cursor will jump there)
#*<tt>D</tt> : Delete and yank the 5-digit number into memory (rest of line)
+
#*<code>D</code> : Delete and yank the 5-digit number into memory (rest of line)
#*<tt>Fr</tt> : Find the "r" character to the left on this line (cursor will jump to the last "r")
+
#*<code>Fr</code> : Find the "r" character to the left on this line (cursor will jump to the last "r")
#*<tt>p</tt> : Paste the number that was yanked earlier after the "r" character
+
#*<code>p</code> : Paste the number that was yanked earlier after the "r" character
#*''Enter'' : Move the cursor to the next line
+
#*''Enter'' : Move the cursor to the first non-whitespace character of the next line
#*<tt>@q</tt> : Execute the contents of register <tt>q</tt> (itself!), which is for now, empty, so nothing will happen. <span style="color:blue;">The trick here is that when we run it again with "@q" after you have ended the recording, it will call itself after processing each line! This recursive loop will process all the subsequent lines in that file, until it hits the end-of-file. This is the essence that makes complex repeats so flexible and powerful.</span>
+
#*<code>@q</code> : Execute the contents of register <code>q</code> (itself!), which is for now, empty, so nothing will happen. <span style="color:blue;">The trick here is that when we run it again with "@q" after you have ended the recording, it will call itself after processing each line! This recursive loop will process all the subsequent lines in that file, until it hits the end-of-file. This is the essence that makes complex repeats so flexible and powerful.</span>
#*<tt>q</tt> : End the recording
+
#*<code>q</code> : End the recording
#Hit <tt>@q</tt> to apply the same manipulation to the rest of the file from the 2nd line onwards. Enjoy the art of flying cursor and automatic text manipulation on your screen.
+
#Hit <code>@q</code> to apply the same manipulation to the rest of the file from the 2nd line onwards. Enjoy the art of flying cursor and automatic text manipulation on your screen.
   
 
With this knowledge, you can now quickly perform complex editing of any structured text with relative ease.
 
With this knowledge, you can now quickly perform complex editing of any structured text with relative ease.
   
This works much the same way as typing <tt>9999@q</tt> to execute the 'q' register as many times as the count you pass, except that you do not need to think about the size of your file before doing so to make sure enough 9's are added. One could be really clever and do <tt>:if cond | exe 'normal @q' | endif</tt> to provide a base case to end the recursion if desired, but that's probably not necessary in most cases.
+
This works much the same way as typing <code>9999@q</code> to execute the 'q' register as many times as the count you pass, except that you do not need to think about the size of your file before doing so to make sure enough 9s are added.
   
==See also==
+
==Ending the recursion==
  +
Normally, a macro recorded in this way will repeat until it reaches the end of the file. But, you can easily take advantage of the fact that a macro will stop running if it encounters an error to end the recursion when you want. For example, suppose you have a list of file names (perhaps you obtained it with <code>:r !ls *.c</code>), and you want to verify that every one of these file names occurs in another buffer (a makefile, for instance). You could do this quickly and easily as follows:
*[[Recursive mappings|This can also be done with a mapping]]
 
 
==Comments==
 
 
This concept is so cool! But, I think the tip suffers from the fact that the task at hand might be done more efficiently using another method (like regex).
 
 
How about an example of something that this is the better method for? For example, I can't think of a much better way to do this:
 
 
I wanted to verify that all file names in a list appear somewhere in another file. So, I formatted the file list to one file name per line, starting at character zero in the line, split the window with the file I want to verify the list items are in, and did the following from the list window:
 
   
  +
*Format the file list to one file name per line, with each file name starting at beginning of the line
  +
*Split a new window with the file you wish to contain all the file names (for a total of two windows)
  +
*Position the cursor on the first line of the file list
  +
*Record a recursive macro as follows:
 
<pre>
 
<pre>
 
qfq
 
qfq
 
qf
 
qf
 
0y$
 
0y$
<c-w>
+
<c-w>w
/<c-r>"<cr>
+
/\V<c-r>"<cr>
<c-w>
+
<c-w>w
 
j@f
 
j@f
 
q
 
q
 
</pre>
 
</pre>
   
The result is that register f contains a macro that will search for every line of the list of file names, until it reaches the end of the list of file names ''or until it encounters an error'' such as "pattern not found: {filename}".
+
The result is that register 'f' contains a macro that will search for every line of the list of file names, until it reaches the end of the list of file names ''or until it encounters an error'' such as "pattern not found: {filename}". If you run this macro on the file list, you will either see each file name highlighted in turn until the end of the list, or you will see a pattern not found error and (if 'hlsearch' is on) the missing file will be highlighted in the list.
  +
  +
If you don't wan't to rely on an error in this way to end the recursion, you could also do <code>:if cond | exe 'normal @q' | endif</code> instead of <code>@q</code> while recording the macro to provide a base case to end the recursion.
  +
  +
==Adding recursion to an existing macro==
  +
Forget to add the recursion as a final step, or want to debug first before running the macro on every line in the file? An easy solution is, after you have the macro doing everything but the recursive call, just append it to the register. Assuming that you've recorded the macro into the 'q' register, just do this:
  +
<pre>
  +
qQ
  +
@q
  +
q
  +
</pre>
  +
This works using the uppercased character to append in the corresponding register, see [[Best_Vim_Tips#Appending_to_registers|Best Vim Tips: Appending to registers]].
  +
  +
==Note==
  +
One may wonder, "why is it necessary to empty out a register prior to recording into it?" A sequence such as <code>qf</code> starts recording into an empty "f" register by definition. However, without emptying the register with <code>qfq</code> prior to recording, the register will not be empty when you add <code>@f</code> to your macro, and thus whatever is currently stored there will be executed, instead of doing nothing until you actually run the macro, as intended.
  +
  +
==See also==
 
*[[Recursive mappings|This can also be done with a mapping]]
  +
 
==Comments==
  +
  +
As said under 'Note' this approach is a little tricky because it is easy to 'miss' prior contents of registers, which can become even more tricky when using viminfo (who isn't?).
   
  +
Therefore, I vastly prefer recording the macro in non-recursive fashion (just leave off the trailing @f). Then, repeat your macro by doing general repeat (normalmode:) 100@q or 100@@ to repeat the last executed macro 100 times. This has the added benefit of being able to monitor/guide the process.
If we include this example, though, we should consider a rename of the tip...it has nothing to do with "quick editing of structured text".
 

Revision as of 07:55, 11 July 2012

Tip 1577 Printable Monobook Previous Next

created 2008 · complexity basic · author Hongleong · version 7.0


Often, when dealing with a data file in text format (e.g. csv), we need to massage the data to modify, filter or rearrange it. Using a clever trick to recursively use Vim's complex repeats (i.e. "macro recording"), such tasks can be done easily without having to be a REGEXpert.

Let's say we have a text file with the following contents:

dr-------- 20906
drwx------ 20913
drwxr-x--- 20704
drwxr-xr-x 21104
lrwxrwxrwx 20606
-------r-- 21004
-rw-r----- 20716
-rwxrwx--- 21102

For some reason, we want to move the 5-digit number at the end of the line to after the last "r" character on the same line, and we want to repeat it for the whole file. Notice that all these numbers begin with "2" and there can be multiple "r" per line.

Here's the end-result that we want:

dr20906--------
dr20913wx------
drwxr20704-x---
drwxr-xr21104-x
lrwxrwxr20606wx
-------r21004--
-rw-r20716-----
-rwxr21102wx---

Yes, this can be done via :substitute with back references (Try: %s/\v(.+r)(.+)\s(\d{5})/\1\3\2/), but that can be intimidating for some. An easier, more flexible and often quicker method to accomplish the same task is to use "complex repeats", which really aren't too complex.

Here's how:

  1. Place the cursor on the first character of the first line of the file.
  2. Hit the following keys to record the repeat-sequence:
    • qqq : Start and immediately end recording for register q -- this essentially empties register q
    • qq : Start recording actions into register q
    • f2 : Find the "2" character to the right on this line (cursor will jump there)
    • D : Delete and yank the 5-digit number into memory (rest of line)
    • Fr : Find the "r" character to the left on this line (cursor will jump to the last "r")
    • p : Paste the number that was yanked earlier after the "r" character
    • Enter : Move the cursor to the first non-whitespace character of the next line
    • @q : Execute the contents of register q (itself!), which is for now, empty, so nothing will happen. The trick here is that when we run it again with "@q" after you have ended the recording, it will call itself after processing each line! This recursive loop will process all the subsequent lines in that file, until it hits the end-of-file. This is the essence that makes complex repeats so flexible and powerful.
    • q : End the recording
  3. Hit @q to apply the same manipulation to the rest of the file from the 2nd line onwards. Enjoy the art of flying cursor and automatic text manipulation on your screen.

With this knowledge, you can now quickly perform complex editing of any structured text with relative ease.

This works much the same way as typing 9999@q to execute the 'q' register as many times as the count you pass, except that you do not need to think about the size of your file before doing so to make sure enough 9s are added.

Ending the recursion

Normally, a macro recorded in this way will repeat until it reaches the end of the file. But, you can easily take advantage of the fact that a macro will stop running if it encounters an error to end the recursion when you want. For example, suppose you have a list of file names (perhaps you obtained it with :r !ls *.c), and you want to verify that every one of these file names occurs in another buffer (a makefile, for instance). You could do this quickly and easily as follows:

  • Format the file list to one file name per line, with each file name starting at beginning of the line
  • Split a new window with the file you wish to contain all the file names (for a total of two windows)
  • Position the cursor on the first line of the file list
  • Record a recursive macro as follows:
qfq
qf
0y$
<c-w>w
/\V<c-r>"<cr>
<c-w>w
j@f
q

The result is that register 'f' contains a macro that will search for every line of the list of file names, until it reaches the end of the list of file names or until it encounters an error such as "pattern not found: {filename}". If you run this macro on the file list, you will either see each file name highlighted in turn until the end of the list, or you will see a pattern not found error and (if 'hlsearch' is on) the missing file will be highlighted in the list.

If you don't wan't to rely on an error in this way to end the recursion, you could also do :if cond | exe 'normal @q' | endif instead of @q while recording the macro to provide a base case to end the recursion.

Adding recursion to an existing macro

Forget to add the recursion as a final step, or want to debug first before running the macro on every line in the file? An easy solution is, after you have the macro doing everything but the recursive call, just append it to the register. Assuming that you've recorded the macro into the 'q' register, just do this:

qQ
@q
q

This works using the uppercased character to append in the corresponding register, see Best Vim Tips: Appending to registers.

Note

One may wonder, "why is it necessary to empty out a register prior to recording into it?" A sequence such as qf starts recording into an empty "f" register by definition. However, without emptying the register with qfq prior to recording, the register will not be empty when you add @f to your macro, and thus whatever is currently stored there will be executed, instead of doing nothing until you actually run the macro, as intended.

See also

Comments

As said under 'Note' this approach is a little tricky because it is easy to 'miss' prior contents of registers, which can become even more tricky when using viminfo (who isn't?).

Therefore, I vastly prefer recording the macro in non-recursive fashion (just leave off the trailing @f). Then, repeat your macro by doing general repeat (normalmode:) 100@q or 100@@ to repeat the last executed macro 100 times. This has the added benefit of being able to monitor/guide the process.