Files
nuwiki/development/vimwiki-gap.md
T

786 lines
54 KiB
Markdown
Raw Normal View History

# vimwiki → nuwiki Parity Gaps
Tracking document for closing the gap between nuwiki and the original
[vimwiki](https://github.com/vimwiki/vimwiki) plugin across **configuration**,
**commands**, and **key mappings**.
Source of truth for upstream: vimwiki `master`
`autoload/vimwiki/vars.vim` (option defaults), `plugin/vimwiki.vim` +
`ftplugin/vimwiki.vim` (commands/mappings), `doc/vimwiki.txt` (prose).
Audited 2026-05-31. Check items off as they land. Each row cites the nuwiki
fix site.
---
## P1 — Core workflow gaps (fix first)
- [x] **Vim client split/tab link-follow commands**`VimwikiSplitLink`,
`VimwikiVSplitLink`, `VimwikiGoBackLink`, `VimwikiTabnewLink`,
`VimwikiTabDropLink` exist only in the Neovim branch. Vim users lose
split-window link following and back-navigation.
_Fix:_ `ftplugin/vimwiki.vim` (Vim branch) — commands added + `:Nuwiki*`
aliases; `follow_link_drop()` helper in `autoload/nuwiki/commands.vim`;
true `:tab drop` tab-reuse wired in both clients (`lua/nuwiki/commands.lua`
`open_uri` `'tabdrop'` case); `<C-S-CR>` repointed to tab-drop.
- [x] **`gl<symbol>` change-symbol mappings** — upstream's one-key bullet/number
symbol change (`gl*` `gl#` `gl-` `gl1` `gla` `gli` … plus `gL…`) has no
nuwiki keymap; only `:…ChangeSymbol*` commands exist.
_Fix:_ added normal-mode `gl{-,*,#,1,i,I,a,A}` (item) + `gL…` (whole list) in
`lua/nuwiki/keymaps.lua` lists group and `ftplugin/vimwiki.vim` Vim-branch
lists block, wired to the existing `list_change_symbol`. `1)` stays
command-only (upstream shadows it with `1.`); normal-mode only (server acts on
cursor item / whole list, not a visual range).
- [x] **`gl<Space>` / `gL<Space>` semantics diverge** — upstream = *remove
checkbox* from item / list siblings; nuwiki rebound to *remove done items*.
Same keys, different effect (parity trap).
_Fix:_ bound **bare `gl`/`gL`** to remove-checkbox-item / -in-list for exact
upstream parity (`lua/nuwiki/keymaps.lua` lists block, `ftplugin/vimwiki.vim`
Vim-branch lists block); they share the `gl…` prefix so they fire after
`timeoutlen`, just like upstream. Remove-*done* is now command-only:
`:NuwikiRemoveDone` (current list) gained a `-bang` so `:NuwikiRemoveDone!`
reaches the whole-buffer sweep that `gL<Space>` previously held (the only
prior entry point). Normal-mode only.
## Regressions (reported by users — verify, then fix)
- [x] **`<CR>` link-follow: wrong window placement + no create-on-follow on the
Vim clients** — two related Vim-branch bugs (Neovim path was already correct):
(a) on coc.nvim, `<CR>` opened the target in a **new tab** instead of the
current window; (b) on **both** vim-lsp and coc, `<CR>` on a link to a
not-yet-created page **failed to open/create it**.
_Root cause:_ the Vim follow path delegated to the LSP client's jump UI
(`:LspDefinition` / `CocActionAsync('jumpDefinition')`). Those fetch the
target's text before jumping — vim-lsp `readfile()`s it for the quickfix entry
(`autoload/lsp/utils/location.vim`), which throws `E484` for a missing file
and aborts the jump (→ no create-on-follow); coc, lacking an explicit open
command, fell back to the user's `coc.preferences.jumpCommand` (→ new tab).
The server itself was correct throughout — verified over JSON-RPC that
`textDocument/definition` returns a *synthesised* location for missing pages
and resolves existing links.
_Fix:_ stop delegating. `nuwiki#commands#follow_link_or_create()` /
`follow_link_drop()` now resolve `textDocument/definition` directly and open
the URI ourselves via a shared `s:open_definition(open_cmd)` helper
(`autoload/nuwiki/commands.vim`) — `:edit` for `<CR>`, `tab drop` for
`<C-S-CR>`. `:edit` opens a buffer for a missing path (save creates it,
matching vimwiki) and gives exact current-window placement regardless of the
user's coc jumpCommand. Covered by
`cr.follow_opens_missing_page_in_current_window` in
`development/tests/test-keymaps-vim.vim`; manually reproducible via
`development/start-vim.sh` (vim-lsp) and `development/start-vim-coc.sh` (coc).
- [x] **coc.nvim reported valid links as broken / couldn't resolve** — only via
the new `development/start-vim-coc.sh` launcher: its generated
`coc-settings.json` registered the server with `command`/`filetypes` but no
`initializationOptions`, so `wiki_root` never reached the server and it
resolved links against its default (`~/vimwiki`). _Fix:_ the launcher now
emits `initializationOptions` **and** `settings.nuwiki` mirroring the vim-lsp
client (`autoload/nuwiki/lsp.vim` `s:settings()`). Verified with a headless
coc.nvim run: `[[Notes]]` resolves, only genuinely-missing links warn. (Shipped
plugin only — no production code change; coc users configure their own
`coc-settings.json` per the README snippet.)
## P2 — Moderate (real users hit these)
- [x] **`<Leader>w<Leader>t` collision** — upstream = today-in-new-tab; nuwiki
bound both `<Leader>w<Leader>t` and `<Leader>w<Leader>m` to tomorrow's diary.
_Fix:_ `<Leader>w<Leader>t` now calls `diary_today_tab` (today in a new tab),
`<Leader>w<Leader>m` stays tomorrow — exact upstream parity
(`lua/nuwiki/keymaps.lua`, `ftplugin/vimwiki.vim`; docs + README updated).
Covered by `diary.leader_t_opens_today_in_tab` / `diary.leader_m_opens_tomorrow`.
- [x] **Generated-section captions hardcoded** — TOC/links/tags headings and
their levels were hardcoded (`= Contents =`, `= Generated Links =`, `= Tags =`,
all level 1). _Fix:_ added per-wiki `toc_header`/`toc_header_level`,
`links_header`/`links_header_level`, `tags_header`/`tags_header_level`
(defaults `Contents`/`Generated Links`/`Generated Tags`, level 1 — matching
upstream; this also corrects the tags index heading from `Tags`
`Generated Tags`). A `caption_line(name, level)` helper emits the markers; the
text + level thread through `toc_edit`/`links_edit`/`tag_links_edit` (+ the
auto_toc save hook) and `find_section_range` matches the configured text so
regen stays idempotent. Tests: `captions_honour_custom_header_and_level`
(`link_health.rs`) + config round-trip in `index_and_config.rs`.
- [x] **On-save autoregen family** — added per-wiki `auto_generate_links`,
`auto_generate_tags`, `auto_diary_index` (all default `false`, like upstream),
wired into the `did_save` hook (`crates/nuwiki-lsp/src/lib.rs`) mirroring
`auto_toc`: regen runs only when the section already exists
(`links_rebuild_edit` / `tag_links_rebuild_edit`), and `auto_diary_index`
regenerates the diary index when a dated diary *entry* is saved
(`diary_generate_index_edit`, which handles an open/closed/absent index file).
`auto_tags` is an **intentional divergence** (see below): nuwiki re-indexes on
every change, so tag metadata is always fresh — there's no on-disk metadata
file to update on save. Tests: `links_rebuild_edit_only_acts_when_section_present`
(`link_health.rs`), `tag_links_rebuild_edit_only_acts_when_index_present`
(`commands_tags.rs`), config round-trip + defaults (`index_and_config.rs`).
- [x] **`listsym_rejected`** — the rejected glyph was a hardcoded `const '-'`.
_Fix:_ `ListSyms` gained a `rejected` field + `new_with_rejected()`
(`crates/nuwiki-core/src/listsyms.rs`); per-wiki `listsym_rejected` key (default
`-`) added to `WikiConfig`; `WikiConfig::list_syms()` builds the palette with
it, used by the parse path (`lib.rs`) and the toggle/cycle commands — so a
custom rejected glyph both lexes and round-trips. Tests:
`listsyms::custom_rejected_glyph_is_honoured` + config round-trip in
`index_and_config.rs`.
- [x] **`custom_wiki2html` (+`_args`)** and **`base_url`** — external HTML
converter hook + export URL prefix. _Fix:_ `HtmlConfig` (`config.rs`) gained
`custom_wiki2html`, `custom_wiki2html_args`, `base_url` (all default `""`,
round-tripped through `RawWiki`/`From`/defaults). `write_page`
(`commands.rs`) now takes a `force` flag and, when `custom_wiki2html` is set,
hands the page to `run_custom_wiki2html()` — which shells out via `sh -c`
with vimwiki's exact arg order (`<cmd> <force> <syntax> <ext> <out_dir>
<in_file> <css> <tpl_path> <tpl_default> <tpl_ext> <root_path> <args>`, `-`
for empty optionals) instead of the built-in renderer. `force` is threaded
through `export_current` (false), `export_all` (its own flag), and the
`did_save` auto-export path (false). `write_rss` now prefixes the channel +
item links with `base_url` (`<base_url><diary_rel_path>/<date>.html`) when
set, falling back to the entry's `file://` URI otherwise. Tests:
`write_page_invokes_custom_wiki2html_converter`,
`write_rss_uses_base_url_for_public_links` (`html_export.rs`), config
round-trip + defaults (`index_and_config.rs`).
- [x] **`map_prefix`** — `<Leader>w` was hardcoded across the map layer.
_Fix:_ mirror vimwiki's `g:vimwiki_map_prefix` with a configurable prefix
(Neovim `map_prefix` setup option, default `'<Leader>w'`; Vim
`g:nuwiki_map_prefix`). The whole wiki command family is now built from the
prefix — both entry-point globals (`lua/nuwiki/init.lua`,
`plugin/nuwiki.vim`, via `:exe`) and buffer-local maps
(`lua/nuwiki/keymaps.lua`, `ftplugin/vimwiki.vim`). Setting a custom prefix
relocates `<prefix>{w,t,s,i,n,d,r,c,h,hh,ha}` and `<prefix><Leader>{w,y,t,m,
i}` and leaves nothing under the old `<Leader>w*`. Tests: new
`test-keymaps-vim-prefix.vim` harness (21 cases, driven by
`test-keymaps-vim.sh`) + `map_prefix.relocates_wiki_family`
(`test-keymaps.lua`). Docs: README, `doc/nuwiki.txt`, lua config comment.
- [x] **`<M-CR>` badd-link** — was mouse-only (`<MiddleMouse>`) / command-only
(`:VimwikiBaddLink`); upstream binds `<M-CR>``VimwikiBaddLink` (confirmed
against upstream `ftplugin/vimwiki.vim`). _Fix:_ added normal-mode `<M-CR>`
`badd_link` in the links group of both clients (`lua/nuwiki/keymaps.lua`,
`ftplugin/vimwiki.vim`; docs + README updated). Covered by `map[n].<M-CR>` in
both keymap harnesses.
- [x] **`diary_caption_level` default divergence** — nuwiki defaulted to `1`,
upstream `0`. _Fix:_ `wiki_defaults()` in `crates/nuwiki-lsp/src/config.rs` now
defaults `diary_caption_level: 0` (year captions top-level, months one below),
still per-wiki overridable. Test `defaults_carry_vimwiki_per_wiki_keys` updated.
- [x] **`diary_start_week_day`** — removed in `c63ec67` (weekly diary hardwired
to ISO-Monday). Restored as a **configurable choice** so migrators keep
upstream behaviour while new wikis can use ISO weeks. New per-wiki key
`diary_weekly_style`: `iso` (default — `YYYY-Www`, Monday-based, nuwiki's
original) or `date`/`vimwiki` (`YYYY-MM-DD` of the week-start day, upstream
parity, honouring the restored `diary_start_week_day` = `monday`..`sunday`).
_Impl:_ `nuwiki_core::date::{WeeklyStyle, WeekStart, DiaryCalendar}` own the
today/next/prev logic; `WikiConfig::diary_calendar()` builds it from config;
the diary commands (`commands.rs` `diary_open_relative` + next/prev pivot) use
it. Defaults keep existing nuwiki weekly files working (no breaking change).
Covered by calendar tests in `crates/nuwiki-core/tests/diary.rs` and the
config round-trip in `crates/nuwiki-lsp/tests/index_and_config.rs`.
## P3 — Niche / low impact
### Commands
- [x] **`VimwikiGoto`/`NuwikiGoto` were `-nargs=1`** _(new, 2026-06-03 re-audit;
fixed same day)_ — upstream is `-nargs=*` (`ftplugin/vimwiki.vim:337`); nuwiki's
four defs were `-nargs=1`, so `:VimwikiGoto` (no arg) raised **E471** and
`:VimwikiGoto My Page` (spaced name) raised **E488** — and the handler's
empty-arg `input('Goto page: ')` prompt fallback was unreachable. _Fix:_ all
four defs (Vim+Neovim × `Vimwiki*`/`Nuwiki*`) are now `-nargs=*` (keeping
`-complete=…pages`); `<q-args>` joins a spaced name and an empty arg reaches
the prompt. Both handlers already prompt on empty. Covered by
`cmd.VimwikiGoto_accepts_multiword` (`test-keymaps-vim.vim`).
- [x] **`VimwikiRenameFile` `-nargs=?`** _(new, 2026-06-03 re-audit; fixed
2026-06-03)_ — upstream is `-nargs=? -complete=…file`; nuwiki's defs were bare,
so `:VimwikiRenameFile foo` raised E488. _Fix:_ all four defs are now
`-nargs=?` (arg accepted-and-ignored — rename still delegates to the LSP rename
request, which drives its own prompt). No more E488.
- [x] **`VimwikiVar`/`NuwikiVar`** _(fixed 2026-06-03)_ — config-var get/set
introspection (upstream `vimwiki#vars#cmd`). _Fix:_ global command in both
clients — no arg prints the client config (Vim: every `g:nuwiki_*`; Neovim: the
resolved `setup()` options), one arg gets a key, two+ sets it (server-backed
settings need a restart, which the echo notes). `nuwiki#commands#var` /
`commands.var`. Covered by `cmd.VimwikiVar_sets_global` (vim) /
`cmd.VimwikiVar_sets_option` (nvim) + `surface.{Vimwiki,Nuwiki}Var`.
- [x] **`VimwikiShowVersion`** _(fixed 2026-06-03)_ — added global
`VimwikiShowVersion`/`NuwikiShowVersion` (both clients) echoing
`g:nuwiki_version` (`0.1.0`) + the host editor, via
`nuwiki#commands#show_version` / `commands.show_version`. Covered by
`surface.{Vimwiki,Nuwiki}ShowVersion` in both keymap harnesses.
- [x] **`VimwikiReturn`/`NuwikiReturn`** _(fixed 2026-06-03)_ — upstream exposes
the smart-`<CR>` continuation as a command; nuwiki had it only as the
insert-mode `<expr>` mapping. _Fix:_ buffer-local `-nargs=*` command in both
clients that enters insert at EOL and triggers the same `<CR>` mapping
(`return_cmd` / `vimwiki_return`), continuing the list item / table row.
Upstream's mode-flag args are accepted but unused (continuation is inferred
from the line). Covered by `surface.{Vimwiki,Nuwiki}Return`.
- [x] **`VimwikiTableAlignQ` vs `AlignW`** _(fixed 2026-06-03; earlier framing
corrected)_ — the earlier note ("`gqq` align vs `gww` align-without-resize")
mis-read upstream: `vimwiki#tbl#align_or_cmd('gqq'|'gww')` calls the **same**
`vimwiki#tbl#format` when on a table — the only difference is the **non-table
fallback** (`normal! gqq` vs `normal! gww`, native paragraph-format; `gw` keeps
the cursor put). nuwiki had both unconditionally call `table_align`, so off a
table they did nothing. _Fix:_ a shared `table_align_or_cmd(cmd)` (both clients)
aligns when on a table row, else runs `normal! <cmd>``gqq`/`gq1` +
`VimwikiTableAlignQ``gqq`; `gww`/`gw1` + `VimwikiTableAlignW``gww`;
`NuwikiTableAlign``gqq`. (The `-nargs=?` attribute + the partial-column `2`
arg, accepted-and-ignored, are unchanged; true partial-column align stays a
separate deeper table feature.) Covered by `cmd.search_and_tablealign_nargs`
(`test-keymaps-vim.vim`) + the table alignment cases that still route through it.
- [x] `VimwikiTable` was `-nargs=1` vs upstream `-nargs=*` (couldn't pass
cols+rows). _Fix:_ all four defs (Vim+Neovim × Vimwiki+Nuwiki) are now
`-nargs=*` and forward `<f-args>` to `table_insert` (which already parsed
cols/rows). Covered by `cmd.VimwikiTable_passes_cols_and_rows`
(`test-keymaps.lua`, asserts a `:VimwikiTable 4 3` → 4-col, 3-row table) and
`cmd.VimwikiTable_accepts_cols_rows` (`test-keymaps-vim.vim`, no E488).
Follow-up (same pass): the no-arg **default cols** diverged (nuwiki `3` vs
upstream `5`; rows `2` already matched — confirmed against upstream
`vimwiki#tbl#create`). Aligned both backing fns to default 5 cols.
- [x] `VimwikiListChangeLvl` was `-nargs=?` (range-less) vs upstream
`-range -nargs=+`. _Fix:_ all four defs are now `-range -nargs=+` passing
`<line1>, <line2>, <f-args>`; `list_change_lvl(line1, line2, direction,
[plus_children])` (both clients) parses the required direction + optional
subtree flag and applies the level change to every list item in the range
via the existing `over_range` helper. Covered by
`cmd.ListChangeLvl_range_indents` (`test-keymaps.lua`) and
`cmd.VimwikiListChangeLvl_accepts_range_and_args` (`test-keymaps-vim.vim`,
no E481/E488).
- [x] **Neovim `follow_link_or_create` "botched"** _(new, 2026-05-31
command-attribute audit; FALSE ALARM — verified + tidied)_ — the audit
mis-read the function. `lua/nuwiki/commands.lua` had a single, correct
definition that already did the bare-word → `[[link]]` wrap (via
`wrap_cword_as_wikilink`) and was dispatched by the `<CR>` keymap — there was
no dead `pos_args()` code and no `s:wrap_word_under_cursor` (that name doesn't
exist). The only real artifact was two separate `vim.lsp.buf.definition()`
call sites for the "inside a wikilink" and "nothing to wrap" branches.
_Done:_ collapsed to one tail call (`if not cursor_inside_wikilink() and
wrap_cword_as_wikilink() then return end; vim.lsp.buf.definition()`) —
behaviour identical (wrap still skipped when already inside a link, via
short-circuit), just tidier. No `<CR>` behaviour change.
- [x] **Neovim `*Link` Ex-commands skip the bare-word wrap** _(new, same audit;
broader than first noted)_ — not just Split/VSplit: all eight Neovim
Ex-command link defs (`Vimwiki/Nuwiki` × `FollowLink`/`SplitLink`/
`VSplitLink`/`TabnewLink`) dispatched to raw `lua vim.lsp.buf.definition()`,
bypassing the wrap + create-on-follow that the Vim commands and the Neovim
`<CR>` family provide. _Fix:_ all eight now call
`lua require('nuwiki.commands').follow_link_or_create()` (keeping their
`split`/`vsplit`/`tabnew` prefixes); `TabDropLink` already used
`follow_link_drop` and is unchanged. Covered by
`cmd.VimwikiFollowLink_wraps_bare_word` +
`cmd.VimwikiSplitLink_wraps_bare_word` (`test-keymaps.lua`).
- [x] `VimwikiRebuildTags` `-bang` ("rebuild all") — `:…RebuildTags!` now passes
`{ all: true }`; the server (`tags_rebuild`) re-indexes **every** configured
wiki (via `wikis_snapshot()`) instead of just the current one. `-bang` added to
all four command defs.
- [x] **`:VimwikiSearch` / `VWS` now scope to the wiki** _(fixed 2026-06-03)_
upstream's search runs over the wiki's files, not the editor's CWD; nuwiki's
`lvimgrep /<args>/ **` searched CWD-relative `**`. _Fix:_ both commands route
through `nuwiki#commands#search` / `commands.search` (both clients), which
resolves the wiki whose root contains the current file (else the first
configured wiki, else CWD) and `lvimgrep`s `<root>/**/*<ext>` into the location
list; an empty pattern reuses the last search (`@/`); a no-match is caught and
reported (no raw `E480`). Added a `NuwikiSearch` alias alongside
`VimwikiSearch`/`VWS`. The `-nargs=*` attr fix (2026-06-03) is folded in.
nuwiki uses Vim's `lvimgrep` — the same mechanism vimwiki uses. **Minor
presentation divergence:** nuwiki populates + opens the location list (`/j`,
then `:lopen`) showing all matches, whereas upstream jumps to the first match
and leaves the list closed. Covered by `cmd.search_and_tablealign_nargs`
(`test-keymaps-vim.vim`).
- [x] **`VimwikiChangeSymbolTo` / `VimwikiListChangeSymbolI` lack `-range`**
_(new, 2026-05-31 re-audit; fixed same day)_ — upstream both carry
`-range -nargs=1`; nuwiki had `-nargs=1` only, so a visual-range invocation
raised `E481`. _Fix:_ all four defs (Vim+Neovim × the `Vimwiki*`/`Nuwiki*`
current-item forms — `VimwikiChangeSymbolTo`/`ListChangeSymbolI` and
`NuwikiChangeSymbol`) are now `-range -nargs=1` passing `<line1>,<line2>,
<q-args>` to a new `list_change_symbol_range(symbol, l1, l2)` helper that
loops `over_range` (both clients). `ChangeSymbolInListTo`/`InList` stay
range-less (and correctly keep `whole_list=true`). Covered by
`cmd.ChangeSymbolTo_range_sets_marker` (`test-keymaps.lua`) +
`cmd.VimwikiChangeSymbolTo_accepts_range` (`test-keymaps-vim.vim`).
- [x] **`VimwikiGenerateLinks` lacks `-nargs=?`** _(new, 2026-05-31 re-audit;
fixed same day)_ — upstream takes an optional rel-path arg; nuwiki was bare,
so `:VimwikiGenerateLinks foo` raised `E488`. _Fix:_ all four defs are now
`-nargs=?`; `links_generate([path])` (both clients) dispatches the existing
server command `nuwiki.links.generateForPath` (with `{path}`) when an arg is
given, else `nuwiki.links.generate` — a **real** path filter, not
accepted-and-ignored (the server's `links_generate_for_path` already
implements subtree scoping). Covered by
`cmd.GenerateLinks_accepts_optional_path` (`test-keymaps.lua`) +
`cmd.VimwikiGenerateLinks_accepts_path` (`test-keymaps-vim.vim`).
- [x] **`NuwikiGenerateTags` missing** _(new, 2026-05-31 re-audit; fixed same
day)_ — added `NuwikiGenerateTags` (Vim + Neovim) mirroring
`VimwikiGenerateTags``tags_generate_links`, with `-nargs=?` +
`-complete=…tags`. Covered by `cmd.NuwikiGenerateTags_exists` (both harnesses).
- [x] **`VimwikiIndex` family now has global entry points** _(fixed 2026-06-03)_
— upstream defines the Index family in `plugin/`; nuwiki had `Index`/`TabIndex`
only buffer-local (so a wiki couldn't be opened from a non-wiki buffer; only
`:…UISelect`/`:…ShowVersion` were global). _Fix:_ global
`Vimwiki/NuwikiIndex` + `Vimwiki/NuwikiTabIndex` (with `-count` for the wiki
number) added in `plugin/nuwiki.vim` (Vim) and `init.lua`
`_setup_global_commands` (Neovim), dispatching to the existing
`wiki_index`/`wiki_tab_index`. The buffer-local copies still shadow them inside
wiki buffers. Covered by `cmd.global_entry_points` in both harnesses.
- [x] **`VimwikiColorize` / `NuwikiColorize` drop their argument in the Neovim
branch** — both are `-nargs=1`, but the Neovim defs called `colorize()` with no
`<q-args>` (`ftplugin/vimwiki.vim:445,517`) while the Vim branch passes it. The
color name was silently ignored under Neovim (real bug, not just parity).
_Fix:_ Neovim command defs now pass `<q-args>`. While verifying, found a second
bug: `colorize()` inferred visual-vs-normal from `vim.fn.visualmode()`, which
returns the *last* session visual mode with stale `'<`/`'>` marks — so the
normal-mode command wrapped a leftover selection instead of the cword.
`colorize(color, visual)` now takes an explicit `visual` flag; the command and
normal mapping always wrap the cword. Third bug: the commands were not
`-range`, so selecting text and running `:VimwikiColorize` (which Vim turns
into `:'<,'>VimwikiColorize`) raised `E481: No range allowed`. All four
command defs are now `-range` and pass the range to the handler — a ranged
(visual) invocation wraps the `'<`/`'>` selection on both clients; the
`x`-mode `<Leader>wc` mapping passes the visual flag directly. Covered by
`cmd.VimwikiColorize_uses_arg`, `cmd.Colorize_ignores_stale_visual_selection`,
`colorize.visual_wraps_selection`, `cmd.Colorize_range_wraps_selection` in
`test-keymaps.lua` and `colorize.{command_wraps_cword,visual_wraps_selection,
ranged_command_no_error}` in `test-keymaps-vim.vim`.
- [x] **Colour spans aren't concealed** — a colorized word shows the literal
`<span style="color:…">…</span>` markup instead of just the coloured text.
_Fix:_ `autoload/nuwiki/colors.vim` `nuwiki#colors#refresh()` scans the buffer
and defines, per colour, a syntax region that conceals the `<span …>`/`</span>`
tags (`concealends`) and paints the wrapped text in the span's *actual* colour
(`guifg`/`ctermfg`). Wired from the syntax file (initial + on `:colorscheme`
reload), an `ftplugin` `TextChanged` autocmd, and directly from `colorize()`
for immediate effect. The LSP `@vimwikiColor` token no longer links to
`Constant` so it doesn't override the real colour on the concealed text in
Neovim. Works in Vim, coc, and Neovim (pure syntax + `:highlight`, no LSP
needed). Covered by `colorize.conceal_hides_tags_shows_text` (both harnesses)
and `colorize.command_conceals_immediately` (`test-keymaps-vim.vim`).
- [x] **`VimwikiTableMoveColumn{Left,Right}` dispatch to different backing names
per client** — converged: the Vim-branch commands now call
`nuwiki#commands#table_move_column_left/right` (existing aliases over the
`table_move_left/right` impls), matching the Neovim branch.
- [x] **`NuwikiTabIndex` lacks `-count` in the Neovim branch** — added `-count`;
also switched the Neovim `Index`/`TabIndex` family (`Vimwiki/NuwikiTabIndex`
and `Vimwiki/NuwikiIndex`) from `vim.v.count` (always 0 in command context) to
`<count>`, so a `:2NuwikiIndex` / `:3NuwikiTabIndex` count is now honored.
(The `Index` half was caught by the 2026-05-31 second re-audit and fixed then.)
- [x] **`VimwikiToggleListItem` / `Increment` / `DecrementListItem` `-range`** —
all three (both branches) are now `-range`; the client loops the per-line op
over `[<line1>, <line2>]` (`toggle_list_item_range` / `list_cycle_symbol_range`).
The server edits are version-less single-line, so each line's edit applies
independently — no server protocol change needed. Covered by
`cmd.toggle_list_item_range` in `test-keymaps.lua`.
- [x] **Diary-note family lacks `-count`** _(new, 2026-05-31 re-audit; fixed
same day)_ — upstream's `:Vimwiki{Make,TabMake,MakeYesterday,MakeTomorrow}DiaryNote`
+ `VimwikiDiaryIndex` (and the `Nuwiki*` forms) carry `-count=0`, where the
count selects the wiki number. _Fix:_ a real wiki selector end-to-end —
- Server (`commands.rs`): `OptionalUriArg` gained an optional `wiki` selector
and `resolve_diary_wiki(backend, uri, wiki)` now prefers it (via the existing
`resolve_wiki_selector`, reused — accepts a 0-indexed number) over the
buffer URI; used by `diary_open_relative` + `diary_open_index`.
- Clients: `s:diary_open`/`diary_open` (both clients) and
`diary_today/today_tab/yesterday/tomorrow/index` take a count and send
`{wiki: count-1}` when > 0. All 22 diary-note + DiaryIndex command defs
across the 4 contexts are now `-count=0` passing `<count>`. `diary_next`/
`diary_prev` correctly ignore the count.
Tests: `cmd.VimwikiMakeDiaryNote_has_count` (`test-keymaps.lua`),
`cmd.Vimwiki{MakeDiaryNote,DiaryIndex}_accepts_count` (`test-keymaps-vim.vim`).
_(The `Index`/`TabIndex` family was already `-count=0`, mapping the count to
the wiki number via `wiki_index`/`wiki_tab_index`.)_
- [x] **`VimwikiToggleRejectedListItem` / `NuwikiToggleRejected` lack `-range`**
_(new, 2026-06-02 re-audit; fixed same day)_ — upstream is `-range`
(`ftplugin/vimwiki.vim:351`) and binds it in **visual** mode (`glx`, line 543);
nuwiki's four defs were bare, so `:'<,'>VimwikiToggleRejectedListItem` raised
E481 and a ranged invocation acted cursor-only. This is the same `-range` class
fixed above for `ToggleListItem`/`Increment`/`Decrement``ToggleRejected` was
simply missed in that pass. _Fix:_ all four defs (Vim+Neovim × `Vimwiki*`/
`Nuwiki*`) are now `-range`, routed through a new `reject_list_item_range(l1,
l2)` looping `over_range` (both clients), mirroring `toggle_list_item_range`.
The visual `glx` keymap stays cursor-only, consistent with its `<C-Space>`/
`gln`/`glp` siblings (the keymap-range question is a separate, family-wide
item). Covered by `cmd.reject_list_item_range` (`test-keymaps.lua`) +
`cmd.VimwikiToggleRejectedListItem_accepts_range` (`test-keymaps-vim.vim`).
- [x] **`VimwikiRemoveDone` regained upstream's `-range`** _(new, 2026-06-02
re-audit; fixed 2026-06-02)_ — upstream is `-range` (`ftplugin/vimwiki.vim:362`,
"remove done items in the selection"); nuwiki had repurposed it to `-bang` only,
so `:'<,'>VimwikiRemoveDone` raised E481. _Fix:_ all four defs are now
`-bang -range`, dispatched by `nuwiki#commands#remove_done` /
`commands.remove_done(bang, range, l1, l2)` (both clients): `!` → whole-buffer
sweep (unchanged); an explicit range (`<range> > 0`, e.g. `:'<,'>`) → a new
`list_remove_done_range` sending `{range:[l1-1,l2-1]}`; otherwise the
cursor's current list. Server `remove_done_edit` gained an `Option<(u32,u32)>`
range that filters whole-doc victims by start line (`commands.rs`). Tests:
`remove_done_ranged_restricts_to_selected_lines` +
`remove_done_ranged_returns_none_when_range_has_no_done` (`commands_lists.rs`),
`cmd.remove_done_range` (`test-keymaps.lua`),
`cmd.VimwikiRemoveDone_accepts_range_and_bang` (`test-keymaps-vim.vim`).
- [x] **`-complete=` specs** _(new; done 2026-05-31 command-attribute pass)_
upstream attaches command-line completion to several commands; nuwiki had
none, so `<Tab>` at the `:` line never completed page / tag / colour names.
_Fix:_ new `autoload/nuwiki/complete.vim` with pure client-side, synchronous
completers (config + filesystem, never the LSP, so they work under vim-lsp,
coc and Neovim alike), wired via `-complete=customlist,…` into all branches:
- `nuwiki#complete#pages``VimwikiGoto`/`NuwikiGoto` (globs every wiki
root's `*<ext>` files → root-relative page names).
- `nuwiki#complete#colors``VimwikiColorize`/`NuwikiColorize` (keys from a
configured `color_dic` + colours already used in buffer `color:…` spans).
- `nuwiki#complete#tags``VimwikiSearchTags`/`GenerateTagLinks`/
`GenerateTags` (+ Nuwiki forms) — scans the wikis' files for `:tag:` runs
(the tag index lives in the server, which a synchronous completer can't
query; the file scan reads the same source the server indexes).
**Divergence (documented):** no file completer for `VimwikiRenameFile`
upstream completes a new-name arg because it renames itself, but nuwiki
delegates renaming to the LSP rename request (`:LspRename`/coc), which drives
its own prompt and takes no filename argument. Covered by
`complete.pages_globs_and_filters`, `complete.tags_scans_wiki_files`,
`complete.colors_from_buffer_spans` (`test-keymaps-vim.vim`).
- [x] **Minor command-attribute gaps** _(new)_ — all now real, not cosmetic:
`VimwikiNormalizeLink` is `-nargs=?` and with `1` wraps the `'<`/`'>` selection
(`wrap_visual_as_wikilink`, both clients; the `x`-mode `+` passes it too);
`VimwikiCheckLinks` is `-range` and a ranged invocation filters the report to
the current buffer's selected lines; `VimwikiColorize` `-nargs=1``-nargs=*`
(bare `:VimwikiColorize` prompts). Covered by
`normalize.visual_wraps_selection` (vim) /
`cmd.normalize_link_visual_wraps_selection` (nvim).
- [x] **`VimwikiSplitLink` / `VimwikiVSplitLink` lack `-nargs=*`** _(new,
2026-05-31 third re-audit; corrected during the 2026-05-31 command-attribute
pass)_ — the earlier note was wrong: upstream's optional args are NOT a target
link but `reuse_other_split_window` (a:1) and `move_cursor` (a:2) — the link
is always the one under the cursor (`vimwiki#base#follow_link`). nuwiki's defs
took no args, so `:VimwikiSplitLink 0 1` (upstream muscle memory / migrated
scripts) raised E488. _Fix:_ all eight defs (both clients × Split/VSplit ×
Vimwiki/Nuwiki) are now `-nargs=*`, restoring signature parity. **Divergence
(documented):** the two flags are *accepted but not acted on* — nuwiki always
opens a fresh split and follows in it, because the follow is an async LSP
jump and honoring `move_cursor`/`reuse` would require threading a completion
callback through that path (disproportionate for this rarely-used arg form;
the no-arg case — the 99% path — is fully equivalent). Covered by
`cmd.VimwikiSplitLink_accepts_args` (`test-keymaps-vim.vim`, no E488).
### Config
- [x] **`autowriteall`** _(new, 2026-06-03 re-audit; fixed same day)_ — upstream
global (`vars.vim:136`, default **`1`/ON**): while a wiki buffer is current,
mirror Vim's global `'autowriteall'` (auto-save a modified buffer on page
switch / `:make`), restoring the prior value on leave (upstream
`plugin/vimwiki.vim:165-166,39`). nuwiki had no equivalent → out-of-box
behavioral divergence. _Fix:_ client-side, both clients — Neovim
`lua/nuwiki/ftplugin.lua` (`setup_autowriteall`, BufEnter/BufLeave save+set/
restore) gated by the `autowriteall` setup option (default true); Vim
`ftplugin/vimwiki.vim` (`NuwikiAutoWriteAll` augroup) gated by
`g:nuwiki_autowriteall` (default 1). Covered by `config.autowriteall_applied`
in both harnesses.
- [x] **`table_auto_fmt`** _(new, 2026-06-03 re-audit; fixed same day)_
upstream global (`vars.vim:183`, default **`1`/ON**): re-format the table under
the cursor on `InsertLeave` (upstream `plugin/vimwiki.vim:330`). nuwiki had no
equivalent (tables formatted only via `gqq`/`gww`). _Fix:_ client-side
`InsertLeave` autocmd, gated by config — Neovim `table_auto_fmt` setup option
(default true, `ftplugin.lua` `setup_table_auto_fmt`); Vim
`g:nuwiki_table_auto_fmt` (default 1, `NuwikiTableAutoFmt` augroup). Both guard
on the cursor line containing `|` before the (async LSP) `table_align`, so
non-table edits don't round-trip. Covered by `config.table_auto_fmt_autocmd`
in both harnesses. _(Divergence: nuwiki's `table_align` is an async server
edit, vs upstream's synchronous format — the result lands a beat after Esc.)_
- [x] **`table_reduce_last_col`** _(fixed 2026-06-03)_ — per-wiki flag (default
off): `column_widths` clamps the last table column to width 1 so it isn't
padded to fill; threaded through `table_align_edit`/`table_move_column_edit`
and both table commands. Test `table_reduce_last_col_keeps_last_column_minimal`.
- [x] **`user_htmls`** _(fixed 2026-06-03)_`allToHtml` now prunes orphan HTML
(no wiki source) under `html_path` (`export_ops::prune_orphan_html`), skipping
the CSS file and any basename in `user_htmls`; gated on
`!html_filename_parameterization` (mirrors upstream). Test
`prune_orphan_html_deletes_sourceless_keeps_protected`.
- [x] **`hl_headers` / `hl_cb_checked`** _(resolved as divergence 2026-06-03)_
upstream opt-in toggles (default off) that turn ON per-level header / checked-
checkbox highlighting. nuwiki **already** highlights headers per-level
(`@vimwikiHeading.level1..6`) and checkboxes always-on via LSP semantic
tokens — richer than the upstream toggles and on by default. Implementing the
toggle faithfully (default off) would *remove* nuwiki's coloring, a
regression. Folded into the LSP-highlighting divergence family (with `maxhi`).
- [x] **`list_margin` — buffer-side parity + markdown default** _(new,
2026-06-02 re-audit; fixed 2026-06-02)_ — upstream (`vars.vim:516`, doc
`vimwiki.txt:2667`): left-margin **width in spaces for buffer-side generated
lists** — generated links (`:VimwikiGenerateLinks`), the TOC, the tags index,
and the diary index — default `-1` but **`0` for markdown** wikis
(`vars.vim:651`). nuwiki had instead applied it **only** as an HTML CSS
`margin-left:<n>em`. _Fix (true-parity, chosen over keeping the HTML meaning):_
dropped the HTML `em`-margin entirely (renderer `list_margin` field/method +
the `render_page_html` param removed) and now prepend `max(0, list_margin)`
leading spaces to every generated bullet in `build_toc_text` /
`build_links_text` / `build_tag_links_text` / `diary::build_index_body` (the
TOC keeps its per-depth nesting after the base margin); headings stay at
column 0. `From<RawWiki>` derives `0` for markdown wikis when the key is unset.
**Divergence (documented in the field doc):** upstream resolves a negative
value to the buffer's `'shiftwidth'`, which the server can't observe, so
negatives collapse to zero indent (matches nuwiki's prior default output).
Tests: `list_margin_indents_generated_bullets_not_headings` (`link_health.rs`),
`markdown_wiki_derives_list_margin_zero_when_unset` (`index_and_config.rs`).
- [x] **`diary_months`** _(new, 2026-06-02 re-audit; fixed 2026-06-02)_
upstream global (`vars.vim:141`) maps month numbers → names for the generated
diary-index tree; nuwiki hardcoded English. _Fix:_ per-wiki `diary_months:
Vec<String>` (default the 12 English names) on `WikiConfig`/`RawWiki`, threaded
into `build_index_body`; a missing/empty slot falls back to the English
`month_name`. Tests: `build_index_body_uses_custom_diary_months` +
`…_falls_back_per_missing_month_slot` (`diary.rs`), config round-trip
(`index_and_config.rs`).
- [ ] **`markdown_header_style`** _(new, 2026-06-02 re-audit; deferred)_
upstream global (`vars.vim:174`, default `1`): blank lines after a **generated**
header in markdown syntax. **Intentionally deferred:** nuwiki's generators
emit vimwiki syntax only (`caption_line` always writes `= H =`, never `# H`),
so there's no markdown header to attach the style to yet. This is part of a
broader "markdown generated-content" gap — markdown-style heading generation
comes first, then this blank-line control on top. See the divergence note
below.
- [x] **`diary_caption_level = -1` now representable** _(new, 2026-06-02
re-audit; fixed 2026-06-02)_ — upstream allows `-1` (`vars.vim:507` `min: -1`);
nuwiki had typed the field as `u8`, so a `-1` config failed deserialization
and silently fell back to `0`. _Fix:_ widened to `i8` across
`WikiConfig`/`RawWiki`/`WikiDefaults`; `build_index_body` clamps `< 0` to a
base tree level of `0`. (nuwiki builds the index tree from dates, so upstream's
`-1` = "no per-page captions" semantic doesn't apply — it just clamps; noted in
the field doc.) Tests: `diary_caption_level_accepts_negative`
(`index_and_config.rs`), `build_index_body_clamps_negative_caption_level_to_zero`
(`diary.rs`).
- [x] **`auto_header`** _(fixed 2026-06-03)_ — new wiki pages get a `= Stem =`
H1 from the filename, skipping the wiki/diary index. Client `BufNewFile`
autocmd, both clients (`init.lua` + `plugin/nuwiki.vim`; `auto_header`
handlers in both command layers). Default off.
- [x] **`create_link`** _(fixed 2026-06-03)_ — server `resolve_target_uri` only
synthesises a missing-page URI when `create_link` is set; off → following a
missing link is a no-op. Default true.
- [x] **`dir_link`** _(fixed 2026-06-03)_ — a `[[dir/]]` link resolves to
`<dir>/<dir_link><ext>` when set, else opens the directory
(`resolve_target_uri`, using `LinkTarget.is_directory`).
- [x] **`auto_chdir`** _(fixed 2026-06-03)_`:lcd` into the owning wiki root on
buffer enter; both clients (`ftplugin.lua` `setup_auto_chdir` /
`NuwikiAutoChdir` augroup), via `wiki_root_for` resolvers. Default off.
- [x] **`bullet_types` / `cycle_bullets`** _(fixed 2026-06-03)_ — per-wiki
`bullet_types` (default `-`,`*`,`#`) + `cycle_bullets` (default off): on
indent/dedent of an unordered item, `change_level_edit` rotates the glyph
through `bullet_types` (depth-keyed ring). Tests `cycle_bullets_*`.
- [x] **`commentstring`** _(fixed 2026-06-03)_ — aligned to upstream's no-space
`%%%s` (`ftplugin/vimwiki.vim`); comment parsing is independent (lexer).
- [x] **`color_tag_template`** _(fixed 2026-06-03)_`:VimwikiColorize` now
wraps via a configurable template (`color_tag_template` / `g:nuwiki_color_tag_template`,
split on `__CONTENT__` with `__COLORFG__` → colour), both clients; colour
spans render in HTML export via the `valid_html_tags` `span` passthrough.
`color_dic` now ships a populated default palette (red/green/blue/…) instead
of empty.
- [x] **`rss_max_items` / `rss_name`** _(fixed 2026-06-03)_`HtmlConfig`
fields (default `10` / `rss.xml`); `rss_path` honours `rss_name`, `write_rss`
caps at `rss_max_items` (`0` = unlimited).
- [x] **RSS feed structure fidelity** _(fixed 2026-06-03)_`write_rss` now
emits upstream's shape: `xmlns:atom`; channel `<link>` → the diary index page
(`base_url + diary_rel + diary_index + .html`); `<atom:link rel="self">`;
channel + item `<pubDate>` (RFC-822, derived from the diary date via
`to_days_epoch` weekday math); per-item `<guid isPermaLink="false">` with the
bare date; and a `<![CDATA[…]]>` rendered-HTML body per entry
(`render_entry_body` reads+parses+renders with the wiki's colour/valid-tag/
emoji opts; `cdata_safe` splits `]]>`). Tests:
`write_rss_uses_base_url_for_public_links`, `write_rss_honours_rss_name_and_max_items`.
- [x] **`emoji_enable`** _(fixed 2026-06-03)_ — render-time `:alias:` → glyph
substitution (curated common set, `substitute_emoji`/`emoji_for`), default on,
wired via `with_emoji` from `HtmlConfig`. _(Divergence: upstream's
`emoji_enable` is buffer conceal + completion; nuwiki implements it as export
substitution — the user-facing emoji output — which is the practical intent.)_
2026-06-02 00:37:37 +00:00
- [x] **`html_header_numbering` (+`_sym`)** — numbered HTML headers. _Fix:_
`HtmlConfig` (`config.rs`) gained `html_header_numbering: u8` (start level,
`0` = off) + `html_header_numbering_sym: String` (trailing symbol), both
round-tripped through `RawWiki`/defaults. A `HeadingNumberer` in
`nuwiki-core/src/render/html.rs` walks top-level headings in document order,
bumping per-depth counters and emitting a dotted prefix (`1`, `1.1`, …);
`render_body` numbers only document-level headers (nested ones in lists/
quotes stay unnumbered, matching upstream's line scan). Wired via
`with_header_numbering()` from `export.rs`. Tests:
`render_page_html_numbers_headers_when_enabled`,
`…_numbering_skips_headers_above_start_level`,
`…_no_header_numbering_by_default` (`html_export.rs`) + config round-trip +
defaults (`index_and_config.rs`).
- [x] **`valid_html_tags`** _(fixed 2026-06-03)_ — HTML export passes the
allowlisted inline tags through verbatim and escapes the rest, via a
render-time tag-aware escaper (`write_escaped_allowing`/`match_allowed_tag`),
mirroring upstream's `s:safe_html_line` regex (no parser change). Default
`b,i,s,u,sub,sup,kbd,br,hr,div,center,strong,em`; `span` always allowed.
Tests in `html_export.rs`.
- [x] **`generated_links_caption`** + **`toc_link_format`** _(fixed 2026-06-03)_
`generated_links_caption` emits `[[page|FirstHeading]]` from a
page→heading map (`ops::page_captions`); `toc_link_format` `1`
`[[#anchor]]` (no description) vs `0``[[#anchor|title]]`. **`markdown_link_ext`
stays deferred** — part of the markdown generated-content cluster (see the
divergence note below).
- [ ] **`list_ignore_newline` / `text_ignore_newline`** — config keys round-trip
(`HtmlConfig`, default `true` = upstream's default, which nuwiki already
honours: soft newlines render as spaces). The **`false``<br>`** behaviour
is **deferred**: the parser collapses soft breaks to spaces before the
renderer sees them, so `=false` needs a parser-level soft-break node (risky;
touches every parse consumer). Documented as a parser limitation; the common
default works.
### Mappings
- [x] **Visual `<CR>` normalize-link** _(fixed 2026-06-03)_ — upstream binds
visual `<CR>` to `<Plug>VimwikiNormalizeLinkVisualCR``:VimwikiNormalizeLink 1`,
identical to visual `+`. nuwiki had only visual `+`. _Fix:_ added `x <CR>`
the same wrap-selection handler (`normalize_link(true)`) in both clients
(`lua/nuwiki/keymaps.lua`, `ftplugin/vimwiki.vim`). Covered by
`map.visual_cr_wraps_selection` in both harnesses.
- [x] **Insert `<S-CR>` multiline list item** _(fixed 2026-06-03)_ — upstream's
insert `<S-CR>` (`VimwikiReturn 2 2``kbd_cr` with no new marker) continues
the current list item on a new line indented to align under the item's text.
_Fix:_ new `smart_shift_return` (both clients, `<expr>` insert mapping): on a
list item returns `<CR>` + (indent +) `marker-width + 1 (+4 for a checkbox)`
spaces — no marker; off a list item, a plain `<CR>`. Mirrors `smart_return`'s
auto-indent handling (column math verified against upstream's `s:text_begin`).
Both `smart_return` and `smart_shift_return` also gained upstream's
`pumvisible()` guard (return a plain `<CR>` to accept a completion). _Minor
divergence:_ `<S-CR>` on an *empty* bullet emits the aligned no-marker
continuation rather than upstream's "blank line above, keep the marker" — a
niche case; the common non-empty continuation is exact. Covered by
`map.insert_shift_cr_multiline` (`test-keymaps.lua`) +
`cr.shift_cr_multiline{,_checkbox}` (`test-keymaps-vim.vim`).
- [x] **`gLH` / `gLL` / `gLR`** _(fixed 2026-06-03)_ — upstream binds these as
case-variant **aliases** of `gLh` / `gLl` / `gLr` (dedent / indent whole item,
renumber all lists; upstream `ftplugin/vimwiki.vim:553,555,561`). nuwiki had
only the lowercase forms. _Fix:_ added the uppercase-suffix aliases (same
actions) in both clients (`lua/nuwiki/keymaps.lua`, `ftplugin/vimwiki.vim`).
Covered by `map[n].gL{H,L,R}` in both harnesses.
- [x] **`<D-CR>` (Cmd+Return) tab-drop alias** _(new, 2026-05-31 re-audit)_
added `<D-CR>``follow_link_drop` alongside `<C-S-CR>` in both clients
(`lua/nuwiki/keymaps.lua`, `ftplugin/vimwiki.vim`); docs updated. Covered by
`map[n].<D-CR>` in both harnesses.
- [x] **`<Leader>w<Leader>m` missing from the Vim *global* map block** _(new)_
`plugin/nuwiki.vim`'s global family now binds `<Leader>w<Leader>m` (tomorrow)
and `<Leader>w<Leader>t` opens today in a new tab (was the same collision the
buffer-local maps had). Covered by `map[n].<Leader>w<Leader>m` in both harnesses.
- [x] **`<Leader>w<Leader>m` missing from the Neovim *global* map block**
_(new, 2026-05-31 map_prefix audit)_ — the same collision survived in
`lua/nuwiki/init.lua`'s `_setup_global_mappings`: `<prefix><Leader>t` was
bound to tomorrow and `<prefix><Leader>m` (tomorrow) was absent entirely.
Fixed so `<prefix><Leader>t` opens today in a new tab and `<prefix><Leader>m`
is tomorrow — now consistent with the Vim global side + upstream. Covered by
`global_maps.diary_tab_and_tomorrow_targets` (`test-keymaps.lua`).
---
## Intentional divergences (no action — architectural)
These upstream options have no nuwiki equivalent by design, because nuwiki is
an LSP-backed reimplementation rather than a pure-VimL plugin. Listed so future
audits don't re-flag them.
- `nested_syntaxes` / `automatic_nested_syntaxes` — code-fence languages are
auto-detected from the fence tag (`syntax/vimwiki.vim`); always on, no toggle.
- `maxhi` — existence-based link highlighting; superseded by LSP diagnostics.
- `conceal*` (`conceallevel`, `conceal_onechar_markers`, …) — semantic tokens
replace conceal.
- `ext2syntax` — syntax chosen by extension / `syntax` key automatically.
- `folding` (upstream string) — nuwiki uses `folding = 'lsp' | 'expr' | 'off'`
backed by LSP foldingRange.
- `key_mappings` (dict) — replaced by Lua `mappings.<group>` + the
`g:nuwiki_no_<group>_mappings` globals.
- `auto_tags` (upstream `0`) — upstream auto-updates an on-disk tag *metadata*
file on save. nuwiki has no such file: the LSP re-indexes tags on every
`didChange`, so tag search/jump/completion are always fresh. The setting's
intent is therefore always satisfied (the `auto_generate_tags` regen of the
in-buffer links section is the configurable on-save piece).
- Mouse maps (`<2-LeftMouse>`, `<MiddleMouse>`, …) — upstream binds them
unconditionally; nuwiki ships them **opt-in** (`mappings.mouse` /
`g:nuwiki_mouse_mappings`). Deliberate, so they're not always-on.
- `global_ext` (upstream `1`) — nuwiki's ftdetect always maps the configured
wiki extension(s) to the `vimwiki` filetype regardless of location; there's no
per-wiki "only inside the root" toggle. Effectively always-on, like
`nested_syntaxes`.
- `syntax` default name — nuwiki defaults the per-wiki `syntax` to `vimwiki` (its
primary syntax) where upstream defaults to `default`; a naming difference, not
a behavioural one.
- `CJK_length`, `listing_hl*`, `schemes_*`, `w32_dir_enc`, `menu`,
`rx_todo` / `tag_format` — syntax internals, menu, or Vim/Win shims.
- **Default output-location derivations** _(noted 2026-06-02 re-audit)_ — where a
key is unset, nuwiki derives a different default *location* than upstream, by
design (root-relative, self-contained per wiki): `path_html`
`<root>/_html` (upstream: sibling `<wikidir>_html/`); `template_path`
`<root>/_templates` (upstream: global `~/vimwiki/templates/`); `diary_rel_path`
`diary` vs upstream `diary/` (trailing slash only — paths are joined
identically, so behaviour matches). Listed so future audits stop re-flagging
them; users can still set any of these explicitly.
- **Generated-content syntax is vimwiki-only (for now)** _(noted 2026-06-02
re-audit)_ — the buffer-side generators (TOC, generated links, tags index,
diary index) always emit vimwiki markup (`= H =` headings via `caption_line`),
regardless of the wiki's `syntax`. Consequently the markdown-specific
`markdown_header_style` (blank lines after a generated *markdown* header) has
nothing to attach to and is deferred. This is a deliberate scoping choice, not
an oversight: markdown generated-content (markdown-style headings, then
`markdown_header_style`, `markdown_link_ext` in generated links) is planned as
a single later effort. Until then, markdown wikis get vimwiki-syntax generated
sections.
---
## Notes
- Audit confidence: upstream defaults pulled from vimwiki `master`; a couple of
values (`CJK_length`, `links_space_char`) were summarized loosely but don't
affect the gap list.
- nuwiki adds some commands with no upstream equivalent (e.g.
`:NuwikiFindOrphans`) — additive, not divergences.
- Re-audited 2026-05-31 (config / mappings / commands, three parallel agents
against vimwiki `master`). All recent P1/P2/P3 fixes — split/tab link-follow,
`gl`/`gL` change-symbol + bare-`gl`/`gL` remove-checkbox, `<CR>`-family
create-on-follow, `<M-CR>` badd, `<Leader>w<Leader>t`/`m`, `VimwikiColorize`
arg+range, colour-span conceal — confirmed present and correct in **both**
clients. New gaps from that pass (diary-note `-count`, `-complete` specs,
`<D-CR>`, global `<Leader>w<Leader>m`, plus `global_ext`/`syntax`-name notes)
were added above; no previously-closed item regressed.
- Re-audited again 2026-05-31 (third pass) after the range/bang/visual and
diary-week work. All those fixes confirmed in both clients; no regressions.
Confirmed Neovim **does** bind the text objects (`nuwiki.textobjects.attach`).
Found + fixed an oversight (Neovim `Vimwiki/NuwikiIndex` still read
`vim.v.count`; now `<count>`, completing the `TabIndex` fix). New gaps logged:
`VimwikiSplitLink`/`VSplitLink` missing `-nargs=*`; mouse-maps-opt-in noted as
an intentional divergence. Config pass found no new gaps; all shipped defaults
match upstream except the documented `syntax`-name divergence.
- Re-audited 2026-06-02 (fourth pass — commands / config / mappings, three
parallel agents) against vimwiki `a54a300` (cloned locally; upstream had **no
commits since the 2026-05-31 pass**, so this was miss-catching, not new
upstream surface). **Mappings: clean** (~55 keys + the global family verified
identical in both clients; no new gaps, no regressions). **Commands:** one real
bug — `VimwikiToggleRejectedListItem` missing `-range` (fixed same day, see
P3); plus `VimwikiRemoveDone`'s lost `-range` logged as low-priority.
**Config:** four new items logged (`list_margin` semantics/markdown-default,
`diary_months`, `markdown_header_style`, `diary_caption_level = -1`); the
default output-location derivations (`path_html`/`template_path`/
`diary_rel_path`) recorded as intentional divergences. No previously-closed
item regressed.
- Fixed the fourth-pass findings (2026-06-02), in order: `VimwikiRemoveDone`
`-range`; `list_margin` reworked to upstream's buffer-side meaning (HTML
`em`-margin dropped) + markdown-`0` default; `diary_months`;
`diary_caption_level` widened to `i8`. `markdown_header_style` deferred as part
of the documented "generated-content is vimwiki-only" divergence. All five
P1-blocking sections remain clear; only niche P3 items (and the deferred
markdown work) are left.
- Re-audited 2026-06-03 (fifth pass — commands / config / mappings, three
parallel agents) against vimwiki `a54a300` (still no upstream commits since
2026-04-30). **Mappings: clean** again (confirmed `gLH/gLL/gLR` correct; no new
gaps, no regressions). **Commands:** one real bug — `VimwikiGoto`/`NuwikiGoto`
were `-nargs=1` (E471/E488 + unreachable prompt), fixed same day; plus
`VimwikiRenameFile`'s missing `-nargs=?` logged (subsumed by the LSP-rename
divergence). **Config:** five new items — `autowriteall` and `table_auto_fmt`
(both ON upstream → out-of-box divergences, implemented same day),
`table_reduce_last_col`, `user_htmls`, `hl_headers`/`hl_cb_checked` logged; and
a `commentstring` value-mismatch note. No previously-closed item regressed.
- Cleared the **entire P3 Commands section** (2026-06-03): `VimwikiRenameFile`
`-nargs=?`; `VimwikiVar`/`NuwikiVar` (get/set client config); `VimwikiReturn`/
`NuwikiReturn` (command form of smart-`<CR>`); the `TableAlignQ` vs `AlignW`
split (corrected — the real difference is the non-table `normal! gqq`/`gww`
fallback, now via `table_align_or_cmd`); `:VimwikiSearch`/`VWS` scoped to the
wiki root; and global `Index`/`TabIndex` entry points. All client-side (no
server change). Both harnesses green (Neovim 299, Vim 291/18/21).
- Merged the `calendar-support` branch (2026-06-03): calendar-vim integration
now wires `g:calendar_action`/`g:calendar_sign` to nuwiki's diary paths via a
`FileType vimwiki` autocmd (`plugin/nuwiki.vim`, both clients), with a
`use_calendar` opt-out (`g:nuwiki_no_calendar` / setup `use_calendar = false`).
So `use_calendar` is no longer an intentional divergence — removed from that
list above. See `development/calendar-vim-integration.md`.
- Cleared the **entire P3 Mappings section** (2026-06-03): visual `<CR>`
normalize-link (same handler as visual `+`), and insert `<S-CR>` → multiline
list item (`smart_shift_return`, continuation with no new marker aligned under
the text). Both client-side, both clients, tested in both harnesses (Neovim
305, Vim 299/18/21). Only P3 Config items now remain open.
- Closed the **P3 Config section** (2026-06-03), in groups: rss_name/rss_max_items
+ RSS structure fidelity, toc_link_format, generated_links_caption,
table_reduce_last_col, color_dic palette, commentstring, auto_chdir;
auto_header, create_link, dir_link; cycle_bullets/bullet_types;
valid_html_tags (render-time allowlist passthrough), emoji_enable (export
substitution), color_tag_template (configurable colorize wrap); user_htmls
(orphan-HTML pruning). **Intentional non-implementations:**
`hl_headers`/`hl_cb_checked` are superseded by nuwiki's always-on LSP
per-level header + checkbox highlighting (like `maxhi`). **Deferred:** the
markdown cluster (`markdown_header_style`, `markdown_link_ext`, markdown-style
generated headers) remains one future effort; `list_ignore_newline` /
`text_ignore_newline` = `false` (→ `<br>`) needs a parser-level soft-break
node (the `= true` default, which is upstream's default, already works).