Commit Graph

134 Commits

Author SHA1 Message Date
gffranco 87ba4c1764 test(calendar): add Vim + Neovim integration harnesses
CI / cargo fmt --check (push) Successful in 26s
CI / cargo clippy (push) Successful in 27s
CI / cargo test (push) Successful in 31s
CI / editor keymaps (push) Successful in 1m30s
Add headless harnesses covering the calendar-vim integration, wired into
CI alongside the existing keymap/config suites:

- test-calendar-vim.sh/.vim (+ -optout.vim): pure-VimL diary hooks
  (calendar_sign markers, calendar_action path resolution from wiki root,
  calendar window close), plugin/nuwiki.vim hook auto-wiring that
  overrides calendar-vim's defaults, and opt-out via g:nuwiki_use_calendar
  and g:nuwiki_no_calendar. Uses a stub calendar-vim on the runtimepath.
- test-calendar.sh/.lua: Neovim setup() wiring + window-close under the
  real window API, and opt-out via use_calendar = false.

Fixes surfaced by the suite:
- diary.vim: new diary entries set filetype 'vimwiki' (was the raw
  extension, e.g. 'wiki'); the stray FileType autocmd also blocked the
  calendar window from closing.
- init.lua: drop the redundant Neovim FileType autocmd (plugin/nuwiki.vim
  already wires both editors) and translate use_calendar=false into
  g:nuwiki_no_calendar so the opt-out actually disables wiring.
2026-05-31 21:45:50 -03:00
gffranco 7a3e11cfeb feat(calendar): integrate calendar-vim with wiki diary paths
CI / cargo fmt --check (push) Successful in 34s
CI / cargo clippy (push) Successful in 43s
CI / cargo test (push) Successful in 41s
CI / editor keymaps (push) Successful in 1m29s
Wire g:calendar_action/g:calendar_sign to nuwiki diary hooks that
resolve diary paths from the wiki root config instead of calendar-vim's
default ~/diary. Auto-detected on FileType vimwiki when calendar-vim is
present; opt out with g:nuwiki_use_calendar=0 or g:nuwiki_no_calendar.

- autoload/nuwiki/diary.vim: calendar_action/calendar_sign using wiki cfg;
  open diary in the previous window and close the calendar window after.
- autoload/vimwiki/diary.vim: vimwiki# aliases for drop-in compatibility.
- plugin/nuwiki.vim + lua/nuwiki/init.lua: auto-wire hooks, overwriting
  calendar-vim's defaults while respecting user-set custom hooks.
- lua/nuwiki/config.lua: add use_calendar option (on by default).
- development/start-*.sh + _common.sh: clone calendar-vim, fix vimrc var
  expansion, pass wiki root to the Vim/Neovim clients.
2026-05-31 21:34:41 -03:00
gffranco 39692ba99f dev: start-* launchers always start from a clean setup
CI / cargo fmt --check (push) Successful in 18s
CI / cargo clippy (push) Successful in 31s
CI / cargo test (push) Successful in 27s
CI / editor keymaps (push) Successful in 1m24s
The launchers reused whatever was left under $XDG_CACHE_HOME/nuwiki-dev
between runs — seed_wiki() skipped reseeding when index.wiki already
existed, so stale edits from a previous session leaked into the next, and
old swap/viminfo/coc state carried over.

Each launch now starts clean:
- seed_wiki() reseeds the managed scratch wiki from a pristine copy every
  run (rm -rf + write_sample_wiki). A user-pointed NUWIKI_DEV_WIKI is never
  wiped — it's seeded only when empty, so a real wiki is never clobbered.
  NUWIKI_DEV_NO_SEED still bypasses seeding entirely.
- new reset_dirs() helper wipes + recreates each launcher's owned state dirs
  (vim viminfo/swap/undo, coc data, nvim XDG state/data/config) before launch.

Cached plugin clones (vim-lsp, async.vim, coc.nvim) are kept — only the
wiki and editor state are reset. Header docs updated to describe the
fresh-each-launch behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:07:15 +00:00
gffranco 6983daa637 fix(follow-link): resolve+open definitions ourselves on the Vim clients
CI / cargo fmt --check (push) Successful in 16s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 32s
CI / editor keymaps (push) Successful in 1m39s
The Vim follow-link path delegated to the LSP client's jump UI
(:LspDefinition / coc jumpDefinition), which broke two ways for wiki
link-following:

1. **No create-on-follow (vim-lsp + coc).** vim-lsp's location handler
   readfile()s the target to build a quickfix entry; for a not-yet-created
   page that throws E484 and aborts the jump, so <CR> on [[NewPage]] did
   nothing. The server was correct throughout — textDocument/definition
   returns a *synthesised* location for missing pages (verified over
   JSON-RPC).
2. **Wrong window placement (coc).** Without an explicit open command coc
   fell back to coc.preferences.jumpCommand (e.g. a tab command).

Stop delegating: nuwiki#commands#follow_link_or_create() /
follow_link_drop() now resolve textDocument/definition directly and open
the URI themselves via a shared s:open_definition(open_cmd) helper — :edit
for <CR>, `tab drop` for <C-S-CR>. :edit opens a buffer for a missing path
(save creates it, matching vimwiki) and gives exact current-window
placement regardless of the user's coc jumpCommand. Removes
s:jump_definition / s:drop_from_response.

Also fix the coc action name: CocAction('getDefinition') does not exist
(E605 "Action getDefinition does not exist") — the registered action is
'definitions'. Fixed in s:open_definition and badd_link.

coc config delivery: the setup guidance shipped without
initializationOptions, so coc users never sent wiki_root and the server
resolved links against its default root (existing pages flagged broken,
follow-to-create landed in the cwd). The printed snippet
(autoload/nuwiki/lsp.vim) now emits initializationOptions populated with
the resolved settings, and the README coc snippet documents it as required.

Add development/start-vim-coc.sh — a coc.nvim dev launcher (sibling of
start-vim.sh) that wires coc's prebuilt release branch + a generated
coc-settings.json (with initializationOptions), deliberately omitting
vim-lsp so the coc path is exercised; doubles as a reproducer.

Tests: new cr.follow_opens_missing_page_in_current_window case in
test-keymaps-vim.vim (stubs CocAction to a missing page, asserts
current-window :edit). Keymap suite 254+18 green; config-parity (Vim +
Neovim) green. Neovim path unchanged and re-confirmed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:01:14 +00:00
gffranco 52848a0663 addin spec for calendar integration
CI / cargo fmt --check (push) Successful in 25s
CI / cargo clippy (push) Successful in 38s
CI / cargo test (push) Successful in 39s
CI / editor keymaps (push) Successful in 1m37s
2026-05-31 11:26:25 -03:00
gffranco 45e2d1769a docs(keymaps): correct lists group comment to gl/gL
CI / cargo fmt --check (push) Successful in 33s
CI / cargo clippy (push) Successful in 29s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m30s
The lists mapping comment still listed gl<Space>; bare gl/gL are now the
remove-checkbox maps after the P1 #3 parity change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:03:04 -03:00
gffranco ac4dd144e4 docs(gap): expand audit findings
Reframe diary_start_week_day as a wrongly-removed client-side concern
that matters for parity (non-Monday users lose the upstream option).
Add four P3 command-divergence rows (Neovim VimwikiColorize arg-drop,
TableMoveColumn backing-name split, NuwikiTabIndex missing -count,
ToggleListItem/Increment/Decrement missing -range) and a P3 mappings
row for the gLH/gLL/gLR case-variant aliases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:02:59 -03:00
gffranco c5814a3e21 docs(gap): log <CR> link-follow new-tab regression
Pressing <CR> on a link now opens a new tab and switches to it instead
of reusing the current window (reported on Vim 9.x + Dein). Records the
investigation so far — the <CR> path is unchanged by recent P1 work and
both client jump paths open same-window by default — and the env gotchas
(stale dein copy, double rtp) to rule out before treating it as a code bug.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 09:02:52 -03:00
gffranco 1521d6c096 docs(client): document bare gl/gL remove-checkbox + tick parity gap P1 #3
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 29s
CI / cargo test (push) Successful in 26s
CI / editor keymaps (push) Successful in 1m22s
Update the lists keymap tables and group hints (gl/gL = remove checkbox;
remove-done is command-only via :NuwikiRemoveDone[!]), note the 'timeoutlen'
prefix behavior, and tick P1 #3 in the vimwiki gap tracker.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:41:33 -03:00
gffranco cca2043294 test(client): cover bare gl/gL remove-checkbox + command-driven remove-done
Swap the gl<Space>/gL<Space> surface assertions for bare gl/gL; add behavioral
cases for gl (item) and gL (whole list) remove-checkbox, and drive remove-done
through :NuwikiRemoveDone / :NuwikiRemoveDone! now that it is command-only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:41:27 -03:00
gffranco a15ae184a0 feat(client): bind bare gl/gL to remove-checkbox for vimwiki parity
Upstream vimwiki binds bare gl/gL to remove-checkbox (item / whole list);
nuwiki had rebound the conceptual keys to gl<Space>/gL<Space> as remove-done,
a same-keys-different-effect parity trap. Bind bare gl/gL to remove-checkbox
instead — they share the gl… prefix so they fire after 'timeoutlen', exactly
as upstream. Remove-done becomes command-only; add -bang to *RemoveDone so
:NuwikiRemoveDone! reaches the whole-buffer sweep that gL<Space> previously
held (its only entry point).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:41:22 -03:00
gffranco a53ba167d6 fix(lsp): silence clippy too_many_arguments on render_page_html
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 33s
CI / cargo test (push) Successful in 33s
CI / editor keymaps (push) Successful in 1m34s
Adding list_margin pushed render_page_html to 8 params, tripping
clippy::too_many_arguments under -D warnings in CI. Every parameter is a
distinct render input and the function has a single production caller, so
scope an #[allow] rather than introduce an options-struct abstraction.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:13:30 -03:00
gffranco 164b326216 docs(client): document gl/gL list change-symbol mappings
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Failing after 23s
CI / cargo test (push) Successful in 37s
CI / editor keymaps (push) Successful in 1m27s
Add gl<sym>/gL<sym> to the lists keymap tables in README and help, noting
that 1) is command-only. Tick parity gap P1 #2.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:10:15 -03:00
gffranco 8a99765e02 test(client): assert gl/gL list change-symbol mappings
Add the 16 gl<sym>/gL<sym> keys to the lists mapping_surface in both
keymap harnesses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:10:10 -03:00
gffranco 061785c6a0 feat(client): add gl/gL one-key list change-symbol mappings
Upstream's one-key marker-change keys had no nuwiki binding — the
functionality was reachable only via :NuwikiChangeSymbol. Add normal-mode
gl{-,*,#,1,i,I,a,A} (current item) and gL{...} (whole list) to both
clients, wired to the existing list_change_symbol command. NumericParen
1) stays command-only, matching upstream's default number_types shadowing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 08:10:05 -03:00
gffranco 214707e327 docs(client): document link-follow commands on both editors + tab-drop
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Failing after 18s
CI / cargo test (push) Successful in 40s
CI / editor keymaps (push) Successful in 1m26s
The split/tab link-follow commands are no longer Neovim-only; document
:Nuwiki/Vimwiki{Split,VSplit,Tabnew,TabDrop,GoBack}Link and note that
<C-S-CR> follows a link in a tab reusing an existing one. Tick parity
gap P1 #1 in the tracking doc.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 07:54:32 -03:00
gffranco 82b6bc9c60 test(client): cover Vim/Neovim link-follow command surface + tab-drop
Add SplitLink/VSplitLink/TabnewLink/TabDropLink/GoBackLink to both
keymap harness command lists, plus a smoke check that
nuwiki.commands.follow_link_drop is callable.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 07:54:25 -03:00
gffranco f65d861f72 feat(client): add Vim split/tab/back link-follow commands + true tab-drop
The plain-Vim client only defined VimwikiFollowLink; the split/vsplit/
tabnew/tabdrop/back command surface existed only in the Neovim branch.
Add :Vimwiki{Split,VSplit,Tabnew,TabDrop,GoBack}Link (+ :Nuwiki* aliases)
to the Vim branch, backed by a new nuwiki#commands#follow_link_drop()
helper that runs `:tab drop` to reuse an already-open tab.

Both clients now implement real tab-drop: open_uri gains a 'tabdrop'
case and M.follow_link_drop(); the Neovim TabDropLink commands and the
<C-S-CR> mappings (Vim + Lua) are repointed off the old tabnew cheat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 07:54:20 -03:00
gffranco 00a671e486 docs(parity): track vimwiki config/command/mapping gaps
Add development/vimwiki-gap.md, a checkbox-tracked audit of where nuwiki
diverges from upstream vimwiki across configuration options, Ex-commands,
and key mappings. Items are prioritised P1–P3 with a fix site cited on
each, plus an "intentional divergences" section so the LSP-rearchitected
choices aren't re-flagged in future audits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 07:30:22 -03:00
gffranco e9243f743e feat(client): add per-subgroup Vim mapping opt-out globals
CI / cargo fmt --check (push) Successful in 40s
CI / cargo clippy (push) Failing after 38s
CI / cargo test (push) Successful in 38s
CI / editor keymaps (push) Successful in 1m30s
The plain-Vim ftplugin only exposed a whole-layer
g:nuwiki_no_default_mappings gate, so Vim users could not drop a single
keymap group the way Neovim users can via mappings.<group> = false. Add
g:nuwiki_no_{wiki_prefix,links,lists,headers,table_editing,diary,
html_export,text_objects}_mappings, each wrapping its group block, to
reach parity with lua/nuwiki/keymaps.lua. Cover them with a dedicated
opt-out harness and document them in the README and help file.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:40:29 -03:00
gffranco 3a33d53c22 feat(lsp): drive checkbox commands from the wiki listsyms palette
Parse editor and export documents with each wiki's configured listsyms,
and make toggle/cycle/reject plus parent-propagation walk the real
palette glyphs instead of the hardcoded ' .oOX'. Falls back to the
default palette when no wiki matches the URI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:32:15 -03:00
gffranco 85094d6e3b feat(core): make checkbox lexing honor a configurable listsyms palette
Add a ListSyms type (vimwiki's g:vimwiki_listsyms) and thread it through
the lexer so checkbox glyphs are recognized from the configured palette
rather than the hardcoded ' .oOX'. Glyphs map to the existing five-bucket
CheckboxState, so the HTML renderer and AST consumers are unchanged.
VimwikiSyntax::parse_with_listsyms opts into a custom palette; the trait
parse() keeps the default.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:32:07 -03:00
gffranco d22c81ea4f docs(config): drop client-side keys and refresh per-wiki reference
Remove nested_syntaxes, maxhi and diary_start_week_day from the config
reference (block, tables and key lists) since the server no longer
consumes them, and note that language-tagged code fences are highlighted
automatically with no nested_syntaxes key.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:12:14 -03:00
gffranco c63ec679ae feat(lsp): wire diary/auto_toc/links_space_char/list_margin; drop client-side keys
Consume several per-wiki config keys that the server deserialized but
ignored, and remove keys whose effect is purely client-side:

- diagnostics: re-publish open-doc diagnostics on
  didChangeConfiguration so a link_severity change takes effect
  immediately; fix the single-key unwrap in apply_change so a minimal
  `{ diagnostic: {...} }` payload isn't mistaken for a namespace wrapper.
- auto_toc: rebuild an existing TOC section on save (new
  ops::toc_rebuild_edit, no-op when the page has no TOC).
- diary index: honour diary_header, diary_sort and diary_caption_level
  when rendering the diary index body.
- links_space_char: apply on rename so spaces in the link target and
  the on-disk path become the configured glyph (default " " = verbatim).
- list_margin: thread the per-wiki value into render_page_html.
- remove nested_syntaxes, maxhi and diary_start_week_day from the server
  config: nested-syntax and heading highlighting are client-side, and
  the weekly diary is ISO-week based so a custom week start has no clean
  server-side meaning.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:12:08 -03:00
gffranco 0741c349db feat(core): support configurable list_margin in HTML list rendering
Add `HtmlRenderer::with_list_margin`. When the margin is >= 0 the
outermost list gets an inline `margin-left:<n>em`; nested lists and the
default of -1 stay unstyled (deferring to the stylesheet). Wires the
per-wiki vimwiki `list_margin` key into the export pipeline.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 23:11:56 -03:00
gffranco e3f28d7dfc test(config): cover link_severity in parity harnesses and docs
CI / cargo fmt --check (push) Successful in 23s
CI / cargo clippy (push) Successful in 31s
CI / cargo test (push) Successful in 31s
CI / editor keymaps (push) Successful in 1m28s
Both editor harnesses now emit diagnostic.link_severity and exercise a
non-default override ('error') in the sample scenario, diffed against the
shared golden. Drop the README "not wired" caveat now that the value flows
through, and document the g:nuwiki_link_severity global.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 21:53:56 -03:00
gffranco 5ee72c40e2 feat(client): send diagnostic.link_severity from both clients
Neovim adds a `diagnostic = { link_severity = 'warn' }` default so the value
is actually included in the payload. Vim reads the flat g:nuwiki_link_severity
global and nests it into the same `diagnostic` dict, keeping the two clients'
server-bound payloads identical.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 21:53:51 -03:00
gffranco 9b6413c3de feat(lsp): wire diagnostic.link_severity through from client config
The broken-link diagnostic severity was already consulted by
collect_diagnostics, but Config.diagnostic was hardcoded to the default and
never populated from initializationOptions/didChangeConfiguration. Parse a
client-supplied `diagnostic.link_severity` ('off'|'hint'|'warn'|'error',
case-insensitive) into LinkSeverity and apply it in both from_init_params and
apply_change. Partial config updates that omit `diagnostic` preserve the
existing severity rather than resetting it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 21:53:46 -03:00
gffranco b922f0d612 test(config): verify Vim/Neovim config payload parity
CI / cargo fmt --check (push) Successful in 15s
CI / cargo clippy (push) Successful in 27s
CI / cargo test (push) Successful in 30s
CI / editor keymaps (push) Successful in 1m24s
Expose the init_options payload builders publicly in both clients
(M.init_options/M.server_settings in Lua, nuwiki#lsp#settings() in Vim) so
they can be inspected. Add editor harnesses that dump each client's
server-bound config as deterministic key=value lines and diff against a
shared golden file: nvim == golden and vim == golden together prove the two
clients send identical config. Add a server-side test asserting the Vim flat
shape and the Neovim {nuwiki:{...}} wrapper desugar to the same WikiConfig.
Wire both harnesses into CI.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 19:24:20 -03:00
gffranco 47af0d6c1e docs(config): add configuration reference tables and fix links_space_char default
Document every config key (top-level, mappings, per-wiki, HTML export, and
g:nuwiki_* globals) with types, defaults, and accepted values in the README
Configuration section. Correct the links_space_char doc comment: the default
is a literal space (spaces kept verbatim), not "_". Note that
diagnostic.link_severity is accepted but not yet wired through to the server.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 19:24:08 -03:00
gffranco 63ef1b59a2 docs(parity): document new commands and extend keymap harness coverage
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 44s
CI / cargo test (push) Successful in 47s
CI / editor keymaps (push) Successful in 1m30s
Update the README command tables for the full :Nuwiki*/:Vimwiki* surface
and add command-registration assertions (removeCheckbox, catUrl, and the
other parity entry points) to both the Vim and Neovim keymap harnesses.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 17:36:05 -03:00
gffranco 9bebd86577 feat(client): wire full Vimwiki command-parity surface
Register the named :Vimwiki*/:Nuwiki* entry points that previously
existed only as mappings or were missing entirely — including
RemoveSingleCB/RemoveCBInList, CatUrl, TabMakeDiaryNote,
NormalizeLink, Renumber{List,AllLists}, TableAlign, ChangeSymbol(InList),
ListToggle, Increment/DecrementListItem, and the DeleteLink/RenameLink/
GenerateTags compat aliases. Both the Vim (vim-lsp/coc) and Neovim
client functions are added to back them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 17:35:28 -03:00
gffranco 8d8d85daf9 feat(lsp): add list.removeCheckbox and link.catUrl commands
removeCheckbox strips the `[ ]` marker (plus its trailing space) from
the current item or every item in the list. catUrl returns the
`file://` URL of the current page's HTML export, mirroring vimwiki's
:VimwikiCatUrl.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 17:32:41 -03:00
gffranco b1155dee6a Expand README: coc setup, complete Nuwiki-first command/keymap tables
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 47s
CI / editor keymaps (push) Successful in 1m56s
Document third-party LSP client setup (vim-lsp auto-registration and the
coc.nvim coc-settings.json snippet), add a Requirements section and
:NuwikiInstall. Rewrite the command reference as Nuwiki-first tables with
their :Vimwiki* counterparts, list the full command and mapping surface
(including mouse maps), and fix the <Leader>ww description.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 15:54:12 -03:00
gffranco cd9d587aab Add missing :Nuwiki* aliases and drop dead deferred-stub code
Every :Vimwiki* command now has a canonical :Nuwiki* counterpart
(tables, colorize, clipboard paste, list-level/remove-done, and the
Neovim-only split/tab link-follow variants). Remove the now-unused
"not yet implemented" stub helpers (_not_yet, deferred,
s:notify_deferred) and the stale "deferred" comments, since every
command is implemented and server-backed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 15:54:05 -03:00
gffranco 8ab6015405 Major clean up and improvements to vimwiki compatibility and documentation.
CI / cargo fmt --check (push) Successful in 33s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Successful in 33s
CI / editor keymaps (push) Successful in 1m25s
Reviewed-on: #1
2026-05-30 18:35:40 +00:00
gffranco 95645a2b91 fix(syntax): highlight code blocks with embedded language syntax
CI / cargo fmt --check (push) Successful in 1m10s
CI / cargo clippy (push) Successful in 19s
CI / cargo test (push) Successful in 28s
Release / build x86_64-unknown-linux-gnu (push) Successful in 1m1s
Release / build aarch64-unknown-linux-musl (push) Successful in 54s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m8s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m13s
CI / editor keymaps (push) Successful in 1m32s
Release / gitea release (push) Successful in 23s
`{{{lang … }}}` fences were painted as one String group. Add automatic
nested-syntax highlighting (like vimwiki): scan the buffer for the languages
used, `:syntax include` each under a cluster, and define a contained region
per language so e.g. `{{{sh` blocks get shell highlighting and `{{{python`
gets Python. Bare `{{{` / `{{{class="…"` fences fall back to nuwikiPreformatted.
A small alias map maps labels like bash/shell → sh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 22:24:27 -03:00
gffranco fa2d1b8b42 feat(keywords): add STOPPED keyword
CI / cargo fmt --check (push) Successful in 26s
CI / cargo clippy (push) Successful in 19s
CI / cargo test (push) Successful in 31s
CI / editor keymaps (push) Successful in 1m18s
Adds STOPPED alongside TODO/DONE/STARTED/FIXME/FIXED/XXX across the stack:
lexer, AST Keyword enum, HTML renderer (class="stopped"), LSP keyword_str,
and the Vim syntax red group (nuwikiKeywordAttn). Grouped with the
attention/pending keywords (red) for both export and in-editor highlighting.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 22:05:23 -03:00
gffranco d2475e136b fix(syntax): colour keywords red/green like the HTML export
CI / cargo fmt --check (push) Successful in 39s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Successful in 27s
CI / editor keymaps (push) Successful in 1m16s
All keywords shared one `nuwikiKeyword` group linked to `Todo`, so the editor
showed TODO, DONE and XXX in the same colour. Split into nuwikiKeywordAttn
(TODO/FIXME/XXX, red) and nuwikiKeywordDone (DONE/FIXED/STARTED, green) with
explicit colours so the distinction survives any colorscheme — matching the
export's per-keyword classes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 21:49:36 -03:00
gffranco e0f97a5caf fix(export): emit one CSS class per keyword for independent colouring
CI / cargo fmt --check (push) Successful in 22s
CI / cargo clippy (push) Successful in 39s
CI / cargo test (push) Successful in 30s
CI / editor keymaps (push) Successful in 1m17s
All keywords previously shared `class="todo"`, so a stylesheet couldn't
distinguish e.g. red TODO from green DONE. Emit a distinct class per keyword
(todo/done/started/fixme/fixed/xxx); TODO keeps the vimwiki `todo` class so
stock vimwiki CSS still styles it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 21:43:11 -03:00
gffranco 9a751037ce fix(vim): redraw after :NuwikiExportBrowse shells out to the browser
CI / cargo fmt --check (push) Successful in 18s
CI / cargo clippy (push) Successful in 27s
CI / cargo test (push) Successful in 28s
CI / editor keymaps (push) Successful in 1m21s
A silent `:!xdg-open`/`:!open` swaps to the shell's alternate screen and
returns without repainting, leaving the editor UI garbled until the next
redraw. Force a full repaint after launching the browser. Neovim already
avoids this via async jobstart.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 21:20:15 -03:00
gffranco e90bbab39e fix(export): render vimwiki-compatible HTML for templates, links, and tasks
Stock vimwiki templates and stylesheets rendered broken pages because the
HTML output diverged from vimwiki conventions on three fronts:

- Templates: the renderer only substituted nuwiki's `{{key}}` placeholders,
  so vimwiki's `%title%`/`%content%`/`%root_path%`/`%date%`/`%wiki_css%`
  passed through literally — dropping the body and breaking asset links.
  Both delimiter styles are now recognised; `%wiki_css%` aliases the css var.
- Links: `[[todo.wiki]]` exported to `todo.wiki.html`. render_page_html now
  takes the wiki file extension and strips it from wiki/interwiki targets
  (via index::strip_wiki_extension) before the `.html` URL is built, matching
  in-editor navigation. file:/local: links keep their literal extension.
- Tasks: checkbox items used bespoke `task-*` classes plus an `<input>`,
  which double-rendered against vimwiki stylesheets. Emit `done0..done4`
  (`[ ] [.] [o] [O] [X]`) and `rejected` (`[-]`) classes with no input — the
  stylesheet draws the box.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 21:19:55 -03:00
gffranco 7f6d811a47 fix(wiki): build wiki picker list from config so it works before opening a file
CI / cargo fmt --check (push) Successful in 36s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 27s
CI / editor keymaps (push) Successful in 1m14s
The picker fetched the wiki list via workspace/executeCommand, but the LSP
only starts once a vimwiki buffer exists — so a wiki could not be selected
until one was already open (chicken-and-egg). Read the list straight from
config instead (g:nuwiki_wikis / scalar fallback, the same source as
open_wiki_path) and open the chosen index directly; that auto-starts the
server via the FileType autocmd.

- Vim: new public nuwiki#commands#wiki_list(); wiki_ui_select() uses it +
  inputlist() + :edit. Global :VimwikiUISelect / :NuwikiUISelect commands.
- Neovim: config.wiki_cfg(n) + config.wiki_list() (init.lua delegates);
  commands.wiki_ui_select() uses them + vim.ui.select + :edit; global
  VimwikiUISelect / NuwikiUISelect user commands registered in setup().

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 20:04:31 -03:00
gffranco d6672fe218 fix(conceal): reveal cursor line in normal mode for natural navigation
concealcursor=nc kept the cursor's own line concealed in normal mode, so
the hidden characters of a wikilink ([[, target, ]]) still occupied buffer
columns — each l/h moved one hidden position with no visible movement,
making the cursor feel stuck. Drop the `n` flag (keep `c` for incsearch) so
the cursor line shows raw markup in normal mode while other lines stay
concealed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 20:03:32 -03:00
gffranco 66964be393 chore(fmt): apply cargo fmt to recent LSP changes
CI / cargo fmt --check (push) Successful in 11s
CI / cargo clippy (push) Successful in 14s
CI / cargo test (push) Successful in 21s
CI / editor keymaps (push) Successful in 1m10s
Pure rustfmt churn — the anchor-match, source-relative resolution,
and config-unwrap commits introduced lines that needed rustfmt
reformatting. CI's `cargo fmt --check` job was blocking on this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 23:34:31 -03:00
gffranco 59b48b4786 feat(folding): open every heading fold by default on file open
CI / cargo fmt --check (push) Failing after 32s
CI / cargo clippy (push) Successful in 15s
CI / cargo test (push) Successful in 21s
CI / editor keymaps (push) Successful in 1m6s
Heading-block folds were computed but all collapsed on file open,
so users had to `zR` after every `:edit`. Set `foldlevel=99` once at
ftplugin attach so the fold structure is still there (closeable with
`zc`/`zM`) but the buffer renders fully expanded.

`foldlevel` is window-local; we set it on initial attach only — not
in the Lua BufWinEnter re-apply — so closing folds with `zc` in a
window survives navigating away and back to that buffer. Split
windows still get the same value because Vim copies window-local
options to the new window when splitting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 23:02:17 -03:00
gffranco 0dfe0910fc fix(tables): cycle Tab into the next row instead of inserting one
Pressing <Tab> past the last cell unconditionally appended a new row
below, even when there was already a next row to jump into. Tabbing
through a header → separator → body table from the header row
inserted a blank row between header and separator instead of landing
in the first body cell.

After the final cell, walk forward from the current row to the end
of the table, skip any separator row, and jump to the first cell of
the next data row. Only insert a fresh row when the cursor is on the
table's last row (or only separator rows follow). Applied to both
the Vim (autoload/nuwiki/commands.vim) and Neovim (lua/nuwiki/
commands.lua) sides so the two harnesses stay in sync; covered by
new keymap tests in scripts/test-keymaps-vim.vim and test-keymaps.lua.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:55:40 -03:00
gffranco e5b0042a90 fix(lsp): refresh diagnostics after initial workspace scan completes
`didOpen` for the wiki's index page fires almost immediately after
`initialized`, but the background `index_workspace` task hasn't yet
parsed the other pages. Link-health diagnostics computed at that
point look up every cross-page link target in a near-empty index and
flag them all as missing-page — and the diagnostics are never
recomputed once the scan catches up, so the editor shows stale red
squiggles on every link until the buffer is edited.

After each wiki's initial scan finishes, walk every open document
that lives in that wiki, recompute its diagnostics against the now-
populated index, and re-publish. Snapshots both the URI list and
each doc's `(ast, text, version)` so we don't hold DashMap iterators
across awaits.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:47:46 -03:00
gffranco 769580af75 chore(scripts): add test-personal-wiki.sh + NUWIKI_DEV_NO_SEED
test-personal-wiki.sh wraps start-vim.sh to open the user's real wiki
at ~/.vimwiki/personal_wiki (override with NUWIKI_PERSONAL_WIKI) — used
when reproducing user-reported bugs against actual content instead of
the seeded smoke-test wiki.

Also adds a NUWIKI_DEV_NO_SEED env var to start-vim.sh so seed_wiki
doesn't drop a scratch Notes.wiki into the user's real notes when we
point WIKI_DIR at an already-populated wiki.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:47:13 -03:00
gffranco f477f6e6a7 fix(lsp): resolve wikilinks source-relative first, with .. collapsing
`[[llm-wiki-pattern]]` in `tips/index.wiki` should resolve to
`tips/llm-wiki-pattern.wiki` — vimwiki's default is source-relative
for non-absolute targets. The index keyed every page by its
workspace-relative path, so the link looked up `llm-wiki-pattern`
(root-relative) and missed the sibling. Goto-definition then
synthesised a non-existent root URL, and `nuwiki.link` diagnostics
flagged every such link as missing-page.

  - `WorkspaceIndex::resolve_wiki_path` and `page_for_wiki_target`
    centralise the lookup: try `{source_dir}/{path}` first, fall back
    to root-relative. Used by goto-definition, diagnostics, and the
    workspace `checkLinks` walker.
  - `collapse_dots` resolves `.` / `..` segments before lookup so
    `[[../Other]]` from `posts/foo` correctly hits `Other` at the
    wiki root (and is reported broken if no such page exists, rather
    than silently going to nowhere).
  - `canonical_link_name` applies the same rule when keying the
    backlinks index, so backlinks queried by the resolved page name
    pick up references that were written source-relative.
  - `OutgoingLink` carries `is_absolute` now so `classify_outgoing`
    (driven from cached index data, not the live AST) gets the same
    treatment without re-parsing the source.
  - `classify_outgoing`'s File/Local branch now reuses
    `resolve_file_path` instead of duplicating the `//absolute` /
    relative-to-source logic inline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:30:32 -03:00