Merge multiple lines (two blocks) in Vim
I'd like to merge two blocks of lines in Vim, ie take lines n..m
and append them to lines a..b
. If you prefer a pseudocode explanation: [a[i] + b[i] for i in min(len(a), len(b))]
Example:
abc
def
...
123
45
...
should become
abc123
def45
Is there a nice way to do this without doing copy&paste manually?
You can certainly do all this with a single copy/paste (using block-mode selection), but I'm guessing that's not what you want.
If you want to do this with just Ex commands
:5,8del | let l=split(@") | 1,4s/$/=remove(l,0)/
will transform
work it
make it
do it
makes us
harder
better
faster
stronger
~
into
work it harder
make it better
do it faster
makes us stronger
~
UPDATE: An answer with this many upvotes deserves a more thorough explanation.
In Vim, you can use the pipe character ( |
) to chain multiple Ex commands, so the above is equivalent to
:5,8del
:let l=split(@")
:1,4s/$/=remove(l,0)/
Many Ex commands accept a range of lines as a prefix argument - in the above case the 5,8
before the del
and the 1,4
before the s///
specify which lines the commands operate on.
del
deletes the given lines. It can take a register argument, but when one is not given, it dumps the lines to the unnamed register, @"
, just like deleting in normal mode does. let l=split(@")
then splits the deleted lines into a list, using the default delimiter: whitespace. To work properly on input that had whitespace in the deleted lines, like:
more than
hour
our
never
ever
after
work is
over
~
we'd need to specify a different delimiter, to prevent "work is" from being split into two list elements: let l=split(@","n")
.
Finally, in the substitution s/$/=remove(l,0)/
, we replace the end of each line ( $
) with the value of the expression remove(l,0)
. remove(l,0)
alters the list l
, deleting and returning its first element. This lets us replace the deleted lines in the order in which we read them. We could instead replace the deleted lines in reverse order by using remove(l,-1)
.
An elegant and concise Ex command solving the issue can be obtained by combining the :global
, :move
, and :join
commands. Assuming that the first block of lines starts on the first line of the buffer, and that the cursor is located on the line immediately preceding the first line of the second block, the command is as follows.
:1,g/^/''+m.|-j!
For detailed explanation of the technique used, see the answer I gave to the question "Vim paste -d ' ' behavior out of the box?".
To join blocks of line, you have to do the following steps:
jj
CTRL-v
$
CTRL-END
x
kk$
p
The movement is not the best one (I'm not an expert), but it works like you wanted. Hope there will be a shorter version of it.
Here are the prerequisits so this technique works well:
abc
and def
) have the same length XOR 上一篇: 评论/取消注释vim中的多个固定行
下一篇: 在Vim中合并多行(两个块)