How can I assign a vim syntax group to a 'Foo( ... )'? For arbitrary ... including contained parenthesis - vim

I am trying to give my debug macros a different color, not just the macro names, but at least the opening and closing parenthesis in the same color.
As a test I'm current using the following line:
,--- Uncolored paren!?
v
int foobar; Dout( (( ) ) ( ) ); f((char*)x);
^^^^^^^^^^^^^^^^^^
\__ all of this should be colored.
If I try the following syntax rule:
syn region Debug matchgroup=DebugDelim start=/Dout(/ end=/)/ contains=cParen
then this isn't working: the first closing parenthesis that should be part of cParen isn't part of any highlighting group anymore and remains therefore uncolored.
Is this a bug in vim or am I doing something wrong?
Note: cParen here is defined as 'usual' (in the vim's c.vim syntax file).
EDIT: After figuring out the answer to my previous question (namely that contained groups match inside the start and end pattern unless you use a matchgroup argument), I changed my question to what remains a question to me.

After more than a week of research and trying things; the main conclusion is that vim's syntax system needs a redesign. It is flawed.
Having said that, the best answer would be 'it can not be done', because the system is just too broken; but -- with lots and lots of efforts a sort of horrible work around can be forged (for THIS case -- not for what I really needed) that looks as follows:
" Highlight my debug macros.
" A group with things that are normally already excluded from cParen.
syn cluster cwNotInParen contains=#cParenGroup,cCppParen,cErrInBracket,cCppBracket,#cStringGroup,#Spell
" Create a new cParen syntax group that will have Debug highlighting.
syn region cwDebugParen transparent matchgroup=cwDebugParenDelim contains=ALLBUT,#cwNotInParen
\ start='(' end=")\#="
" Define cParen last, so it will overrule the previous one.
syn clear cParen
syn region cParen transparent contains=ALLBUT,#cwNotInParen,cwDebugParen
\ start='(' end=')'
" Redefine cCppParen to also exclude cwDebugParen
syn clear cCppParen
syn region cCppParen transparent contained contains=ALLBUT,#cParenGroup,cErrInBracket,cParen,cwDebugParen,cBracket,cString,#Spell
\ start='(' skip='\\$' excludenl end=')' end='$'
" Redefine cCppBracket to also exclude cwDebugParen.
syn clear cCppBracket
syn region cCppBracket transparent contained contains=ALLBUT,#cParenGroup,cErrInParen,cParen,cwDebugParen,cBracket,cString,#Spell
\ start='\[\|<::\#!' skip='\\$' excludenl end=']\|:>' end='$'
" Add a syntax group for "Debug( ... );".
syn region cwDebugMacros transparent matchgroup=cwDebugMacrosDelim contains=cwDebugParen
\ start="\v\W(Debug|DoutFatal|DoutEntering|Dout|ASSERT|assert)\(#="hs=s+1
\ start="\v^(Debug|DoutFatal|DoutEntering|Dout|ASSERT|assert)\(#="
\ end="\v\);?"
hi link cwDebugMacrosDelim Debug " Highlight 'Debug' and the close paren of that macro.
hi link cwDebugParenDelim Debug " Highlight the open paren of the Debug macro.
This works as follows:
A new syntax group is created called cwDebugParen. It is basically the same as cParen but will have highlighting for the parenthesis. Because the syntax definition is containing ALLBUT in many places, this new group will now be accepted everywhere. It is possible - but not very convenient - to just replace the whole syntax file with your own and then add the new cwDebugParen to all ALLBUT,... lists, but in this case it is easier to remove cParen and redefine it after the definition of cwDebugParen, effectively hiding that because the last rule that matches will be used. Unfortunately the definition of cParen depends on some conditionals so the whole would get pretty complex still; therefore I just picked the one cParen definition that is used based on my .vimrc.
Because cParen doesn't have a matchgroup I found that its start pattern is still also matched by cwDebugParen?! -- this seems a bug to me but at least can be avoided by adding cwDebugParen to the ALLBUT list of cParen.
cwDebugParen itself does have a matchgroup, which is necessary for the syntax highlighting of the parens of course. Note that although it ends on the closing paren ')', I use a zero-match (:help \#=) because the same paren is needed to end the enclosing cwDebugMacros region. Note that using end=')'me=s-1 would have the same effect here.
Finally a new region is added, cwDebugMacros, that starts at a pattern like Debug( and ends on );. The two start= arguments are necessary because they use a different highlight start (hs) since one starts at the beginning of the line while the other demands that there is a non-word character in front of Debug. Also the opening paren here is a zero-match because we need it to start the cwDebugParen region that we contain in this region (fortunately we can use a zero-match here; because me=s-1 isn't support for start!?). That then will gobble up all input up till the closing paren, so we don't have to be afraid that our end will match a closing paren that isn't the closing paren of our debug macro. Note that I used end="\v\);?" with a question mark after the semi-colon so that IF the semi-colon directly follows the closing paren then it is also colored the same color (I like that) while if for whatever reason there is white-space (including comments) in between then things aren't broken - only the semi-colon isn't colored anymore then.
Edit: Added a redefine for cCppParen and cCppBracket which use the construct ALLBUT=...,cParen,... and therefore now also need to exclude cwDebugParen.

Related

Limit vim syntax highlighting to diff

I am making changes to an existing C code. If I just want to check my changes, I can easily use vimdiff for that, with old & modified files.
What I want is to limit some syntax highlighting to just the diff part.
Particularly I need to highlight TABS, but only those TABS contained within DiffAdd & DiffChange sections/regions.
What I tried:
syntax region TESTRGN start="TESTRGN_START" end="TESTRGN_END"
highlight TESTRGN ctermbg=lightgreen guibg=lightgreen
syntax match LeadingTabsInRegion display contained containedin=TESTRGN /^\( *\t\+\)\+/
highlight LeadingTabsInRegion ctermbg=darkred guibg=darkred
Above snippet highlights the leading TABS within TESTRGN & tabs in remaining file remain un-highlighted.
However, if I change TESTRGN to DiffAdd in the syntax match line, it does not work as I expected.
My understanding is that DiffAdd is not a region defined using syntax region ... & hence containedin=DiffAdd does not work.
So is there any method/work-around for doing what I am trying to do?
First, what's so bad about having the highlighting everywhere? The diff highlighting stands out well and therefore provides visible scope; couldn't you just ignore the highlighting elsewhere?
In Vim, syntax groups are defined and then linked to colors and attributes defined in highlight groups. The diff stuff uses the second part of that mechanism, but not the syntax part. Therefore, you unfortunately cannot refer to the diff regions within :syntax commands.
There's only a very ugly workaround for this: You have to determine the line numbers that have diff highlighting (via repeatedly stepping through the changes with the ]c motion, and/or using synID() to check for diff highlighting). With those line numbers, you can then define :syntax match commands with the special \%l atom that only matches certain lines. Of course, any addition / deletion of lines would invalidate your definitions, which would need to be frequently re-synchronized (triggered by :autocmd). That's a lot of effort vs. just ignoring the highlighting elsewhere.
Though you need to reset whenever the line was changed, this is just a tip for one of line number based ideas, you can get a list of all diff highlighted lines with:
let dl = filter(range(1, line('$')),
\'index([hlID("DiffChange"), hlID("DiffText"), hlID("DiffAdd")],
\diff_hlID(v:val, 1)) != -1')
And using this, it might be possible to set your TESTRGN with:
exec "syntax match TESTRGN /\\(" . join(map(dl, '"\\%" . v:val . "l"'), "\\|") . "\\).*/"

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 custom syntax highlighting

I'm wanting to create custom syntax highlighting in vim for a task-list.
Task items begin with a hyphen. Two types of task items are relevant: (a) items without an '#done' tag. (b) items with an #done tag. (a) and (b) need to be highlighted differently.
I'm using taskpaper, which works fine, but the issue is, I'm trying to make this to work for task items that span multiple lines. For example:
- Regular item (works)
- Completed item #done (works)
- Multi-line item. This item continues on to
the line below. (doesn't work)
- Multi-line completed item. This item continues
on to the line below. (doesn't work). #done
The highlighting file at taskpaper works for the first two, but not for the second two. As a workaround hack, I tried this for the last case above:
syn region multLineDoneItem start="{" end="}" fold
HiLink multLineDoneItem NonText
But now, I'm forced to mark multi-line done items with braces like so:
- {Multi-line completed item. This item continues
on to the line below. (workaround works).}
I've already searched stackexchange and elsewhere. I would appreciate any help! :)
You could try using the \ze regex atom in the end part of your syntax region. This would allow you to match everything up to but not including the next task. I haven't looked at how you do matching but something like this might work.
syn region muiltLineItem start="^-" end="\(\s*\n)\+\ze^-" fold
syn region multiLineDoneItem start="^-" end="#done\s*\n\(\s*\n\)*\ze^-" fold
HiLink multiLineItem Normal
HiLink multiLineDoneItem NonText
I haven't tested this at all but I think it, or something like it, should work. If you wish to take indentation into account the \z regex atom will allow you to keep matching lines with the same indent.
UPDATE:
Try this:
syn match multilineItem "^-\_.\{-}\ze\(\n-\|\%$\)" fold
syn match multilineDoneItem "^-\(\%(\_^-\)\#!\_.\)\{-}#done\s*\n\ze" fold
command -nargs=+ HiLink highlight default link <args>
HiLink multilineItem Normal
HiLink multilineDoneItem NonText
delcommand HiLink
Oh, also this should work for all four cases and not just the multi-line items.

How to change word recognition in vim spell?

I like that vim 7.0 supports spell checking via :set spell, and I like that it by default only checks comments and text strings in my C code. But I wanted to find a way to change the behavior so that vim will know that when I write words containing underscores, I don't want that word spell checked.
The problem is that I often will refer to variable or function names in my comments, and so right now vim thinks that each piece of text that isn't a complete correct word is a spelling error. Eg.
/* The variable proj_abc_ptr is used in function do_func_stuff' */
Most of the time, the pieces seperated by underscores are complete words, but other times they are abbreviations that I would prefer not to add to a word list. Is there any global way to tell vim to include _'s as part of the word when spell checking?
Here are some more general spell-checking exception rules to put in .vim/after/syntax/{LANG}.vim files:
" Disable spell-checking of bizarre words:
" - Mixed alpha / numeric
" - Mixed case (starting upper) / All upper
" - Mixed case (starting lower)
" - Contains strange character
syn match spellingException "\<\w*\d[\d\w]*\>" transparent contained containedin=pythonComment,python.*String contains=#NoSpell
syn match spellingException "\<\(\u\l*\)\{2,}\>" transparent contained containedin=pythonComment,python.*String contains=#NoSpell
syn match spellingException "\<\(\l\+\u\+\)\+\l*\>" transparent contained containedin=pythonComment,python.*String contains=#NoSpell
syn match spellingException "\S*[/\\_`]\S*" transparent contained containedin=pythonComment,python.*String contains=#NoSpell
Change pythonComment,python.*String for your language.
transparent means that the match inherits its highlighting properties from the containing block (i.e. these rules do not change the way text is displayed).
contained prevents these matches from extending past the containing block (the last rule ends with \S* which would likely match past the end of a block)
containedin holds a list of existing syntax groups to add these new rules to.
contains=#NoSpell overrides any and all inherited groups, thus telling the spellchecker to skip the matched text.
You'll need to move it into its own group. Something like this:
hi link cCommentUnderscore cComment
syn match cCommentUnderscore display '\k\+_\w\+'
syn cluster cCommentGroup add=cCommentUnderscore
In some highlighters you may need contains=#NoSpell on the end of the match line, but in C, the default is #NoSpell, so it should be fine like that.

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