Udated 2023-09-11: Changed example to asterisk as it is simpler and the question now refers to \zs instead of \ze as \zs seems to be the issue.
I am trying to adapt the VIM Markdown syntax file so that its behavior for italics with asterisks is more like the Github Markdown editor.
This pattern works exactly the way I want for matching italics:
syn match markdownItalic "\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*" contains=markdownItalicAsteriskStart,markdownItalicAsteriskEnd
To conceal the asterisks, I also need two patterns that exactly match the single asterisk characters that start and end the italics section. Trailing/leading whitespace NOT included. I have solved this, too. This is the search pattern I have found for finding the ending asterisk:
\(\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\)\zs\*
Unfortunately this only works when I use it interactively in VIM, but not when I use it in markdown.vim (s:conceal is a variable set analogous to s:concealends in the original):
exe 'syn match markdownItalicAsteriskEnd "\(\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\)\zs\*"' s:conceal
I suspect that it is \zs
that does not work in my syntax file and have tried an alternative \@<=
version. This almost works, but unfortunately cannot backtrack across more than one line (documented limitation) and therefore does not work for paragraphs in italics of more than two lines:
\(\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\)\@<=\*
Does anybody know what can cause \zs to fail in a Markdown syntax file?
Udate 2023-09-12: Some Tests and a Workaround
markdownItalicAsteriskStart
I have found three ways to detect the starting asterisk. They all work perfectly.
Using \@=
:
\*\(\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*\)\@=
Using \ze
:
\*\ze\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*
With offset:
exe 'syn match markdownItalicAsteriskStart "\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*"ms=s,me=s+1' s:conceal
markdownItalicAsteriskEnd
Knowing three ways to solve the starting asterisk I thought I would try the corresponding methods for the terminating asterisk. Unfortunately only one works for me.
Using \@<=
(works, but limited to two lines as stated in the documentation):
\(\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\)\@<=\*
Using \zs
(works perfectly in an interactive session, but does nothing when used in the syntax file; this is the subject of the question):
\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\zs\*
With offset (does not work):
exe 'syn match markdownItalicAsteriskEnd "\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*"ms=e-1,me=e' s:conceal
The reason why this version with offsets will not work across newlines is documented here. But it does nothing at all for me and I have no idea why.
Workaround
This is the workaround I will use. It works very well with the only limitation that paragraphs in italics cannot span more than two lines. Quite OK for me.
let s:conceal = ''
let s:concealends = ''
if has('conceal') && get(g:, 'markdown_syntax_conceal', 1) == 1
let s:conceal = ' conceal'
let s:concealends = ' concealends'
endif
exe 'syn match markdownItalicAsteriskStart "\*\(\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*\)\@="' s:conceal
exe 'syn match markdownItalicAsteriskEnd "\(\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\)\@<=\*"' s:conceal
syn match markdownItalic "\*\w\(\([^\n]\|\n[^\n]\)\{-}\)\w\*" contains=markdownItalicAsteriskStart,markdownItalicAsteriskEnd
:help :execute
?let s:concealends = ' concealends'
and I did something similar (I will edit my question). I tried now without execute and "hard-wired" conceal, justsyn match markdownItalicUnderscoreEnd "\(\(\(^\|\W\)_\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\)\zs_\)\ze\($\|\W\)" conceal
. Unfortunately the beavior remains the same.