I'm trying to write a substitution command that would search for a line that doesn't end with a space character and others (see regex) and isn't followed by an empty line. The line length may vary between 3 and 60 characters. I've ended up with the following command:


User should be able to confirm a replacement.

Consider the text below:

To the Reader
The book you have in your hands

After all the confirmations it should be transformed to:


To the Reader

The book you have in your hands

But with the above command it transforms to:


To the Reader
The book you have in your hands

So it processes only the first match.

1 Answer 1


The main issue you're having here is that your match includes the beginning of the next line. After the replacement, Vim will keep looking for the next match, but it will start right after the last match ended, so it will miss the second line, since it won't match a regex that is anchored at the start of a line.

In the specific example you gave, the first match will take Preface\nT and then, once that's dealt with, Vim will start looking for the next one at o the Reader, but that's not the start of a line, so not a match.

To solve this issue, you need some kind of zero-width match, such as \@= or \@!. (Note that under \v "verymagic" these are simply written @= and @!, since all non-alphanumeric symbols are by default special unless quoted!)

But a simpler solution is to use the \ze end-of-match marker, which can play a similar role and requires a lot less grouping. (In fact, you can do this whole regex with zero groups!)

So that's the main issue... But there are (quite a few) other issues with your regex:

  • [^\_s] actually matches any character other than \, _ or the letter s, which is definitely not what you want! I guess you wanted to refer to \_s for a blank including end-of-line (note: under "verymagic" that's actually written _s, since symbols are special by default.) But end-of-line normally needs to be matched explicitly, so you're fine with \S for a non-blank here.
  • The {3,60} is repeating the group, but I doubt you want up to 60 non-blank characters. Instead, I believe you want a non-blank, followed by any characters, ending with a specific character. So use a . for the middle part. If the total length of the line is to be between 3 and 60 characters, and you have special matches for the first and last, then you want between 1 and 58 in the middle.
  • Similarly the last part [^\s\.:;\?!], the \s doesn't work there (it's matching a literal s or literal \) and you don't need backslashes before . or ? either. For the blanks, use an explicit space and you can use \t for a tab. (Those are the only two you really care about.)

Putting it all together, you get this:

%s/\v^\S.{1,58}[^ \t.:;?!]\ze\n\S/\0\r/gc

After the newline, it's matching a non-blank \S directly. Since the match will include the whole first line (and nothing of the second), and since we're not grouping anything anymore, you can now use \0 on the replacement side, to include the whole match.

Testing on your example, I got what you described, so I believe this is what you wanted, or at least pretty close to it.

  • 1
    Great, it works! Thanks! But I need some time to understand the new approach. Never used \S , ze etc.
    – ka3ak
    Aug 3, 2019 at 7:54
  • 👍 BTW I find that \ze and its counterpart \zs are by far one of the most useful (and perhaps underrated) feature of Vim regex. Learn them and use them every day 😁. \S is fairly straightforward, the uppercase character classes are just the negation of the corresponding lowercase ones. They're in Perl and PCRE as well, so might be familiar from there too. One thing to watch is the ones starting with \_, \@ and \% lose the `` under "verymagic", that's not obvious at first. Good luck!
    – filbranden
    Aug 3, 2019 at 11:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.