5

I would like to adapt this syntax file so that the formatting in VIM matches more closely what this online Markdown editor (GitLab-flavor) displays (the output on the right is relevant, this is how I want the text to look in VIM). In particular, I am looking at the detection of italics triggered by framing text with asterisks.

I made some progress by simplifying the start and end patterns and by adding "oneline". But actually I would like the region to extend beyond a single line to an entire paragraph. In other words, the region should include subsequent lines until an empty line is encountered. If I omit the "oneline" attribute, the region just goes on and does not stop on an empty line. How can I make this region include subsequent non-empty lines but stop at the first empty line (or, obviously, the end pattern, whatever comes first)?

Note that a piece of text that contains the start pattern, but no end pattern and that is terminated by an empty line should NOT be detected as a region. The empty line is not an alternative end, it should just be interpreted as "stop looking here for the end pattern".

exe 'syn region markdownItalic matchgroup=markdownItalicDelimiter start="\*\S\@=" end="\S\@<=\*" keepend oneline contains=markdownLineStart' . s:concealends

Update 2023-09-05

Here is how the relevant section of my markdown.vim file looks now. It does italics and bold in a style that corresponds more closely to the way Markdown is rendered on Github. I personally don't use markdownBoldItalic or markdownStrike, so I did not bother to change those.

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 markdownItalicDelimiterStart "\*\ze\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\*"' s:conceal
exe 'syn match markdownItalicDelimiterEnd "\(\*\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\)\@<=\*"' s:conceal
syn match markdownItalic "\*\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\*" contains=markdownItalicDelimiterStart,markdownItalicDelimiterEnd
exe 'syn match markdownItalicDelimiterStart "_\ze\S\(\([^\n]\|\n[^\n]\)\{-}\)\S_"' s:conceal
exe 'syn match markdownItalicDelimiterEnd "\(_\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\)\@<=_"' s:conceal
syn match markdownItalic "_\S\(\([^\n]\|\n[^\n]\)\{-}\)\S_" contains=markdownItalicDelimiterStart,markdownItalicDelimiterEnd
exe 'syn match markdownBoldDelimiterStart "\*\*\ze\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\*\*"' s:conceal
exe 'syn match markdownBoldDelimiterEnd "\(\*\*\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\)\@<=\*\*"' s:conceal
syn match markdownBold "\*\*\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\*\*" contains=markdownBoldDelimiterStart,markdownBoldDelimiterEnd
exe 'syn match markdownBoldDelimiterStart "__\ze\S\(\([^\n]\|\n[^\n]\)\{-}\)\S__"' s:conceal
exe 'syn match markdownBoldDelimiterEnd "\(__\S\(\([^\n]\|\n[^\n]\)\{-}\)\S\)\@<=__"' s:conceal
syn match markdownBold "__\S\(\([^\n]\|\n[^\n]\)\{-}\)\S__" contains=markdownBoldDelimiterStart,markdownBoldDelimiterEnd
exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\*\*\*\S\@=" end="\S\@<=\*\*\*\|^$" skip="\\\*" contains=markdownLineStart,@Spell' . s:concealends
exe 'syn region markdownBoldItalic matchgroup=markdownBoldItalicDelimiter start="\w\@<!___\S\@=" end="\S\@<=___\w\@!\|^$" skip="\\_" contains=markdownLineStart,@Spell' . s:concealends
exe 'syn region markdownStrike matchgroup=markdownStrikeDelimiter start="\~\~\S\@=" end="\S\@<=\~\~\|^$" contains=markdownLineStart,@Spell' . s:concealends

1 Answer 1

1

An empty line can be recognized by two consecutive newlines. You can match everything that is not two newlines with the following pattern:

\([^\n]\|\n[^\n]\)*

Place this between the "start" and "end" patterns to match the whole italicized region at once. It will require that your syntax item is a match rather than a region:

syn match markdownItalic "\*\S\@=\([^\n]\|\n[^\n]\)\{-}\S\@<=\*"

The \{-} ensures that the match ends at the first possible end-marker. Alternatively, you could include * in the negated character list.

1
  • Thanks a lot and apologies for the delay. Believe it or not, it took me over 6 years to find the time to get back to this problem. Your input is the solution! With a bit of finetuning, I finally wound up with the following (use only asterisks as the end that are preceded by non-whitespace and followed by whitespace or an end of line): syn match markdownItalic "*\S\@=([^\n]\|\n[^\n])*([^[:space:]])\@<=*($\| \+)" Sep 1 at 16:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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