fix(syntax): highlight code blocks with embedded language syntax
CI / cargo fmt --check (push) Successful in 1m10s
CI / cargo clippy (push) Successful in 19s
CI / cargo test (push) Successful in 28s
Release / build x86_64-unknown-linux-gnu (push) Successful in 1m1s
Release / build aarch64-unknown-linux-musl (push) Successful in 54s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m8s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m13s
CI / editor keymaps (push) Successful in 1m32s
Release / gitea release (push) Successful in 23s

`{{{lang … }}}` fences were painted as one String group. Add automatic
nested-syntax highlighting (like vimwiki): scan the buffer for the languages
used, `:syntax include` each under a cluster, and define a contained region
per language so e.g. `{{{sh` blocks get shell highlighting and `{{{python`
gets Python. Bare `{{{` / `{{{class="…"` fences fall back to nuwikiPreformatted.
A small alias map maps labels like bash/shell → sh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-28 22:24:27 -03:00
parent fa2d1b8b42
commit 95645a2b91
+72 -1
View File
@@ -75,7 +75,7 @@ syntax match nuwikiHeading6 /^\s*======\s.\{-}\s======\s*$/
syntax match nuwikiHorizontal /^-\{4,}\s*$/ syntax match nuwikiHorizontal /^-\{4,}\s*$/
syntax match nuwikiCommentLine /^%%.*$/ syntax match nuwikiCommentLine /^%%.*$/
syntax region nuwikiCommentBlock start=/^%%+/ end=/+%%/ keepend 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 region nuwikiMathBlock start=/^{{\$/ end=/^}}\$/ keepend
syntax match nuwikiPlaceholder /^%\(title\|nohtml\|template\|date\)\>.*$/ syntax match nuwikiPlaceholder /^%\(title\|nohtml\|template\|date\)\>.*$/
@@ -117,6 +117,7 @@ highlight default link nuwikiHorizontal Comment
highlight default link nuwikiCommentLine Comment highlight default link nuwikiCommentLine Comment
highlight default link nuwikiCommentBlock Comment highlight default link nuwikiCommentBlock Comment
highlight default link nuwikiPreformatted String highlight default link nuwikiPreformatted String
highlight default link nuwikiPreDelim Comment
highlight default link nuwikiMathBlock Number highlight default link nuwikiMathBlock Number
highlight default link nuwikiPlaceholder PreProc highlight default link nuwikiPlaceholder PreProc
" Bold / Italic use explicit attributes so they work in plain Vim too (no " 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 nuwikiTransclusion Identifier
highlight default link nuwikiUrl Underlined 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' let b:current_syntax = 'vimwiki'