I have this file

Another comment

This is a comment

Anything after end or before program must be highlighted as a Comment. I've battled with vim for hours, what am I doing wrong?

syntax region Comment start=/\%^/ end=/\zeprogram/
syntax region Comment start=/end\zs/ end=/\%$/

syntax keyword PreProc end start program

let b:current_syntax = 'minor'

Before program is working, after end is not.


This won't work, since it would require that the Vim syntax engine would parse end twice, first as the keyword and second as the region start.

Even though \zs makes the match start only after end, the regex engine needs to go through the end part to be able to match it.

I can offer a few alternatives, some of which work better than the others... In order of worst to best:

Look-behind zero-width match (almost works)

The usual workaround for when \zs doesn't work because it needs to go twice through the text is to replace it with a look-behind match:

syntax region Comment start=/\(end\)\@<=/ end=/\%$/

In this case, this almost works. But it only really seems to work when there's a trailing whitespace after end. It seems Vim won't try to match at the end of line after a match that consumes all the way to the last character.

The look-behind successfully works around the issue with scanning the same word twice, because it actually starts the match later and then backtracks, effectively scanning the text twice...

Using matchgroup= for delimiters

Another option is to use matchgroup=PreProc to highlighting the "start" and "end" delimiters of the region with the PreProc group.

Since the opposite delimiters are zero-width matches (start or end of document), highlighting them also is not a problem.

syntax region Comment matchgroup=PreProc start=/\%^/ end=/program/
syntax region Comment matchgroup=PreProc start=/end/ end=/\%$/
syntax keyword PreProc start program

Note that end can't be a keyword anymore, otherwise we have the same problem again where that prevents it from matching the second Comment region start.

Hierarchy of syntax groups contained within each other

This is the solution I'd most likely recommend, since it gives you the most control, doesn't prevent you from using all keywords and works reliably.

The idea is to mark the groups with "contained", so they only match explicitly where allowed, and use "contains" to list the groups allowed to match inside that region.

We have Comment matching everything, then it contains a Normal section from program to end, and the PreProc keywords can match inside Normal.

syntax region Comment start=/\%^/ end=/\%$/ contains=Normal
syntax region Normal start=/program/ end=/end/ keepend contained contains=PreProc
syntax keyword PreProc end start program contained

We need to use keepend on Normal to be able to match the "end" part even though that also matches a keyword.

| improve this answer | |
  • 1
    Thank you. The last one is a bit bittersome because I have lots of other keywords, and I would need to "contains/contained" them all. syntax region Comment start=/^end/ end=/\%$/ contains=PreProc did the job. It's funny how syntax region Comment start=/\%^/ end=/\zeprogram/ works as expected. – Afonso Matos Apr 5 at 16:56
  • @AfonsoMatos Glad one of the solutions worked for you! I believe \zeprogram works because Vim is scanning the text forwards and looks for the next match where the last one ended, which in this case would be on the \ze. end\zs is different in that it needs to go backwards... – filbranden Apr 5 at 17:09

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.