diff --git a/syntax/vimwiki.vim b/syntax/vimwiki.vim index 8cc3e55..635be2f 100644 --- a/syntax/vimwiki.vim +++ b/syntax/vimwiki.vim @@ -75,7 +75,7 @@ syntax match nuwikiHeading6 /^\s*======\s.\{-}\s======\s*$/ syntax match nuwikiHorizontal /^-\{4,}\s*$/ syntax match nuwikiCommentLine /^%%.*$/ syntax region nuwikiCommentBlock start=/^%%+/ end=/+%%/ keepend -syntax region nuwikiPreformatted start=/^{{{/ end=/^}}}/ keepend +syntax region nuwikiPreformatted matchgroup=nuwikiPreDelim start=/^\s*{{{/ end=/^\s*}}}\s*$/ keepend syntax region nuwikiMathBlock start=/^{{\$/ end=/^}}\$/ keepend syntax match nuwikiPlaceholder /^%\(title\|nohtml\|template\|date\)\>.*$/ @@ -117,6 +117,7 @@ highlight default link nuwikiHorizontal Comment highlight default link nuwikiCommentLine Comment highlight default link nuwikiCommentBlock Comment highlight default link nuwikiPreformatted String +highlight default link nuwikiPreDelim Comment highlight default link nuwikiMathBlock Number highlight default link nuwikiPlaceholder PreProc " Bold / Italic use explicit attributes so they work in plain Vim too (no @@ -134,4 +135,74 @@ highlight default link nuwikiWikilink Underlined highlight default link nuwikiTransclusion Identifier highlight default link nuwikiUrl Underlined +" ===== Nested syntax for code blocks ===== +" +" Highlight `{{{lang … }}}` fences with the embedded language's syntax, like +" vimwiki's automatic nested syntaxes. We scan the buffer for the languages +" actually used, `:syntax include` each one under a cluster, then define a +" contained region per language. Defined *after* nuwikiPreformatted so the +" language regions win for tagged fences (Vim gives later items priority), +" while bare `{{{` / `{{{class="…"` fences fall back to nuwikiPreformatted. + +" Labels that don't match a Vim filetype name 1:1. +let s:nuwiki_nested_aliases = { + \ 'bash': 'sh', 'shell': 'sh', 'zsh': 'sh', 'console': 'sh', + \ 'js': 'javascript', 'ts': 'typescript', 'py': 'python', + \ 'rs': 'rust', 'yml': 'yaml', 'md': 'markdown', 'c++': 'cpp', + \ } + +" The language label of a fence line: the first whitespace-separated token +" after `{{{` that isn't a `key=val` attribute (mirrors the Rust lexer). +function! s:nuwiki_fence_label(line) abort + let l:after = matchstr(a:line, '^\s*{{{\zs.*$') + for l:tok in split(l:after, '\s\+') + if l:tok !~# '=' + return l:tok + endif + endfor + return '' +endfunction + +function! s:nuwiki_setup_nested() abort + let l:included = {} + let l:defined = {} + for l:lnum in range(1, line('$')) + let l:line = getline(l:lnum) + if l:line !~# '^\s*{{{' + continue + endif + let l:label = s:nuwiki_fence_label(l:line) + if l:label ==# '' || has_key(l:defined, l:label) + continue + endif + let l:defined[l:label] = 1 + let l:ft = get(s:nuwiki_nested_aliases, tolower(l:label), tolower(l:label)) + let l:cluster = 'nuwikiNested_' . substitute(l:ft, '[^a-zA-Z0-9]', '_', 'g') + if !has_key(l:included, l:cluster) + if exists('b:current_syntax') + unlet b:current_syntax + endif + try + execute 'syntax include @' . l:cluster . ' syntax/' . l:ft . '.vim' + catch + " No syntax file for this language — leave it to nuwikiPreformatted. + continue + endtry + if exists('b:current_syntax') + unlet b:current_syntax + endif + let l:included[l:cluster] = 1 + endif + let l:region = 'nuwikiNestedRegion_' . substitute(l:label, '[^a-zA-Z0-9]', '_', 'g') + execute 'syntax region ' . l:region + \ . ' matchgroup=nuwikiPreDelim' + \ . ' start=/^\s*{{{\s*' . escape(l:label, '/\.*$^~[]') . '\>.*$/' + \ . ' end=/^\s*}}}\s*$/' + \ . ' contains=@' . l:cluster + \ . ' keepend' + endfor +endfunction + +call s:nuwiki_setup_nested() + let b:current_syntax = 'vimwiki'