vim syntax: how to use contained fields - vim

Following up on this question
The syntax of my logs has changed. I now have to include a username before the time, so the new format is:
[username] time LEVEL filepath:line - message
I have no problem defining the various regions:
syn region logUser start=+^\[+ end=+\] +me=e-1 contained
syn match logTime "\d\{4}-\d\{2}-\d\{2} \d\{2}:\d\{2}:\d\{2},\d\{3}" contained
syn keyword logCritical CRITICAL contained
syn keyword logError ERROR contained
syn keyword logWarn WARN contained
syn keyword logInfo INFO contained
syn keyword logDebug DEBUG contained
syn match logFileAndNumber " \S\+:\d\+ - " contains=logFile,logLineNumber
syn match logFile " \S\+:" contained
syn match logLineNumber "\d\+" contained
The problem is I can't figure out how to create a region at the beginning, so that I don't get highlighting any time I see one of the log levels, or if a timestamp appears in the message or something. The answer to the linked question works for the previous format, but doesn't have much in the way of explanation so I don't really understand what it's doing.
EDIT: Is there no way to simply define the smaller regions and then define the larger region as a sequence of the smaller ones? I know this wouldn't work, but something along the lines of:
syn match logBeginning "$logUser $logTime [$logCritical|$logError|$logWarn|$logInfo|$logDebug]"

If the sequence (user, time, severity, ...) is fixed, you can use nextgroup=... attribute to chain them together. So, make the first (logUser) an uncontained one (so that it matches anywhere), and instruct Vim to check for logTime after it (only followed by whitespace). All other groups are contained to avoid matches elsewhere.
syn region logUser start=+^\[+ end=+\] +me=e-1 nextgroup=logTime skipwhite
syn match logTime "\d\{4}-\d\{2}-\d\{2} \d\{2}:\d\{2}:\d\{2},\d\{3}" contained nextgroup=logCritical,logError,logWarn,logInfo,logDebug skipwhite
...
Cp. :help :syn-nextgroup.

Related

Why taskinfo syntax file does not work as expect?

I have following syntax file:
syn match TaskName /^\[.*\]/
syn match TaskType /^\[.*\]\s*\zs[a-z]*/
syn match TaskDescription /^\[.*\]\s*[a-z]*\s\+\zs.*/
hi def link TaskName Title
hi def link TaskType Todo
hi def link TaskDescription Comment
and the context is:
Task Type Command
[file-run] local no description
why only [file-run] is matched?
If I type /^\[.*\]\s*\zs[a-z]* in normal mode, local will be matched.
The reason it is not matching is that :syn match does not try and
evaluate text it has already matched. So, it cannot match against the text already matched by :syn match TaskName.
Additionally, there's a lot that could be improved upon your patterns, notably:
You have a lot of pattern atoms that will match the empty string.
This makes pattern matching slow, as patterns like [a-z]* will match
everywhere (this pattern in particular is even pointed out as an example of
a pattern to avoid in the help documentation for :syn-pattern). In most
cases it is better to match 1 or more matches with \+ than 0 or
more matches with *.
You can make all of your patterns more brief and more clear using other
arguments of :syn match.
I would suggest leveraging the power of the nextgroup argument in combination
with the skipwhite argument of :syn match:
nextgroup allows you to tell
Vim to try and match the groups specified after this match.
The skipwhite argument allows you to skip over tabs and spaces when
trying to match the next group with nextgroup.
Keeping these in mind, you could rewrite your patterns to look like:
syn match TaskName /^\[.\+\]/ nextgroup=TaskType skipwhite
syn match TaskType /[a-z]\+/ nextgroup=TaskDescription skipwhite
syn match TaskDescription /\w\+\(\s\+\w\+\)*/
In order to do this, I've also edited your TaskDescription match to be “a word,
followed by 0 or more words separated by whitespace".
You can see that utilizing nextgroup and skipwhite makes each syntax match
more brief, in addition to making the contents of each group more clear.
Relevant :help queries:
:h :syn-match
:h :syn-pattern
:h :syn-nextgroup
:h :syn-skipwhite

Vim syntax: Spell checking some strings, but not others

This is a followup to Vim syntax: Spell checking between certain regions I'm trying to create a syntax file for this language called Sugar Cube 2. You can find more about it here: http://www.motoslave.net/sugarcube/2/docs/macros.html
The link has this syntax: <<link "linkText" "passageName">> e.g.:
<<link "Onward, Reginald!" "ThePassageName">>
I would like to spell check that "Onward, Reginald!" but not "ThePassageName". How do I do that? I tried messing around with lines like this, but I think I'm going in the wrong direction:
syn region noSpellString start=+"+ end=+"+ skip=+\\"+ contains=#NoSpell
syn region spellString start=+"+ end=+"+ skip=+\\"+ nextgroup=noSpellString
syn match linkMacro "<<link\s+" nextgroup=spellString skipwhite skipempty
Your approach looks good to me. :syn region is the right choice if the strings can span multiple lines. For single-line strings, :syn match (with a suitable pattern that skips contained escaped quotes) would be better.
Your example almost works:
The linkMacro pattern needs to use \s\+ to match.
the spellString is missing the skipwhite
syn region noSpellString start=+"+ end=+"+ skip=+\\"+ contains=#NoSpell
syn region spellString start=+"+ end=+"+ skip=+\\"+ nextgroup=noSpellString skipwhite
syn match linkMacro "<<link\s\+" nextgroup=spellString skipwhite skipempty
For syntax scripts, all syntax groups should start with a prefix that is equal to the syntax name. So, syntax/sugarcube2.vim should have syntax names like sugarcube2LinkMacro, sugarcube2SpellString, etc.

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.

In a VIM syntax file how can I use a start match in the end match?

In VIM syntax files one can write a syntax region match like this:
syn region xqString start=+'+ end=+'+
syn region xqString start=+"+ end=+"+
What I want to write instead is
syn region xqString start=+(['"])+ end=+\1+
where \1 is the match found in start. Any answers on how to do this or if it's not possible?
See :help :syn-ext-match
External matches :syn-ext-match
These extra regular expression items are available in region patterns:
*/\z(* */\z(\)* *E50* *E52*
\z(\) Marks the sub-expression as "external", meaning that it is can
be accessed from another pattern match. Currently only usable
in defining a syntax region start pattern.
*/\z1* */\z2* */\z3* */\z4* */\z5*
\z1 ... \z9 */\z6* */\z7* */\z8* */\z9* *E66* *E67*
Matches the same string that was matched by the corresponding
sub-expression in a previous start pattern match.
So you could do syn region xqString start=+\z(['"]\)+ skip=+\\.+ end=+\z1+

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

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?

Resources