Question

I decide to learn more about vim and its syntax highlighting. Using examples for others, I am creating my own syntax file for Markdown. I have seen mkd.vim and it has this problem too. My issue is between list items and code block highlighting.

Code Block definition:

  • first line is blank
  • second line begins with at least 4 spaces or 1 tab
  • block is finished with a blank line

Example:

Regular text

    this is code, monospaced and left untouched by markdown
    another line of code

Regular Text

My Vim syntax for code block:

syn match mkdCodeBlock   /\(\s\{4,}\|\t\{1,}\).*\n/ contained nextgroup=mkdCodeBlock  

hi link mkdCodeBlock  comment

Unorder List item definition:

  • first line is blank
  • second line begins with a [-+*] followed by a space
  • the list is finished with a blank line then a normal (non-list) line
  • in between line items any number of blank lines can be added
  • a sub list is specified by indenting (4 space or 1 tab)
  • a line of normal text after a list item is include as a continuation of that list item

Example:

Regular text

- item 1

    - sub item 1
    - sub item 2
- item 2
this is part of item 2
so is this


- item 3, still in the same list
    - sub item 1
    - sub item 2

Regular text, list ends above

My Vim syntax for unorder list item definition (I only highlight [-+*]):

syn region  mkdListItem start=/\s*[-*+]\s\+/ matchgroup=pdcListText end=".*" contained nextgroup=mkdListItem,mkdListSkipNL contains=@Spell skipnl 
syn match mkdListSkipNL /\s*\n/ contained nextgroup=mkdListItem,mkdListSkipNL skipnl

hi link mkdListItem  operator

I cannot get the highlighting to work with the last two rule for list and with a code block.

This is an example that breaks my syntax highlighting:

Regular text

- Item 1
- Item 2
part of item 2

    - these 2 line should be highlighted as a list item
    - but they are highlighted as a code block

I currently cannot figure out how to get the highlighting to work the way I want it too


Forgot to add a "global" syntax rule used in both rules listed below. It is to ensure a that they start with a blank line.

syn match mkdBlankLine   /^\s*\n/    nextgroup=mkdCodeBlock,mkdListItem transparent

Another Note: I should have been more clear. In my syntax file, the List rules appear before the Blockquote Rules


Was it helpful?

Solution

Just make sure that the definition of mkdListItem is after the definition of mkdCodeBlock, like this:

syn match mkdCodeBlock   /\(\s\{4,}\|\t\{1,}\).*\n/ contained nextgroup=mkdCodeBlock  
hi link mkdCodeBlock  comment

syn region  mkdListItem start=/\s*[-*+]\s\+/ matchgroup=pdcListText end=".*" contained nextgroup=mkdListItem,mkdListSkipNL contains=@Spell skipnl 
syn match mkdListSkipNL /\s*\n/ contained nextgroup=mkdListItem,mkdListSkipNL skipnl
hi link mkdListItem  operator

syn match mkdBlankLine   /^\s*\n/    nextgroup=mkdCodeBlock,mkdListItem transparent

Vim documentation says in :help :syn-define:

"In case more than one item matches at the same position, the one that was defined LAST wins. Thus you can override previously defined syntax items by using an item that matches the same text. But a keyword always goes before a match or region. And a keyword with matching case always goes before a keyword with ignoring case."

OTHER TIPS

hcs42 was correct. I do remember reading that section now, but I forgot about it until hcs24 reminded me about it.

Here is my updated syntax (few other tweaks) that works:

"""""""""""""""""""""""""""""""""""""""
" Code Blocks:

"   Indent with at least 4 space or 1 tab
"   This rule must appear for mkdListItem, or highlighting gets messed up
syn match mkdCodeBlock   /\(\s\{4,}\|\t\{1,}\).*\n/ contained nextgroup=mkdCodeBlock  

"""""""""""""""""""""""""""""""""""""""
" Lists:

"   These first two rules need to be first or the highlighting will be
"   incorrect

"   Continue a list on the current line or next line
syn match mkdListCont /\s*[^-+*].*/ contained nextgroup=mkdListCont,mkdListItem,mkdListSkipNL contains=@Spell skipnl transparent

"   Skip empty lines
syn match mkdListSkipNL /\s*\n/ contained nextgroup=mkdListItem,mkdListSkipNL 

"   Unorder list
syn match  mkdListItem /\s*[-*+]\s\+/ contained nextgroup=mkdListSkipNL,mkdListCont  skipnl 

Tao Zhyn, that maybe covers your use cases but it doesn't cover the Markdown syntax. In Markdown a list item could contain a code block. You could take a look at my solution here

TL;DR; the problem is that vim doesn't let you say something like: a block that have the same indentation as its container + 4 spaces. The only solution I found is to generate rules for each kind of blocks that could be contained in a list items for each level of indentation (actually I support 42 level of indentation but it's an arbitrary number)

So I have markdownCodeBlockInListItemAtLevel1 that must be contained in a markdownListItemAtLevel1 and it needs to have at least 8 leading spaces, an then markdownCodeBlockInListItemAtLevel2 that must be contained in a markdownListItemAtLevel2 that must be contained in a markdownListItemAtLevel1 ant needs to have at least 10 leading spaces, ecc...

I know that a few years have passed but maybe someone would consider this answer helpful since all syntax based on indentation suffers of the same problem

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top