egberts/vim-syntax-bind-named

Reusing a recursive syntax again?

egberts opened this issue · 5 comments

Hi @inkarkat, I ran into your name over at vim.stackoverflow.com as someone who may be knowledgable to answer my question.

I have this syntax.vim, it's huge, very huge. 875 unique syntaxes, and over 4,500 lines. It's for ISC Bind9 named.conf configuration file. There is a stock Vim syntax/named.vim but its about 10 years old, and needs replacing.

I've gotten recursive syntax working for example-named.conf line 23 to 96 via namedAMLSection syntax. That is 100% working and recursively working too.

I've got this generic named_E_AMLSection_SC syntax (it''s recursive), and I've found that I cannot reuse it in other parts of the code. So, I end up duplicating a large part of the existing syntaxes with new syntax identifier name, each time I need to use a recursive syntax.

Is it possible that there a the limitation of recursion and its reuse?

How to View

Execute

git clone https://github.com/egberts/vim-syntax-bind-named.git
cd vim-syntax-bind-named
cp syntax/bind-named.vim ~/.vim/syntax/
cp ftdetect/bind-named.vim ~/.vim/ftdetect/
vim example-named.conf

Around line 846-8 in example-named.conf, there is a keyword response-padding. Its syntax is namedOV_ResponsePadding`.

Same problem reusing named_E_AMLSection_SC syntax for the server-names keyword (line 1343-4)

Some BNF

# EBNF detailed at http://www.zytrax.com/books/dns/ch7/address_match_list.html
#
# EBNF reiterated here:
#
#   address_match_element = [!] { element; ... };
#    address_match_nosemicolon = element ; [ element; ... ]
#
#    element = [!] 
#                    ( <ip_addr>[/prefix]
#                    |  key <key-name>
#                    | <acl_name>
#                    | <address_match_nosemicolon>

If this is too hard, or you have no time, I would understand.

I noticed something.

You cannot reuse a syntax via containedin by another group of syntax.

Syntax, A, C, and E all works.

           A                 B
   nextgroup=C         nextgroup=C
           |                   |
            \                 /
              \             /
                \         /
           syntax region C
                   /   \
                 /       \
               /           \
             D               E
   containedin=C    containedin=C

Syntax B doesn't work.

What does work is cloning C into C1 and C2

           A                 B
   nextgroup=C         nextgroup=C
           |                   |
           |                   |
           |                   |
           |                   |
syntax region C1   syntax region C2
              | \           /|
              |   \       /  |
              |     \   /    |
              |     /   \    |
              |   /       \  |
              | /           \|
             D               E
   containedin=C    containedin=C

Hi @egberts! Your syntax looks very clean and indeed is larger than anything I've written so far. I'm not sure whether I can help you with this, but let's give it a try; maybe some of my comments will provide you with additional insights.

I've tried to reproduce Syntax B doesn't work from your first comment with a minimal example:

" The quick (brown fox) jumps over my brown rat.
" The lazy (brown dog) jumps over my brown rat.

syntax match A "\<quick\>" skipwhite nextgroup=C
syntax match B "\<lazy\>" skipwhite nextgroup=C
syntax region C start="(" end=")" contained
syntax match D "\<\w\w\w\>" contained containedin=C
syntax match E "\<brown\>" contained containedin=C

hi link A Statement
hi link B PreProc
hi link C StatusLine
hi link D WarningMsg
hi link E ErrorMsg

But everything is colored as expected‽


Does it make a difference if you're using contains= instead of containedin=? The latter is mainly meant to be used if you cannot modify the source definition (e.g. when you're extending another syntax).


My gut feeling is that it should be possible to implement this without duplicating definitions (as you currently do). However, if you really can't find a way around it (it could even be a Vim bug, and then you'd still want to work around it even if it gets fixed, to support the many older Vim versions still in use), metaprogramming can help: You can parameterize the duplicate definitions and use :execute. Applied to your second comment's example:

syntax region C1 start="(" end=")" contained
syntax region C2 start="(" end=")" contained
syntax match D "\<\w\w\w\>" contained containedin=C1,C2
syntax match E "\<brown\>" contained containedin=C1,C2

can become:

let s:regions = ['C1', 'C2']
for s:r in s:regions
    execute 'syntax region' s:r 'start="(" end=")" contained'
    execute 'syntax match D "\<\w\w\w\>" contained containedin=' . join(s:regions, ',')
    execute 'syntax match E "\<brown\>" contained containedin=' . join(s:regions, ',')
endfor

It's not shorter here, but it can make a huge difference if there are more instance.

Making heavy use of contains really does make a difference, because containedin= appears to reserve those syntax such that no other syntax can easily reuse it.

Yep. Minimal usage of containedin did the trick. You rock, man!!!