Vim syntax files ... trying to understand "contains" - vim

I'm trying to patch up a new vim syntax file for some custom format I'm using. Most of it I can understand, but the keyword "contains" is giving me trouble.
Is there anyone here who could give me an explanation of what it does (I've read the help -> didn't quite get it) in a way as if he were explaining it to a tree.

In general, you can only have one syntax highlighting method in one place. Therefore, to use C-like syntaxes as an example, if you define a region to start on an opening brace '{' and end on a closing brace '}', the syntax highlighting for that region will be the same.
contains= allows you to configure other syntax highlighting groups to be contained within an outer group. To follow the previous example, you may want int to be highlighted even when it is in the outer region. You could then have something like:
syn keyword Keyword int
syn region BraceBlock start='{' end='}' contains=Keyword
It is quite common to need to add items later to the list of contained keywords. There are a few ways of doing this. Firstly, you can use contains=ALL or contains=ALLBUT,Error to allow anything to be in a region. Secondly, you can use containedin to push something into the contains of another region:
syn region BraceBlock start='{' end='}'
syn keyword Keyword int containedin=BraceBlock
Thirdly, you can define anything that is "contained" as valid in this group:
syn region BraceBlock start='{' end='}' contains=CONTAINED
syn keyword Keyword int contained
Finally, you can use clusters, which make it quite easy to decide what goes where:
syn region BraceBlock start='{' end='}' contains=#MyCluster
syn keyword Keyword int
syn cluster MyCluster contains=Keyword
syn keyword Conditional if else
syn cluster MyCluster add=Conditional
" Now conditionals and keywords can appear in a BraceBlock
Without knowing exactly what you want to understand, I'm not sure what else to say - what are you trying to achieve and what is causing you problems?

Related

How do I start a Vim syntax-region at the very start of the document, while also allowing a keyword-match in that same position?

Thanks to another question, I've managed to assign a syntax-region to my document that starts at the very start (\%^) of the document:
syn region menhirDeclarations start=/\%^./rs=e-1 end=/%%/me=s-1
To get that to work, the start pattern had to match the very first character of the document. That is, the above won't work with just start=/\%^/; it needs that last . (The match, when successful, then excludes that character; but it has to actually match before that happens …)
Problem is, any :syn-keyword match at the same location — even one lower in :syn-priority — is going to preempt my above region-match. Basically, this means I can't have any keyword that's allowed to match at the start of the document, or that keyword, when so placed, will prevent the above "whole-top-of-the-document" region from matching at all.
Concrete example. With the following syntax:
syn keyword menhirDeclarationKeyword %parameter %token " ...
syn region menhirDeclarations start=/\%^./rs=e-1 end=/%%/me=s-1
… the document …
%token <blah> blah
blah blah blah
… will not contain the requisite menhirDeclarations region, because menhirDeclarationKeyword matched at the very first character, consuming it, and preventing menhirDeclarations from matching.
I can bypass this by declaring everything in the syntax definition as a :syn-match or :syn-region, and defining the above region very last … but that's probably a performance problem, and more importantly, really difficult to manage.
tl;dr: Is there any way to match a region at the very start of the document, and allow keywords to match at the same location?
To keep the keywords, you have to make them contained. Else, Vim's syntax rules will always give them precedence, and they won't allow your region(s) to match. If I remember correctly from your last question, the whole document is parsed as a sequence of different regions; that would be great. Else, you have to create new regions or matches for those parts of the document that are not yet covered, but may also contain keywords.
syn keyword menhirDeclarationKeyword contained %parameter %token
syn region menhirDeclarations start=/\%^%token\>/rs=e-1 end=/%%/me=s-1 contains=menhirDeclarationKeyword
If that isn't feasible, you indeed have to use :syntax match instead:
syn match menhirDeclarationKeyword "\<%token\>"
Don't assume that this will be slower; measure it via :help :syntime, on various complex input files.
The "difficult to manage" part can be addressed via Vimscript metaprogramming. For example, you can keep all keywords in a list and build the definitions dynamically with a loop:
for s:keyword in ['parameter', 'token']
execute printf('syntax match menhirDeclarationKeyword "\<%%%s\>"', s:keyword)
endfor

Correctly match begin and end curly braces in syntax highlighting

I am customizing tex.vim syntax file to make LaTeX macro \PVerb to be hightlighted the same way as be \verb. I mimicked the line for \verb in the syntax file, adding the following line:
syn region texZone start="\\PVerb{" end="}\|%stopzone\>"
That said, I want to hightlight text region from \PVerb{ to its matching }.
But, if there are {}'s inside \PVerb{...}, the enclosing } will be matched too early, e.g.,
\PVerb{{text1}...{text2}}
The ...{text2} part won't get hightlighted because of the } before it.
So how to bypass the } in these cases, which is not the correct matching brace?
I have tried suggestion presented in this question, but couldn't get it work the highlighted region extended beyond the matching }.
This is what I have tried (added innerBrace as suggested by #Ingo Karkat):
syn region innerBrace start=+{+ end=+}+ transparent contains=texZone,innerBrace
syn region texZone start="\\PVerb{" end="}\|%stopzone\>" contains=innerBrace
And this is what I got:
You can see the normal text on line 3 is highlighted verbatim as well.
If I delete contains=innerBrace in syn region texZone, the following is what I got:
The problem is that innerBrace already matches the { that concludes the start of the \PVerb{ region start. I was able to correct this via a negative look-behind on the innerBrace region start, disallowing a match when there's \PVerb immediately before it. This restricts that syntax group to true inner braces, and the spanning and nesting works.
syn region innerBrace start=+\%(\\PVerb\)\#<!{+ end=+}+ transparent contains=innerBrace
syn region texZone start="\\PVerb{" end="}\|%stopzone\>" contains=innerBrace
Also, innerBrace need not contain texZone again (as it itself is already only contained in such).
Interestingly, for me the { of \PVerb{ already matches the innerBrace. I am not sure, why this happens, so I modified the start pattern of the innerBrace not to match at the \PVerb{ part:
For me, this works:
syn region MyTexZone start="\\PVerb{" end="}" containedin=TOP
syn region innerBrace start="\(\\PVerb\)\#<!{" end="}" transparent containedin=MyTexZone
Note, you shouldn't use a Syntax group that is already defined in the tex.vim syntax file. That makes it harder to debug what is happening.

Vim syntax match

I am implementing a syntax highlight for a proprietary C expansion that we use in VIM.
The syntax is this:
int __event(foobar) my_foobar_function()
{
//whatever
if(foobar)
// do something
}
Now what I would like to do is to highlight __event(foobar), so I wrote this:
syn region xREG start=/__event(/ end=/)/ contains=xFOO
syn keyword xFOO foobar contained
hi xREG ctermfg=darkblue
hi xFOO ctermfg=cyan
This highlights the __event() correctly, however, the foobar in if(foobar) also gets highlighted.
My question is how can I restrict the xFOO group to be highlighted ONLY in xREG and nowhere else.
Thank you.
When you extend an existing syntax (like C), you need to consider the existing syntax items. The following line from syntax/c.vim causes the inclusion of your xFOO group via the ALLBUT=:
syn region cParen transparent start='(' end=')' end='}'me=s-1 contains=ALLBUT,cBlock,#cParenGroup,cCppParen,cErrInBracket,cCppBracket,cCppString,#Spell
Fortunately, it provides an extension point: You have to add your group to the #cParenGroup cluster:
syn cluster cParenGroup add=xFoo
That should do the trick!

LaTeX section highlighting in vim

In LaTeX, a section looks like:
\section{Section Title}
I would like to highlight such sections, or section titles. I tried to put the following in ~/.vim/bundle/latexrc/after/syntax/tex.vim:
syn match texSectioning "\\section\>" skipwhite nextgroup=texSectioningTitle
syn region texSectioningTitle contained matchgroup=Delimiter start='{' end='}' contains=#texSectioningGroup
syn cluster texSectioningGroup contains=texMatcher,texComment,texDelimiter
(Note that this kind of syntax is not handled by the default tex.vim syntax file. It only defines "section zones", which are pretty much worthless for me.)
I then define the following in my color scheme:
hi texSectioning gui=bold guifg=red
And nothing happens; that is, section titles do not appear in red in my LaTeX code (even after I reloaded the file completely).
I am totally confused as to how vim's syntax work, and how to debug it.
Edit
Some more information: it sometimes works and sometimes not. Completely unpredictable. What could be the problem? Pathogen? Something else? I'm completely puzzled.
You have defined the new syntax items texSectioning, texSectioningTitle and texSectioningGroup, but you have not linked them to a highlighting group, so Vim doesn't know how to display them. Try adding these lines:
hi def link texSectioning Statement
hi def link texSectioningTitle String
hi def link texSectioningGroup Comment
The Statement, String and Comment colourings are defining by the colourscheme you are using. These are just examples: you can replace them with any group defined in the colourscheme file.
Here is the answer: the tex.vim divides the text in zones, in which the syntax must be explicitly allowed. The key element is that command:
syn cluster texChapterGroup contains=#texSectioningGroup
This says to vim that inside a texChapterGroup, the syntax cluster texSectioningGroup is allowed. The next thing to do is simply to define that cluster as usual.
Another detail is that the region texSectioningTitle must be contained, otherwise it will match arbitrary pairs of {} in LaTeX.
So a complete solution goes like this:
syn match texSectioningCommand '\\section\>' skipwhite nextgroup=texSectioningTitle contains=#texSectioningGroup
syn region texSectioningTitle start='{' end='}' contained
syn cluster texSectioningGroup contains=texSectioningCommand
syn cluster texChapterGroup contains=#texSectioningGroup
Edit Here is why the behaviour was apparently unpredictable: vim does not read the entire file to figure out the syntax. So in a big enough chapter, my section syntax would work because vim did not go far enough to see it was in a chapter zone.
Just to update the information to highlight section easily. Using containedin means that all the other syntax matches contains this new syntax match. Then just define the color you want.
syn match texSectioningCommand '\\section\>' containedin=ALLBUT,texComment
hi texSectioningCommand guifg=#ec5f67 ctermfg=203
Alternatively, a simple new syntax match could be added to the texFoldGroup in order to be evaluated inside the the block document.
syn match texSectioningCommand '\\section\>'
syn cluster texFoldGroup add=texSectioningCommand
hi texSectioningCommand guifg=#ec5f67 ctermfg=203

Sub-match syntax highlighting in Vim

First, I'll show the specific problem I'm having, but I think the problem can be generalized.
I'm working with a language that has explicit parenthesis syntax (like Lisp), but has keywords that are only reserved against the left paren. Example:
(key key)
the former is a reserved word, but the latter is a reference to the variable named "key"
Unfortunately, I find highlighting the left paren annoying, so I end up using
syn keyword classification key
instead of
syn keyword classification (key
but the former triggers on the variable uses as well.
I'd take a hack to get around my problem, but I'd be more interested in a general method to highlight just a subset of a given match.
Using syn keyword alone for this situation doesn't work right because you want your highlighting to be more aware of the surrounding syntax. A combination of syn region, syn match, and syn keyword works well.
hi link lispFuncs Function
hi link lispFunc Identifier
hi link sExpr Statement
syn keyword lispFuncs key foo bar contained containedin=lispFunc
syn match lispFunc "(\#<=\w\+" contained containedin=sExpr contains=lispFuncs
syn region sExpr matchgroup=Special start="(" end=")" contains=sExpr,lispFuncs
The above will only highlight key, foo, and bar using the Function highlight group, only if they're also matched by lispFunc.
If there are any words other than key, foo, and bar which come after a (, they will be highlighted using the Identifier highlight group. This allows you to distinguish between standard function names and user-created ones.
The ( and ) will be highlighted using the Special highlight group, and anything inside the () past the first word will be highlighted using the Statement highlight group.
There does appear to be some capability for layered highlighting, as seen here: Highlighting matches in Vim over an inverted pattern
which gives ex commands
:match myBaseHighlight /foo/
:2match myGroup /./
I haven't been able to get anything like that to work in my syntax files, though. I tried something like:
syn match Keyword "(key"
syn match Normal "("
The highlighting goes to Normal or Keyword over the whole bit depending on what gets picked up first (altered by arrangement in the file)
Vim soundly rejected using "2match" as a keyword after "syn".

Resources