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

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+

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.

vim syntax: how to use contained fields

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.

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 regex & highlight syntax: find a match and ignore sub-match in it

I am trying to write a syntax highlighter in VIM. How do you highlight a match within another match?
To find each match, I created two syn match lines, which work where the matches are separate.
syn match celString "^xpath=.\{-};" -> matches "xpath=.........;"
syn match celComment "\${.\{-}}" -> matches "${LIB_METADATA};"
The first line is pink for the xpath string and blue for the ${..} string.
The second line is pink for the xpath string, but the ${..} contained inside that string is ignored.
I've tried to change the order of the syn match lines, but that doesn't have any effect.
I'd appreciate your ideas.
By default, Vim only applies the syntax groups to text that hasn't yet been assigned a syntax. To specify that one group can contain other groups, use the contains=... attribute:
:syn match celString "^xpath=.\{-};" contains=celComment
The order of definition shouldn't matter here. See :help :syn-contains for more information.

Resources