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!!!