diff --git a/README.md b/README.md index 3ff07ea..7206819 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,335 @@ # nuwiki -A Vim/Neovim plugin providing full vimwiki syntax support, implemented as a -Rust-based language server (LSP). +**A vimwiki-compatible Vim/Neovim plugin backed by a Rust language server.** -> **Status:** v1.0 (Phases 0–10) shipped — parsing, highlighting, navigation, -> editor glue, and release pipeline are all live. v1.1 (Phases 11–19) in -> progress, closing the gap to a full vimwiki replacement: tags, diary, -> server-side edit commands, link health, HTML export commands, multi-wiki, -> and the `:Vimwiki*` keymap layer. +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 just wire up keystrokes and +display results. -See [`SPEC.md`](./SPEC.md) for the full project specification. +- ✅ Full vimwiki syntax (`.wiki`) — headings, lists with checkboxes, + tables, math, code, tags, links (wiki / interwiki / diary / file / + raw URL / transclusion). +- ✅ Goto-definition, backlinks, hover, completion, workspace symbol + search, semantic highlighting — all over LSP. +- ✅ Diary (today/yesterday/tomorrow/next/prev), tags (search + link + generation), TOC generation, broken-link diagnostics, orphan finder. +- ✅ Multi-wiki with cross-wiki interwiki links (`[[wn.name:Page]]`). +- ✅ HTML export with templates, CSS, auto-export on save, RSS feed. +- ✅ Folding (LSP `foldingRange` + regex fallback), header & link + text objects, the `` two-step link-create-and-follow. +- ✅ `:Vimwiki*` command compat + `:Nuwiki*` canonical aliases. -## Implementation status +See [`SPEC.md`](./SPEC.md) for the architecture, design decisions, and +the full phase log; the [pending-work section](./SPEC.md#13-pending-implementation) +lists the few items still in flight (table/list rewriter commands and +colorize). -### v1.0 — foundation +--- + +## Installation + +The plugin ships a `nuwiki-ls` binary that the editor talks to over +LSP stdio. The `install()` Lua helper downloads a pre-built release for +your platform, falling back to `cargo build --release` if no asset +matches. + +### lazy.nvim + +```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 +``` + +Then ensure an LSP client is loaded (Vim path prefers +[`vim-lsp`](https://github.com/prabirshrestha/vim-lsp), falls back to +[`coc.nvim`](https://github.com/neoclide/coc.nvim)). Neovim 0.11+ uses +its built-in LSP client — no extra plugin needed. + +### Trying it without touching your config + +```sh +./start-nvim.sh # spawns Neovim with a scratch wiki under + # $XDG_CACHE_HOME/nuwiki-dev +./start-vim.sh # same, but for plain Vim — clones vim-lsp + # into the cache on first run +``` + +Both scripts build the LSP binary, seed a sample wiki, and launch +the editor against an isolated state dir. Pass a wiki file path to +open it directly; `NUWIKI_DEV_WIKI=/path/to/wiki` points at your +own sandbox instead. + +--- + +## Configuration + +Defaults are vimwiki-compatible. Full schema (with type annotations and +defaults) lives in [`lua/nuwiki/config.lua`](./lua/nuwiki/config.lua). + +```lua +require('nuwiki').setup({ + -- Single-wiki shorthand (vimwiki-compatible). + wiki_root = '~/vimwiki', + file_extension = '.wiki', + syntax = 'vimwiki', -- 'vimwiki' | 'markdown' (future) + log_level = 'warn', + + -- Or the multi-wiki form. The single-wiki keys above desugar into + -- a one-entry `wikis = [...]` list, so you can pick either shape. + wikis = { + { + name = 'personal', + root = '~/vimwiki', + file_extension = '.wiki', + diary_rel_path = 'diary', + diary_index = 'diary', + -- HTML export (Phase 17) + html_path = '~/vimwiki/_html', + template_path = '~/vimwiki/_templates', + template_default = 'default', + template_ext = '.tpl', + css_name = 'style.css', + auto_export = false, -- export on save + exclude_files = {}, -- glob patterns + }, + }, + + -- Keymap groups — flip subgroups off independently. Defaults all on. + mappings = { + enabled = true, + wiki_prefix = true, -- w* + links = true, -- , , , , +, … + lists = true, -- , gln/glp/glx, gl, o/O + headers = true, -- =, -, ]], [[, ]=, [=, ]u, [u + table_editing = true, -- gqq, gq1, gww, gw1, , + diary = true, -- , + html_export = true, -- wh, whh + text_objects = true, -- ah, ih + }, + + -- Folding strategy: 'lsp' (server-driven foldingRange — default on + -- Neovim 0.11+), 'expr' (pure regex), or 'off'. + folding = 'lsp', + + -- Broken-link diagnostic severity: 'off' | 'hint' | 'warn' | 'error'. + diagnostic = { + link_severity = 'warn', + }, +}) +``` + +Vim users without Lua should set the equivalent globals before sourcing +the plugin: + +```vim +let g:nuwiki_wiki_root = '~/vimwiki' +let g:nuwiki_file_extension = '.wiki' +let g:nuwiki_log_level = 'warn' +let g:nuwiki_no_default_mappings = 0 " set to 1 to skip the keymap layer +``` + +--- + +## Usage + +### Commands + +Both `:Vimwiki*` (migration compat) and `:Nuwiki*` (canonical native) +are registered as buffer-local commands on every `.wiki` buffer. +Selected highlights — see `:Vimwiki` for the full list. + +| Command | What it does | +|---|---| +| `:VimwikiIndex [N]` | Open wiki N's `index.wiki` (default: 1) | +| `:VimwikiUISelect` | Pick a wiki from a list (multi-wiki only) | +| `:VimwikiMakeDiaryNote` | Open today's diary page | +| `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | Chronological diary nav | +| `:VimwikiTOC` | Generate / refresh table of contents on the current page | +| `:VimwikiGenerateLinks` | Flat list of every page in the wiki | +| `:VimwikiCheckLinks` | Send broken links to the quickfix list | +| `:VimwikiSearchTags {tag}` | Quickfix of all occurrences of `:tag:` | +| `:VimwikiGenerateTagLinks [tag]` | Insert a section linking all pages with `` | +| `:VimwikiRebuildTags` | Force a full workspace re-index | +| `:VimwikiRenameFile` | Rename current page; rewrites incoming links | +| `:VimwikiDeleteFile` | Delete current page (server-side `DeleteFile`) | +| `:Vimwiki2HTML` | Render current page to HTML | +| `:Vimwiki2HTMLBrowse` | Render + open in default browser | +| `:VimwikiAll2HTML[!]` | Export the whole wiki (`!` forces, otherwise incremental) | +| `:VimwikiRss` | Write `rss.xml` of diary entries | +| `:VimwikiFollowLink` | Follow link under cursor (``) | +| `:VimwikiBacklinks` | Show references to current page | + +### Default keymaps + +All buffer-local. Disable any group via `mappings. = false` in +Neovim, or `let g:nuwiki_no_default_mappings = 1` in Vim. + +**Links** + +| Key | Action | +|---|---| +| `` | Smart follow: inside `[[…]]` → jump; on a bare word → wrap as `[[word]]` (second `` follows) | +| `` | Follow in horizontal split | +| `` | Follow in vertical split | +| `` | Follow in new tab | +| `` | Jump back (``) | +| `` / `` | Next / previous wikilink on or after the cursor | + +**Lists** + +| Key | Action | +|---|---| +| `` / `` / `` | Toggle checkbox (terminal byte aliases) | +| `gln` / `glp` | Cycle checkbox state (forward / backward) | +| `glx` | Toggle the rejected marker `[-]` | +| `gnt` | Jump to the next unfinished task | +| `o` / `O` | Open a new line and continue the list marker (and checkbox if any) | + +**Headers** + +| Key | Action | +|---|---| +| `=` / `-` | Add / remove a heading level | +| `]]` / `[[` | Next / previous heading | +| `]=` / `[=` | Next / previous sibling heading | +| `]u` / `[u` | Jump to parent heading | + +**Wiki / diary / export** + +| Key | Action | +|---|---| +| `ww` | Today's diary | +| `ws` | Pick a wiki (`vim.ui.select` / `inputlist`) | +| `wi` | Diary index | +| `ww` / `wy` / `wt` | Today / yesterday / tomorrow | +| `wi` | Rebuild diary index page | +| `wn` / `wd` / `wr` | Goto / delete / rename page | +| `wh` / `whh` | Export current page → HTML / open in browser | +| `wha` | Export every page | +| `` / `` | Next / previous diary entry | + +**Text objects** + +| Key | Selection | +|---|---| +| `ah` / `ih` | A heading (with / without the heading line itself) | + +Additional text objects (`aH`, `iH`, `a\`, `i\`, `ac`, `ic`, `al`, +`il`) ship once the table/list rewriter commands from +[SPEC §13.1](./SPEC.md#131-phase-14--listtablelinkcolorize-commands) +land. + +### Health check + +```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. Drop the original `vimwiki` plugin from your config, +install nuwiki, and point `wiki_root` at the same directory. Existing +pages, diary entries, tags, and templates Just Work. + +Two known behavior deltas vs. upstream vimwiki: + +- nuwiki's index is built lazily on workspace scan; the very first + `:VimwikiCheckLinks` after launch may be empty until the background + task completes. Subsequent runs are instant. +- `gln` and `glp` both call `nuwiki.list.cycleCheckbox`, which is + forward-only at present. A reverse cycle lands once the v1.1 list + rewriters do. + +--- + +## Repository layout + +``` +nuwiki/ +├── Cargo.toml workspace root +├── crates/ +│ ├── nuwiki-core/ parser, AST, renderer (editor-independent) +│ ├── nuwiki-lsp/ LSP protocol bridge +│ └── nuwiki-ls/ stdio LSP binary entry point +├── plugin/ universal Vim/Neovim entry point +├── lua/nuwiki/ Neovim Lua glue +├── autoload/nuwiki/ Vim VimL glue +├── ftdetect/ filetype detection for .wiki +├── ftplugin/vimwiki.vim per-buffer settings + commands + keymaps +├── syntax/vimwiki.vim static fallback highlighting +├── doc/ `:h nuwiki` +├── scripts/ test harness + build helpers +└── .gitea/workflows/ CI / release pipelines +``` + +## Development + +### Build + test + +```sh +cargo build --workspace +cargo test --workspace # 381 Rust tests +./scripts/test-keymaps.sh # 19 Neovim keymap cases (LSP-driven) +./scripts/test-keymaps-vim.sh # 12 Vim keymap cases (pure-VimL) +``` + +CI runs all four on every push (`.gitea/workflows/ci.yaml`). MSRV: Rust +1.83. + +### Trying changes against a real wiki + +```sh +./start-nvim.sh # isolated state under +./start-vim.sh # $XDG_CACHE_HOME/nuwiki-dev +``` + +Both regenerate their minimal config on every launch and seed a +scratch wiki with sample pages, links, checkboxes, and tags. + +### Implementation status + +#### v1.0 — foundation | Phase | Name | Status | |---|---|---| @@ -29,48 +345,21 @@ See [`SPEC.md`](./SPEC.md) for the full project specification. | 9 | Editor Glue | ✅ done | | 10 | CI/CD release pipeline | ✅ done | -### v1.1 — full vimwiki replacement +#### v1.1 — full vimwiki replacement | Phase | Name | Status | |---|---|---| | 11 | Plumbing prerequisites | ✅ done | | 12 | Tags | ✅ done | | 13 | Workspace edits + executeCommand | ✅ done | -| 14 | List & table edit commands | ✅ done (focused subset; table/list-renumber/colorize deferred) | +| 14 | List & table edit commands | ✅ done (focused subset; table/list-renumber/colorize deferred — see [SPEC §13.1](./SPEC.md#131-phase-14--listtablelinkcolorize-commands)) | | 15 | Link health + TOC/index generation | ✅ done | | 16 | Diary | ✅ done | | 17 | HTML export commands | ✅ done | | 18 | Multi-wiki | ✅ done | | 19 | Editor glue v2 (`:Vimwiki*` compat, keymaps, text objects, folding) | ✅ done | -## Repository layout - -``` -nuwiki/ -├── Cargo.toml workspace root -├── crates/ -│ ├── nuwiki-core/ parser, AST, renderer (no editor deps) -│ ├── nuwiki-lsp/ LSP protocol bridge -│ └── nuwiki-ls/ binary that speaks LSP over stdio -├── plugin/ universal Vim/Neovim entry point -├── lua/nuwiki/ Neovim Lua glue -├── autoload/nuwiki/ Vim VimL glue -├── ftdetect/ filetype detection for .wiki -├── ftplugin/ per-buffer filetype settings -├── syntax/ static fallback highlighting -├── doc/ `:h nuwiki` -├── scripts/ build-time helpers -└── .gitea/workflows/ CI / release pipelines -``` - -## Building - -```sh -cargo build --workspace -cargo test --workspace -``` - -MSRV: Rust 1.83. +--- ## License @@ -80,5 +369,5 @@ Dual-licensed under either of: - 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. +intentionally submitted for inclusion in this work shall be dual licensed +as above, without any additional terms or conditions.