One issue with your current approach is that after joining the lines, what you have is a single long line, so the range on the second command doesn't make sense anymore. You could fix that fairly easily by dropping it from the second command.
I'd also match a final \s*\ze\S
in the substitute regex, both to remove all trailing/leading spaces and also to prevent it from adding an extra blank line at the end of the block (if it ends with a period.)
This 'formatexpr'
is also pretty incompatible with a non-zero 'textwidth'
. As Vim tries to reformat lines after the 'textwidth'
is reached, whenever this ends up splitting it into two or more lines, the cursor will end up at the start of the last line and you'll be most probably inserting text at the wrong location.
You can work around that by simply setting 'textwidth'
to 0 whenever you use this 'formatexpr'
or by actually making it ignore a 'textwidth'
setting and refuse to break lines while you're inserting text. You can do the latter by checking mode()
and seeing if you're in an Insert or Replace mode. (The docs at :help 'formatexpr'
detail this option.)
In any case above, this will only trigger when you use a gq
command, such as gqip
to reflow the current paragraph, at one sentence per line.
This is the final function I got that does the job:
function! OneSentencePerLine()
if mode() =~# '^[iR]'
return
endif
let start = v:lnum
let end = start + v:count - 1
execute start.','.end.'join'
s/[.!?]\zs\s*\ze\S/\r/g
endfunction
set formatexpr=OneSentencePerLine()
It's actually possible to have it reformat the text on-the-fly and have 'textwidth'
trigger it. (In that case, you might even want to use a very short 'textwidth'
to trigger it early enough to catch even short sequences.) But the tricky part of that is keeping track of the cursor position and ensuring the cursor will be at the same location (on the same word, same insertion position) after reformatting the text. That's far from trivial. Possibly not something for an answer here... (But we'll see, I've seen answers with long pieces of code before. Or someone might take inspiration and write a plug-in to implement such a smart format expression.)