I repeatedly have the following situation, and wonder how it can be handled better than I do it now. These lines must be merged


with these:


Desired result:


What I do now is to mark and yank the second block as a block, go to the first semicolon in the first block, and press P. Result is:

/path/subdir        ;text
/path/longsubdir    ;text

But this is obviously not what I want. How can I avoid the extra blanks?


3 Answers 3


With the latest Vim version 8.2.2914, you can now simply paste without adding padding using the zp command.


Not sure if you can avoid extra blanks...

I would do it this way:

  1. select /subdir/ and other dirs with <C-v> + motion
  2. delete it
  3. go to /path's and paste befor ;
  4. select para and remove spaces with vip:norm! f diw

There probably might be a better way with or without plugins, idk.

PS, ofc the last command that deletes the spaces should depend on what you end up having in a merged text. You can go with a safer option vip:s/ \+//g

enter image description here

  • Out of curiosity, how did you make this gif/animation? A moving picture speaks a million words :-)
    – Chris
    Jun 10, 2021 at 9:45
  • 1
    @Chris ScreenToGif
    – Maxim Kim
    Jun 10, 2021 at 10:18

Here's a programmatic way of doing it. It's a bit complicated, but it only took me 5 minutes of trial and error to get right :)

Here's the start:


The key is to try to :join the matching lines with a :global command. We can do a touch up of the ordering and such later.

A first attempt might be :global/;/+3move. | -join!, which says to do the following for each line containing a semi-colon: move the line 3 lines down to below the current line, then join them without spaces.

The problem is, after the lines start moving, 3 isn't the right number anymore! There are various ways to calculate the right offset (and you might be able to get away with a good search pattern if you're careful, something like /[^;]*), but I chose to just go with a counter.

Initialize the counter with 3, which works for the first one (3 for the number of /path;text lines you have):

let lines = 3

Then, on each run of the :global, we use the counter for the offset and also decrement the counter:

:global/;/execute print('+%dmove.', lines) | -join! | let lines -= 1

This isn't quite right. The last step is to touch up the ordering (we end up with the text in the middle).

We can do this afterwards using a whole-file substitute, but I found it fine to do as part of the :global. We just swap the order of two groups:

:global/;/execute printf('+%dmove.', lines) | -join! | substitute,\v(;[^/]+)(/.*),\2\1, | let lines -= 1

Or, as one long command:

:let lines = 3 | global/;/execute printf('+%dmove.', lines) | -join! | substitute,\v(;[^/]+)(/.*),\2\1, | let lines -= 1

and with abbreviations/short names

:let c = 3 | g/;/exec printf('+%dm.', c) | -j! | s,\v(;[^/]+)(/.*),\2\1, | let c -= 1

Final result:


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.