feat(13.1): colorize + color_dic → ColorNode renderer
CI / cargo fmt --check (push) Successful in 26s
CI / cargo clippy (push) Successful in 1m24s
CI / cargo test (push) Successful in 1m24s
CI / editor keymaps (push) Successful in 1m45s

Closes the last pending §13.1 entries:

- **`nuwiki.colorize`** (client-side, Lua + VimL) — wraps the word
  at cursor (or the visual selection on Neovim) in an inline
  `<span style="color:%c">…</span>` template. Matches vimwiki's
  default `color_tag_template`. Bound to `<Leader>wc` in both
  normal and visual mode on both editor paths; the previous
  "deferred" stub is gone.

- **`color_dic` → renderer** — `HtmlConfig` gains a
  `color_dic: HashMap<String, String>` field reading the same key
  out of `initializationOptions` / `didChangeConfiguration`. The
  HTML export path threads it into `HtmlRenderer::with_colors`;
  `ColorNode` now picks `style="color:<value>"` when the colour
  name is in the dict, falling back to the existing
  `class="color-<name>"` when it isn't.

SPEC §13.1 is fully checked off — the table moved from "deferred
sketch" to a per-command status table marking all 11 commands done,
plus a note on the `color_dic` follow-up landing alongside. README's
"Phase 14 list & table edit commands" row now reads  without the
deferred-subset caveat, and the §13.1-blocked text-objects note in
the keymaps section is rephrased as "planned follow-ups".

Tests: 4 new in `phase17_colorize.rs` covering the renderer
fallback when the dict is empty, the inline-style emission for a
listed name, the fallback path for an unlisted name in a populated
dict, and the `initializationOptions` JSON shape. Total 414 Rust
tests pass; clippy clean; keymap harness still 20/20.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 14:45:39 +00:00
parent 4245424d2f
commit de8b8fa30d
10 changed files with 226 additions and 47 deletions
+55 -1
View File
@@ -501,7 +501,61 @@ function M.table_move_column_right()
exec('nuwiki.table.moveColumn', { p })
end
M.colorize = _not_yet(':VimwikiColorize')
--- `:VimwikiColorize {color}` — wrap the word (or visual selection)
--- under cursor in an inline span tag. Matches vimwiki's behaviour:
--- the template is `<span style="color:%c">%s</span>` with `%c`
--- replaced by the user-supplied colour and `%s` by the wrapped text.
--- Pure-Lua; no server roundtrip needed.
local function visual_selection_range()
local mode = vim.fn.visualmode()
if mode == '' then return nil end
local s = vim.api.nvim_buf_get_mark(0, '<')
local e = vim.api.nvim_buf_get_mark(0, '>')
if s[1] == 0 or e[1] == 0 then return nil end
return s[1], s[2], e[1], e[2]
end
function M.colorize(color)
if not color or color == '' then
color = vim.fn.input('Colour: ')
if color == '' then return end
end
local sl, sc, el, ec = visual_selection_range()
local row = vim.api.nvim_win_get_cursor(0)[1]
local line = vim.api.nvim_get_current_line()
local open_tag = string.format('<span style="color:%s">', color)
local close_tag = '</span>'
if sl and sl == el then
-- Single-line visual selection.
local new_line = line:sub(1, sc)
.. open_tag
.. line:sub(sc + 1, ec + 1)
.. close_tag
.. line:sub(ec + 2)
vim.api.nvim_buf_set_lines(0, sl - 1, sl, false, { new_line })
return
end
-- Fall back to the cword.
local cword = vim.fn.expand('<cword>')
if cword == '' then return end
local col = vim.api.nvim_win_get_cursor(0)[2]
local left = col
while left > 0 and line:sub(left, left):match('[%w_%-]') do
left = left - 1
end
if not line:sub(left + 1, left + 1):match('[%w_%-]') then
left = left + 1
end
local right = left + 1
while right <= #line and line:sub(right, right):match('[%w_%-]') do
right = right + 1
end
local before = line:sub(1, left)
local word = line:sub(left + 1, right - 1)
local after = line:sub(right)
local new_line = before .. open_tag .. word .. close_tag .. after
vim.api.nvim_buf_set_lines(0, row - 1, row, false, { new_line })
end
-- §13.1 Cluster C — link helpers.