09b5a2bd46
Added per-wiki auto_generate_links, auto_generate_tags, auto_diary_index (default false, like upstream), wired into the did_save hook mirroring auto_toc: - auto_generate_links / auto_generate_tags rebuild the Generated Links / Generated Tags section ONLY when it already exists (new links_rebuild_edit / tag_links_rebuild_edit, like toc_rebuild_edit) — never inserts one into a page that lacks it. - auto_diary_index regenerates the diary index page when a dated diary entry (not the index itself) is saved, via diary_generate_index_edit (handles the index file open / on-disk / absent). auto_tags is recorded as an intentional divergence: nuwiki re-indexes tags on every change, so the tag metadata is always fresh (no on-disk file to update). Tests: links_rebuild_edit_only_acts_when_section_present, tag_links_rebuild_edit_only_acts_when_index_present, config round-trip + defaults. README/doc/lua comment updated. fmt/clippy clean; 0 Rust failures. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
630 lines
29 KiB
Markdown
630 lines
29 KiB
Markdown
# 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
|
||
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, -- <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 |
|
||
| `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) | `'<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.
|