Files
nuwiki/README.md
T
gffranco db9c1c5c11
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 53s
CI / editor keymaps (push) Successful in 1m29s
feat: real range/bang/visual handling for the deferred command gaps
Implements the four items previously left as "needs handler work" — all
fully wired, not cosmetic.

- ToggleListItem / IncrementListItem / DecrementListItem are now -range
  (both clients). The client loops the per-line op over [<line1>,<line2>]
  (toggle_list_item_range / list_cycle_symbol_range). The server's edits are
  version-less single-line WorkspaceEdits, so each line applies independently
  — no server protocol change. `:'<,'>VimwikiToggleListItem` toggles the whole
  selection.
- VimwikiRebuildTags gains -bang: `:…RebuildTags!` passes { all: true } and
  the server (tags_rebuild) re-indexes every configured wiki via
  wikis_snapshot(), not just the current one.
- VimwikiNormalizeLink is -nargs=?: with `1` (upstream's visual flag; the
  x-mode `+` mapping now passes it) it wraps the '< / '> selection as
  [[selection]] (new wrap_visual_as_wikilink in both clients).
- VimwikiCheckLinks is -range: a ranged invocation filters the broken-link
  report to the current buffer's selected lines (client-side filter in
  results_to_qf / check_links).

Tests: cmd.toggle_list_item_range + cmd.normalize_link_visual_wraps_selection
(test-keymaps.lua), normalize.visual_wraps_selection (test-keymaps-vim.vim).
README + doc/nuwiki.txt + gap doc updated. fmt/clippy clean; nvim 274,
vim 266+18, all Rust tests + config-parity green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:57:03 +00:00

616 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 `<CR>`: continues list markers (`-`, `*`, `1.`,
`a)`, `i)`, …) and checkboxes; breaks out of an empty bullet; inserts
a fresh row when inside a `|…|` table.
- Insert-mode `<Tab>` / `<S-Tab>` jumps between table cells, adding a
new row past the last column.
- `<C-D>` / `<C-T>` indent/dedent the current list item; `<C-L><C-J>`
/ `<C-L><C-K>` cycle through the marker styles; `<C-L><C-M>` 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 `<CR>` on a bare word: wraps it as `[[word]]`, second press
follows the link.
### Diary
- Daily diary out of the box (`<Leader>ww`, `<C-Down>` / `<C-Up>`).
- 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) (recommended,
auto-registered) or [`coc.nvim`](https://github.com/neoclide/coc.nvim)
(one-time `coc-settings.json` entry — 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 && ln -s ../target/release/nuwiki-ls bin/nuwiki-ls
```
### 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** — [`coc.nvim`](https://github.com/neoclide/coc.nvim) reads
its server list from `coc-settings.json`, which the plugin can't edit
on your behalf, so add the entry once yourself (open it with
`:CocConfig`):
```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.<command>` 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',
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,
},
},
-- 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/gL, o/O, …
headers = true, -- =, -, ]], [[, ]=, [=, ]u, [u
table_editing = true, -- gqq, <A-Left>, <A-Right>, <Tab> in insert mode
diary = true, -- <C-Down>, <C-Up>
html_export = true, -- <Leader>wh, <Leader>whh
text_objects = true, -- ah, ih, aH, iH, al, il, a\, i\, ac, ic
mouse = false, -- <2-LeftMouse>, <RightMouse>, … (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_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_no_folding = 0 " set to 1 to skip foldexpr setup
let g:nuwiki_mouse_mappings = 0 " set to 1 to enable mouse maps
" Drop individual keymap subgroups (mirror the Lua `mappings.<group>` 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'` |
| `folding` | string | `'lsp'` | `'lsp'` \| `'expr'` \| `'off'` |
| `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`.
| Key | Default | Controls |
|-----|---------|----------|
| `enabled` | `true` | master switch for the whole keymap layer |
| `wiki_prefix` | `true` | `<Leader>w*` |
| `links` | `true` | `<CR>`, `<S-CR>`, `<Tab>`, `<BS>`, `+`, … |
| `lists` | `true` | `<C-Space>`, `gln`/`glp`/`glx`, `gl`/`gL`, `o`/`O`, … |
| `headers` | `true` | `=`, `-`, `]]`, `[[`, `]=`, `[=`, `]u`, `[u` |
| `table_editing` | `true` | `gqq`, `<A-Left>`, `<A-Right>`, `<Tab>` (insert) |
| `diary` | `true` | `<C-Down>`, `<C-Up>` |
| `html_export` | `true` | `<Leader>wh`, `<Leader>whh` |
| `text_objects` | `true` | `ah`, `ih`, `aH`, `iH`, `al`, `il`, `a\`, `i\`, `ac`, `ic` |
| `mouse` | `false` | `<2-LeftMouse>`, `<RightMouse>`, … (opt-in) |
#### Per-wiki keys (`wikis[]`)
| 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 |
| `listsyms` | string | `' .oOX'` | checkbox progression chars |
| `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` |
#### HTML export keys (per-wiki)
| Key | Type | Default | Accepted values |
|-----|------|---------|-----------------|
| `html_path` | string (path) | `'<root>/_html'` | output directory |
| `template_path` | string (path) | `'<root>/_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` |
| `exclude_files` | list | `{}` | glob patterns |
| `color_dic` | table | `{}` | `{ name = 'color', … }` |
#### 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_log_level` | `'warn'` | `error` \| `warn` \| `info` \| `debug` |
| `g:nuwiki_link_severity` | `'warn'` | broken-link severity: `off` \| `hint` \| `warn` \| `error` |
| `g:nuwiki_no_default_mappings` | `0` | `1` skips the whole keymap layer |
| `g:nuwiki_no_wiki_prefix_mappings` | `0` | `1` skips the `<Leader>w*` group |
| `g:nuwiki_no_links_mappings` | `0` | `1` skips the link group (`<CR>`, `+`, …) |
| `g:nuwiki_no_lists_mappings` | `0` | `1` skips the list group (`<C-Space>`, `gl*`, `o`/`O`, …) |
| `g:nuwiki_no_headers_mappings` | `0` | `1` skips the header group (`=`, `-`, `]]`, …) |
| `g:nuwiki_no_table_editing_mappings` | `0` | `1` skips the table group (`gqq`, `<A-Left>`, insert `<Tab>`) |
| `g:nuwiki_no_diary_mappings` | `0` | `1` skips the diary nav group (`<C-Down>`, `<C-Up>`) |
| `g:nuwiki_no_html_export_mappings` | `0` | `1` skips the HTML export group (`<Leader>wh*`) |
| `g:nuwiki_no_text_objects_mappings` | `0` | `1` skips the text-object group (`ah`, `il`, …) |
| `g:nuwiki_no_folding` | `0` | `1` skips foldexpr setup |
| `g:nuwiki_mouse_mappings` | `0` | `1` enables mouse maps |
---
## 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<Tab>` or `:Vimwiki<Tab>` to browse them; coc.nvim users can
also call the server directly with `:CocCommand nuwiki.<command>`.
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 |
| — | `:VimwikiSearch {pat}` (`:VWS`) | `:lvimgrep` `{pat}` across every page |
**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 `<tag>` |
| `:NuwikiColorize {color}` | `:VimwikiColorize {color}` | Wrap the word / visual selection in a coloured `<span>` |
| `: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.<group> = false` in Neovim, or
`let g:nuwiki_no_<group>_mappings = 1` in Vim.
**Links** (normal mode)
| Key | Action |
|---|---|
| `<CR>` | Smart follow — inside `[[…]]` jumps; on a bare word wraps as `[[word]]` (second `<CR>` follows) |
| `<S-CR>` | Follow in horizontal split |
| `<C-CR>` | Follow in vertical split |
| `<C-S-CR>` / `<D-CR>` | Follow in a tab, reusing an existing tab if the file is already open (`<D-CR>` is the macOS Cmd alias) |
| `<M-CR>` | Add the link target to the buffer list (`:badd`, no jump) |
| `<BS>` | Jump back (`<C-o>`) |
| `<Tab>` / `<S-Tab>` | Next / previous wikilink on or after the cursor |
| `+` | Wrap word / visual selection as a wikilink (no follow) |
**Lists** (normal mode)
| Key | Action |
|---|---|
| `<C-Space>` / `<C-@>` / `<Nul>` | 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<sym>` | Set the current item's marker to `<sym>` — one of `-` `*` `#` `1` (`1.`) `i` (`i)`) `I` (`I)`) `a` (`a)`) `A` (`A)`) |
| `gL<sym>` | Set every item in the list to `<sym>` (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 |
|---|---|
| `<CR>` | Continue the list with the same marker (preserves checkbox), or break out on an empty bullet |
| `<C-D>` / `<C-T>` | Dedent / indent the current item |
| `<C-L><C-J>` / `<C-L><C-K>` | Cycle the marker style (`-``*``#``1.``1)``a)``A)``i)``I)`) |
| `<C-L><C-M>` | 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 |
| `<A-Left>` / `<A-Right>` | Move the column under the cursor left / right |
| `<Tab>` / `<S-Tab>` (insert) | Jump to the next / previous cell; `<Tab>` past the last cell inserts a fresh row |
| `<CR>` (insert) | Insert a fresh empty row below the current one |
**Wiki / diary / export** (normal mode)
| Key | Action |
|---|---|
| `<Leader>ww` | Open the wiki index |
| `<Leader>wt` | Open the wiki index in a new tab |
| `<Leader>ws` | Pick a wiki |
| `<Leader>wi` | Open the diary index |
| `<Leader>w<Leader>w` / `<Leader>w<Leader>y` | Diary today / yesterday |
| `<Leader>w<Leader>t` | Diary today, in a new tab |
| `<Leader>w<Leader>m` | Diary tomorrow |
| `<Leader>w<Leader>i` | Rebuild the diary index page |
| `<Leader>wn` / `<Leader>wd` / `<Leader>wr` | Goto / delete / rename page |
| `<Leader>wh` / `<Leader>whh` | Export the current page to HTML / open in browser |
| `<Leader>wha` | Export every page |
| `<Leader>wc` | Wrap word (or visual selection) in a colour span |
| `<C-Down>` / `<C-Up>` | 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 |
| `<S-2-LeftMouse>` / `<C-2-LeftMouse>` | Follow in a horizontal / vertical split |
| `<MiddleMouse>` | Add the link target to the buffer list |
| `<RightMouse>` | Jump back (`<C-o>`) |
**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.