2

Suppose I have this in a text buffer (vim --clean mydoc.txt):

One
Two
Three
Four

I highlight all lines using :call matchadd('ErrorMsg', '.*'), resulting in:

matchadd() highlight

Notice that the newline character on each line is also highlighted. Is there a pattern I can use to avoid highlighting the newlines? I would like a single call of matchadd() to highlight the same as in the picture above, but without highlighting the newlines.

3
  • 3
    This works: :call matchadd('ErrorMsg', '^.*$'). I haven't posted it as an answer as I have yet to work out the why of it. – Andrew Ho-Lee Mar 10 at 22:16
  • @Andrew I posted an answer that explains why '^.*' works (the $ isn't even needed) and offers a slightly better version of it, using '.\+' as a pattern. – filbranden May 13 at 3:54
  • 1
    Great explanation, thanks @filbranden! – Andrew Ho-Lee May 13 at 21:04
3

The reason why this happens is that the pattern '.*' matches an empty string (with zero width), so it ends up matching the ends of the lines and highlighting those as well.

The match engine (similar to the syntax engine) will highlight a match and start searching for the next match right where the last match ended. So, in your example, it will match One successfully, then it will try to match the end of the line after One, and since the '.*' pattern matches an empty string, it will match the end of the line and highlight it too. Then it will move on to the next line, with Two, and do the same there. Note that you're actually getting two separate matches in each line.

You have a few ways to solve this issue. As @AndrewHoLee mentioned in a comment, you can add an anchor of ^ to the pattern, so that it matches only at the beginning of the line. Such as:

:call matchadd('ErrorMsg', '^.*$')

Or, slightly simpler:

:call matchadd('ErrorMsg', '^.*')

Both of these work the same, and they prevent the second match because the ^ anchor will prevent a match from starting there. This approach has one small disadvantage though, in that empty lines will match it and get highlighted. (An empty match such as the end of the line is still possible, as long as it's also at the beginning of the line.)

Another solution is to ensure a zero-width match will not happen, which can be done by using a \+ multi item to match one or more character, rather than * for zero or more.

 :call matchadd('ErrorMsg', '.\+')

(The solution in the answer by @DCSlagel effectively does this, since the [^\$] group needs to match a character other than backslash or dollar-sign, so it ensures at least one character gets matched here. Of course, there's a drawback in that a line starting with one of those characters will skip it when highlighting the match.)

0

This seems to work, but I'm not confident that it will work in every case.. is flawed.

call matchadd("ErrorMsg", "[^\$].*")

It is saying 'select zero or more items while not end of line'. It says match everything that is not a '$'. So as soon as there is a '$' in the text the '$' will not be highlighted. It has nothing to do with the end-of-line symbol: $.

However, for some reason, this causes matchadd() to not highlight the non-visible location of the end-of-line symbol.!?

Currently @Andrew Ho-Lee's comment and @filbranden's answer are the best responses.

3
  • 3
    I don't think this is right, but why it works is mysterious. In particular the "[^\$]" part is doing nothing, as it means "one character which is not dollar sign" (the ` does nothing here and the $` does not mean end of line inside []. – Mass Apr 9 at 23:19
  • Yes, @Mass, you are right. I've updated this answer to include your input. – DC Slagel Apr 12 at 18:03
  • 1
    I posted an answer that explains why this fixes the issue, explores the shortcomings (lines starting with backslash or dollar) and offers a more general solution. – filbranden May 13 at 3:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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