Consider the text:


First we highlight the regular expression /foobar/:

:syntax match Special /foobar/

enter image description here

Next we highlight the regular expression /foobarbaz/:

:syntax match Error /foobarbaz/

This match begins in the same position as our Special group, but it was defined later, and so it takes precedence. (See `:help :syn-priority.)

enter image description here

Now let's clear that (:syntax clear Error), and instead try starting our highlighting at bar by using \zs to set the start of the match.

:syntax match Error /foo\zsbarbaz/

enter image description here

Okay, that didn't work, but perhaps it's because by altering the start of the match, this match now begins inside our earlier group. If that's the case, we can fix it with the containedin argument:

:syntax match Error /foo\zsbarbaz/ containedin=Special

enter image description here

That seems to have worked!

Now let's try starting our highlighting from the baz:

:syntax match Error /foobar\zsbaz/ containedin=Special

enter image description here

Huh. Now that we've moved the start of our Error match past the end of the Special match, it no longer works. (Note that I'm not using the contained argument, so even if the Error group starts outside of the Special group, it should still match: removing the containedin argument also has no effect.)

The sole mention of \zs in syntax.txt doesn't appear to be relevant (it regards multi-line patterns), and :help \zs also doesn't shed any light on this.

What's going on?

N.B. For this particular example, I could use an hs syntax pattern offset to achieve the desired highlighting, and with more complicated regular expressions I could use \@<= look-behind. But \zs is recommended over \@<= for performance reasons, and in any case, I'd still like to understand how the feature works.

  • 1
    Were you ever able to find anything else out about this? This has me very intrigued. – ZeroKnight Mar 25 '18 at 11:07
  • Maybe the behaviour is implied by "The next char is the first char of the whole match." in pattern.txt, i.e. they don't actually overlap. But that would mean that your last example should work if you leave the containedin out! I think it is a bug (documentation, if not otherwise). – Sakari Cajanus Nov 13 '18 at 9:36
  • 2
    @SakariCajanus 'containedin' allows an item to start inside another group; it does not require it. If there is no overlap, then the last example should work regardless of whether or not 'containedin' is set. – Rich Nov 13 '18 at 11:07
  • Also you need to state the version of the program you are using since this functionality has evolved substantially over the last 25 years. If you want the authoritative source to: "What exactly is going on here" you'll have to get into the C source and start hacking. It's all right there, nothing is hidden: github.com/vim/vim – Eric Leschinski Jul 14 at 22:45

Your Answer

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

Browse other questions tagged or ask your own question.