gffranco 46ae618b5f parity(2): insert-mode list bindings (<C-D>/<C-T>/<C-L><C-?>)
Match upstream vimwiki's insert-mode list editing:
  <C-D>      list_change_level(-1)   dedent current item
  <C-T>      list_change_level(+1)   indent current item
  <C-L><C-J> list_cycle_symbol(+1)   cycle marker forward
  <C-L><C-K> list_cycle_symbol(-1)   cycle marker backward
  <C-L><C-M> list_toggle_or_add_chk  toggle if has checkbox, else add `[ ]`

Cycle order matches vimwiki's canonical run:
  - → * → # → 1. → 1) → a) → A) → i) → I) → (wrap)

Both Lua and VimL paths wired. Lua callbacks fire directly (no `<C-o>`
dance) since list_change_level / changeSymbol mutate the buffer via
the LSP applyEdit pipeline, which works cleanly in insert mode. VimL
keeps the `<C-o>:call …<CR>` idiom since that's the standard there.

Harness adds 5 cases covering the new insert-mode bindings plus one
direct LSP roundtrip for changeSymbol — surfaced a long-standing
stale-binary footgun in test-keymaps.sh (rebuilt only when bin was
missing; now rebuilds every run since incremental cargo is fast).
Harness also captures server log_messages and dumps them on failure
so future swallowed-error bugs (Err → log + Ok(None)) are visible.

Gates: 421 Rust / 25 Neovim / 12 Vim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 21:11:53 +00:00

nuwiki

A vimwiki-compatible Vim/Neovim plugin backed by a Rust language server.

nuwiki replaces the original vimwiki plugin while keeping its file format, keymaps, and :Vimwiki* command surface intact. The substantive work happens in a Rust LSP daemon — Vim and Neovim are thin client layers that just wire up keystrokes and display results.

  • Full vimwiki syntax (.wiki) — headings, lists with checkboxes, tables, math, code, tags, links (wiki / interwiki / diary / file / raw URL / transclusion).
  • Goto-definition, backlinks, hover, completion, workspace symbol search, semantic highlighting — all over LSP.
  • Diary (today/yesterday/tomorrow/next/prev), tags (search + link generation), TOC generation, broken-link diagnostics, orphan finder.
  • Multi-wiki with cross-wiki interwiki links ([[wn.name:Page]]).
  • HTML export with templates, CSS, auto-export on save, RSS feed.
  • Folding (LSP foldingRange + regex fallback), header & link text objects, the <CR> two-step link-create-and-follow.
  • :Vimwiki* command compat + :Nuwiki* canonical aliases.

See SPEC.md for the architecture, design decisions, and the full phase log.


Installation

The plugin ships a nuwiki-ls binary that the editor talks to over LSP stdio. The install() Lua helper downloads a pre-built release for your platform, falling back to cargo build --release if no asset matches.

lazy.nvim

{
  'gffranco/nuwiki',
  build = ":lua require('nuwiki').install()",
  ft = { 'vimwiki' },
  opts = {
    wiki_root = '~/vimwiki',
  },
}

vim-plug

Plug 'gffranco/nuwiki', { 'do': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"' }

In init.lua / init.vim:

require('nuwiki').setup({ wiki_root = '~/vimwiki' })

Dein

call dein#add('gffranco/nuwiki', {
  \ 'build': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"'
  \ })

Plain Vim (no plugin manager)

git clone https://code.gfran.co/gffranco/nuwiki ~/.vim/pack/gffranco/start/nuwiki
cd ~/.vim/pack/gffranco/start/nuwiki && cargo build --release -p nuwiki-ls
mkdir -p bin && ln -s target/release/nuwiki-ls bin/nuwiki-ls

Then ensure an LSP client is loaded (Vim path prefers vim-lsp, falls back to coc.nvim). Neovim 0.11+ uses its built-in LSP client — no extra plugin needed.

Trying it without touching your config

./start-nvim.sh           # spawns Neovim with a scratch wiki under
                          # $XDG_CACHE_HOME/nuwiki-dev
./start-vim.sh            # same, but for plain Vim — clones vim-lsp
                          # into the cache on first run

Both scripts build the LSP binary, seed a sample wiki, and launch the editor against an isolated state dir. Pass a wiki file path to open it directly; NUWIKI_DEV_WIKI=/path/to/wiki points at your own sandbox instead.


Configuration

Defaults are vimwiki-compatible. Full schema (with type annotations and defaults) lives in lua/nuwiki/config.lua.

require('nuwiki').setup({
  -- Single-wiki shorthand (vimwiki-compatible).
  wiki_root      = '~/vimwiki',
  file_extension = '.wiki',
  syntax         = 'vimwiki',     -- 'vimwiki' | 'markdown' (future)
  log_level      = 'warn',

  -- Or the multi-wiki form. The single-wiki keys above desugar into
  -- a one-entry `wikis = [...]` list, so you can pick either shape.
  wikis = {
    {
      name           = 'personal',
      root           = '~/vimwiki',
      file_extension = '.wiki',
      diary_rel_path = 'diary',
      diary_index    = 'diary',
      -- HTML export (Phase 17)
      html_path        = '~/vimwiki/_html',
      template_path    = '~/vimwiki/_templates',
      template_default = 'default',
      template_ext     = '.tpl',
      css_name         = 'style.css',
      auto_export      = false,    -- export on save
      exclude_files    = {},       -- glob patterns
    },
  },

  -- Keymap groups — flip subgroups off independently. Defaults all on.
  mappings = {
    enabled       = true,
    wiki_prefix   = true,  -- <Leader>w*
    links         = true,  -- <CR>, <S-CR>, <Tab>, <BS>, +, …
    lists         = true,  -- <C-Space>, gln/glp/glx, gl<Space>, o/O
    headers       = true,  -- =, -, ]], [[, ]=, [=, ]u, [u
    table_editing = true,  -- gqq, gq1, gww, gw1, <A-Left>, <A-Right>
    diary         = true,  -- <C-Down>, <C-Up>
    html_export   = true,  -- <Leader>wh, <Leader>whh
    text_objects  = true,  -- ah, ih
  },

  -- Folding strategy: 'lsp' (server-driven foldingRange — default on
  -- Neovim 0.11+), 'expr' (pure regex), or 'off'.
  folding = 'lsp',

  -- Broken-link diagnostic severity: 'off' | 'hint' | 'warn' | 'error'.
  diagnostic = {
    link_severity = 'warn',
  },
})

Vim users without Lua should set the equivalent globals before sourcing the plugin:

let g:nuwiki_wiki_root      = '~/vimwiki'
let g:nuwiki_file_extension = '.wiki'
let g:nuwiki_log_level      = 'warn'
let g:nuwiki_no_default_mappings = 0   " set to 1 to skip the keymap layer

Usage

Commands

Both :Vimwiki* (migration compat) and :Nuwiki* (canonical native) are registered as buffer-local commands on every .wiki buffer. Selected highlights — see :Vimwiki<Tab> for the full list.

Command What it does
:VimwikiIndex [N] Open wiki N's index.wiki (default: 1)
:VimwikiUISelect Pick a wiki from a list (multi-wiki only)
:VimwikiMakeDiaryNote Open today's diary page
:VimwikiDiaryNextDay / :VimwikiDiaryPrevDay Chronological diary nav
:VimwikiTOC Generate / refresh table of contents on the current page
:VimwikiGenerateLinks Flat list of every page in the wiki
:VimwikiCheckLinks Send broken links to the quickfix list
:VimwikiSearchTags {tag} Quickfix of all occurrences of :tag:
:VimwikiGenerateTagLinks [tag] Insert a section linking all pages with <tag>
:VimwikiRebuildTags Force a full workspace re-index
:VimwikiRenameFile Rename current page; rewrites incoming links
:VimwikiDeleteFile Delete current page (server-side DeleteFile)
:Vimwiki2HTML Render current page to HTML
:Vimwiki2HTMLBrowse Render + open in default browser
:VimwikiAll2HTML[!] Export the whole wiki (! forces, otherwise incremental)
:VimwikiRss Write rss.xml of diary entries
:VimwikiFollowLink Follow link under cursor (<CR>)
:VimwikiBacklinks Show references to current page

Default keymaps

All buffer-local. Disable any group via mappings.<group> = false in Neovim, or let g:nuwiki_no_default_mappings = 1 in Vim.

Links

Key Action
<CR> Smart follow: inside [[…]] → jump; on a bare word → wrap as [[word]] (second <CR> follows)
<S-CR> Follow in horizontal split
<C-CR> Follow in vertical split
<C-S-CR> Follow in new tab
<BS> Jump back (<C-o>)
<Tab> / <S-Tab> Next / previous wikilink on or after the cursor

Lists

Key Action
<C-Space> / <C-@> / <Nul> Toggle checkbox (terminal byte aliases)
gln / glp Cycle checkbox state (forward / backward)
glx Toggle the rejected marker [-]
gnt Jump to the next unfinished task
o / O Open a new line and continue the list marker (and checkbox if any)

Headers

Key Action
= / - Add / remove a heading level
]] / [[ Next / previous heading
]= / [= Next / previous sibling heading
]u / [u Jump to parent heading

Wiki / diary / export

Key Action
<Leader>ww Today's diary
<Leader>ws Pick a wiki (vim.ui.select / inputlist)
<Leader>wi Diary index
<Leader>w<Leader>w / <Leader>w<Leader>y / <Leader>w<Leader>t Today / yesterday / tomorrow
<Leader>w<Leader>i Rebuild diary index page
<Leader>wn / <Leader>wd / <Leader>wr Goto / delete / rename page
<Leader>wh / <Leader>whh Export current page → HTML / open in browser
<Leader>wha Export every page
<C-Down> / <C-Up> Next / previous diary entry

Text objects

Key Selection
ah / ih A heading (with / without the heading line itself)

Additional text objects (aH, iH, a\, i\, ac, ic, al, il) are planned follow-ups now that the table/list rewriter commands they depend on have shipped.

Health check

:checkhealth nuwiki

Reports the binary path, the LSP client status, registered executeCommand entries, whether foldingRange was negotiated, and whether the configured HTML output directory is writable.

Migrating from vimwiki

The file extension, syntax, command names, and default keymaps all match vimwiki. Drop the original vimwiki plugin from your config, install nuwiki, and point wiki_root at the same directory. Existing pages, diary entries, tags, and templates Just Work.

Two known behavior deltas vs. upstream vimwiki:

  • nuwiki's index is built lazily on workspace scan; the very first :VimwikiCheckLinks after launch may be empty until the background task completes. Subsequent runs are instant.
  • gln and glp both call nuwiki.list.cycleCheckbox, which is forward-only at present. A reverse cycle lands once the v1.1 list rewriters do.

Repository layout

nuwiki/
├── Cargo.toml              workspace root
├── crates/
│   ├── nuwiki-core/        parser, AST, renderer (editor-independent)
│   ├── nuwiki-lsp/         LSP protocol bridge
│   └── nuwiki-ls/          stdio LSP binary entry point
├── plugin/                 universal Vim/Neovim entry point
├── lua/nuwiki/             Neovim Lua glue
├── autoload/nuwiki/        Vim VimL glue
├── ftdetect/               filetype detection for .wiki
├── ftplugin/vimwiki.vim    per-buffer settings + commands + keymaps
├── syntax/vimwiki.vim      static fallback highlighting
├── doc/                    `:h nuwiki`
├── scripts/                test harness + build helpers
└── .gitea/workflows/       CI / release pipelines

Development

Build + test

cargo build --workspace
cargo test  --workspace                 # 381 Rust tests
./scripts/test-keymaps.sh               # 19 Neovim keymap cases (LSP-driven)
./scripts/test-keymaps-vim.sh           # 12 Vim keymap cases (pure-VimL)

CI runs all four on every push (.gitea/workflows/ci.yaml). MSRV: Rust 1.83.

Trying changes against a real wiki

./start-nvim.sh                         # isolated state under
./start-vim.sh                          # $XDG_CACHE_HOME/nuwiki-dev

Both regenerate their minimal config on every launch and seed a scratch wiki with sample pages, links, checkboxes, and tags.

Implementation status

v1.0 — foundation

Phase Name Status
0 Scaffolding done
1 Core AST done
2 Syntax Plugin Interface done
3 Vimwiki Lexer done
4 Vimwiki Parser done
5 Renderer done
6 LSP Foundation done
7 Semantic Tokens done
8 Navigation done
9 Editor Glue done
10 CI/CD release pipeline done

v1.1 — full vimwiki replacement

Phase Name Status
11 Plumbing prerequisites done
12 Tags done
13 Workspace edits + executeCommand done
14 List & table edit commands done
15 Link health + TOC/index generation done
16 Diary done
17 HTML export commands done
18 Multi-wiki done
19 Editor glue v2 (:Vimwiki* compat, keymaps, text objects, folding) done

License

Dual-licensed under either of:

at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this work shall be dual licensed as above, without any additional terms or conditions.

S
Description
No description provided
Readme 1.1 MiB
v0.5.2 Latest
2026-07-02 12:02:20 +00:00
Languages
Vim Script 49.1%
Lua 36.7%
Shell 11.2%
HTML 3%