Closing out §13.1's smallest cluster. Three commands move from
"not yet implemented" stubs to real behaviour:
- `nuwiki.link.pasteWikilink` (server, executeCommand) — derives
the current page name from the source URI + wiki root, returns a
`WorkspaceEdit` inserting `[[<page>]]` at the requested cursor
position.
- `nuwiki.link.pasteUrl` (server) — same lookup, but the inserted
text is the page's relative HTML output URL
(`<page>.html`, including subdir segments) so the snippet survives
when the export root moves.
- `nuwiki.link.normalize` (client) — wraps the word at cursor as
`[[word]]` without following. Reuses the `wrap_cword_as_wikilink`
helper that already powers the `<CR>` two-step. Pure-VimL on the
Vim path; pure-Lua on the Neovim path. No LSP round-trip.
Keymaps:
- `+` (normal + visual) now actually calls `normalize_link` on both
editor paths instead of stubbing with a "deferred" notification.
Tests:
- 5 new Rust unit tests in `cluster_c_link_helpers.rs` covering
command-list presence + the page-name derivation for root and
subdirectory pages + the URL / wikilink shape strings.
- Neovim keymap harness gains a `links.normalize_via_+` case (20
passing now, up from 19). Vim harness inherits the same
command-presence check via its existing smoke tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CI log for the keymaps job showed Lua crashing with
`attempt to call field 'get_clients' (a nil value)` plus a flood of
`W18: Invalid character in group name` on the `@vimwiki*` highlight
links. Both signal that apt's `neovim` package is too old (Ubuntu
LTS ships 0.6/0.7) — `vim.lsp.get_clients` only landed in 0.10, and
Tree-sitter-style `@group.modifier` highlight names only became
valid sometime in the 0.8-ish era.
Switched the installer to pull the official static tarball
(`neovim/neovim/releases/v0.11.3/nvim-linux-x86_64.tar.gz`) and
symlink it into `/usr/local/bin/nvim`. Vim from apt is kept — it
only runs the pure-VimL harness so its age doesn't matter.
Also added a defensive fallback in the harness:
`get_lsp_clients = vim.lsp.get_clients or vim.lsp.get_active_clients`,
plus a `server_capabilities` nil guard. Won't matter on the pinned
0.11 runner, but means a future regression that drops the deps will
fail with a clear `lsp.attached` FAIL message instead of a Lua
stack trace.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`scripts/test-keymaps.sh` boots `nvim --headless` against a scratch
wiki, sources `scripts/test-keymaps.lua`, and asserts the post-state
of each buffer-local mapping registered by `ftplugin/vimwiki.vim` +
`lua/nuwiki/keymaps.lua`.
The harness covers the bindings that actually have observable effects
on the buffer or cursor:
- **Lists** (LSP round-trip): `<C-Space>` / `<C-@>` / `<Nul>` toggle,
`gln` cycle, `glx` reject, plus a `[X]→[ ]` round-trip.
- **Tasks**: `gnt` jumps to the next unfinished `[ ]`.
- **Headings** (LSP round-trip): `=` adds a level, `-` removes one,
`=` on h6 clamps without growing.
- **Header nav** (pure Lua): `]]` next, `[[` prev, `]u` parent.
- **Link nav** (pure Lua): `<Tab>` to the next `[[…]]`.
- **`<CR>` two-step**: first press wraps a bare word as `[[word]]`
and stops (matches vimwiki's review-then-follow).
- **Bullet continuation**: `o` / `O` keep the marker, and `o` on a
`[ ]` line carries the checkbox forward.
Each case sets buffer lines, places the cursor (1-based), fires
`nvim_feedkeys` with `replace_termcodes`, waits long enough for the
LSP `executeCommand` round-trip (800 ms by default; 100 ms for the
pure-client maps), and asserts either the resulting lines, a specific
line, the cursor's new line, or the buffer's new name.
Output goes via `$NUWIKI_KEYMAP_RESULTS` (env-var IPC keeps it
robust to `--clean -u` argument parsing). Exit code mirrors the
harness: 0 on green, 1 on any FAIL. Local run yields:
SUMMARY: 19 passed, 0 failed
CI: new `keymaps` job in `.gitea/workflows/ci.yaml` installs Neovim
and runs the script, so a regression in `lua/nuwiki/keymaps.lua`,
`autoload/nuwiki/commands.vim`, or the LSP command surface is caught
on every push.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>