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/;/jdoesn't work because it is done in two passes: first the buffer is scanned, then the command is applied to the matching lines. – romainl Dec 31 '15 at 06:59