July 17, 2007

vim multiple repeat command

:g/PATTERN/,/$/-1d
Or simpler:
:g/PATTERN/-1d
or even more simpler
:g/PATTERN/d
can be used to delete all lines that match PATTERN

See help :g for more

Another post from VIM TIP #227
:v/./,/./-j

I'll break down this incredible collapse-multiple-blank-lines command for everyone, now that I finally figured out how it works.

First, however, I'll rewrite it this way to illustrate that some of those slashes have totally different meaning than others:

:v_._,/./-1join

Note that to delimit expressions like these, just about any symbol can be used in place of the typical slashes... in this case, I used underscores. What we have is an inverse search (:v, same as :g!) for a dot ('.') which means anything except a newline. So this will match empty lines and proceed to execute [command] on each of them.

:v_._[command]

The remaining [command] is this, which is a fancy join command, abbreviated earlier as just 'j'.

,/./-1join

The comma tells it to work with a range of lines:

:help :,

With nothing before the comma, the range begins at the cursor, which is where that first blank line was. The end of the range is specified by a search, which to my knowledge actually does require slashes. The slash and dot mean to search for anything (again), which matches the nearest non-empty line and offsets by {offset} lines.

/./{offset}

The {offset} here is -1, meaning one line above. In the original command we just saw a minus sign, to which vim assumes a count of 1 by default, so it did the same thing as how I've rewritten it, but simply with one character fewer to type.

/./-1

There is a caveat about join that makes this trick possible. If you specify a range of only one line to "join", it will do nothing. For example, this command tells vim to join into one line all lines from 5 to 5, which does nothing:

:5,5join

In this case, any time you have more than one empty line (the case of interest), the join will see a range greater than one and join them together. For all single empty lines, join will leave it alone.

There's no good way use a delete command with :v/./ because you have to delete one line for every empty line you find. Join turned out to be the answer.

This command only merges truly "empty" lines... if any lines contain spaces and/or tabs, they will not be collapsed. To make sure you kill those lines, try this:

:v/^[^ \t]\+$/,/^[^ \t]\+$/-j

Or, to just clean such lines up first,

:%s/^[ \t]\+$//g

No comments:

Post a Comment