I'd like to join every 3 lines of a file with a tab character as separator. How can this be done using Vim?
I'm aware of the macro mechanism, but I?m looking for something more elegant.
I'd like to join every 3 lines of a file with a tab character as separator. How can this be done using Vim?
I'm aware of the macro mechanism, but I?m looking for something more elegant.
I would go with two :help :normal commands…
Append a tab to every line:
:%normal A^i
with the literal ^i being obtained with <C-v><Tab>.
Join every group of three lines:
:%normal 3J
It turns out that this works:
:g/\n/,+1s//\t
The :global will match every line in the buffer (or in the range, if you pass it a range.)
The /\n/ is being used as a regex that will match every line, in this case, on the line break itself. We could have used something like /^/ (or perhaps /./ or /\S/ to match non-empty or non-blank lines), here we're using /\n/ since we want to use that pattern in the following :s, so we can omit it there to use the same pattern.
Then, for each line processed by :g, we use a range of that line up to line +1. That means two lines, in this case, current line and next one. Since we want to join three lines, we want to replace the line break on two lines, so from current line up to line +1. (You could generalize that to using + the number of lines in the blocks you want to join, minus two.)
Finally, we perform the substitution s//\t, which is equivalent to s/\n/\t/ (using an empty pattern will match the previously used on, in this case the one passed to :g.) This :substitute will replace the matched line breaks with a tab character, effectively joining lines where it matches. Since we're using ranges of two lines, it will only do so two lines at a time, effectively replacing two line breaks, which will join three lines.
This works because the way :global works when there are edits on the affected lines. It first "marks" the lines that should be acted on, but then if the line is no longer there, it will skip it. So while it will first mark every line, when the :s joins every second and third line to the first in a block, the marks on them will no longer be there, so the end result is that :g will not try to process this line again and will move on to the next "marked" line, which will then become the start of the next block.
I would recommend using macro, doing the process manually once and bind it into one key, but if you want to use it regularly I would recommend you to add a mapped command in your .vimrc