Sunday, December 21, 2014

Vim Macros: A More Complicated Problem

Here’s a more difficult problem than my previous post on Vim macros: I collected a bunch of links to post on my website using a simple 2-column table:

blogs.perl.org  |http://blogs.perl.org/mt/mt-cp.fcgi?__mode=view&id=3159
Wiki            |http://tinypig.pbworks.com/
Tumblr          |http://tinypigdotcom.tumblr.com/
WordPress       |http://tinypig.wordpress.com/
Perl Monks      |http://www.perlmonks.org/?node=tinypig
GitHub          |https://github.com/tinypigdotcom

My HTML links template I want to add them to is basically this:

<h3>Links</h3>
<ul>
<li><a href="">LINK</a><br/></li>
</ul>

Appending the first file to the second, I get this:

<h3>Links</h3>
<ul>
<li><a href="">LINK</a><br/></li>
</ul>

blogs.perl.org  |http://blogs.perl.org/mt/mt-cp.fcgi?__mode=view&id=3159
Wiki            |http://tinypig.pbworks.com/
Tumblr          |http://tinypigdotcom.tumblr.com/
WordPress       |http://tinypig.wordpress.com/
Perl Monks      |http://www.perlmonks.org/?node=tinypig
GitHub          |https://github.com/tinypigdotcom

Note the pipes (|) separating the columns. Having these (or some other unique character) make it much easier to work with the data because we can use Vim’s F find command to move to the exact location we need. Also note there should be a blank line at the end of the file so that we can go down successfully and our find on the pipe symbol will fail, ending the recursive macro.


Step Keys Meaning
1 qqq Start and immediately stop recording macro into buffer ‘q’, to empty it
2 qq Start recording a macro into buffer ‘q’
3 0 Go to the beginning of the line
4 f| Find the next pipe symbol
5 0 Go to the beginning of the line
6 3k Go up three times
7 Y “Yank” the current line (in this case, the link template line) into the buffer
8 p Immediately “put” the line right below, effectively duplicating it
9 3j Go down three times
10 dt| “Delete to” pipe, which also captures the deleted text into the buffer
11 4k Go up four times
12 f>f> Find greater-than symbol, then find the next one
13 p “Put” the buffer (the data from the first column we deleted 3 steps back)
14 ge “Go” to the “end” of the previous word
15 l Go right
16 dt< Delete to greater-than symbol, effectively removing trailing space
17 0 Go to the beginning of the line
18 4j Go down four times
19 l Go right, skipping over the pipe symbol
20 D Delete the rest of the line (again capturing the text)
21 4k Go up four times
22 f" Find next quote symbol in the current line
23 p “Put” the buffer (data from the second column)
24 0 Go to the beginning of the line
25 4j Go down four times
26 dd Delete the current line
27 @q Add the instruction to start running macro ‘q’ again, creating the recursive call
28 q Stop recording
29 @q Actually run macro ‘q’



Here’s what it looks like in practice:

Wednesday, December 17, 2014

Fixing Multiple Lines with Vim Macros


Sometimes I have a tedius code change where I have to perform the same task on multiple lines in vim and I can’t use ‘.’ because each line is a little different or the changes are too complicated. In this situation, I use a recursive macro in vim.

Here, I started with a list of items that I want to turn into a dispatch table - the code itself isn’t important here, it’s the transformation that I want to illustrate:

my %actions = (
    'start_shopping',
    'add_item',
    'remove_item',
    'use_coupon',
    'checkout',
);

Step Keys Meaning
1 qqq Start and immediately stop recording macro into buffer ‘q’, to empty it
2 qq Start recording a macro into buffer ‘q’
3 0 Go to the beginning of the line (always start there, if possible, for consistency)
4 f' Find the next single quote (always start with a find. That way, in the infinite loop we are creating, when it fails the loop will end)
5 l go right to get to first “real” character
6 yw Yank the word
7 $ go to end of line
8 P Put (before the comma)
9 F' Find the previous single quote
10 x delete it
11 i start inserting
12 <SPACE>=><SPACE>\& (literal characters to insert)
13 <ESC> Escape, leaving insert mode
14 F' Find the previous
15 x delete it
16 0 Go to the beginning of the line
17 j Go down a line
18 @q Add the instruction to start running macro ‘q’ again, creating the recursive call
19 q Stop recording
20 @q Actually run macro ‘q’

If you have made a mistake and it starts going haywire (this happens to me once in a while) just <CTRL>-c and <ESCAPE> and u for undo and it should go back to the way it was before you ran the macro.

Here’s what it looks like in practice:


Note: I have this line in my .vimrc file:

map z @qz

Since I don’t use z’s default functionality, instead I have it run @q on an infinite loop which allows me to skip step 18 from the example and then step 20 just becomes “z”.

For this next group of commands, I’m going to use the same concept to align all the =>. The block must begin with the line where => is furthest to the right. If it doesn’t belong there, you can always put it there to align it and then move it back to its place afterward. We’ll start on the second line this time since the first line is already positioned correctly.

Step Keys Meaning
1 qqq Start and immediately stop recording macro into buffer ‘q’, to empty it
2 qq Start recording a macro into buffer ‘q’
3 0 Go to the beginning of the line
4 f= Find the next =
5 50i<SPACE><ESC> Insert 50 spaces to make sure it’s long enough
6 0 Go to the beginning of the line
7 k Go up
8 f= Find the next =
9 j Go down
10 dw “Delete word” in this case will delete to the next non-space
11 0 Go to the beginning of the line
12 j Go down a line
13 @q Add the instruction to start running macro ‘q’ again, creating the recursive call
14 q Stop recording
15 @q Actually run macro ‘q’

Here’s what it looks like in practice:


Credit to Hongleong for the qqq emptying idea (there is much more info at that link regarding recursive vim macros)

For routinely aligning text in vim, I found this video to be extremely helpful.

Just in case that disappears, I have copied two of the links included there: the Tabular plugin and a macro which uses it.

Animated GIFs created with LICEcap

Friday, December 05, 2014

Using Files for Inter-process Communication



I was abusing a test suite I had written to add test data to the database and I was running it inside an infinite loop because it was going to take a while to generate the 10,000 records I wanted. When it came time to stop, I had difficulty either control-c-ing or killing the right processes to get it to quit, so the second time I ran it, I did this instead:

while true
do
    if [ -f $HOME/stop_while ]; then
        break
    fi
    ./thing_i_wanted_repeated
done

Using files for inter-process communication seems so much more straightforward to me.

Thursday, December 04, 2014

XML Nested CDATA sections




"Nested CDATA sections are not allowed." But I need that! I have XML that contains XHTML which in turn has a CDATA section of its own.  Surprisingly I found the answer at Wikipedia.

Here's my XML:

<apple>
    <banana>
    <![CDATA[
    <script type="text/javascript">
    /* <![CDATA[ */
    var language = "en";
    /* ]]]]><![CDATA[> */
    </script>
    <script type="text/javascript" src="//www.this.com/that.js">
    </script>
    <noscript>
    <div style="display:inline;">
    <img src="//www.this.com/that/?l=b5&amp;scrum=2"/>
    </div>
    </noscript>]]></banana>
</apple>

Although the Wikipedia entry covers it sufficiently, I thought it would be interesting to diagram a bit further (click to enlarge):