Possible explanation of the problem
I think the reason why :g/;/j
doesn't work is because the :g
command operates with a 2-pass algorithm:
- during the first pass it marks the lines containing the pattern
;
- during the second pass it operates on the marked lines
During the second pass, :g
joins the line 1;
with line 2;
because 1;
was marked during the first pass.
However I suspect (not sure) that it doesn't join 1; 2;
with 3;
because the line 2;
doesn't exist anymore, its content has been merged with the line 1;
which has already been processed.
So :g
looks for the next line which was marked during first pass (3;
) and joins it with the following one (4;
). After that the problem repeats, it can't join 3; 4;
with 5;
because the line 4;
doesn't exist anymore.
Solution 1 (with vimscript)
Maybe you could call a function whenever a line containing ;
is found to check whether the previous line also contains a semicolon:
function! JoinLines()
if getline(line('.')-1) =~ ';'
.-1join
endif
endfunction
Then use the following global command:
:g/;/call JoinLines()
Or without a function:
:g/;/if getline(line('.')-1) =~ ';' | -j | endif
Solution 2 (without vimscript)
:g/;/.,/^[^;]*$/-1j
Whenever the global command :g
finds the pattern ;
it executes the command: .,/^[^;]*$/-1j
It can be broken down like this:
:g/pattern/a,bj
Where :
pattern = ;
a = . = number of current line
b = /^[^;]*$/-1 = number of next line without any semicolon minus one
b
can be broken down further like this:
/ = look for the number of the next line matching the following pattern
^ = a beginning of line
[^;] = then any character except a semicolon
* = the last character can be repeated 0 or more times
$ = an end of line
/ = end of pattern
-1 = removes one to the number you just got
j
is the abbreviated form of the Ex command :join
which like most other Ex commands can be preceded by a range.
Here it's preceded by the range: .,/^[^;]*$/-1
(a,b
)
A range follows the form a,b
where a
and b
are generally 2 line numbers, and allows you to operate on a group of lines whose number is between a
and b
, instead of just one.
So the j
command joins all the lines between the current one (a
) and the next one which doesn't contain any semicolon minus one (b
).
For more information, see:
:help :global
:help :join
:help :range
:g/;/j
doesn't work because it is done in two passes: first the buffer is scanned, then the command is applied to the matching lines.