feat(client): conceal colour spans down to their coloured text
CI / cargo fmt --check (push) Successful in 25s
CI / cargo clippy (push) Successful in 37s
CI / cargo test (push) Successful in 37s
CI / editor keymaps (push) Successful in 1m48s

A :VimwikiColorize'd word showed the literal
`<span style="color:NAME">TEXT</span>` markup. Now the tags are concealed
and TEXT is painted in NAME, so the buffer shows just the coloured word —
matching the HTML export.

New autoload/nuwiki/colors.vim: nuwiki#colors#refresh() scans the buffer
and, per distinct colour, defines a syntax region that conceals the
`<span …>` / `</span>` tags (concealends) and highlights the body with the
span's actual colour (guifg always; ctermfg for named colours). Idempotent
(clears the group before redefining) and tracked in b:nuwiki_color_seen.

Refresh is driven from three places so it's both correct and immediate:
- syntax/vimwiki.vim: initial pass + re-establish after :colorscheme reload
  (resets the per-buffer cache since :syntax clear drops the regions);
- ftplugin TextChanged/InsertLeave autocmd for spans that arrive via paste/
  undo/external edits;
- colorize() itself calls refresh() right after wrapping, so a freshly
  colorized word conceals instantly (TextChanged doesn't fire reliably mid
  command / in headless ex-mode).

The LSP @vimwikiColor token no longer links to Constant, so in Neovim it
doesn't override the real per-span colour on the concealed text. Pure syntax
+ :highlight — works identically in Vim, coc.nvim, and Neovim, no LSP needed.

Tests: colorize.conceal_hides_tags_shows_text (both harnesses) and
colorize.command_conceals_immediately (vim). Sample wiki's colorize section
notes the conceal. nvim 269, vim 259+18, config-parity green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-31 16:01:21 +00:00
parent 2facb40265
commit 15ba774a28
9 changed files with 146 additions and 2 deletions
+61
View File
@@ -0,0 +1,61 @@
" autoload/nuwiki/colors.vim — conceal colour spans down to their text.
"
" `:VimwikiColorize NAME` wraps text in `<span style="color:NAME">TEXT</span>`.
" This hides the two tags (conceal) and paints TEXT in NAME, so the buffer
" shows just the coloured word — matching how the HTML export renders it.
"
" Pure syntax + `:highlight`, no LSP needed, so it works the same in Vim,
" coc.nvim, and Neovim. New colours appear as the user runs :VimwikiColorize,
" so the ftplugin re-runs refresh() on text changes; we track which colours
" already have a syntax group in b:nuwiki_color_seen to stay idempotent.
" Scan the buffer for `<span style="color:…">` and define a concealing,
" colouring syntax region for each colour not yet seen.
function! nuwiki#colors#refresh() abort
if !exists('b:nuwiki_color_seen')
let b:nuwiki_color_seen = {}
endif
let l:n = line('$')
let l:lnum = 1
while l:lnum <= l:n
let l:line = getline(l:lnum)
if stridx(l:line, '<span style="color:') >= 0
let l:pos = 0
while 1
let l:mp = matchstrpos(l:line, '<span style="color:[^"]\{-}">', l:pos)
if l:mp[1] < 0
break
endif
let l:pos = l:mp[2]
call s:define(matchstr(l:mp[0], 'color:\zs[^"]\{-}\ze">'))
endwhile
endif
let l:lnum += 1
endwhile
endfunction
" Define (once per colour) a region that conceals `<span …>` / `</span>` and
" paints the wrapped text in that colour.
function! s:define(color) abort
if a:color ==# '' || has_key(b:nuwiki_color_seen, a:color)
return
endif
let b:nuwiki_color_seen[a:color] = 1
let l:grp = 'nuwikiColorSpan_' . substitute(a:color, '[^A-Za-z0-9]', '_', 'g')
let l:esc = escape(a:color, '/\.*$^~[]')
" Clear first so re-runs / load-order never stack duplicate regions.
silent! execute 'syntax clear ' . l:grp
" `concealends` hides the start/end matches (the tags); the region body
" (the text) keeps the l:grp highlight. `oneline` since spans don't wrap.
execute 'syntax region ' . l:grp
\ . ' matchgroup=Conceal'
\ . ' start=/<span style="color:' . l:esc . '">/'
\ . ' end=#</span>#'
\ . ' oneline keepend concealends'
" guifg works for names and #hex under 'termguicolors'. ctermfg only accepts
" named colours, so try it for non-hex and swallow E254 for unknown names.
execute 'highlight default ' . l:grp . ' guifg=' . a:color
if a:color !~# '^#'
silent! execute 'highlight ' . l:grp . ' ctermfg=' . a:color
endif
endfunction