From f4356798149af8b29e18315739543f4389289284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Mon, 11 May 2026 01:19:36 +0000 Subject: [PATCH] =?UTF-8?q?spec:=20define=20v1.1=20=E2=80=94=20full=20vimw?= =?UTF-8?q?iki=20replacement=20(Phases=2011=E2=80=9318)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v1.0 shipped parsing + highlighting + navigation; the gap-analysis vs github.com/vimwiki/vimwiki shows nuwiki covers ~95% of the syntax but 0% of the daily-authoring command surface (≈40 :Vimwiki* commands and ≈60 keymaps), the diary, tags, link health, HTML export commands, and multi-wiki. v1.1 closes that gap without architectural changes — every new editing operation lands as an LSP executeCommand returning a WorkspaceEdit, so nuwiki-core stays the single source of truth. Spec additions: - Status line: v1.0 shipped; v1.1 in design. - §1: v1.1 scope summarised; multi-wiki moved out of "non-goals (v1.0)" and into v1.1. - §10: 8 new phase rows (11–18) under a v1.1 divider. - 11 Tags, 12 Workspace edits + executeCommand, 13 List/table edit commands, 14 Link health + TOC/index generation, 15 Diary, 16 HTML export commands, 17 Multi-wiki, 18 Editor glue v2. - §11: 8 new pending decisions (P10–P17): command namespace, tag syntax, list-edit transport, multi-wiki config shape, folding mechanism, diary path scheme, v1.0 config back-compat, markdown syntax inclusion. - §12: new top-level v1.1 plan with 12 subsections covering stability commitment, tags spec (AST + index), workspace-edit semantics, ~15 nuwiki.* edit commands, link-health diagnostics, diary path conventions + commands, HTML export commands + per-wiki options, multi-wiki schema, full editor-glue v2 (:Vimwiki* compatibility table, default keymaps, text objects, folding, insert-mode behaviour, health-check additions), updated user-config schema example, §9 syntax checklist additions (tags, hex colour, table alignment, multi-line lists), CI/CD notes. v1.0 stability is preserved: AST + Visitor + LSP capabilities + HtmlRenderer output + v1.0 config keys are all backward-compatible. Co-Authored-By: Claude Opus 4.7 (1M context) --- SPEC.md | 551 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 548 insertions(+), 3 deletions(-) diff --git a/SPEC.md b/SPEC.md index 3dd35c4..753bc4f 100644 --- a/SPEC.md +++ b/SPEC.md @@ -1,7 +1,18 @@ # nuwiki — Project Specification -> Last updated: 2026-05-10 -> Status: All phases (0–10) complete +> Last updated: 2026-05-11 +> Status: v1.0 (Phases 0–10) shipped; v1.1 (Phases 11–18) in design + +> ### Versioning convention +> +> - **v1.0** — the parsing + highlighting + navigation foundation. LSP server +> speaks the spec's §6.9 method set; editor glue provides install + setup +> + health check. +> - **v1.1** — full vimwiki replacement. Adds tags, diary, the +> text-manipulation command surface (toggle checkbox, change list symbol, +> format table, rename page, …), multi-wiki, HTML export commands, +> link-health diagnostics, and the keymap/text-object layer that matches +> vimwiki's daily-authoring ergonomics. --- @@ -18,10 +29,15 @@ nuwiki is a Vim/Neovim plugin that provides full vimwiki syntax support, impleme **Non-goals (v1.0):** - MediaWiki syntax support -- Multi-wiki configurations +- Multi-wiki configurations *(addressed in v1.1, see §12)* - Incremental / tree-sitter parsing - Browser-based or WASM build +**v1.1 scope** *(see §12 for the full plan)*: +- Vimwiki-parity command surface (`:Vimwiki*` compat aliases over LSP + `executeCommand`) +- Tags, diary, link health, HTML export commands, multi-wiki, folding + --- ## 2. Repository @@ -575,6 +591,15 @@ A semver-breaking change (`v0.x` → `v0.x+1` or `v1.x` → `v2.0`) is defined a | 8 | Navigation | Definition, references, hover, completion, workspace index | | 9 | Editor Glue | Installable plugin, health check, binary download | | 10 | CI/CD | Automated lint/test/release pipeline | +| **— v1.1: full vimwiki replacement (see §12) —** | | | +| 11 | Tags | `:tag:` lex + parse, `TagNode`, index, tag-as-anchor resolution | +| 12 | Workspace edits + executeCommand | `workspace/rename` with cross-doc link rewrite, `DeleteFile` ops, `executeCommand` infrastructure | +| 13 | List & table edit commands | Server commands returning `WorkspaceEdit` for checkbox toggle, list-symbol/level change, renumber, table align, column move, table insert | +| 14 | Link health + link/TOC generation | Broken-link diagnostics, `nuwiki.toc.generate`, `nuwiki.links.generate`, `nuwiki.workspace.checkLinks` | +| 15 | Diary | Diary subpath, today/yesterday/tomorrow open commands, diary index generation, calendar hook API | +| 16 | HTML export commands | `nuwiki.export.*` commands, `%template` resolution, CSS file management, auto-export option | +| 17 | Multi-wiki | `wikis = [...]` config, per-wiki `WorkspaceIndex`, URI → wiki resolution, wiki-picker commands | +| 18 | Editor glue v2 | Full `:Vimwiki*` command compat layer, default keymaps, text objects (`ah`/`aH`/`a\\`/`ac`/`al`), `foldexpr` folding | --- @@ -593,3 +618,523 @@ These decisions are required before or during the phase indicated. | ~~P7~~ | ~~**Semantic token type mapping**~~ | ~~Phase 7~~ | ✅ **Custom vimwiki-specific token types** | ~20 types (`vimwikiHeading`, `vimwikiBold`, …) + `level1`..`level6` + `centered` modifiers. Phase 9 ships default highlight groups in `syntax/nuwiki.vim` and the Lua glue. | | ~~P8~~ | ~~**Workspace indexing strategy**~~ | ~~Phase 8~~ | ✅ **Lazy + background with progress** | Initial scan runs as a background tokio task; nav features answer with partial data until complete; progress reported via `window/workDoneProgress`. | | ~~P9~~ | ~~**macOS runner**~~ | ~~Phase 10~~ | ✅ **Stay manual** | Release workflow cross-compiles the 4 Linux targets via `cross`; macOS + Windows binaries are produced ad hoc by maintainers and uploaded to the same Gitea release. Revisit when a Mac runner is available. | +| **v1.1 decisions** | | | | | +| P10 | **Command namespace** | Phase 12 | `:Vimwiki*` only · `:Nuwiki*` only · both | Both → easy migration *and* discoverable native commands; one canonical name + alias. | +| P11 | **Tag syntax** | Phase 11 | Strict vimwiki `:tag:` only · Add markdown `#tag` as a second flavour | Strict keeps parity; adding `#tag` collides with hash list markers and ordered-list `#`. | +| P12 | **List-edit transport** | Phase 13 | Server returns `WorkspaceEdit` from `executeCommand` · Editor-side text manipulation in VimL/Lua | `WorkspaceEdit` keeps logic in Rust + testable, but adds latency per keystroke for ``. | +| P13 | **Multi-wiki config shape** | Phase 17 | Lua list (Neovim-native) · JSON config file (cross-editor) | Lua is ergonomic for Neovim; JSON works for Vim + arbitrary clients. | +| P14 | **Folding mechanism** | Phase 18 | LSP `textDocument/foldingRange` · Vim-side `foldexpr` in ftplugin | LSP is correct (server knows AST); foldexpr is what vimwiki users expect and works offline. | +| P15 | **Diary path scheme** | Phase 15 | Fixed `/diary/` · Per-wiki configurable `diary_rel_path` | Vimwiki has `diary_rel_path`; matching keeps migrations clean. | +| P16 | **Backwards-compat for v1.0 config** | Phase 17 | Preserve old single-`wiki_root` shape · Force migration to `wikis = [...]` | Single-wiki should keep working without re-config; `wiki_root` becomes sugar for `wikis = [{ root = wiki_root }]`. | +| P17 | **Markdown syntax in v1.1** | Phase 11+ | Include · Defer to v1.2 | Architecture already supports it (`SyntaxPlugin`); cost is parser work + per-syntax differences in commands. | + +--- + +## 12. v1.1 — Full vimwiki Replacement + +v1.0 shipped the parsing + highlighting + navigation foundation. A vimwiki +user transitioning to nuwiki today gets accurate syntax recognition and +modern LSP nav, but loses the dense interactive layer that makes vimwiki +ergonomic for daily authoring: ≈40 `:Vimwiki*` commands, ≈60 keymaps, the +diary, the tag system, link health, HTML export commands, and multi-wiki +configuration. + +v1.1 closes that gap. The architecture is unchanged — every new editing +operation lands as an LSP `executeCommand` returning a `WorkspaceEdit`, so +the `nuwiki-core` AST stays the single source of truth and the editor glue +stays pure wiring. + +### 12.1 Stability commitment + +v1.1 is additive. The following stay backward-compatible: + +- `nuwiki-core` public AST + Visitor (additions only; no field renames) +- LSP capabilities advertised in v1.0 +- `Renderer` trait + `HtmlRenderer` default output shape +- v1.0 user-config keys (`wiki_root`, `file_extension`, `syntax`, `log_level`) + +New AST nodes (e.g. `TagNode`) and new LSP capabilities are opt-in by +inspection. The Visitor trait gains default-bodied `visit_*` methods for new +node kinds so existing Visitor implementations don't break. + +### 12.2 Tags (Phase 11) + +#### Syntax + +Vimwiki tags are colon-delimited sequences of non-space characters: + +``` +:tag-one:tag-two:other-tag: +``` + +Placement rules (matching vimwiki): + +- On line 1 or 2 of a file → file-level tag +- Within 2 lines after a `= Heading =` → header-level tag +- Otherwise → standalone anchor at the source line + +#### AST additions + +``` +TagNode (block) + span: Span + tags: Vec + scope: TagScope -- File | Heading(idx) | Standalone + +PageMetadata (extend) + tags: Vec -- file-level tags, accumulated +``` + +`TagNode` slots into `BlockNode` as a new variant. The Visitor gains +`visit_tag` (default-bodied). + +#### Indexing + +`WorkspaceIndex` extends `IndexedPage` with: + +``` +IndexedPage (extend) + tags: Vec + +TagInfo + name: String + scope: TagScope + span: Span +``` + +Plus a `tags_by_name: HashMap>` reverse map. The +existing backlink machinery in §6 stays as-is. + +#### LSP behaviour + +- `tags-as-anchors` extends `textDocument/definition`: `[[Page#some-tag]]` + resolves to the tag's location in the target page (in addition to headings). +- `workspace/symbol` results include tags (with `SymbolKind::PROPERTY` or a + custom kind). +- New `executeCommand` operations: `nuwiki.tags.search`, + `nuwiki.tags.generateLinks(tag, ...)`, `nuwiki.tags.rebuild` (force re-index). + +### 12.3 Workspace edits + `executeCommand` (Phase 12) + +#### Capability + +``` +ServerCapabilities { + execute_command_provider: Some(ExecuteCommandOptions { + commands: vec!["nuwiki.*", …], + }), + rename_provider: Some(true), + workspace: Some(WorkspaceServerCapabilities { + file_operations: Some(FileOperationOptions { + will_rename: Some(...), + did_rename: Some(...), + will_delete: Some(...), + did_delete: Some(...), + }), + }), +} +``` + +#### `workspace/rename` semantics + +When a user renames `Page A` → `Page B`: + +1. Server emits a `WorkspaceEdit` containing a `RenameFile` op (`A.wiki` → + `B.wiki`). +2. For every page in the index that links to `A`, the edit also rewrites + `[[A]]` → `[[B]]` (preserving description, anchor, kind). +3. Anchors don't trigger renames — `[[A#anchor]]` follows. + +#### Custom commands router + +A single `executeCommand` handler dispatches by command name. Every command +takes either: + +``` +{ uri: Url, position?: Position, range?: Range, ...args } +``` + +and returns a `WorkspaceEdit` (which the client applies via +`workspace/applyEdit`). + +### 12.4 List & table editing commands (Phase 13) + +| Command | Operates on | Result | +|---|---|---| +| `nuwiki.list.toggleCheckbox` | list item under cursor | `[ ]` ↔ `[X]`, propagates parent state | +| `nuwiki.list.cycleCheckbox` | list item | `[ ]` → `[.]` → `[o]` → `[O]` → `[X]` cycle | +| `nuwiki.list.rejectCheckbox` | list item | toggle `[-]` rejected state | +| `nuwiki.list.changeSymbol` | list item, args: `symbol: ListSymbol`, `whole_list: bool` | rewrite marker, renumber if numeric | +| `nuwiki.list.changeLevel` | list item, args: `delta: i32`, `whole_subtree: bool` | re-indent + adjust child levels | +| `nuwiki.list.renumber` | list, args: `whole_file: bool` | re-sequence numeric markers | +| `nuwiki.list.removeDone` | range or current list | remove every `[X]` / `[-]` item and its checked children | +| `nuwiki.list.nextTask` | document | navigate to next unfinished task (returns `Location`, not edit) | +| `nuwiki.table.align` | table under cursor | reformat columns to max width | +| `nuwiki.table.moveColumn` | cell, args: `dir: "left"\|"right"` | swap column with neighbour | +| `nuwiki.table.insert` | cursor, args: `cols, rows` | insert blank table at cursor | +| `nuwiki.heading.addLevel` | heading | promote (`==` → `===`) | +| `nuwiki.heading.removeLevel` | heading | demote | +| `nuwiki.link.normalize` | word/selection | convert to `[[wikilink]]`, add description if missing | +| `nuwiki.link.pasteWikilink` | cursor | paste current page name as an absolute wikilink | +| `nuwiki.link.pasteUrl` | cursor | paste the corresponding HTML output URL | +| `nuwiki.colorize` | range, args: `color: String` | wrap in colour tag per `color_tag_template` | + +All operations resolve to a textual diff over the existing source so the +AST stays the source of truth. None of them rely on editor-side state. + +### 12.5 Link health + TOC/index generation (Phase 14) + +#### Broken-link diagnostics + +A new diagnostic source `nuwiki.link` runs after each parse: + +- Wiki target → not in `WorkspaceIndex` → severity from config (default + `Warning`) +- Anchor target → page exists but anchor doesn't → `Warning` +- `file:` / `local:` → file exists on disk → `Warning` if missing +- Raw URL / external → never diagnosed (we don't fetch) +- Configurable filtering via `diagnostic.link.severity` (`off|hint|warn|error`) + +#### Commands + +| Command | Behaviour | +|---|---| +| `nuwiki.toc.generate` | inserts a nested list of headings in the current page; replaces existing `%toc` placeholder if present | +| `nuwiki.links.generate` | inserts a flat list of all wiki pages (vimwiki's `:VimwikiGenerateLinks`) | +| `nuwiki.workspace.checkLinks` | returns a `Vec` for the client to render in a quickfix-style view; also emits diagnostics | +| `nuwiki.workspace.findOrphans` | returns pages with no incoming links | + +### 12.6 Diary (Phase 15) + +#### Path conventions + +``` +// # default: "diary" + diary.wiki # configurable: diary_index + 2026-05-11.wiki # daily: YYYY-MM-DD + 2026-W19.wiki # weekly: YYYY-Www (configurable) + 2026-05.wiki # monthly + 2026.wiki # yearly +``` + +#### Custom commands + +| Command | Behaviour | +|---|---| +| `nuwiki.diary.openToday` | open / create today's diary page | +| `nuwiki.diary.openYesterday` | analogous | +| `nuwiki.diary.openTomorrow` | analogous | +| `nuwiki.diary.openIndex` | open the diary index page | +| `nuwiki.diary.generateIndex` | rebuild the diary index page with current entries grouped by year/month | +| `nuwiki.diary.next` | navigate to the chronologically next diary entry from the current one | +| `nuwiki.diary.prev` | analogous | + +#### Calendar hook + +External plugins (Calendar.vim, neorg-style calendars) can query: + +- `nuwiki.diary.listEntries(year, month)` → list of dates with entries +- `nuwiki.diary.openForDate(date)` → opens that date's entry + +These are `executeCommand` operations, not LSP standard methods. + +### 12.7 HTML export commands (Phase 16) + +The `HtmlRenderer` from Phase 5 stays as the engine. v1.1 wraps it in +user-facing commands and the per-wiki HTML lifecycle. + +#### Per-wiki HTML options + +| Key | Default | Description | +|---|---|---| +| `html_path` | `/_html` | output directory | +| `template_path` | `/_templates` | template lookup root | +| `template_default` | `default` | base template name | +| `template_ext` | `.tpl` | template extension | +| `template_date_format` | `%Y-%m-%d` | format passed to `{{date}}` | +| `css_name` | `style.css` | CSS file copied/created at export | +| `auto_export` | `false` | export on save | +| `html_filename_parameterization` | `false` | URL-safe slugs in output filenames | +| `exclude_files` | `[]` | globs to skip | + +#### Commands + +| Command | Behaviour | +|---|---| +| `nuwiki.export.currentToHtml` | render current page; honours `%template`, copies CSS if missing | +| `nuwiki.export.allToHtml` | export every page in the wiki | +| `nuwiki.export.allToHtmlForce` | `:VimwikiAll2HTML!` equivalent | +| `nuwiki.export.browse` | export current + open default browser to result | +| `nuwiki.export.rss` | emit `rss.xml` of recent diary entries (when diary enabled) | + +#### Template variables + +The `HtmlRenderer` template substitution gains: + +- `{{title}}` (already shipped) +- `{{content}}` (already shipped) +- `{{date}}` (formatted via `template_date_format`) +- `{{root_path}}` (relative path from output to `html_path` — for stylesheet + hrefs in nested pages) +- `{{toc}}` (rendered TOC for current page) + +### 12.8 Multi-wiki (Phase 17) + +#### Config shape + +```lua +require('nuwiki').setup({ + wikis = { + { + name = 'personal', + root = '~/vimwiki', + file_extension = '.wiki', + syntax = 'vimwiki', + diary_rel_path = 'diary', + html_path = '~/vimwiki/_html', + -- … any per-wiki option + }, + { + name = 'work', + root = '~/work-notes', + syntax = 'vimwiki', + }, + }, + log_level = 'warn', +}) +``` + +The v1.0 single-wiki shape continues to work; internally it desugars to +`wikis = [{ root = wiki_root, file_extension = file_extension, syntax = syntax }]`. + +#### Per-wiki context + +`WorkspaceIndex` becomes one per wiki. Backend holds: + +``` +indexes: Arc>>> +``` + +URI → wiki resolution walks the URI's filesystem path upward, matching +against each registered root. Cross-wiki links (interwiki) resolve through +the matching wiki's index. + +#### Custom commands + +| Command | Behaviour | +|---|---| +| `nuwiki.wiki.select` | client-side picker over `wikis`; resolves to a wiki id | +| `nuwiki.wiki.openIndex` | open the selected wiki's `index.wiki` | +| `nuwiki.wiki.tabOpenIndex` | open in new tab (client-side) | +| `nuwiki.wiki.listAll` | returns the registered wikis for editor UIs | + +### 12.9 Editor glue v2 (Phase 18) + +The thickest user-facing layer. Per SPEC §6.3 it still contains no logic — +every command body is a wrapper that issues `workspace/executeCommand`. + +#### `:Vimwiki*` command compatibility + +`ftplugin/nuwiki.vim` defines every command listed in §4 of vimwiki's help, +mapped to the corresponding `executeCommand`: + +| Vimwiki command | nuwiki implementation | +|---|---| +| `:VimwikiIndex` `[count]` | `nuwiki.wiki.openIndex(count)` | +| `:VimwikiTabIndex` | tab-open variant | +| `:VimwikiUISelect` | `nuwiki.wiki.select` | +| `:VimwikiDiaryIndex` | `nuwiki.diary.openIndex` | +| `:VimwikiMakeDiaryNote` | `nuwiki.diary.openToday` | +| `:VimwikiMakeYesterdayDiaryNote` | `nuwiki.diary.openYesterday` | +| `:VimwikiMakeTomorrowDiaryNote` | `nuwiki.diary.openTomorrow` | +| `:VimwikiFollowLink` | client `vim.lsp.buf.definition()` | +| `:VimwikiGoBackLink` | client `` (jumplist) | +| `:VimwikiSplitLink` / `:VimwikiVSplitLink` | client-side `:split`/`:vsplit` + `definition` | +| `:VimwikiTabnewLink` / `:VimwikiTabDropLink` | client-side `:tabnew` + `definition` | +| `:VimwikiNextLink` / `:VimwikiPrevLink` | `nuwiki.cursor.nextLink` / `prevLink` | +| `:VimwikiGoto {name}` | `nuwiki.wiki.gotoPage(name)` | +| `:VimwikiDeleteFile` | server-side delete via `executeCommand` | +| `:VimwikiRenameFile` | client prompt → `workspace/rename` | +| `:VimwikiRemoveDone` | `nuwiki.list.removeDone` | +| `:VimwikiNextTask` | `nuwiki.list.nextTask` | +| `:Vimwiki2HTML` | `nuwiki.export.currentToHtml` | +| `:Vimwiki2HTMLBrowse` | `nuwiki.export.browse` | +| `:VimwikiAll2HTML[!]` | `nuwiki.export.allToHtml[Force]` | +| `:VimwikiRss` | `nuwiki.export.rss` | +| `:VimwikiToggleListItem` | `nuwiki.list.toggleCheckbox` | +| `:VimwikiToggleRejectedListItem` | `nuwiki.list.rejectCheckbox` | +| `:VimwikiListChangeLvl CMD` | `nuwiki.list.changeLevel` / `changeSymbol` | +| `:VimwikiSearch` / `:VWS` | client `:lvimgrep` wrapper over wiki root | +| `:VimwikiBacklinks` / `:VWB` | client `vim.lsp.buf.references()` | +| `:VimwikiTable [cols] [rows]` | `nuwiki.table.insert` | +| `:VimwikiTableMoveColumnLeft/Right` | `nuwiki.table.moveColumn` | +| `:VimwikiGenerateLinks` | `nuwiki.links.generate` | +| `:VimwikiDiaryGenerateLinks` | `nuwiki.diary.generateIndex` | +| `:VimwikiDiaryNextDay` / `:VimwikiDiaryPrevDay` | `nuwiki.diary.next/prev` | +| `:VimwikiTOC` | `nuwiki.toc.generate` | +| `:VimwikiCheckLinks` | `nuwiki.workspace.checkLinks` | +| `:VimwikiRebuildTags` | `nuwiki.tags.rebuild` | +| `:VimwikiSearchTags {tag}` | `nuwiki.tags.search` | +| `:VimwikiGenerateTagLinks` | `nuwiki.tags.generateLinks` | +| `:VimwikiColorize {color}` | `nuwiki.colorize` | +| `:VimwikiPasteLink` | `nuwiki.link.pasteWikilink` | +| `:VimwikiPasteUrl` | `nuwiki.link.pasteUrl` | + +Each `:Vimwiki*` form ships with a matching `:Nuwiki*` alias (P10 +resolution). + +#### Default keymaps + +`g:nuwiki_key_mappings` (Vim) / `mappings = { … }` (Neovim) controls +opt-in keymap registration. Default: enabled; group flags let users disable +the table/list/heading/diary subsets independently. + +Buffer-local maps mirror vimwiki: + +- `ww` / `wt` / `ws` / `wi` / + `ww` etc. — wiki navigation +- `` / `` / `` / `` / `` — link follow variants +- `` / `` / `` — link history + per-page link navigation +- `=` / `-` / `[[` / `]]` / `[=` / `]=` / `]u` / `[u` — header manipulation + + navigation (pure VimL/Lua, regex over current buffer — no LSP round-trip) +- `+` — `nuwiki.link.normalize` +- `` — `nuwiki.list.toggleCheckbox` +- `gnt` — `nuwiki.list.nextTask` +- `gl` / `gL` — checkbox remove (single / list) +- `gln` / `glp` — checkbox cycle up/down +- `gll` / `gLl` / `glh` / `gLh` — list level change (single / subtree) +- `glr` / `gLr` — list renumber +- `gl*` / `gl#` / `gl-` / `gl+` / `gl1` / `gla` / `glA` / `gli` / `glI` — + list symbol change (single / subtree variants `gL`*) +- `glx` — `:VimwikiToggleRejectedListItem` +- `gqq` / `gww` / `gq1` / `gw1` — table align +- `` / `` — table move column +- `` / `` — diary prev/next day +- `wh` / `whh` — HTML export current / browse +- `wc` — colorize (asks for colour) +- `wn` — goto/create new page +- `wd` / `wr` — delete / rename current page +- Mouse: `<2-LeftMouse>`, ``, ``, + `` (opt-in via `g:nuwiki_mouse_mappings`) + +#### Insert-mode behaviour + +- Table cells: ``/``/`` navigate cells, create rows + on overflow; implemented in Lua/VimL via a small autocmd +- Lists: `` inserts the next bullet/number; `` continues without + a new bullet; ``/`` indent/dedent; ``/`` + cycle the list symbol; `` toggles a list item on/off +- All gated by `b:did_ftplugin` and remappable + +#### Text objects + +`omap`/`xmap` defining: + +| Object | Selects | +|---|---| +| `ah` / `ih` | a header (with/without trailing blank lines + header line itself) | +| `aH` / `iH` | a header + all sub-headers ([count] climbs parent levels) | +| `a\` / `i\` | a table cell | +| `ac` / `ic` | a table column | +| `al` / `il` | a list item (with/without children) | + +Pure VimL/Lua — no LSP round-trip. Reuses our AST-shaped regexes. + +#### Folding + +Per P14 resolution; the spec'd default (subject to that decision): + +- `setlocal foldmethod=expr` + `foldexpr=NuwikiFold(v:lnum)` (or LSP + `foldingRange` driving it on Neovim 0.11+) +- Folds at heading boundaries; nested headings nest folds +- List-item folds when configured via `g:nuwiki_folding = 'list'` +- Custom variant via `g:nuwiki_folding = 'custom'` + +#### Health check additions + +`:checkhealth nuwiki` v1.1 reports: + +- LSP commands registered (per command name) +- Index loaded for each registered wiki +- Tags table built +- HTML output path writable +- Default keymaps installed (or disabled by config) + +### 12.10 Updated user-config schema + +```lua +require('nuwiki').setup({ + -- single-wiki shorthand (v1.0 compat) + wiki_root = '~/vimwiki', + file_extension = '.wiki', + syntax = 'vimwiki', + log_level = 'warn', + + -- or the v1.1 multi-wiki form + wikis = { + { + name = 'personal', + root = '~/vimwiki', + file_extension = '.wiki', + syntax = 'vimwiki', + diary_rel_path = 'diary', + diary_frequency = 'daily', -- daily | weekly | monthly | yearly + diary_start_week_day = 'monday', + template_path = '~/vimwiki/_templates', + template_default = 'default', + template_ext = '.tpl', + css_name = 'style.css', + html_path = '~/vimwiki/_html', + auto_export = false, + auto_toc = false, + auto_tags = false, + exclude_files = {}, + bullet_types = { '-', '*', '#' }, + listsyms = ' .oOX', + listsym_rejected = '-', + listsyms_propagate = true, + color_dic = { red = 'red', green = 'green' }, + }, + }, + + -- workspace-wide behaviour + diagnostic = { + link_severity = 'warn', -- off | hint | warn | error + }, + mappings = { + enabled = true, + table_editing = true, + list_editing = true, + header_nav = true, + diary = true, + html_export = true, + mouse = false, + }, + folding = 'syntax', -- off | syntax | list | custom +}) +``` + +### 12.11 Updated §9 syntax checklist (additions) + +The following items extend the v1.0 checklist: + +#### Tags +- [ ] Inline tag sequence: `:tag-one:tag-two:` +- [ ] File-level tag (line 1–2 of a wiki page) +- [ ] Header-level tag (within 2 lines after a heading) +- [ ] Tag-as-anchor target (`[[Page#some-tag]]`) +- [ ] Hex-colour code rendering in code blocks (`` `#ffe119` ``) + +#### Tables (additions) +- [ ] Column-alignment markers in header separator: `|:--|` / `|:--:|` / `|--:|` + +#### Lists (parser extensions) +- [ ] Multi-line list item (indented continuation lines) +- [ ] Mixed-symbol lists at the same level + +### 12.12 Updated CI/CD + +No new workflow files. The existing `ci.yaml` continues to gate +`fmt`/`clippy`/`test`. New phases land behind feature additions in the +existing crates; the test suite grows accordingly. +