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.
\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...
matchgroup= for delimiters
Another option is to use
matchgroup=PreProc to highlighting the "start" and "end" delimiters of the region with the
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
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
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.