# nuwiki **A vimwiki-compatible Vim/Neovim plugin backed by a Rust language server.** nuwiki replaces the original [vimwiki](https://github.com/vimwiki/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 wire up keystrokes and display results. ## Features ### Syntax & rendering - Full vimwiki syntax (`.wiki`) — headings, lists with checkboxes, numbered / lettered / Roman lists, tables (with `|---|`, `|:--|--:|:--:|` alignment markers and `>` / `\/` colspan/rowspan), inline math, code fences, tags, comments, and links (wiki, interwiki, diary, file, local, raw URL, transclusion). - Transclusions support attributes: `{{image.png|alt|style="border:1px"}}`. - HTML export with templates, CSS, RSS feed, and incremental `:VimwikiAll2HTML`. ### Editor intelligence (LSP) - Goto-definition (follow wikilinks), backlinks, hover preview, completion on `[[`, document outline, workspace symbol search. - Semantic-token highlighting (no syntax-file fallback needed). - Broken-link diagnostics and an orphan-page finder. - Folding: server-driven `foldingRange` by default, regex fallback when the server isn't ready. ### Editing - Insert-mode smart ``: continues list markers (`-`, `*`, `1.`, `a)`, `i)`, …) and checkboxes; breaks out of an empty bullet; inserts a fresh row when inside a `|…|` table. - Insert-mode `` / `` jumps between table cells, adding a new row past the last column. - `` / `` indent/dedent the current list item; `` / `` cycle through the marker styles; `` toggles or adds a checkbox. - Five text-object pairs in operator-pending + visual mode: `ah`/`ih` (heading section), `aH`/`iH` (heading + subtree), `al`/`il` (list item), `a\`/`i\` (table cell), `ac`/`ic` (table column). - Two-step `` on a bare word: wraps it as `[[word]]`, second press follows the link. ### Diary - Daily diary out of the box (`ww`, `` / ``). - Configurable cadence: set `diary_frequency = 'weekly'` (or `'monthly'` / `'yearly'`) per wiki and the commands address `2026-W19.wiki`, `2026-05.wiki`, `2026.wiki` instead. Navigation stays at the same cadence as the entry under the cursor. - Diary index page generation (`:VimwikiDiaryGenerateLinks`). ### Other - Multi-wiki with cross-wiki `[[wn.name:Page]]` interwiki links and a `:VimwikiUISelect` picker. - TOC generation, tag search, tag-to-page link lists. - `:Vimwiki*` command compat + canonical `:Nuwiki*` aliases. - Plain Vim and Neovim both supported with feature parity (text objects, folding, smart insert-mode bindings). --- ## Installation The plugin ships a `nuwiki-ls` binary that the editor talks to over LSP stdio. The `install()` helper downloads a pre-built release for your platform, falling back to `cargo build --release` if no asset matches. ### lazy.nvim ```lua { 'gffranco/nuwiki', build = ":lua require('nuwiki').install()", ft = { 'vimwiki' }, opts = { wiki_root = '~/vimwiki', }, } ``` ### vim-plug ```vim Plug 'gffranco/nuwiki', { 'do': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"' } ``` In `init.lua` / `init.vim`: ```lua require('nuwiki').setup({ wiki_root = '~/vimwiki' }) ``` ### Dein ```vim call dein#add('gffranco/nuwiki', { \ 'build': 'vim -e -s -c "source scripts/download_bin.vim" -c "q"' \ }) ``` ### Plain Vim (no plugin manager) ```sh 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 ``` Plain Vim users also need an LSP client — [`vim-lsp`](https://github.com/prabirshrestha/vim-lsp) is the recommended path, with [`coc.nvim`](https://github.com/neoclide/coc.nvim) as a fallback. Neovim 0.11+ uses its built-in LSP client and needs no extra plugin. ### Try it without touching your config ```sh ./start-nvim.sh # spawns Neovim against an isolated sample wiki ./start-vim.sh # same, for plain Vim (clones vim-lsp on first run) ``` Both scripts build the LSP binary, seed a scratch wiki, and launch the editor against an isolated state dir under `$XDG_CACHE_HOME/nuwiki-dev`. --- ## Configuration Defaults are vimwiki-compatible. Every key has a sensible default — the example below shows the full surface for reference. ```lua require('nuwiki').setup({ -- Single-wiki shorthand (vimwiki-compatible). wiki_root = '~/vimwiki', file_extension = '.wiki', syntax = 'vimwiki', -- 'vimwiki' | 'markdown' (future) log_level = 'warn', -- Multi-wiki: each entry honours every per-wiki key vimwiki understands. -- Pick either this OR the single-wiki shorthand above. If both are set, -- `wikis` wins. wikis = { { name = 'personal', root = '~/vimwiki', index = 'index', -- index page stem file_extension = '.wiki', -- Diary diary_rel_path = 'diary', diary_index = 'diary', diary_frequency = 'daily', -- 'daily' | 'weekly' | 'monthly' | 'yearly' diary_sort = 'desc', -- 'desc' | 'asc' diary_header = 'Diary', -- HTML export html_path = '~/vimwiki/_html', template_path = '~/vimwiki/_templates', template_default = 'default', template_ext = '.tpl', css_name = 'style.css', auto_export = false, -- export on save auto_toc = false, -- auto-refresh TOC on save exclude_files = {}, -- glob patterns -- Checkbox progression characters: " .oOX" by default. listsyms = ' .oOX', listsyms_propagate = true, -- Inline highlighting: 6 heading levels by default. maxhi = 1, -- Embed other syntaxes inside `{{{lang …}}}` fences. nested_syntaxes = {}, }, }, -- Keymap groups. Flip subgroups off independently. Defaults all on. mappings = { enabled = true, wiki_prefix = true, -- w* links = true, -- , , , , +, … lists = true, -- , gln/glp/glx, gl, o/O, … headers = true, -- =, -, ]], [[, ]=, [=, ]u, [u table_editing = true, -- gqq, , , in insert mode diary = true, -- , html_export = true, -- wh, whh text_objects = true, -- ah, ih, aH, iH, al, il, a\, i\, ac, ic mouse = false, -- <2-LeftMouse>, , … (opt-in) }, -- 'lsp' (server-driven foldingRange, default) | 'expr' (regex) | 'off'. folding = 'lsp', -- Broken-link diagnostic severity: 'off' | 'hint' | 'warn' | 'error'. diagnostic = { link_severity = 'warn', }, }) ``` Vim users without Lua set the equivalent globals before loading the plugin: ```vim 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 let g:nuwiki_no_folding = 0 " set to 1 to skip foldexpr setup let g:nuwiki_mouse_mappings = 0 " set to 1 to enable mouse maps ``` --- ## Usage ### Commands Both `:Vimwiki*` (migration compat) and `:Nuwiki*` (canonical native) are registered as buffer-local commands on every `.wiki` buffer. Selected highlights — type `:Vimwiki` for the full list. | Command | What it does | |---|---| | `:VimwikiIndex [N]` | Open wiki N's index page (default: 1) | | `:VimwikiTabIndex [N]` | Same, in a new tab | | `:VimwikiUISelect` | Pick a wiki from a list (multi-wiki only) | | `:VimwikiGoto {page}` | Open `{page}.wiki` by name | | `:VimwikiMakeDiaryNote` | Open today's diary entry (or this week / month / year, depending on `diary_frequency`) | | `:VimwikiMakeYesterdayDiaryNote` / `:VimwikiMakeTomorrowDiaryNote` | Step the diary back / forward by one period | | `:VimwikiDiaryIndex` | Open the diary index page | | `:VimwikiDiaryGenerateLinks` | Rebuild the diary index from current entries | | `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | Chronological diary nav (same cadence as the current entry) | | `:VimwikiTOC` | Generate / refresh the table of contents on the current page | | `:VimwikiGenerateLinks` | Insert a flat list of every page in the wiki | | `:VimwikiCheckLinks` | Send broken links to the quickfix list | | `:VimwikiSearchTags {tag}` | Quickfix listing every occurrence of `:tag:` | | `:VimwikiGenerateTagLinks [tag]` | Insert a section linking every page that has `` | | `:VimwikiRebuildTags` | Force a full workspace re-index | | `:VimwikiRenameFile` | Rename the current page, rewriting incoming links | | `:VimwikiDeleteFile` | Delete the current page | | `:Vimwiki2HTML` | Render the current page to HTML | | `:Vimwiki2HTMLBrowse` | Render the current page and open it in the default browser | | `:VimwikiAll2HTML[!]` | Export the whole wiki (`!` forces; otherwise incremental) | | `:VimwikiRss` | Write `rss.xml` for diary entries | | `:VimwikiFollowLink` | Follow the link under the cursor | | `:VimwikiBacklinks` | Show all references to the current page | | `:VimwikiNextLink` / `:VimwikiPrevLink` | Jump to the next / previous wikilink on the page | | `:VimwikiBaddLink` | Add the target of the link under the cursor to the buffer list | ### Default keymaps All buffer-local. Disable any group via `mappings. = false` in Neovim, or `let g:nuwiki_no_default_mappings = 1` in Vim. **Links** (normal mode) | Key | Action | |---|---| | `` | Smart follow — inside `[[…]]` jumps; on a bare word wraps as `[[word]]` (second `` follows) | | `` | Follow in horizontal split | | `` | Follow in vertical split | | `` | Follow in a new tab | | `` | Jump back (``) | | `` / `` | Next / previous wikilink on or after the cursor | | `+` | Wrap word / visual selection as a wikilink (no follow) | **Lists** (normal mode) | Key | Action | |---|---| | `` / `` / `` | Toggle the checkbox under the cursor (the three aliases cover terminal byte differences) | | `gln` / `glp` | Cycle the checkbox state forward / backward | | `glx` | Toggle the rejected marker `[-]` | | `gnt` | Jump to the next unfinished task | | `glh` / `gll` | Dedent / indent the current list item | | `gLh` / `gLl` | Dedent / indent including the item's sublist | | `glr` | Renumber the current ordered list | | `gLr` | Renumber every ordered list in the buffer | | `gl` | Remove every completed item from the current list | | `gL` | Remove every completed item in the buffer | | `o` / `O` | Open a new line and continue the list marker (preserves any checkbox) | **Lists** (insert mode) | Key | Action | |---|---| | `` | Continue the list with the same marker (preserves checkbox), or break out on an empty bullet | | `` / `` | Dedent / indent the current item | | `` / `` | Cycle the marker style (`-` → `*` → `#` → `1.` → `1)` → `a)` → `A)` → `i)` → `I)`) | | `` | Toggle / add a checkbox on the current item | **Headers** | Key | Action | |---|---| | `=` / `-` | Increase / decrease the heading level | | `]]` / `[[` | Next / previous heading (any level) | | `]=` / `[=` | Next / previous sibling heading | | `]u` / `[u` | Jump to the parent heading | **Tables** | Key | Action | |---|---| | `gqq` / `gq1` / `gww` / `gw1` | Align the table under the cursor | | `` / `` | Move the column under the cursor left / right | | `` / `` (insert) | Jump to the next / previous cell; `` past the last cell inserts a fresh row | | `` (insert) | Insert a fresh empty row below the current one | **Wiki / diary / export** (normal mode) | Key | Action | |---|---| | `ww` | Open today's diary | | `ws` | Pick a wiki | | `wi` | Open the diary index | | `ww` / `wy` / `wt` | Today / yesterday / tomorrow | | `wi` | Rebuild the diary index page | | `wn` / `wd` / `wr` | Goto / delete / rename page | | `wh` / `whh` | Export the current page to HTML / open in browser | | `wha` | Export every page | | `wc` | Wrap word (or visual selection) in a colour span | | `` / `` | Next / previous diary entry | **Text objects** (operator-pending + visual) | Object | Selection | |---|---| | `ah` / `ih` | The heading's section (`ih` excludes the heading line) | | `aH` / `iH` | The heading's section plus every subheading underneath | | `al` / `il` | The list item under the cursor (`il` excludes the marker / checkbox) | | `a\` / `i\` | The table cell under the cursor (`i\` excludes the surrounding `\|`) | | `ac` / `ic` | The table column under the cursor (`ic` excludes the header separator row) | ### Health check (Neovim) ```vim :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. To migrate: 1. Drop the original `vimwiki` plugin from your config. 2. Install nuwiki. 3. Point `wiki_root` (or each `wikis[i].root`) at your existing directory. Existing pages, diary entries, tags, and templates work without modification. The first workspace scan after launch may show an empty `:VimwikiCheckLinks` result until the background index completes; subsequent runs are instant. --- ## License Dual-licensed under either of: - Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE)) - MIT license ([`LICENSE-MIT`](./LICENSE-MIT)) 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.