parity(1): vimwiki per-wiki config keys, table colspan/rowspan,

named link commands, mouse maps

Parity audit Cluster 1 — small wins.

WikiConfig (server) extended with 14 vimwiki globals:
- `index` (`g:vimwiki_index`), wired into `:VimwikiIndex` so users
  with a custom index page stem (e.g. `home`) get the right file.
- `diary_frequency`, `diary_start_week_day`, `diary_caption_level`,
  `diary_sort`, `diary_header` — diary keys (weekly/monthly/yearly
  semantics land in Cluster 4; the keys parse now so config
  migration doesn't need to wait).
- `maxhi`, `listsyms`, `listsyms_propagate`, `list_margin`,
  `links_space_char`, `nested_syntaxes`, `auto_toc` — list +
  highlight knobs the renderer/handlers can consult.

All keys threaded through `RawWiki` → `WikiConfig::from(RawWiki)`,
defaults centralised in `wiki_defaults()` so `empty()`, `from_root()`,
the legacy single-wiki path, and the `RawWiki` impl stay in sync.

Lua front-door (`lua/nuwiki/config.lua`) now documents the multi-wiki
`wikis = {…}` shape with every accepted per-wiki key — users who
prefer Lua tables no longer have to round-trip through
`init_options` raw JSON.

HTML renderer (`crates/nuwiki-core/src/render/html.rs`):
- New `table_spans()` pre-pass resolves vimwiki's `>` (col_span) and
  `\\/` (row_span) continuation markers into proper HTML
  `colspan="N"` / `rowspan="N"` attributes on the lead cell. Continuation
  cells emit nothing, matching what browsers expect.
- The old behaviour (emitting `class="col-span"` / `class="row-span"`
  as visual markers) is gone — replaced with real merging so the
  rendered HTML matches the source semantics.

Named ex-commands:
- `:VimwikiNextLink` / `:VimwikiPrevLink` — were keymapped only
  (`<Tab>`/`<S-Tab>`); now have explicit `:` commands in both editor
  paths, dispatching to new `nuwiki.commands.link_next` / `link_prev`
  pure-Vim/Lua helpers.
- `:VimwikiBaddLink` — adds the link target's file to the buffer
  list without switching focus. Goes through
  `textDocument/definition` and runs `:badd <fname>` on the resolved
  URI.

Mouse maps (opt-in via `mappings.mouse = true` in Lua or
`g:nuwiki_mouse_mappings = 1` in Vim):
- `<2-LeftMouse>` follows, `<S-2-LeftMouse>` / `<C-2-LeftMouse>`
  follow in split/vsplit, `<MiddleMouse>` adds to buflist,
  `<RightMouse>` goes back. Mirrors upstream vimwiki's defaults.

Tests: 7 new in `parity_cluster_1.rs` covering the per-wiki config
defaults + raw JSON parse, the legacy-single-wiki path, colspan
folding into a lead cell with no leftover class markers, rowspan
across rows, no-attrs when the table has no spans, and a sanity
paragraph render. Total 421 Rust tests pass; clippy clean; both
keymap harnesses still green (20 nvim + 12 vim).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 15:10:27 +00:00
parent de8b8fa30d
commit ee0309b3c2
9 changed files with 634 additions and 40 deletions
+41
View File
@@ -165,6 +165,47 @@ end
--- 3. Press inside an existing `[[…]]` follows immediately.
--- "Not on a word" falls through to plain definition so users can
--- still chord a definition request anywhere.
--- Jump to the next / previous `[[…]]` on or after the cursor.
--- Public so `:VimwikiNextLink` / `:VimwikiPrevLink` can call it.
local function jump_link(direction)
local flags = direction > 0 and 'W' or 'bW'
local pos = vim.fn.searchpos([[\[\[]], flags)
if pos[1] > 0 then
vim.api.nvim_win_set_cursor(0, { pos[1], pos[2] - 1 })
end
end
function M.link_next() jump_link(1) end
function M.link_prev() jump_link(-1) end
--- `:VimwikiBaddLink` — add the link target's file to the buffer list
--- (`:badd`) without switching to it. Uses the LSP definition response
--- to find the file.
function M.badd_link()
local client = find_client()
if not client then
vim.notify('nuwiki: language server not attached', vim.log.levels.ERROR)
return
end
local params = position_params(client)
params.textDocument = { uri = buf_uri() }
local req = client.request
local handler = function(_, result)
local loc
if type(result) == 'table' then
loc = result[1] or result
end
local uri = type(loc) == 'table' and (loc.uri or loc.targetUri) or nil
if not uri then return end
local path = vim.uri_to_fname(uri)
pcall(vim.cmd, 'badd ' .. vim.fn.fnameescape(path))
vim.notify('nuwiki: added ' .. path, vim.log.levels.INFO)
end
local bufnr = vim.api.nvim_get_current_buf()
if type(req) == 'function' then
client:request('textDocument/definition', params, handler, bufnr)
end
end
function M.follow_link_or_create()
if cursor_inside_wikilink() then
vim.lsp.buf.definition()
+22 -1
View File
@@ -6,11 +6,31 @@
local M = {}
M.defaults = {
-- v1.0 single-wiki shorthand. The server desugars these into a
-- one-entry `wikis = {…}` list. Use either this or the multi-wiki
-- form below; if both are set, `wikis` wins.
wiki_root = '~/vimwiki',
file_extension = '.wiki',
syntax = 'vimwiki',
log_level = 'warn',
-- v1.1 multi-wiki shape. Each entry honours every per-wiki key the
-- server understands. Leave nil to fall through to the shorthand
-- above.
--
-- Per-wiki keys (all optional, server defaults documented in SPEC
-- §12.11):
-- name, root, file_extension, syntax, index
-- diary_rel_path, diary_index, diary_frequency,
-- diary_start_week_day, diary_caption_level, diary_sort,
-- diary_header
-- html_path, template_path, template_default, template_ext,
-- template_date_format, css_name, auto_export, auto_toc,
-- html_filename_parameterization, exclude_files, color_dic
-- maxhi, listsyms, listsyms_propagate, list_margin,
-- links_space_char, nested_syntaxes
wikis = nil,
-- Phase 19: per-buffer glue. Each subgroup mirrors vimwiki's
-- `g:vimwiki_key_mappings` shape and can be flipped off
-- independently to suppress that group of keymaps.
@@ -23,7 +43,8 @@ M.defaults = {
table_editing = true, -- gqq, gq1, gww, gw1, <A-Left>, <A-Right>
diary = true, -- <C-Down>, <C-Up>
html_export = true, -- <Leader>wh, <Leader>whh
text_objects = true, -- ah, ih,
text_objects = true, -- ah, ih, aH, iH, al, il, a\, i\, ac, ic
mouse = false, -- <2-LeftMouse>, <RightMouse>, … (opt-in)
},
-- Phase 19: folding. `'lsp'` uses the server's `foldingRange`
+17
View File
@@ -271,6 +271,23 @@ function M.attach(bufnr, mappings)
map('n', '<Leader>whh', cmd.export_browse, { desc = 'nuwiki: export + browse' }, bufnr)
map('n', '<Leader>wha', cmd.export_all, { desc = 'nuwiki: export all' }, bufnr)
end
-- ===== Mouse (opt-in via `mappings.mouse = true`) =====
-- Mirrors upstream vimwiki: double-click follows, shift/ctrl-double
-- open in split/vsplit, middle-click adds the linked file to the
-- buffer list, right-click + left-click goes back.
if mappings.mouse then
map('n', '<2-LeftMouse>', cmd.follow_link_or_create,
{ desc = 'nuwiki: follow link (mouse)' }, bufnr)
map('n', '<S-2-LeftMouse>', function() vim.cmd('split'); cmd.follow_link_or_create() end,
{ desc = 'nuwiki: follow link in split (mouse)' }, bufnr)
map('n', '<C-2-LeftMouse>', function() vim.cmd('vsplit'); cmd.follow_link_or_create() end,
{ desc = 'nuwiki: follow link in vsplit (mouse)' }, bufnr)
map('n', '<MiddleMouse>', cmd.badd_link,
{ desc = 'nuwiki: badd link target' }, bufnr)
map('n', '<RightMouse>', '<C-o>',
{ desc = 'nuwiki: go back (mouse)', remap = true }, bufnr)
end
end
return M