It's actually quite subtle. Here is a step-by-step derivation.
First off, the pattern \n$
matches the ends of paragraphs. It's end-of-line, followed by an end-of-line- the only way this can happen is if the end of a line precedes an empty line. This is roughly equivalent to \n\ze\n
, except that doesn't work at the end of the buffer.
Recognize that ,-/{re}/
denotes a range, starting from the current result of :g/./
(because of the empty string before the comma). Together ,-/\n$/j
denotes the ranged command :join
. This is not the normal command J
. (!)
g/./{cmd}
collects all lines containing any characters (i.e., not empty) and runs the ranged command on them. The real clever bit is that you don't have to worry about going over lines multiple times because we're joining them. Try
:g/./,-/\n$/p
You will see that at first this prints the entire paragraph which we want to join, but then it starts printing successive sub-paragraphs. Since we're joining this doesn't matter. g/
works over its matches one-by-one.
There is one remaining detail- what is the -
for? If you try
:g/./,/\n$/p
this almost works, but it sometimes adds white-space after paragraphs. This is because the /
inside a range is defined as the next line that includes the pattern. The -
moves the end range up by one before searching so the potential empty line is not joined in. This would also be equivalent:
:g/./,-1/\n$/p