4

Lets say I have a line like the following in a vim syntax file:

syntax match Emphasis "\v\s+_\zs[^_\n]+\ze_\s+"

In this line I'd like to create multiple capture groups:

  1. the first underscore as "EmphasisStartMarker"
  2. the content between the two underscores as "Emphasis"
  3. the second underscore as "EmphasisEndMarker"

I know that \zs starts matching and \ze ends it. So I tried:

syntax match EmphasisStartMarker "\v\s+\zs_\ze[^_\n]+_\s+"

And

syntax match EmphasisEndMarker "\v\s+_[^_\n]+\zs_\ze\s+"

However, this does not work, probably because somehow the start marker already consumes the content and the end marker text.

I could not find a description for matching capture groups and how to write that in a syntax file. I'd like to have something like:

syntax match "\v\s+(_)([^_\n]+)(_)\s+"
1=EmphasisStartMarker
2=Emphasis
3=EmphasisEndMarker

I know there is something with region and start and end, but that won't be useful if I have more than 3 capturing groups.

What is VIM's syntax for this? Is it possible? If not, how to workaround?

4

Yes, \zs and \ze cannot be used for this because the actual match still is the full expression, as these special atoms just "cut off" parts of the result, but don't change the actual regular expression matching.

You could use lookbehind / lookahead (\@<= / \@=) instead, but this usually is solved via syntax containment (contains=), or priorization (nextgroup=).


Your example is pretty simple. Assuming you don't actually want different highlighting for the identical start and end markers, you can match the entire emphasis with your regexp and then contain the start and end markers:

syntax match Emphasis "\v\s+\zs_[^_\n]+_\ze\s+" contains=EmphasisMarker
syntax match EmphasisMarker "_" contained

This works because the inner emphasis cannot include an _ character. If it could, I would have defined this the other way around:

syntax match EmphasisMarker "\v\s+\zs_[^_\n][^\n]+[^_\n]_\ze\s+" contains=Emphasis
syntax match Emphasis "\v[^_\n][^\n]+[^_\n]\ze_" contained

I'm still using \z* to exclude the whitespace around it. Note that one usually uses (^|\s)\zs...\ze(\s|$) (or \<...\> if possible) to also allow matches at the start and end of the line. (But I don't know your exact syntax, so I kept this as it was.)

  • I think I get how this should work. I tried your second example code, but it doesn't work for me. Once it matches EmphasisMarker also the inside of the two outer _ is EmphasisMarker and not the contained Emphasis. I don't know what is wrong with it, because it does seem to make sense the way you wrote it and I simply copied it into a new syntax file to try. Should I add my test to the question or make a new question? – Zelphir Nov 3 '16 at 19:42
  • You're right; I had minor typos and omissions in the second alternative. Fixed that. – Ingo Karkat Nov 4 '16 at 8:12
  • Thank you for editing again. I have a question regarding the edit though: Why do I need to \ze in the contained expression? And why if so, not also \zs? I am actually using a different expression I came up with using your help and it also works. Here it is: syntax match XLPandocEmphasisMarker "\v(^|\s+)\zs_([^_\n]|^(\s_))+_\ze($|\s+)" contains=XLPandocEmphasis syntax match XLPandocEmphasis "\v([^_\n]|^(\s_))+\ze" contained – Zelphir Nov 5 '16 at 21:51
  • Great you've got that solved! The contained pattern will "overshoot" the containing group, as the last part of the pattern matches almost anything. So the \ze_ says: But stop at the end of the containing group. No \zs is needed because consumption only happens left to right. – Ingo Karkat Nov 7 '16 at 7:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.