# 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). Code fences tagged with a language (`{{{python … }}}`) get that language's syntax automatically — no `nested_syntaxes` config 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. You can run it any time with `:NuwikiInstall` (works in both Vim and Neovim) instead of relying on a plugin-manager build hook. ### Requirements - **Neovim 0.11+** — uses the built-in LSP client; no extra plugin. Older releases fall back to a `vim.lsp.start` autocmd but aren't officially supported. - **Vim 9+** — needs a third-party LSP client: [`vim-lsp`](https://github.com/prabirshrestha/vim-lsp) or [`coc.nvim`](https://github.com/neoclide/coc.nvim) — both auto-registered, no manual config (see [Vim LSP client setup](#vim-lsp-client-setup)). - **Rust toolchain (1.83+ stable)** — only needed when building the binary from source; pre-built release downloads skip this. ### 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 && cp target/release/nuwiki-ls bin/ ``` ### Vim LSP client setup Neovim 0.11+ registers the server through its built-in LSP client automatically — nothing to configure. Plain Vim needs one of the following clients on its `runtimepath`; nuwiki picks them up in this order when the first `.wiki` buffer loads. **vim-lsp (recommended)** — zero configuration. Once [`vim-lsp`](https://github.com/prabirshrestha/vim-lsp) (and its [`async.vim`](https://github.com/prabirshrestha/async.vim) dependency) is installed, nuwiki calls `lsp#register_server()` for you and vim-lsp auto-enables it. Your wiki settings (`g:nuwiki_wiki_root`, `g:nuwiki_wikis`, …) are forwarded as the server's initialization options. **coc.nvim** — also zero configuration. nuwiki registers itself with [`coc.nvim`](https://github.com/neoclide/coc.nvim) programmatically (`coc#config('languageserver.nuwiki', …)`) when the first `.wiki` buffer loads, forwarding the same wiki settings (`g:nuwiki_wiki_root`, `g:nuwiki_wikis`, your `g:vimwiki_*` config, the global shorthand, …) as the server's initialization options. **No `coc-settings.json` `languageserver` entry needed** — change your wiki config and reload; the new settings are picked up. To manage the entry yourself instead, set `let g:nuwiki_no_coc_register = 1` and add it to `coc-settings.json` manually (nuwiki then just prints the snippet on first load): ```json { "languageserver": { "nuwiki": { "command": "~/.vim/pack/gffranco/start/nuwiki/bin/nuwiki-ls", "filetypes": ["vimwiki"], "initializationOptions": { "wiki_root": "~/vimwiki", "file_extension": ".wiki", "syntax": "vimwiki" } } } } ``` Point `command` at the installed binary — by default it lives in the plugin's `bin/` directory (adjust the path for your plugin manager), or set `g:nuwiki_binary_path` and reuse that value. `initializationOptions` is **required** — it carries `wiki_root` (and the other [config keys](#configuration)) to the server, exactly as the vim-lsp registration does automatically. Without it the server resolves links against its default root, so existing pages show up as broken links and following a link to a new page creates it in the wrong directory. (Run `:NuwikiInstall`-time guidance, or `:call nuwiki#lsp#start()` once, to have nuwiki print this snippet with your configured paths already filled in.) With coc you can also invoke server commands directly via `:CocCommand nuwiki.` instead of the `:Vimwiki*` / `:Nuwiki*` aliases. ### Try it without touching your config ```sh ./development/start-nvim.sh # spawns Neovim against an isolated sample wiki ./development/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_weekly_style = 'iso', -- 'iso' (YYYY-Www) | 'date' (week-start YYYY-MM-DD, vimwiki) diary_start_week_day = 'monday', -- week start for diary_weekly_style='date' 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', custom_wiki2html = '', -- external converter; empty = built-in renderer custom_wiki2html_args = '', -- extra args appended to the converter call base_url = '', -- public URL prefix for RSS item links auto_export = false, -- export on save auto_toc = false, -- auto-refresh TOC on save auto_generate_links = false, -- regen Generated Links on save auto_generate_tags = false, -- regen Generated Tags index on save auto_diary_index = false, -- regen diary index on diary-entry save toc_header = 'Contents', -- :VimwikiTOC caption (+ toc_header_level = 1) links_header = 'Generated Links', tags_header = 'Generated Tags', exclude_files = {}, -- glob patterns -- Checkbox progression characters: " .oOX" by default. listsyms = ' .oOX', listsym_rejected = '-', -- glyph for a cancelled checkbox [-] listsyms_propagate = true, }, }, -- Keymap groups. Flip subgroups off independently. Defaults all on. mappings = { enabled = true, wiki_prefix = true, -- w{w,t,s,i}, w{w,y,t,m,i} links = true, -- , , , , , , , , , + (n/x), w{n,d,r}, wc (n/x) lists = true, -- , gnt, gln, glp, glx, glh, gll, gLh, gLl (aliases gLH/gLL), glr, gLr (alias gLR), gl{-,*,#,1,i,I,a,A}, gL{…}, gl, gL, o, O; insert , , , headers = true, -- =, -, ]], [[, ]=, [=, ]u, [u table_editing = true, -- gqq, gq1, gww, gw1, , ; insert , diary = true, -- , html_export = true, -- wh, whh, wha 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', -- Mirror Vim's &autowriteall while in a wiki buffer (auto-save on switch). autowriteall = true, -- Re-align the table under the cursor on InsertLeave. table_auto_fmt = true, -- 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_link_severity = 'warn' " 'off'|'hint'|'warn'|'error' let g:nuwiki_no_default_mappings = 0 " set to 1 to skip the keymap layer let g:nuwiki_map_prefix = 'w' " wiki command prefix let g:nuwiki_no_folding = 0 " set to 1 to skip foldexpr setup let g:nuwiki_autowriteall = 1 " set to 0 to not touch &autowriteall let g:nuwiki_table_auto_fmt = 1 " set to 0 to skip InsertLeave table re-align let g:nuwiki_mouse_mappings = 0 " set to 1 to enable mouse maps " Drop individual keymap subgroups (mirror the Lua `mappings.` toggles): " g:nuwiki_no_{wiki_prefix,links,lists,headers,table_editing,diary,html_export,text_objects}_mappings ``` ### Reference #### Top-level options | Key | Type | Default | Accepted values | |-----|------|---------|-----------------| | `wiki_root` | string (path) | `'~/vimwiki'` | any directory path | | `file_extension` | string | `'.wiki'` | any extension, leading `.` | | `syntax` | string | `'vimwiki'` | `'vimwiki'` (`'markdown'` planned) | | `log_level` | string | `'warn'` | `'error'` \| `'warn'` \| `'info'` \| `'debug'` | | `map_prefix` | string | `'w'` | prefix for the wiki command family (`w`, `t`, `w`, …); mirrors vimwiki's `g:vimwiki_map_prefix`. Vim users set `g:nuwiki_map_prefix` | | `folding` | string | `'lsp'` | `'lsp'` \| `'expr'` \| `'off'` | | `autowriteall` | bool | `true` | mirror Vim's `'autowriteall'` while in a wiki buffer (vimwiki `autowriteall`); Vim users set `g:nuwiki_autowriteall` | | `table_auto_fmt` | bool | `true` | re-align the table under the cursor on `InsertLeave` (vimwiki `table_auto_fmt`); Vim users set `g:nuwiki_table_auto_fmt` | | `auto_chdir` | bool | `false` | `:lcd` into the owning wiki's root when a wiki buffer becomes current (vimwiki `auto_chdir`); Vim users set `g:nuwiki_auto_chdir` | | `auto_header` | bool | `false` | insert a level-1 header from the filename on new wiki pages (vimwiki `auto_header`); honours `links_space_char`. Vim users set `g:nuwiki_auto_header` | | `use_calendar` | bool | `true` | wire up calendar-vim integration when it's on the runtimepath (`g:calendar_action`/`g:calendar_sign`); set `false` (or `g:nuwiki_no_calendar`) to opt out | | `wikis` | list of tables | `nil` | per-wiki tables (see below); wins over the single-wiki shorthand | | `mappings` | table | all on | keymap subgroups (see below) | | `diagnostic` | table | `{ link_severity = 'warn' }` | `link_severity`: `'off'` \| `'hint'` \| `'warn'` \| `'error'` | #### `mappings` subgroups Each is a boolean. All default to `true` except `mouse`. The `w` prefix in the `wiki_prefix`, `links`, and `html_export` rows is the default `map_prefix` and moves with it. | Key | Default | Controls (every binding in the group) | |-----|---------|----------| | `enabled` | `true` | master switch for the whole keymap layer | | `wiki_prefix` | `true` | `ww`, `wt`, `ws`, `wi`, `ww`, `wy`, `wt`, `wm`, `wi` | | `links` | `true` | ``, ``, ``, ``, ``, ``, ``, ``, ``, `+` (normal + visual), `` (visual: normalize selection), `wn`, `wd`, `wr`, `wc` (normal + visual) | | `lists` | `true` | `` (also ``/``), `gnt`, `gln`, `glp`, `glx`, `glh`, `gll`, `gLh`, `gLl`, `gLH`, `gLL`, `glr`, `gLr`, `gLR`, `gl-`, `gl*`, `gl#`, `gl1`, `gli`, `glI`, `gla`, `glA`, `gL-`, `gL*`, `gL#`, `gL1`, `gLi`, `gLI`, `gLa`, `gLA`, `gl`, `gL`, `o`, `O`; insert: ``, ``, ``, ``, ``, ``, `` (multiline item) | | `headers` | `true` | `=`, `-`, `]]`, `[[`, `]=`, `[=`, `]u`, `[u` | | `table_editing` | `true` | `gqq`, `gq1`, `gww`, `gw1`, ``, ``; insert: ``, `` | | `diary` | `true` | ``, `` | | `html_export` | `true` | `wh`, `whh`, `wha` | | `text_objects` | `true` | `ah`, `ih`, `aH`, `iH`, `al`, `il`, `a\`, `i\`, `ac`, `ic` | | `mouse` | `false` | `<2-LeftMouse>`, ``, ``, ``, `` (opt-in) | #### Per-wiki keys (`wikis[]`) Most display/generation keys below can also be set **once at the top level** as a default for every wiki (vimwiki-style global shorthand) — via `setup({ … })` or `g:nuwiki_` — and a per-wiki value overrides it. The keys that honour this are `toc_header`, `toc_header_level`, `toc_link_format`, `links_header`, `links_header_level`, `tags_header`, `tags_header_level`, `html_header_numbering`, `html_header_numbering_sym`, `links_space_char`, `list_margin`, `listsyms`, `listsym_rejected`, and the `auto_*` toggles. | Key | Type | Default | Accepted values | |-----|------|---------|-----------------| | `name` | string | — | any label | | `root` | string (path) | — | any directory path | | `file_extension` | string | `'.wiki'` | any extension | | `syntax` | string | `'vimwiki'` | `'vimwiki'` | | `index` | string | `'index'` | index page stem | | `diary_rel_path` | string | `'diary'` | path relative to `root` | | `diary_index` | string | `'diary'` | diary index page stem | | `diary_frequency` | string | `'daily'` | `'daily'` \| `'weekly'` \| `'monthly'` \| `'yearly'` | | `diary_weekly_style` | string | `'iso'` | `'iso'` (`YYYY-Www`) \| `'date'`/`'vimwiki'` (week-start `YYYY-MM-DD`). Weekly only | | `diary_start_week_day` | string | `'monday'` | `'monday'`–`'sunday'`; week start when `diary_weekly_style = 'date'` | | `diary_caption_level` | int | `0` | `0`–`6` (level of the index caption) | | `diary_sort` | string | `'desc'` | `'desc'` \| `'asc'` | | `diary_header` | string | `'Diary'` | any heading text | | `diary_months` | list | English month names | month-number → display name used in the diary index | | `create_link` | bool | `true` | create the target page when following a link to a missing page; `false` makes the follow a no-op | | `dir_link` | string | `''` | index stem opened when following a link to a directory (e.g. `'index'`); empty opens the directory itself | | `bullet_types` | list | `['-', '*', '#']` | unordered-bullet glyphs for this wiki; drives `cycle_bullets` | | `cycle_bullets` | bool | `false` | rotate an unordered item's glyph through `bullet_types` as it is indented/dedented | | `generated_links_caption` | bool | `false` | emit `[[page\|Heading]]` (first heading as caption) from `:VimwikiGenerateLinks` | | `toc_link_format` | int | `0` | `0` = `[[#anchor\|title]]` (with description); `1` = `[[#anchor]]` (anchor only) | | `table_reduce_last_col` | bool | `false` | don't pad the last table column to fill — keep it at its minimum width | | `listsyms` | string | `' .oOX'` | checkbox progression chars | | `listsym_rejected` | string | `'-'` | glyph for a cancelled checkbox (`[-]`); first char used | | `listsyms_propagate` | bool | `true` | `true` \| `false` | | `list_margin` | int | `-1` | `-1` (auto) or `≥ 0` | | `links_space_char` | string | `' '` | char that replaces spaces in synthesised link paths | | `auto_toc` | bool | `false` | `true` \| `false` | | `auto_generate_links` | bool | `false` | regenerate an existing Generated Links section on save | | `auto_generate_tags` | bool | `false` | regenerate an existing Generated Tags index on save | | `auto_diary_index` | bool | `false` | regenerate the diary index when a diary entry is saved | | `toc_header` / `toc_header_level` | string / int | `'Contents'` / `1` | `:VimwikiTOC` caption + heading level | | `links_header` / `links_header_level` | string / int | `'Generated Links'` / `1` | `:VimwikiGenerateLinks` caption + level | | `tags_header` / `tags_header_level` | string / int | `'Generated Tags'` / `1` | `:VimwikiGenerateTagLinks` index caption + level | #### HTML export keys (per-wiki) | Key | Type | Default | Accepted values | |-----|------|---------|-----------------| | `html_path` | string (path) | `'/_html'` | output directory | | `template_path` | string (path) | `'/_templates'` | template directory | | `template_default` | string | `'default'` | template stem | | `template_ext` | string | `'.tpl'` | template extension | | `template_date_format` | string | `'%Y-%m-%d'` | strftime format | | `css_name` | string | `'style.css'` | stylesheet filename | | `auto_export` | bool | `false` | `true` \| `false` (export on save) | | `html_filename_parameterization` | bool | `false` | `true` \| `false` | | `custom_wiki2html` | string | `''` | external converter command; empty = built-in renderer. Called as ` ` (`-` for empty optionals), matching vimwiki | | `custom_wiki2html_args` | string | `''` | extra args appended to the `custom_wiki2html` call | | `base_url` | string | `''` | public URL prefix; when set, diary RSS item links become `/.html` instead of `file://` | | `exclude_files` | list | `{}` | glob patterns | | `color_dic` | table | `{}` | `{ name = 'color', … }` colour-tag name → CSS value | | `color_tag_template` | string | `'__CONTENT__'` | template for a `color_dic` colour span; `__STYLE__` → `color:`, `__CONTENT__` → inner HTML | | `valid_html_tags` | string | `'b,i,s,u,sub,sup,kbd,br,hr,div,center,strong,em'` | comma-separated inline HTML tags passed through export unescaped (`span` is always allowed) | | `emoji_enable` | bool | `true` | substitute `:alias:` emoji shortcodes with their glyph during export | | `text_ignore_newline` | bool | `true` | a single newline inside a paragraph is a space in HTML; `false` → `
` | | `list_ignore_newline` | bool | `true` | a single newline inside a list item is a space in HTML; `false` → `
` | | `rss_name` | string | `'rss.xml'` | filename of the generated diary RSS feed, relative to `html_path` | | `rss_max_items` | int | `10` | cap on the number of diary items in the RSS feed | | `user_htmls` | list | `{}` | basenames of HTML files with no wiki source that `:VimwikiAll2HTML` must not prune | #### Vim globals (`g:nuwiki_*`) For users configuring without Lua. | Global | Default | Meaning | |--------|---------|---------| | `g:nuwiki_wiki_root` | `'~/vimwiki'` | single-wiki root | | `g:nuwiki_file_extension` | `'.wiki'` | wiki file extension | | `g:nuwiki_syntax` | `'vimwiki'` | wiki syntax | | `g:nuwiki_diary_rel_path` | `'diary'` | diary subdirectory (relative to the wiki root) | | `g:nuwiki_wikis` | _(unset)_ | list of per-wiki dicts for multi-wiki setups; wins over the scalar globals | | `g:nuwiki_log_level` | `'warn'` | `error` \| `warn` \| `info` \| `debug` | | `g:nuwiki_link_severity` | `'warn'` | broken-link severity: `off` \| `hint` \| `warn` \| `error` | | `g:nuwiki_map_prefix` | `'w'` | prefix for the wiki command family | | `g:nuwiki_no_default_mappings` | `0` | `1` skips the whole keymap layer | | `g:nuwiki_no_wiki_prefix_mappings` | `0` | `1` skips the `wiki_prefix` group (`ww`, `wt`, `ws`, `wi`, `w{w,y,t,m,i}`) | | `g:nuwiki_no_links_mappings` | `0` | `1` skips the `links` group (``, ``, ``, ``, ``, ``, ``, ``, ``, `+`, `w{n,d,r,c}`) | | `g:nuwiki_no_lists_mappings` | `0` | `1` skips the `lists` group (``, `gnt`, `gln`, `glp`, `glx`, `glh`, `gll`, `gLh`, `gLl` (aliases `gLH`/`gLL`), `glr`, `gLr` (alias `gLR`), `gl{-,*,#,1,i,I,a,A}`, `gL{…}`, `gl`, `gL`, `o`, `O`; insert ``, ``, ``, ``) | | `g:nuwiki_no_headers_mappings` | `0` | `1` skips the `headers` group (`=`, `-`, `]]`, `[[`, `]=`, `[=`, `]u`, `[u`) | | `g:nuwiki_no_table_editing_mappings` | `0` | `1` skips the `table_editing` group (`gqq`, `gq1`, `gww`, `gw1`, ``, ``; insert ``, ``) | | `g:nuwiki_no_diary_mappings` | `0` | `1` skips the `diary` nav group (``, ``) | | `g:nuwiki_no_html_export_mappings` | `0` | `1` skips the `html_export` group (`wh`, `whh`, `wha`) | | `g:nuwiki_no_text_objects_mappings` | `0` | `1` skips the `text_objects` group (`ah`, `ih`, `aH`, `iH`, `al`, `il`, `a\`, `i\`, `ac`, `ic`) | | `g:nuwiki_no_folding` | `0` | `1` skips foldexpr setup | | `g:nuwiki_mouse_mappings` | `0` | `1` enables mouse maps | | `g:nuwiki_binary_path` | _(auto)_ | path to a prebuilt `nuwiki-ls` binary, bypassing the bundled one | | `g:nuwiki_build_from_source` | `0` | `1` builds the binary with `cargo` instead of downloading a release | | `g:nuwiki_no_calendar` | `0` | `1` opts out of calendar-vim integration | | `g:nuwiki_auto_chdir` | `0` | `1` `:lcd` into the wiki root when a wiki buffer becomes current | | `g:nuwiki_auto_header` | `0` | `1` insert a level-1 header from the filename on new wiki pages | --- ## Usage ### Commands `:Nuwiki*` is the canonical native surface; every command also has a `:Vimwiki*` counterpart for drop-in migration. Both are registered as buffer-local commands on every `.wiki` buffer (the wiki picker is also global so you can switch wikis before any `.wiki` file is open). Type `:Nuwiki` or `:Vimwiki` to browse them; coc.nvim users can also call the server directly with `:CocCommand nuwiki.`. Only the legacy short forms `:VWB` / `:VWS` and `:VimwikiSearch` keep a Vimwiki-only name (shown as `—` in the Nuwiki column); every other command is mirrored. The split / tab link-following commands are available on both the Neovim and plain-Vim paths. **Wiki & navigation** | Nuwiki | Vimwiki | Action | |---|---|---| | `:NuwikiIndex [N]` | `:VimwikiIndex [N]` | Open wiki N's index page (default: 1) | | `:NuwikiTabIndex [N]` | `:VimwikiTabIndex [N]` | Same, in a new tab | | `:NuwikiUISelect` | `:VimwikiUISelect` | Pick a wiki from a list (multi-wiki) | | `:NuwikiGoto {page}` | `:VimwikiGoto {page}` | Open `{page}.wiki` by name | | `:NuwikiFollowLink` | `:VimwikiFollowLink` | Follow the link under the cursor | | `:NuwikiSplitLink` / `:NuwikiVSplitLink` / `:NuwikiTabnewLink` | `:VimwikiSplitLink` / `:VimwikiVSplitLink` / `:VimwikiTabnewLink` | Follow the link in a horizontal split / vertical split / new tab | | `:NuwikiTabDropLink` | `:VimwikiTabDropLink` | Follow the link in a tab, reusing an existing tab if the file is already open | | `:NuwikiGoBackLink` | `:VimwikiGoBackLink` | Jump back to where you followed the link from | | `:NuwikiBacklinks` | `:VimwikiBacklinks` (`:VWB`) | Show all references to the current page | | `:NuwikiNextLink` / `:NuwikiPrevLink` | `:VimwikiNextLink` / `:VimwikiPrevLink` | Jump to the next / previous wikilink | | `:NuwikiBaddLink` | `:VimwikiBaddLink` | Add the link target under the cursor to the buffer list | | `:NuwikiNormalizeLink` | `:VimwikiNormalizeLink` | Wrap the word / visual selection under the cursor as a wikilink | | `:NuwikiSearch {pat}` | `:VimwikiSearch {pat}` (`:VWS`) | `:lvimgrep` `{pat}` across the current wiki's pages (empty `{pat}` reuses the last search) | | `:NuwikiVar` | `:VimwikiVar [key [val]]` | Get/set nuwiki client config | | `:NuwikiReturn` | `:VimwikiReturn` | Command form of the smart `` list/table continuation | | `:NuwikiShowVersion` | `:VimwikiShowVersion` | Print the nuwiki version + host editor | **Diary** | Nuwiki | Vimwiki | Action | |---|---|---| | `:NuwikiMakeDiaryNote` (`:NuwikiDiaryToday`) | `:VimwikiMakeDiaryNote` | Open today's entry (or this week / month / year, per `diary_frequency`) | | `:NuwikiTabMakeDiaryNote` | `:VimwikiTabMakeDiaryNote` | Open today's entry in a new tab | | `:NuwikiMakeYesterdayDiaryNote` (`:NuwikiDiaryYesterday`) | `:VimwikiMakeYesterdayDiaryNote` | Step the diary back one period | | `:NuwikiMakeTomorrowDiaryNote` (`:NuwikiDiaryTomorrow`) | `:VimwikiMakeTomorrowDiaryNote` | Step the diary forward one period | | `:NuwikiDiaryIndex` | `:VimwikiDiaryIndex` | Open the diary index page | | `:NuwikiDiaryGenerateLinks` | `:VimwikiDiaryGenerateLinks` | Rebuild the diary index from current entries | | `:NuwikiDiaryNextDay` / `:NuwikiDiaryPrevDay` (`:NuwikiDiaryNext` / `:NuwikiDiaryPrev`) | `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | Chronological diary nav (same cadence as the current entry) | **Pages & files** | Nuwiki | Vimwiki | Action | |---|---|---| | `:NuwikiRenameFile` | `:VimwikiRenameFile` (`:VimwikiRenameLink`) | Rename the current page, rewriting incoming links | | `:NuwikiDeleteFile` | `:VimwikiDeleteFile` (`:VimwikiDeleteLink`) | Delete the current page | **Lists, tasks & tables** | Nuwiki | Vimwiki | Action | |---|---|---| | `:NuwikiNextTask` | `:VimwikiNextTask` | Jump to the next unfinished task | | `:NuwikiToggleListItem` | `:VimwikiToggleListItem` | Toggle the checkbox under the cursor (`-range`: every item in the selection) | | `:NuwikiToggleRejected` | `:VimwikiToggleRejectedListItem` | Toggle the rejected marker `[-]` | | `:NuwikiListToggle` | `:VimwikiListToggle` | Toggle a checkbox on the current item (adds `[ ]` if none) | | `:NuwikiIncrementListItem` / `:NuwikiDecrementListItem` | `:VimwikiIncrementListItem` / `:VimwikiDecrementListItem` | Cycle the list marker to the next / previous symbol (`-range`: every item in the selection) | | `:NuwikiChangeSymbol {sym}` | `:VimwikiChangeSymbolTo {sym}` (`:VimwikiListChangeSymbolI`) | Set the current item's marker to `{sym}` | | `:NuwikiChangeSymbolInList {sym}` | `:VimwikiChangeSymbolInListTo {sym}` | Set every item in the list to `{sym}` | | `:NuwikiRemoveDone[!]` | `:VimwikiRemoveDone[!]` | Remove every completed item from the current list; with `!`, from the whole buffer | | `:NuwikiRemoveCheckbox` | `:VimwikiRemoveSingleCB` | Strip the checkbox from the current list item | | `:NuwikiRemoveCheckboxInList` | `:VimwikiRemoveCBInList` | Strip the checkbox from every item in the current list | | `:NuwikiListChangeLvl {increase\|decrease}` | `:VimwikiListChangeLvl {increase\|decrease}` | Indent / dedent the current list item | | `:NuwikiRenumberList` | `:VimwikiRenumberList` | Renumber the current ordered list | | `:NuwikiRenumberAllLists` | `:VimwikiRenumberAllLists` | Renumber every ordered list in the buffer | | `:NuwikiTable {cols} [rows]` | `:VimwikiTable {cols} [rows]` | Insert a table at the cursor | | `:NuwikiTableMoveColumnLeft` / `:NuwikiTableMoveColumnRight` | `:VimwikiTableMoveColumnLeft` / `:VimwikiTableMoveColumnRight` | Move the current table column left / right | | `:NuwikiTableAlign` | `:VimwikiTableAlignQ` / `:VimwikiTableAlignW` | Re-align the current table's columns | **Contents, links & tags** | Nuwiki | Vimwiki | Action | |---|---|---| | `:NuwikiTOC` | `:VimwikiTOC` | Generate / refresh the table of contents on the current page | | `:NuwikiGenerateLinks` | `:VimwikiGenerateLinks` | Insert a flat list of every page in the wiki | | `:NuwikiCheckLinks` | `:VimwikiCheckLinks` | Send broken links to the quickfix list (`-range`: limit to the current buffer's selected lines) | | `:NuwikiFindOrphans` | `:VimwikiFindOrphans` | List pages with no incoming links in the quickfix list | | `:NuwikiRebuildTags[!]` | `:VimwikiRebuildTags[!]` | Re-index the current wiki; `!` re-indexes every configured wiki | | `:NuwikiSearchTags {tag}` | `:VimwikiSearchTags {tag}` | Quickfix listing every occurrence of `:tag:` | | `:NuwikiGenerateTagLinks [tag]` | `:VimwikiGenerateTagLinks [tag]` (`:VimwikiGenerateTags`) | Insert a section linking every page that has `` | | `:NuwikiColorize {color}` | `:VimwikiColorize {color}` | Wrap the word / visual selection in a coloured `` | | `:NuwikiPasteLink` / `:NuwikiPasteUrl` | `:VimwikiPasteLink` / `:VimwikiPasteUrl` | Paste the clipboard contents as a wikilink / raw URL | | `:NuwikiCatUrl` | `:VimwikiCatUrl` | Echo the `file://` URL of the current page's HTML export | **HTML export** | Nuwiki | Vimwiki | Action | |---|---|---| | `:Nuwiki2HTML` (`:NuwikiExport`) | `:Vimwiki2HTML` | Render the current page to HTML | | `:Nuwiki2HTMLBrowse` (`:NuwikiExportBrowse`) | `:Vimwiki2HTMLBrowse` | Render the current page and open it in the default browser | | `:NuwikiAll2HTML[!]` (`:NuwikiExportAll[!]`) | `:VimwikiAll2HTML[!]` | Export the whole wiki (`!` forces; otherwise incremental) | | `:NuwikiRss` | `:VimwikiRss` | Write `rss.xml` for diary entries | ### Default keymaps All buffer-local. Disable the whole layer via `mappings.enabled = false` in Neovim, or `let g:nuwiki_no_default_mappings = 1` in Vim. Drop a single group via `mappings. = false` in Neovim, or `let g:nuwiki_no__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 tab, reusing an existing tab if the file is already open (`` is the macOS Cmd alias) | | `` | Add the link target to the buffer list (`:badd`, no jump) | | `` | 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` | Set the current item's marker to `` — one of `-` `*` `#` `1` (`1.`) `i` (`i)`) `I` (`I)`) `a` (`a)`) `A` (`A)`) | | `gL` | Set every item in the list to `` (same symbol keys) | | `gl` | Remove the checkbox from the current item, keeping its text | | `gL` | Remove the checkbox from every item in the current list | | `o` / `O` | Open a new line and continue the list marker (preserves any checkbox) | Bare `gl` / `gL` share the `gl…` prefix with the maps above, so (like upstream vimwiki) they fire only after `'timeoutlen'`; type a suffix such as `gln` or `gl*` quickly to reach the prefixed maps. Removing *done* items is command-only: `:NuwikiRemoveDone` (current list) / `:NuwikiRemoveDone!` (whole buffer). **Lists** (insert mode) | Key | Action | |---|---| | `` | Continue the list with the same marker (preserves checkbox), or break out on an empty bullet | | `` | Multiline list item continuation (no new marker) | | `` / `` | 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) The `w` prefix below is the default `map_prefix` (`g:nuwiki_map_prefix`); change that option to relocate the whole family. | Key | Action | |---|---| | `ww` | Open the wiki index | | `wt` | Open the wiki index in a new tab | | `ws` | Pick a wiki | | `wi` | Open the diary index | | `ww` / `wy` | Diary today / yesterday | | `wt` | Diary today, in a new tab | | `wm` | Diary 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 | **Mouse** (normal mode, opt-in via `mappings.mouse = true` / `let g:nuwiki_mouse_mappings = 1`) | Key | Action | |---|---| | `<2-LeftMouse>` | Follow the link under the pointer | | `` / `` | Follow in a horizontal / vertical split | | `` | Add the link target to the buffer list | | `` | Jump back (``) | **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. Keep your existing config, or point `wiki_root` / `wikis[i].root` at your directory. **Existing `g:vimwiki_list` config works as-is (Vim & Neovim).** When you haven't configured nuwiki natively (`g:nuwiki_wikis` / `setup()` / `g:nuwiki_*`), both clients read your upstream `g:vimwiki_list` and `g:vimwiki_*` globals and translate them: each wiki's `path`/`path_html`/`template_*`/`auto_export` plus globals like `toc_header_level`, `html_header_numbering`, `html_header_numbering_sym`, `links_space_char`, and `list_margin`. Native config always takes precedence. (Settings nuwiki implements differently — see [`known-issues.md`](./known-issues.md) — are ignored.) On Neovim with lazy.nvim, swap the `vimwiki` plugin spec for nuwiki and call `require('nuwiki').setup()` — your `vim.g.vimwiki_list` is picked up unchanged. 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. A few upstream options are implemented differently or not at all — by design, since nuwiki is LSP-backed rather than pure VimL. See [`known-issues.md`](./known-issues.md) for the full list of divergences. --- ## 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.