Search code examples
vimvim-syntax-highlighting

How do I use the same pattern as begin and end in a vim syntax-region?


So, I'm trying to build a syntax for the Menhir parser-generator for OCaml.

In that language, there's three sections to a file, separated by %% (no, it's not pretty; unfortunately, it's inherited from the ancient ocamlyacc.)

I'm trying to create a separate syntax-region for each of these three, plus one for anything after an extraneous, third %%:

this should be in `menhirDeclarations`
%%
this should be in `menhirRules`
%%
this should be in `menhirOcamlFooter`
%%
this should be in `menhirSeparatorError`
%%
this should still be in the same `menhirSeparatorError`

I've been poring through the :h syn-define docs today, and I've gotten so far as defining a group that matches everything in the first declaration:

syn region menhirDeclarations start=/\%^/ end=/%%/
    \ contains=@menhirComments

… but I'm having a lot of trouble extending this to match the following sections properly. The naive approach isn't working for me, for instance:

" These break each document into the three sections of a Menhir parser definition:
syn region menhirSeparatorError start=/%%/ end=/%%/
    \ contained contains=@menhirComments
syn region menhirOcamlFooter start=/%%/ end=/%%/
    \ contained contains=@menhirCommentsnextgroup=menhirSeparatorError
syn region menhirRules start=/%%/ end=/%%/
    \ contained contains=@menhirComments nextgroup=menhirOcamlFooter
syn region menhirDeclarations start=/\%^/ end=/%%/
    \ contains=@menhirComments nextgroup=menhirRules

How can I get Vim to split the syntax-highlighing of a file into multiple sections, like this?


Solution

  • Your problem is that the @@ separators are included in both start and end patterns of the region, so the end match of one region obscures the potential start match of the next region. In other words, your code would work if sections were delimited by @@@@ instead of @@.

    As you do need to assert both sides of a section, you can stop the matching of the end region via :help :syn-pattern-offset. The me=s-1 (match end is one character before the start of the match) offset still asserts that a section ends with @@, but doesn't consume those two characters any longer. With that, the nextgroup can do its magic and start the next group right after the previous one ended:

    syn region menhirDeclarations start=/\%^./ end=/%%/me=s-1 nextgroup=menhirRules
    syn region menhirRules start=/%%/ end=/%%/me=s-1 contained nextgroup=menhirOcamlFooter
    syn region menhirOcamlFooter start=/%%/ end=/%%/me=s-1 contained nextgroup=menhirSeparatorError
    syn region menhirSeparatorError start=/%%/ end=/\%$/ contained
    

    Note that I somehow had to match at least one character at the start of the buffer; /\%^/ didn't work for me (in Vim version 8.1.536). In order to avoid implementing multiple recurring matches of the last menhirSeparatorError group (which could also be solved with the same approach), I simply let it end at the end of the buffer via /\%$/.