Search code examples
vimsyntax-highlightingvim-syntax-highlightingnano

Can I cascade vim syntax highlighting rules like in nano?


I have a custom file type, which contains lots of fields like

FieldName: FieldValue

I'm trying to create syntax highlighting for this filetype, in nano and vim, such that "Fieldname" appears in blue, and anything from the colon onwards, including the colon, appears in yellow. In nano I achieve this easily due to its 'cascading' nature:

color yellow     start="^FieldName: " end="$"   # Make the whole thing yellow
color brightblue       "^FieldName"             # Then change the first part to blue

In vim, it doesn't look like rules cascade, but I can achieve the above using 'matchgroup', which allows treating 'boundary' matches differently to 'content':

syn region Yellow matchgroup=Blue start="^FieldName" end="$"                                                                                                                                          

So far so good. The problem arises in the special case that some FieldValues need to be coloured differently, because they are special. In nano, I can simply add another rule onto the cascade:

color yellow         start="^FieldNAme: " end="$"     # Make the whole thing yellow
color bold,lightyellow,red "^FieldName: SpecialValue" # Then change up to the special value to red
color yellow               "^FieldName: "             # Then up to the colon to yellow again
color brightblue           "^FieldName"               # Then up to the fieldname to blue

However, when I try to do the same in vim, I am stuck. I have no idea how to match groups at multiple levels. Is there a way to enable such cascading behaviour in vim? Or, alternatively, can someone point me in the right direction to achieve the above effect?


Solution

  • In Vim if inside another syntax match/region then only some of submatches tried. These matches must appear in contains=xxx,yyy,zzz attribute of an upper level match (or, alternatively, they can have an attribute containedin=aaa, where aaa is an upper-level match). Also the sub-matches should usually have contained attribute that forbids matching them on the global level (i.e. uncontained).

    As a final note, if a collision arises (i.e. two or more submatches can match at the same position) then the last one wins. So the order matters.

    Upd. This example is pretty straightforward and doesn't need many rules. Yet exact regex definitions may vary, so giving it rather for illustrative purposes.

    Var.1

    syn match MyFileFieldWhole /^\w\+: .*/ contains=MyFileFieldName,MyFileFieldSpecial
    syn match MyFileFieldName /^\w\+/ contained
    syn keyword MyFileFieldSpecial SpecialValue contained
    
    hi def MyFileFieldWhole guifg=yellow
    hi def MyFileFieldName guifg=lightblue
    hi def MyFileFieldSpecial guifg=red
    

    Var.2

    syn match MyFileFieldName /^\w\+: /me=e-2 nextgroup=MyFileFieldValue
    syn match MyFileFieldValue /: .*/ contains=MyFileFieldSpecial contained
    syn keyword MyFileFieldSpecial SpecialValue contained
    
    hi def MyFileFieldName guifg=lightblue
    hi def MyFileFieldValue guifg=yellow
    hi def MyFileFieldSpecial guifg=red