Keyword in Vim syntax region pattern - vim

I am trying to more or less reproduce for Python the if0 syntax highlighting of C-like files in Vim where code surrounded by #if 0 and #endif are grayed out (highlighted as Comment).
Instead of preprocessor directives, I would like to gray out Python code blocks starting with if 0: or if False:.
Inspired by this question, I came up with the following code which almost works:
syn region pyIfFalse start=/^\(\s*\)if \%(0\+\|False\):\n\+\z(\1\s\+\)\S/ skip=/^\%(\z1\S\|^$\)/ end=/^\z1\#!.*/me=s-1
hi def link pyIfFalse Comment
This results in the following
if 0: # grayed out (there is a space at the beginning)
pass
if 0: # not grayed out
pass
If I replace all if by ef (in the example and in the pattern), both blocks will be grayed out, meaning that the problem must be with the keyword if.
Following this question, one can redefine if as a keyword contained but then it would not be highlighted in regular text so it does not seem like a satisfying solution.
What is quite surprising to me is that the highlighting works for the first block but not the second, with the only difference being a space before the if.

The answer lies in :help syn-priority.
When multiple Match or Region items start in the same position, the item defined last has priority.
A Keyword has priority over Match and Region items.
An item that starts in an earlier position has priority over items that start in later positions.
In the third line of the example, the Keyword had priority over the Region (2.) but in the first line, the Region had priority because it started earlier (3.).
A workaround, for lack of a better alternative, is to downgrade the highlighting of if to a Match defined before the Region (1.).
syn clear pythonConditional
syn keyword pythonConditional elif else
syn match pythonIf /\<if\>/
syn region pyIfFalse start=/^\(\s*\)if \%(0\+\|False\):\n\+\z(\1\s\+\)\S/ skip=/^\%(\z1\S\|^$\)/ end=/^\z1\#!.*/me=s-1
hi def link pythonConditional Conditional
hi def link pythonIf Conditional
hi def link pyIfFalse Comment

Related

Is this change to Vim's syntax coloring file for sed good or disrupting?

Summary
I'm not asking your opinion about the proposed change being good, bad, amazing, or what.
I'm just asking your help to answer this question: does the change I propose for the syntax coloring of the comments break the syntax coloring of something else (keywords, strings, syntax errors, ...)?
The question above is not opinion based, as my change either does break something or it doesn't. That's it.
Original question
I have created the issue #5876 on Vim's GitHub page to propose a change to vim/runtime/syntax/sed.vim, but it has not received much attention, so I'm considering creating a PR for the change.
In fact, I created an issue instead of a PR because I'm not totally confident the change is not disruptive, hence this question.
The issue is with line 20:
syn match sedComment "^\s*#.*$"
because of which only "full line" comments are colored as comments. Using trailing comments following a command (allowed by GNU sed, for instance), stimulates some red background coloring (since it's considered an error by the syntax coloring logic, I guess).
I think it would be reasonable to relax this definition of comments to permit GNU sed-style comments, for the simple reason that the rule is less restrictive.
In this respect, I have noticed that changing that line to
syn match sedComment "\s*#.*$"
i.e. just removing the anchor ^, seems to be enough. I have also tried testing it by putting some # in search and replace strings in a sed script, and it seems fine.
However I don't feel confident with Vim syntax coloring files, so I would like to be sure that regex, as I edited it, is not causing false positives.
To demonstrate why I'm not confident about it, take this single-line sed script
s/aaa/bbb/#ccc
here # is not colored as a comment, and ccc's background is red (like an error?), whereas, just adding a space, give the correct coloring:
s/aaa/bbb/ #ccc
Therefore I think that my edit works (or seems to work) because of precedence rules between the several syntax coloring directives (with respect to this specific example with s/aaa/bbb/#ccc, I think that # just after the closing delimiter of the s command has a meaning in the language, but I don't know; the GNU Sed man page doesn't say anything about it).
Edit
Another example suggested in the comments is the following, the syntax coloring of which is not broken by the proposed change
s/#if !defined(\([^)]*)/#ifndef \1/ # with or without this comment is fine
My research
I have eventually found some time to study :h E410 and now I have an answer.
From there I read that
1. Keyword
[...] It will only match with a complete word [...]
2. Match
This is a match with a single regexp pattern.
3. Region
This starts at a match of the "start" regexp pattern and ends with a match with the
"end" regexp pattern. Any other text can appear in between. A "skip" regexp
pattern can be used to avoid matching the "end" pattern.
Since the line I'm going to change uses the syntax 2.,
point 1. is not relevant,
point 3. is relevant, as in general there can be an overlap between the what syn match and syn region match.
(By the way, I verified that syn region uses a lazy regex, probably \_.\{-} looking at some examples afterwards, to match stuff between the start and end regexp patterns.)
Then from :h :syn-priority
An item that starts in an earlier position has priority over items that start in later positions.
The answer
Therefore, changing
syn match sedComment "^\s*#.*$"
to
syn match sedComment "\s*#.*$"
should not introduce any syntax error, the reason being that if a text matching the above regex is not actually a comment (as is the case for e.g. the command s/#/hash/), then it must be preceded by some non-comment syntax which will be matched by other syn match/syn region groups. Since this match starts before where the group sedComment starts to match, the former has precedence on the latter.
In conclusion, unless something is broken with sed.vim already, the proposed change will not result in a wrong syntax coloring.
Further observations
Actually, there is already something wrong in sed.vim, and my proposed edit does not solve it (I should edit some other line in sed.vim to fix it, but I'm a bit lazy now).
For instance, in the following line, which is illegal because a is not a valid flag, a does not get the Error coloring.
s/x/y/a
My proposed edit does not solve this bug.
For the same reason, since some other syntax rule is eating 1 character after the third delimiter in the substitution command, the GNU-sed-valid comment in the following command is incorrectly colored
s/x/y/#hello
# │└───┴─── colored as Error
# └─ not colored
The coloring would be wrong even for an old version of sed which does not allow trailing comments, as the Error coloring should include the # too.
My proposed edit does not solve this bug either.
One more observation
The change I propose causes single-letter commands to be highlighted as Error if they are followed by a comment
p # this is a comment
│ └─────────────────┴─── colored as Comment (ok)
└────── colored as Error (bad)
This again, is not a worsening, as the current sed.vim colors almost the whole line above as an Error.

How to have a Vim multiline syntax highlight clause?

I'm trying to write the syntax highlight module for a tiny text format that includes three different kinds of list elements (starting with -, o and x respectively), and I'd like to highlight entries based on their kind. For single lines it's easy, I just use a syn match myGroup /^\s\+- .\+/ and I'm done.
Problem is, I've been trying to do it so that the next lines without a list marker keep the same colour as the starting list item line, to no success. I've been trying to do it with syntax regions, but I can't seem to understand how they work.
To be more precise, this is the output I'm trying to reach:
If any change is needed in the file format so that it is easier/possible, I have liberty to change it.
Any clue of how can I get it?
You can try something along these lines
syntax region Minus_Region start=/^\s\+-/ end=/;/
hi Minus_Region guifg='Yellow'
syntax region O_Region start=/^\s\+o/ end=/;/
hi O_Region guifg='Green'
syntax region X_Region start=/^\s\+x/ end=/;/
hi X_Region guifg='Gray'
You define region by its start and its end (in this case ;), no matter how many lines are involved.
For more information, see help
:h syn-region
If you want to finish the regions without having a end marking character (in this case ;), you could do it using the match-end (me) option on the end argument of the regions, and having the regions end on the next region-start marker. Example:
syntax region Minus_Region start=/^\s\+- / end=/^\s\+[-ox] /me=s-1
syntax region O_Region start=/^\s\+o / end=/^\s\+[-ox] /me=s-1
syntax region X_Region start=/^\s\+x / end=/^\s\+[-ox] /me=s-1
The me=s-1 part means "The real match ends at one character to the left of the start position of the pattern match".

Custom Vim syntax highlighting only works for some keywords

I'm writing a custom syntax highlighting file for a proprietary language I have to use a lot. I have written a full file but it seems to only use 3 colors. All punctuation is one color, some of the keywords I have specified are another color, and everything else is a third color.
The odd thing is, I removed every match and every redefinition (hi def link) from the file and the highlighting doesn't seem to have changed at all. In fact, I tried adding some other keyword mappings to try to see where it's breaking, but it seems only certain lines are being evaluated for the highlighting. For instance, this line ends up highlighting the proper text:
syn keyword clangImport IMPORT
However, this line (below) doesn't and is a different color despite not even providing any specific highlighting instructions:
syn keyword clangGroupAttributes ?? GN GA GV GL GP GR PV PN SI CN
Why would it only evaluate certain lines of the syn keyword mapping?
Additionally, none of my regex matching worked at all despite using something as simple as "\d\+".

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.

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