1

I am trying to add some custom highlighting to my Vim. But my regex matching is not working correctly.

let ignoreHiInComments = '^\s*\(\/\/.*\)\@<!'    "Ignore highlighting in C++ comments
call matchadd('UserTypes', ignoreHiInComments.'\zs\w\+\ze::.\+')

This will do the following highlighting:

myclass::myclass() {}     // Correctly highlights "myclass" and not "::myclass()"  
myclass::~myclass() {}    // Correctly highlights "myclass" and not "::~myclass()"
bool myclass::myfunc() {}  // Does not highlight "myclass"

If I change out \w with . like this:

call matchadd('UserTypes', ignoreHiInComments.'\zs.\+\ze::.\+')

Then I get this highlighting:

myclass::myclass() {}      // Correctly highlights "myclass" and not "::myclass()"
myclass::~myclass() {}     // Correctly highlights "myclass" and not "::~myclass()"
bool myclass::myfunc() {}  // Highlights all of "bool myclass", this is incorrect

If I try to match whole words like this:

call matchadd('UserTypes', ignoreHiInComments.'\<\zs.\+\ze::.\+\>')

Then I get this result:

myclass::myclass() {}     // Correctly highlights "myclass" and not "::myclass()"  
myclass::~myclass() {}    // Correctly highlights "myclass" and not "::~myclass()"
bool myclass::myfunc() {}  // Does not highlight "myclass"

I have also tried adding various white space matching elements to my regex, but it hasn't worked.

Can anyone help me understand what I am doing wrong?

2

From pattern.txt:

                            */\@<!*
\@<!    Matches with zero width if the preceding atom does NOT match just
before what follows.  Thus this matches if there is no position in the
current or previous line where the atom matches such that it ends just
before what follows.

Note the word atom.

The definition of atom:

                        */atom*
An atom can be one of a long list of items.  Many atoms match one character
in the text.  It is often an ordinary character or a character class.
Braces can be used to make a pattern into an atom.  The "\z(\)" construct
is only for syntax highlighting.

   atom    ::=      ordinary-atom
        or  \( pattern \)
        or  \%( pattern \)
        or  \z( pattern \)

Thus, \@<! only affect the atom \(\/\/.*\) in ignoreHiInComments and the ^\s* part must be matched, which is not what you want.

To fix it, you can either let the atom include more (\(^\s*\/\/.*\)\@<!), or simply delete ^\s* (\(\/\/.*\)\@<!).

| improve this answer | |
  • Dropped ^\s* and works great now, thanks – Fred Mar 13 at 19:15
1

The problem is with the ^\s* in ignoreHiInComments, that's outside of the negative look-behind match, so it will always only match from the first word of the line since it needs to match ^\s* first.

Did you mean to include that inside the negative look-behind match?

let ignoreHiInComments = '\(^\s*\/\/.*\)\@<!' "Ignore highlighting in C++ comments 

This will work, but only when the comment is the only thing in the line. So it should fix a case such as:

// bool myclass::myfunc won't be highlighted.

But won't work for:

myclass::myclass() {} // myclass::myclass constructor

In which case both will be highlighted.

Another option is to just drop the ^\s* altogether, though this might potentially give you trouble if // happens inside a string, which would mean it's not really a comment.

In any case, hopefully these pointers are enough to help you figure out which match is right for your particular use case.

| improve this answer | |

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.