49 KiB
49 KiB
vimwiki → nuwiki Parity Gaps
Tracking document for closing the gap between nuwiki and the original 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)
- Vim client split/tab link-follow commands —
VimwikiSplitLink,VimwikiVSplitLink,VimwikiGoBackLink,VimwikiTabnewLink,VimwikiTabDropLinkexist 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 inautoload/nuwiki/commands.vim; true:tab droptab-reuse wired in both clients (lua/nuwiki/commands.luaopen_uri'tabdrop'case);<C-S-CR>repointed to tab-drop. gl<symbol>change-symbol mappings — upstream's one-key bullet/number symbol change (gl*gl#gl-gl1glagli… plusgL…) has no nuwiki keymap; only:…ChangeSymbol*commands exist. Fix: added normal-modegl{-,*,#,1,i,I,a,A}(item) +gL…(whole list) inlua/nuwiki/keymaps.lualists group andftplugin/vimwiki.vimVim-branch lists block, wired to the existinglist_change_symbol.1)stays command-only (upstream shadows it with1.); normal-mode only (server acts on cursor item / whole list, not a visual range).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 baregl/gLto remove-checkbox-item / -in-list for exact upstream parity (lua/nuwiki/keymaps.lualists block,ftplugin/vimwiki.vimVim-branch lists block); they share thegl…prefix so they fire aftertimeoutlen, just like upstream. Remove-done is now command-only::NuwikiRemoveDone(current list) gained a-bangso:NuwikiRemoveDone!reaches the whole-buffer sweep thatgL<Space>previously held (the only prior entry point). Normal-mode only.
Regressions (reported by users — verify, then fix)
<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-lspreadfile()s it for the quickfix entry (autoload/lsp/utils/location.vim), which throwsE484for a missing file and aborts the jump (→ no create-on-follow); coc, lacking an explicit open command, fell back to the user'scoc.preferences.jumpCommand(→ new tab). The server itself was correct throughout — verified over JSON-RPC thattextDocument/definitionreturns a synthesised location for missing pages and resolves existing links. Fix: stop delegating.nuwiki#commands#follow_link_or_create()/follow_link_drop()now resolvetextDocument/definitiondirectly and open the URI ourselves via a shareds:open_definition(open_cmd)helper (autoload/nuwiki/commands.vim) —:editfor<CR>,tab dropfor<C-S-CR>.:editopens 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 bycr.follow_opens_missing_page_in_current_windowindevelopment/tests/test-keymaps-vim.vim; manually reproducible viadevelopment/start-vim.sh(vim-lsp) anddevelopment/start-vim-coc.sh(coc).- coc.nvim reported valid links as broken / couldn't resolve — only via
the new
development/start-vim-coc.shlauncher: its generatedcoc-settings.jsonregistered the server withcommand/filetypesbut noinitializationOptions, sowiki_rootnever reached the server and it resolved links against its default (~/vimwiki). Fix: the launcher now emitsinitializationOptionsandsettings.nuwikimirroring the vim-lsp client (autoload/nuwiki/lsp.vims: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 owncoc-settings.jsonper the README snippet.)
P2 — Moderate (real users hit these)
<Leader>w<Leader>tcollision — upstream = today-in-new-tab; nuwiki bound both<Leader>w<Leader>tand<Leader>w<Leader>mto tomorrow's diary. Fix:<Leader>w<Leader>tnow callsdiary_today_tab(today in a new tab),<Leader>w<Leader>mstays tomorrow — exact upstream parity (lua/nuwiki/keymaps.lua,ftplugin/vimwiki.vim; docs + README updated). Covered bydiary.leader_t_opens_today_in_tab/diary.leader_m_opens_tomorrow.- Generated-section captions hardcoded — TOC/links/tags headings and
their levels were hardcoded (
= Contents =,= Generated Links =,= Tags =, all level 1). Fix: added per-wikitoc_header/toc_header_level,links_header/links_header_level,tags_header/tags_header_level(defaultsContents/Generated Links/Generated Tags, level 1 — matching upstream; this also corrects the tags index heading fromTags→Generated Tags). Acaption_line(name, level)helper emits the markers; the text + level thread throughtoc_edit/links_edit/tag_links_edit(+ the auto_toc save hook) andfind_section_rangematches the configured text so regen stays idempotent. Tests:captions_honour_custom_header_and_level(link_health.rs) + config round-trip inindex_and_config.rs. - On-save autoregen family — added per-wiki
auto_generate_links,auto_generate_tags,auto_diary_index(all defaultfalse, like upstream), wired into thedid_savehook (crates/nuwiki-lsp/src/lib.rs) mirroringauto_toc: regen runs only when the section already exists (links_rebuild_edit/tag_links_rebuild_edit), andauto_diary_indexregenerates the diary index when a dated diary entry is saved (diary_generate_index_edit, which handles an open/closed/absent index file).auto_tagsis 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). listsym_rejected— the rejected glyph was a hardcodedconst '-'. Fix:ListSymsgained arejectedfield +new_with_rejected()(crates/nuwiki-core/src/listsyms.rs); per-wikilistsym_rejectedkey (default-) added toWikiConfig;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 inindex_and_config.rs.custom_wiki2html(+_args) andbase_url— external HTML converter hook + export URL prefix. Fix:HtmlConfig(config.rs) gainedcustom_wiki2html,custom_wiki2html_args,base_url(all default"", round-tripped throughRawWiki/From/defaults).write_page(commands.rs) now takes aforceflag and, whencustom_wiki2htmlis set, hands the page torun_custom_wiki2html()— which shells out viash -cwith 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.forceis threaded throughexport_current(false),export_all(its own flag), and thedid_saveauto-export path (false).write_rssnow prefixes the channel + item links withbase_url(<base_url><diary_rel_path>/<date>.html) when set, falling back to the entry'sfile://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).map_prefix—<Leader>wwas hardcoded across the map layer. Fix: mirror vimwiki'sg:vimwiki_map_prefixwith a configurable prefix (Neovimmap_prefixsetup option, default'<Leader>w'; Vimg: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: newtest-keymaps-vim-prefix.vimharness (21 cases, driven bytest-keymaps-vim.sh) +map_prefix.relocates_wiki_family(test-keymaps.lua). Docs: README,doc/nuwiki.txt, lua config comment.<M-CR>badd-link — was mouse-only (<MiddleMouse>) / command-only (:VimwikiBaddLink); upstream binds<M-CR>→VimwikiBaddLink(confirmed against upstreamftplugin/vimwiki.vim). Fix: added normal-mode<M-CR>→badd_linkin the links group of both clients (lua/nuwiki/keymaps.lua,ftplugin/vimwiki.vim; docs + README updated). Covered bymap[n].<M-CR>in both keymap harnesses.diary_caption_leveldefault divergence — nuwiki defaulted to1, upstream0. Fix:wiki_defaults()incrates/nuwiki-lsp/src/config.rsnow defaultsdiary_caption_level: 0(year captions top-level, months one below), still per-wiki overridable. Testdefaults_carry_vimwiki_per_wiki_keysupdated.diary_start_week_day— removed inc63ec67(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 keydiary_weekly_style:iso(default —YYYY-Www, Monday-based, nuwiki's original) ordate/vimwiki(YYYY-MM-DDof the week-start day, upstream parity, honouring the restoreddiary_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.rsdiary_open_relative+ next/prev pivot) use it. Defaults keep existing nuwiki weekly files working (no breaking change). Covered by calendar tests incrates/nuwiki-core/tests/diary.rsand the config round-trip incrates/nuwiki-lsp/tests/index_and_config.rs.
P3 — Niche / low impact
Commands
VimwikiGoto/NuwikiGotowere-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-arginput('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 bycmd.VimwikiGoto_accepts_multiword(test-keymaps-vim.vim).VimwikiRenameFile-nargs=?(new, 2026-06-03 re-audit; fixed 2026-06-03) — upstream is-nargs=? -complete=…file; nuwiki's defs were bare, so:VimwikiRenameFile fooraised 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.VimwikiVar/NuwikiVar(fixed 2026-06-03) — config-var get/set introspection (upstreamvimwiki#vars#cmd). Fix: global command in both clients — no arg prints the client config (Vim: everyg:nuwiki_*; Neovim: the resolvedsetup()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 bycmd.VimwikiVar_sets_global(vim) /cmd.VimwikiVar_sets_option(nvim) +surface.{Vimwiki,Nuwiki}Var.VimwikiShowVersion(fixed 2026-06-03) — added globalVimwikiShowVersion/NuwikiShowVersion(both clients) echoingg:nuwiki_version(0.1.0) + the host editor, vianuwiki#commands#show_version/commands.show_version. Covered bysurface.{Vimwiki,Nuwiki}ShowVersionin both keymap harnesses.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 bysurface.{Vimwiki,Nuwiki}Return.VimwikiTableAlignQvsAlignW(fixed 2026-06-03; earlier framing corrected) — the earlier note ("gqqalign vsgwwalign-without-resize") mis-read upstream:vimwiki#tbl#align_or_cmd('gqq'|'gww')calls the samevimwiki#tbl#formatwhen on a table — the only difference is the non-table fallback (normal! gqqvsnormal! gww, native paragraph-format;gwkeeps the cursor put). nuwiki had both unconditionally calltable_align, so off a table they did nothing. Fix: a sharedtable_align_or_cmd(cmd)(both clients) aligns when on a table row, else runsnormal! <cmd>—gqq/gq1+VimwikiTableAlignQ→gqq;gww/gw1+VimwikiTableAlignW→gww;NuwikiTableAlign→gqq. (The-nargs=?attribute + the partial-column2arg, accepted-and-ignored, are unchanged; true partial-column align stays a separate deeper table feature.) Covered bycmd.search_and_tablealign_nargs(test-keymaps-vim.vim) + the table alignment cases that still route through it.VimwikiTablewas-nargs=1vs upstream-nargs=*(couldn't pass cols+rows). Fix: all four defs (Vim+Neovim × Vimwiki+Nuwiki) are now-nargs=*and forward<f-args>totable_insert(which already parsed cols/rows). Covered bycmd.VimwikiTable_passes_cols_and_rows(test-keymaps.lua, asserts a:VimwikiTable 4 3→ 4-col, 3-row table) andcmd.VimwikiTable_accepts_cols_rows(test-keymaps-vim.vim, no E488). Follow-up (same pass): the no-arg default cols diverged (nuwiki3vs upstream5; rows2already matched — confirmed against upstreamvimwiki#tbl#create). Aligned both backing fns to default 5 cols.VimwikiListChangeLvlwas-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 existingover_rangehelper. Covered bycmd.ListChangeLvl_range_indents(test-keymaps.lua) andcmd.VimwikiListChangeLvl_accepts_range_and_args(test-keymaps-vim.vim, no E481/E488).- 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.luahad a single, correct definition that already did the bare-word →[[link]]wrap (viawrap_cword_as_wikilink) and was dispatched by the<CR>keymap — there was no deadpos_args()code and nos:wrap_word_under_cursor(that name doesn't exist). The only real artifact was two separatevim.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. - Neovim
*LinkEx-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 rawlua vim.lsp.buf.definition(), bypassing the wrap + create-on-follow that the Vim commands and the Neovim<CR>family provide. Fix: all eight now calllua require('nuwiki.commands').follow_link_or_create()(keeping theirsplit/vsplit/tabnewprefixes);TabDropLinkalready usedfollow_link_dropand is unchanged. Covered bycmd.VimwikiFollowLink_wraps_bare_word+cmd.VimwikiSplitLink_wraps_bare_word(test-keymaps.lua). VimwikiRebuildTags-bang("rebuild all") —:…RebuildTags!now passes{ all: true }; the server (tags_rebuild) re-indexes every configured wiki (viawikis_snapshot()) instead of just the current one.-bangadded to all four command defs.:VimwikiSearch/VWSnow scope to the wiki (fixed 2026-06-03) — upstream's search runs over the wiki's files, not the editor's CWD; nuwiki'slvimgrep /<args>/ **searched CWD-relative**. Fix: both commands route throughnuwiki#commands#search/commands.search(both clients), which resolves the wiki whose root contains the current file (else the first configured wiki, else CWD) andlvimgreps<root>/**/*<ext>into the location list; an empty pattern reuses the last search (@/); a no-match is caught and reported (no rawE480). Added aNuwikiSearchalias alongsideVimwikiSearch/VWS. The-nargs=*attr fix (2026-06-03) is folded in. nuwiki uses Vim'slvimgrep— 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 bycmd.search_and_tablealign_nargs(test-keymaps-vim.vim).VimwikiChangeSymbolTo/VimwikiListChangeSymbolIlack-range(new, 2026-05-31 re-audit; fixed same day) — upstream both carry-range -nargs=1; nuwiki had-nargs=1only, so a visual-range invocation raisedE481. Fix: all four defs (Vim+Neovim × theVimwiki*/Nuwiki*current-item forms —VimwikiChangeSymbolTo/ListChangeSymbolIandNuwikiChangeSymbol) are now-range -nargs=1passing<line1>,<line2>, <q-args>to a newlist_change_symbol_range(symbol, l1, l2)helper that loopsover_range(both clients).ChangeSymbolInListTo/InListstay range-less (and correctly keepwhole_list=true). Covered bycmd.ChangeSymbolTo_range_sets_marker(test-keymaps.lua) +cmd.VimwikiChangeSymbolTo_accepts_range(test-keymaps-vim.vim).VimwikiGenerateLinkslacks-nargs=?(new, 2026-05-31 re-audit; fixed same day) — upstream takes an optional rel-path arg; nuwiki was bare, so:VimwikiGenerateLinks fooraisedE488. Fix: all four defs are now-nargs=?;links_generate([path])(both clients) dispatches the existing server commandnuwiki.links.generateForPath(with{path}) when an arg is given, elsenuwiki.links.generate— a real path filter, not accepted-and-ignored (the server'slinks_generate_for_pathalready implements subtree scoping). Covered bycmd.GenerateLinks_accepts_optional_path(test-keymaps.lua) +cmd.VimwikiGenerateLinks_accepts_path(test-keymaps-vim.vim).NuwikiGenerateTagsmissing (new, 2026-05-31 re-audit; fixed same day) — addedNuwikiGenerateTags(Vim + Neovim) mirroringVimwikiGenerateTags→tags_generate_links, with-nargs=?+-complete=…tags. Covered bycmd.NuwikiGenerateTags_exists(both harnesses).VimwikiIndexfamily now has global entry points (fixed 2026-06-03) — upstream defines the Index family inplugin/; nuwiki hadIndex/TabIndexonly buffer-local (so a wiki couldn't be opened from a non-wiki buffer; only:…UISelect/:…ShowVersionwere global). Fix: globalVimwiki/NuwikiIndex+Vimwiki/NuwikiTabIndex(with-countfor the wiki number) added inplugin/nuwiki.vim(Vim) andinit.lua_setup_global_commands(Neovim), dispatching to the existingwiki_index/wiki_tab_index. The buffer-local copies still shadow them inside wiki buffers. Covered bycmd.global_entry_pointsin both harnesses.VimwikiColorize/NuwikiColorizedrop their argument in the Neovim branch — both are-nargs=1, but the Neovim defs calledcolorize()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 fromvim.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 explicitvisualflag; 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) raisedE481: No range allowed. All four command defs are now-rangeand pass the range to the handler — a ranged (visual) invocation wraps the'</'>selection on both clients; thex-mode<Leader>wcmapping passes the visual flag directly. Covered bycmd.VimwikiColorize_uses_arg,cmd.Colorize_ignores_stale_visual_selection,colorize.visual_wraps_selection,cmd.Colorize_range_wraps_selectionintest-keymaps.luaandcolorize.{command_wraps_cword,visual_wraps_selection, ranged_command_no_error}intest-keymaps-vim.vim.- 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.vimnuwiki#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:colorschemereload), anftpluginTextChangedautocmd, and directly fromcolorize()for immediate effect. The LSP@vimwikiColortoken no longer links toConstantso 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 bycolorize.conceal_hides_tags_shows_text(both harnesses) andcolorize.command_conceals_immediately(test-keymaps-vim.vim). VimwikiTableMoveColumn{Left,Right}dispatch to different backing names per client — converged: the Vim-branch commands now callnuwiki#commands#table_move_column_left/right(existing aliases over thetable_move_left/rightimpls), matching the Neovim branch.NuwikiTabIndexlacks-countin the Neovim branch — added-count; also switched the NeovimIndex/TabIndexfamily (Vimwiki/NuwikiTabIndexandVimwiki/NuwikiIndex) fromvim.v.count(always 0 in command context) to<count>, so a:2NuwikiIndex/:3NuwikiTabIndexcount is now honored. (TheIndexhalf was caught by the 2026-05-31 second re-audit and fixed then.)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 bycmd.toggle_list_item_rangeintest-keymaps.lua.- Diary-note family lacks
-count(new, 2026-05-31 re-audit; fixed same day) — upstream's:Vimwiki{Make,TabMake,MakeYesterday,MakeTomorrow}DiaryNoteVimwikiDiaryIndex(and theNuwiki*forms) carry-count=0, where the count selects the wiki number. Fix: a real wiki selector end-to-end —
- Server (
commands.rs):OptionalUriArggained an optionalwikiselector andresolve_diary_wiki(backend, uri, wiki)now prefers it (via the existingresolve_wiki_selector, reused — accepts a 0-indexed number) over the buffer URI; used bydiary_open_relative+diary_open_index. - Clients:
s:diary_open/diary_open(both clients) anddiary_today/today_tab/yesterday/tomorrow/indextake a count and send{wiki: count-1}when > 0. All 22 diary-note + DiaryIndex command defs across the 4 contexts are now-count=0passing<count>.diary_next/diary_prevcorrectly ignore the count. Tests:cmd.VimwikiMakeDiaryNote_has_count(test-keymaps.lua),cmd.Vimwiki{MakeDiaryNote,DiaryIndex}_accepts_count(test-keymaps-vim.vim). (TheIndex/TabIndexfamily was already-count=0, mapping the count to the wiki number viawiki_index/wiki_tab_index.)
VimwikiToggleRejectedListItem/NuwikiToggleRejectedlack-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:'<,'>VimwikiToggleRejectedListItemraised E481 and a ranged invocation acted cursor-only. This is the same-rangeclass fixed above forToggleListItem/Increment/Decrement—ToggleRejectedwas simply missed in that pass. Fix: all four defs (Vim+Neovim ×Vimwiki*/Nuwiki*) are now-range, routed through a newreject_list_item_range(l1, l2)loopingover_range(both clients), mirroringtoggle_list_item_range. The visualglxkeymap stays cursor-only, consistent with its<C-Space>/gln/glpsiblings (the keymap-range question is a separate, family-wide item). Covered bycmd.reject_list_item_range(test-keymaps.lua) +cmd.VimwikiToggleRejectedListItem_accepts_range(test-keymaps-vim.vim).VimwikiRemoveDoneregained 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-bangonly, so:'<,'>VimwikiRemoveDoneraised E481. Fix: all four defs are now-bang -range, dispatched bynuwiki#commands#remove_done/commands.remove_done(bang, range, l1, l2)(both clients):!→ whole-buffer sweep (unchanged); an explicit range (<range> > 0, e.g.:'<,'>) → a newlist_remove_done_rangesending{range:[l1-1,l2-1]}; otherwise the cursor's current list. Serverremove_done_editgained anOption<(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).-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: newautoload/nuwiki/complete.vimwith 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 configuredcolor_dic+ colours already used in buffercolor:…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 forVimwikiRenameFile— 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 bycomplete.pages_globs_and_filters,complete.tags_scans_wiki_files,complete.colors_from_buffer_spans(test-keymaps-vim.vim).
- Minor command-attribute gaps (new) — all now real, not cosmetic:
VimwikiNormalizeLinkis-nargs=?and with1wraps the'</'>selection (wrap_visual_as_wikilink, both clients; thex-mode+passes it too);VimwikiCheckLinksis-rangeand a ranged invocation filters the report to the current buffer's selected lines;VimwikiColorize-nargs=1→-nargs=*(bare:VimwikiColorizeprompts). Covered bynormalize.visual_wraps_selection(vim) /cmd.normalize_link_visual_wraps_selection(nvim). VimwikiSplitLink/VimwikiVSplitLinklack-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 butreuse_other_split_window(a:1) andmove_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 honoringmove_cursor/reusewould 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 bycmd.VimwikiSplitLink_accepts_args(test-keymaps-vim.vim, no E488).
Config
autowriteall(new, 2026-06-03 re-audit; fixed same day) — upstream global (vars.vim:136, default1/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 (upstreamplugin/vimwiki.vim:165-166,39). nuwiki had no equivalent → out-of-box behavioral divergence. Fix: client-side, both clients — Neovimlua/nuwiki/ftplugin.lua(setup_autowriteall, BufEnter/BufLeave save+set/ restore) gated by theautowriteallsetup option (default true); Vimftplugin/vimwiki.vim(NuwikiAutoWriteAllaugroup) gated byg:nuwiki_autowriteall(default 1). Covered byconfig.autowriteall_appliedin both harnesses.table_auto_fmt(new, 2026-06-03 re-audit; fixed same day) — upstream global (vars.vim:183, default1/ON): re-format the table under the cursor onInsertLeave(upstreamplugin/vimwiki.vim:330). nuwiki had no equivalent (tables formatted only viagqq/gww). Fix: client-sideInsertLeaveautocmd, gated by config — Neovimtable_auto_fmtsetup option (default true,ftplugin.luasetup_table_auto_fmt); Vimg:nuwiki_table_auto_fmt(default 1,NuwikiTableAutoFmtaugroup). Both guard on the cursor line containing|before the (async LSP)table_align, so non-table edits don't round-trip. Covered byconfig.table_auto_fmt_autocmdin both harnesses. (Divergence: nuwiki'stable_alignis an async server edit, vs upstream's synchronous format — the result lands a beat after Esc.)table_reduce_last_col(new, 2026-06-03 re-audit) — upstream global (vars.vim:184, default0): don't expand the last column separator to fill the cell. nuwiki absent. Low impact (off by default). Fix: thread a flag into the table-format routine.user_htmls(new, 2026-06-03 re-audit) — upstream global (vars.vim:191, default''): comma-separated HTML files with no wiki source that:VimwikiAll2HTMLmust not delete. nuwiki absent — only relevant if nuwiki's all-to-HTML prunes orphan HTML (it may not, in which case this is moot and worth a one-line divergence note rather than code).hl_headers/hl_cb_checked(new, 2026-06-03 re-audit) — upstream globals (vars.vim:158/157, both default0): per-level header highlight / checked-checkbox line highlight. nuwiki absent. Borderline — either implement via highlight groups/semantic tokens, or fold into theconceal*/maxhihighlighting-divergence family so future audits stop flagging them.list_margin— buffer-side parity + markdown default (new, 2026-06-02 re-audit; fixed 2026-06-02) — upstream (vars.vim:516, docvimwiki.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-1but0for markdown wikis (vars.vim:651). nuwiki had instead applied it only as an HTML CSSmargin-left:<n>em. Fix (true-parity, chosen over keeping the HTML meaning): dropped the HTMLem-margin entirely (rendererlist_marginfield/method + therender_page_htmlparam removed) and now prependmax(0, list_margin)leading spaces to every generated bullet inbuild_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>derives0for 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).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-wikidiary_months: Vec<String>(default the 12 English names) onWikiConfig/RawWiki, threaded intobuild_index_body; a missing/empty slot falls back to the Englishmonth_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, default1): blank lines after a generated header in markdown syntax. Intentionally deferred: nuwiki's generators emit vimwiki syntax only (caption_linealways 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.diary_caption_level = -1now representable (new, 2026-06-02 re-audit; fixed 2026-06-02) — upstream allows-1(vars.vim:507min: -1); nuwiki had typed the field asu8, so a-1config failed deserialization and silently fell back to0. Fix: widened toi8acrossWikiConfig/RawWiki/WikiDefaults;build_index_bodyclamps< 0to a base tree level of0. (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).auto_header— auto H1-from-filename on new page (server-side).create_link— toggle to suppress link-target creation.dir_link— index file to open when following a directory link.auto_chdir—:cdinto wiki root on open (client-side).bullet_types/cycle_bullets— custom bullet glyphs + wrap-around.commentstring('%%%s') — comment toggling/text-objects. (2026-06-03 re-audit: also a value mismatch — nuwiki hardcodes%% %s(with a space) inftplugin/vimwiki.vim:18vs upstream's no-space%%%s; align when picked up.)color_tag_template—color_dicpresent; template regex missing. Related divergence (2026-05-31 config re-audit): nuwiki'scolor_dicdefaults to empty while upstream ships a populated palette (default/red/…). Documented accurately on the nuwiki side (so it's not a doc↔code mismatch); the renderer falls back toclass="color-<name>". Decide whether to seed a default palette or keep empty-by-design.rss_max_items/rss_name—:Rssoutput hardcoded. Fix:HtmlConfig.- RSS feed structure fidelity (new, 2026-05-31 base_url audit) —
nuwiki's
write_rssis intentionally minimal (title/link/guid). Upstream emits more: channel<link>points at the diary index page (base_url + diary_rel_path + diary_index + .html, nuwiki uses barebase_url); per-item<guid isPermaLink="false">holds the bare date basename (nuwiki uses the full URL, no attribute); plus<atom:link rel= "self">, channel/item<pubDate>(from mtime), and a<![CDATA[…]]>rendered-HTML body per item.base_url's scope is faithful (RSS-only, not inter-page links) and thecustom_wiki2htmlarg contract is an exact match — only the feed document shape diverges. emoji_enable— emoji substitution.html_header_numbering(+_sym) — numbered HTML headers. Fix:HtmlConfig(config.rs) gainedhtml_header_numbering: u8(start level,0= off) +html_header_numbering_sym: String(trailing symbol), both round-tripped throughRawWiki/defaults. AHeadingNumbererinnuwiki-core/src/render/html.rswalks top-level headings in document order, bumping per-depth counters and emitting a dotted prefix (1,1.1, …);render_bodynumbers only document-level headers (nested ones in lists/ quotes stay unnumbered, matching upstream's line scan). Wired viawith_header_numbering()fromexport.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).valid_html_tags— allowed inline HTML tags in export.generated_links_caption,toc_link_format,markdown_link_ext.list_ignore_newline/text_ignore_newline— export newline handling.
Mappings
- Visual
<CR>normalize-link (visual+covers the same intent). - Insert
<S-CR>multiline list item. gLH/gLL/gLR(fixed 2026-06-03) — upstream binds these as case-variant aliases ofgLh/gLl/gLr(dedent / indent whole item, renumber all lists; upstreamftplugin/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 bymap[n].gL{H,L,R}in both harnesses.<D-CR>(Cmd+Return) tab-drop alias (new, 2026-05-31 re-audit) — added<D-CR>→follow_link_dropalongside<C-S-CR>in both clients (lua/nuwiki/keymaps.lua,ftplugin/vimwiki.vim); docs updated. Covered bymap[n].<D-CR>in both harnesses.<Leader>w<Leader>mmissing from the Vim global map block (new) —plugin/nuwiki.vim's global family now binds<Leader>w<Leader>m(tomorrow) and<Leader>w<Leader>topens today in a new tab (was the same collision the buffer-local maps had). Covered bymap[n].<Leader>w<Leader>min both harnesses.<Leader>w<Leader>mmissing from the Neovim global map block (new, 2026-05-31 map_prefix audit) — the same collision survived inlua/nuwiki/init.lua's_setup_global_mappings:<prefix><Leader>twas bound to tomorrow and<prefix><Leader>m(tomorrow) was absent entirely. Fixed so<prefix><Leader>topens today in a new tab and<prefix><Leader>mis tomorrow — now consistent with the Vim global side + upstream. Covered byglobal_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 /syntaxkey automatically.folding(upstream string) — nuwiki usesfolding = 'lsp' | 'expr' | 'off'backed by LSP foldingRange.key_mappings(dict) — replaced by Luamappings.<group>+ theg:nuwiki_no_<group>_mappingsglobals.auto_tags(upstream0) — upstream auto-updates an on-disk tag metadata file on save. nuwiki has no such file: the LSP re-indexes tags on everydidChange, so tag search/jump/completion are always fresh. The setting's intent is therefore always satisfied (theauto_generate_tagsregen 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(upstream1) — nuwiki's ftdetect always maps the configured wiki extension(s) to thevimwikifiletype regardless of location; there's no per-wiki "only inside the root" toggle. Effectively always-on, likenested_syntaxes.syntaxdefault name — nuwiki defaults the per-wikisyntaxtovimwiki(its primary syntax) where upstream defaults todefault; 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→diaryvs upstreamdiary/(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 viacaption_line), regardless of the wiki'ssyntax. Consequently the markdown-specificmarkdown_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, thenmarkdown_header_style,markdown_link_extin 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/gLchange-symbol + bare-gl/gLremove-checkbox,<CR>-family create-on-follow,<M-CR>badd,<Leader>w<Leader>t/m,VimwikiColorizearg+range, colour-span conceal — confirmed present and correct in both clients. New gaps from that pass (diary-note-count,-completespecs,<D-CR>, global<Leader>w<Leader>m, plusglobal_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 (NeovimVimwiki/NuwikiIndexstill readvim.v.count; now<count>, completing theTabIndexfix). New gaps logged:VimwikiSplitLink/VSplitLinkmissing-nargs=*; mouse-maps-opt-in noted as an intentional divergence. Config pass found no new gaps; all shipped defaults match upstream except the documentedsyntax-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 —VimwikiToggleRejectedListItemmissing-range(fixed same day, see P3); plusVimwikiRemoveDone's lost-rangelogged as low-priority. Config: four new items logged (list_marginsemantics/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_marginreworked to upstream's buffer-side meaning (HTMLem-margin dropped) + markdown-0default;diary_months;diary_caption_levelwidened toi8.markdown_header_styledeferred 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 (confirmedgLH/gLL/gLRcorrect; no new gaps, no regressions). Commands: one real bug —VimwikiGoto/NuwikiGotowere-nargs=1(E471/E488 + unreachable prompt), fixed same day; plusVimwikiRenameFile's missing-nargs=?logged (subsumed by the LSP-rename divergence). Config: five new items —autowriteallandtable_auto_fmt(both ON upstream → out-of-box divergences, implemented same day),table_reduce_last_col,user_htmls,hl_headers/hl_cb_checkedlogged; and acommentstringvalue-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>); theTableAlignQvsAlignWsplit (corrected — the real difference is the non-tablenormal! gqq/gwwfallback, now viatable_align_or_cmd);:VimwikiSearch/VWSscoped to the wiki root; and globalIndex/TabIndexentry points. All client-side (no server change). Both harnesses green (Neovim 299, Vim 291/18/21). - Merged the
calendar-supportbranch (2026-06-03): calendar-vim integration now wiresg:calendar_action/g:calendar_signto nuwiki's diary paths via aFileType vimwikiautocmd (plugin/nuwiki.vim, both clients), with ause_calendaropt-out (g:nuwiki_no_calendar/ setupuse_calendar = false). Souse_calendaris no longer an intentional divergence — removed from that list above. Seedevelopment/calendar-vim-integration.md.