71 Commits

Author SHA1 Message Date
gffranco 4bee216c05 refactor(client): self-review cleanup — dead code, dedup, parity, stale comments
CI / editor tests (push) Successful in 45s
A code-review pass over the VimL + Lua client layers (no behaviour change
beyond the noted parity fixes; full editor harness 10/10):

Lua (lua/nuwiki/):
- commands.lua: drop the dead <0.10 make_position_params pcall/no-arg fallback
  (min is 0.11; no-arg form is itself deprecated) and its stale comment.
- commands.lua: simplify the exec() client dispatch — the metatable/rawget
  dance is gone; always use the non-deprecated colon form client:request() on
  0.11+. (This is the block an external review misread as "critical".)
- commands.lua: extract parse_list_marker() + has_auto_indent() shared by
  smart_return/smart_shift_return; extract word_boundaries() shared by
  wrap_cword_as_wikilink/colorize (and unify the equivalent char classes).
- commands.lua: :Nuwiki search now surfaces real lvimgrep errors instead of
  reporting every failure as "no match" (E480 stays a WARN) — parity with the
  VimL twin's `catch /E480/`.
- keymaps.lua: collapse open_below/above_with_bullet into open_with_bullet(cmd)
  (checkbox prefix preserved for `o` only, as before).
- lsp.lua: normalize the wiki root before the buffer-path prefix compare
  (Windows separators); buf path was already normalized.
- ftplugin.lua: drop the always-true has('nvim-0.11') guard and hoist the
  identical foldmethod/foldtext out of both branches.
- install.lua: use a local `uv` instead of mutating the global vim.uv.

VimL (autoload/nuwiki/):
- commands.vim: refresh a stale comment pointing at the old in-repo
  nuwiki-lsp/src/commands.rs path → the nuwiki-rs server generally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:59:52 +00:00
gffranco 82444f5013 refactor(viml): extract shared heading_level/is_table_row/wiki_cfg; fix cap + spaceless
CI / editor tests (push) Successful in 44s
Mirrors the Lua util refactor. Pulls the copy-pasted VimL helpers into
autoload/nuwiki/util.vim and points the per-file s: stubs at it:

- heading_level (commands/folding/textobjects) — commands.vim's copy was
  missing the >6 level cap the others had (a 7+ `=` line returned 7). The
  unified version caps at 6 AND accepts spaceless headings (==Heading==),
  matching the server lexer + the Lua client (so fold fallback, text
  objects, and ]] navigation recognize spaceless headings too).
- is_table_row (commands/textobjects) — identical copies.
- wiki_cfg (commands/diary) — these had DRIFTED: commands.vim's included a
  `space` (links_space_char) key diary.vim's lacked. Unified to the superset
  so callers reading `.space` keep working; diary callers ignore the extra key.

Also refreshes the stale plugin/nuwiki.vim header ("all logic lives in the
Rust language server" → thin client; server is nuwiki-rs, downloaded).

Verified: 18-case heading battery in headless vim + full editor harness 10/10.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:38:04 +00:00
gffranco cb11889e72 Remove Rust code, delegate server to nuwiki-rs repo
CI / cargo clippy (pull_request) Failing after 42s
CI / cargo fmt --check (pull_request) Failing after 45s
CI / cargo test (pull_request) Failing after 49s
CI / editor keymaps (pull_request) Has been skipped
Delete all Rust crates (crates/) and Cargo files. The nuwiki-ls binary
is now built in the separate nuwiki-rs repository and downloaded at
install time from the Gitea release assets, with a cargo build fallback
that clones nuwiki-rs.

Update all documentation to reflect the split repo layout.
2026-06-24 12:57:15 +00:00
gffranco 3e8c534859 feat(calendar): diary into the current wiki, not always the first
CI / cargo fmt --check (push) Successful in 46s
CI / cargo clippy (push) Successful in 51s
CI / cargo test (push) Successful in 58s
CI / editor keymaps (push) Successful in 1m29s
The VimL calendar-vim callbacks (nuwiki#diary#calendar_action /
calendar_sign) hardcoded wiki 0, so opening the calendar while editing
ifood_wiki still created/marked diary entries under the first wiki. (The
LSP-driven diary commands already follow the current buffer via its URI;
this brings the calendar in line.)

Track the wiki of the active buffer (nuwiki#diary#track_wiki, driven by
FileType/BufEnter autocmds in plugin/nuwiki.vim) and resolve the calendar
diary path against it, falling back to the first wiki. Verified the
calendar marker/open switch between wikis as you move between their
buffers. test-calendar-vim gains 4 multi-wiki tracking checks (16).
All keymap/config/calendar harnesses green on both clients.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 02:07:11 +00:00
gffranco f0c51fbfcc feat(generate): insert links/tags sections at the cursor line too
CI / cargo fmt --check (push) Successful in 28s
CI / cargo clippy (push) Successful in 36s
CI / cargo test (push) Successful in 40s
CI / editor keymaps (push) Successful in 1m26s
Extend the cursor-line insertion from :NuwikiTOC to the other buffer
list generators: :NuwikiGenerateLinks and :NuwikiGenerateTagLinks. They
appended at end-of-file; a fresh section now goes at the cursor line
(with a leading blank), matching :NuwikiTOC. An existing section is still
refreshed in place, and the auto_generate_*-on-save hooks are unaffected
(they only ever replace).

Shared section_insert() helper computes the insert position/block for all
three (cursor line clamped to the document, else the per-command fallback:
top of file for TOC, EOF for links/tags). Clients send the 0-based cursor
line; handlers parse it via parse_uri_line_arg and thread it through
links_edit / tag_links_edit. Rebuild variants pass None.

Tests: links_edit_inserts_at_cursor_line + tag_links_edit_inserts_at_cursor_line.
577 passed, clippy clean, keymap harnesses green. Docs updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 01:07:18 +00:00
gffranco 94cb58064d feat(toc): insert a fresh TOC at the cursor line
CI / cargo fmt --check (push) Successful in 35s
CI / cargo clippy (push) Successful in 39s
CI / cargo test (push) Successful in 50s
CI / editor keymaps (push) Successful in 1m25s
:NuwikiTOC inserted a new table of contents at the top of the file. It now
inserts at the cursor line instead, so you can place the TOC where you want
it (a blank line separates it from preceding text). An existing TOC is
still refreshed in place, and auto_toc-on-save is unaffected.

The Vim/Lua clients send the 0-based cursor line with nuwiki.toc.generate;
the server threads it into toc_edit (clamped to the document) and falls
back to the top of the file when absent. toc_rebuild_edit passes None.

Tests: toc_edit_inserts_at_cursor_line +
toc_edit_cursor_line_clamped_and_existing_replaced_in_place. 575 passed,
clippy clean, keymap harnesses green. Doc updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:51:40 +00:00
gffranco 9d6d28a1b6 fix(vim): buffer commands honour g:vimwiki_list (<Leader>ww opened ~/vimwiki)
CI / cargo fmt --check (push) Successful in 33s
CI / cargo clippy (push) Successful in 33s
CI / cargo test (push) Successful in 41s
CI / editor keymaps (push) Successful in 1m23s
The g:vimwiki_* drop-in only fed the LSP payload (lsp.vim s:settings).
The buffer-side commands — <Leader>ww (wiki_index), :VimwikiSearch,
auto_chdir, diary, completion — resolve the wiki list independently via
s:wiki_cfg / nuwiki#commands#wiki_list, which read only g:nuwiki_wikis /
g:nuwiki_wiki_root. So with a g:vimwiki_list config the server knew the
wikis but <Leader>ww fell back to the default ~/vimwiki and opened an
empty index.

Extract the resolution into a single source of truth,
autoload/nuwiki/config.vim (nuwiki#config#wikis / #scalar_globals), which
resolves g:nuwiki_wikis or an upstream g:vimwiki_list and folds the global
scalar defaults. lsp.vim (payload), commands.vim, and diary.vim all route
through it — also dedups the s:wiki_cfg copy that lived in both
commands.vim and diary.vim.

Regression guard in test-vimwiki-compat-vim: nuwiki#commands#wiki_list()
must resolve the g:vimwiki_list roots (21 checks). All Vim harnesses +
config-parity goldens green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:40:48 +00:00
gffranco ac14cdd838 feat(coc): auto-register the language server with coc.nvim
CI / cargo fmt --check (push) Successful in 47s
CI / cargo clippy (push) Successful in 46s
CI / cargo test (push) Successful in 56s
CI / editor keymaps (push) Successful in 1m30s
coc users had to hand-maintain a coc-settings.json `languageserver.nuwiki`
entry, which didn't track g:nuwiki_wikis / g:vimwiki_list / the global
shorthand and meant editing JSON per wiki. nuwiki now registers itself
programmatically: on the first .wiki buffer it calls
coc#config('languageserver.nuwiki', {…}) with the s:settings() payload
(same config the vim-lsp path sends). coc reacts to the languageserver
config change (onDidChangeConfiguration → registerClientsByConfig) and
starts the server for the open buffer.

Details:
- Reliable coc detection via :CocConfig (exists('*coc#config') can't be
  trusted — it doesn't trigger autoload in Vim 9.2). The coc#config call
  is wrapped in try/catch and autoloads coc.vim itself; falls back to the
  printed snippet only if coc genuinely isn't there.
- Deferred to User CocNvimInit when coc's node service isn't up yet.
- Opt out with g:nuwiki_no_coc_register (then we just print the snippet).

Dogfooded in start-vim-coc.sh: dropped the manual coc-settings.json
languageserver block; the harness now relies on auto-registration from
g:nuwiki_* (real-user flow). New harness test-coc-register-vim (7 checks,
stubbed coc#config) asserts the injected payload incl. the folded global
shorthand; wired into CI. README coc section rewritten (zero-config).
Rust 573 passed, clippy clean, all harnesses green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:07:50 +00:00
gffranco 9d89e01ed8 feat(config): vimwiki drop-in config + global per-wiki defaults
CI / cargo fmt --check (push) Successful in 31s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 41s
CI / editor keymaps (push) Successful in 1m53s
Two related config-ergonomics features for vimwiki migrants, sharing the
same scalar-global machinery.

1. Upstream g:vimwiki_* drop-in (Vim client). When nuwiki isn't configured
   natively, the Vim client reads g:vimwiki_list + g:vimwiki_* globals and
   translates them into nuwiki's schema: per-wiki path->root,
   path_html->html_path, template_*, css_name, auto_export/auto_toc,
   syntax, ext->file_extension, index, diary_*, name. g:nuwiki_* always
   wins. Lets a vimwiki user drop in nuwiki without rewriting config.

2. Global per-wiki defaults (both clients). A display/generation setting
   given once at the top level — g:nuwiki_<key> / setup({<key>=…}) /
   g:vimwiki_<key> — is folded into every wiki as a default; a per-wiki
   value overrides it (vimwiki's model). Covers toc_header(_level),
   toc_link_format, links_header(_level), tags_header(_level),
   html_header_numbering(_sym), links_space_char, list_margin, listsyms,
   listsym_rejected, and the auto_* toggles. Only values the user
   explicitly set fold — built-in defaults don't (added config.user
   tracking on the Lua side so a default toc_header_level=1 isn't pushed
   onto every wiki). User config tables are never mutated.

Both clients kept in lock-step (config-parity golden enforces identical
payloads). New harnesses test-vimwiki-compat-vim (18) and
test-global-shorthand (8), wired into CI. Server test
vimwiki_compat_payload_sets_toc_level_per_wiki. Docs in README +
known-issues.md (drop-in is currently Vim-only). Rust 573 passed, clippy
clean, all harnesses green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 23:37:10 +00:00
gffranco ddb9d0b916 fix(vars): E121 in get_wikilocal no-index path; add regression test
CI / cargo fmt --check (push) Successful in 39s
CI / cargo clippy (push) Successful in 28s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m45s
s:resolve_index referenced an undefined `a:file` (a copy-paste leftover
from wiki_root_for, whose parameter is named a:file) inside the
buffer-owning-wiki loop. Any get_wikilocal() call with no explicit index
while a wiki buffer was current — i.e. the ftplugin BufRead path — hit
`E121: undefined variable a:file`. The local is `l:file`, and the
surrounding `!empty(l:file)` guard already covers emptiness, so the
clause was both wrong and redundant; removed it.

Add development/tests/test-vars-vim.{sh,vim} covering the shim:
per-wiki-index resolution, global-default fallback, out-of-range clamp,
the no-index owning-wiki path (direct guard for this E121), and the
single-wiki shorthand. Wired into CI. 14/14 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:57:14 +00:00
gffranco 30cccd88bd fix(vars): resolve wiki by index in the vimwiki compat shim (R15)
CI / cargo fmt --check (push) Successful in 26s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m23s
`vimwiki#vars#get_wikilocal(key, N)` previously ignored the wiki-index
argument and always read the scalar `g:nuwiki_*` globals (the first
wiki), so third-party plugins (vimwiki-sync, vim-zettel) got the wrong
wiki's path/ext/syntax in a multi-wiki setup.

Now:
- an explicit numeric index selects that wiki from `g:nuwiki_wikis[N]`,
  falling back per-key to the scalar globals;
- with no index, the wiki owning the current buffer is resolved (matching
  upstream's `g:vimwiki_current_idx` behaviour);
- an out-of-range index clamps to the first wiki.

The single-wiki shorthand path is unchanged. Verified with a headless
Vim test (by-index, global fallback, clamp, no-arg, shorthand) and the
Vim keymap harness (301/18/21, 0 failed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:20:14 +00:00
gffranco 3b92b11948 fix(review): close all 2026-06-03 codebase-review findings (R1-R18)
CI / cargo fmt --check (push) Successful in 20s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 38s
CI / editor keymaps (push) Successful in 1m33s
Resolves the 18 findings from the parallel codebase review, tracked in
development/vimwiki-gap.md.

Correctness / perf:
- Wiki.config -> Arc<WikiConfig> so cloning a Wiki is a refcount bump (R5)
- WorkspaceIndex::remove is no longer O(n^2): a per-source contributions
  map limits the scan to buckets the source actually wrote into (R6)
- render_color now expands color_tag_template (__STYLE__/__CONTENT__),
  consuming the previously-dead field; ColorNode documented as an
  extension point; 3 renderer tests added (R3/R4)
- wiki_root_for returns empty/nil on no-match instead of falling back to
  the first wiki (R2); auto_header honours links_space_char (R7)

Cleanup / dedup:
- Remove dead #[allow(dead_code)] stubs + uncalled pub helpers, narrow
  imports (R10/R11)
- Dedup span_of_inline x3 -> InlineNode::span() (R12)
- diary_step single read lock; page_captions single pass (R13)
- Lua auto_header loop -> wiki_list(); detect_current_symbol cleanup
  (R14/R18)

Client / docs:
- :VimwikiNormalizeLink Vim cmds -> <q-args> (R17); ftplugin header fix
  (R16); vars.vim multi-wiki limitation documented (R15)
- Document 19 config options in README.md + doc/nuwiki.txt; fix
  list_margin/shiftwidth doc and stale comments (R1/R9)
- R8 investigated, confirmed not a real bug (documented)

Verified: Neovim harness 307, Vim harness 301/18/21, Rust suite 568, all
0 failed; clippy clean; fresh parallel-agent audit found no regressions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:13:55 +00:00
gffranco 1246c99c3c fix(config): address P3 config-batch audit findings
CI / cargo fmt --check (push) Failing after 19s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 34s
CI / editor keymaps (push) Successful in 1m41s
Parallel-agent audit of the config batch found:

- cycle_bullets used find_marker_span's third element as an indent column, but
  it's the ABSOLUTE byte offset after the marker — so depth (and the rotated
  glyph) was wrong for any item not near offset 0 (tests passed by coincidence).
  Fixed to use the marker span's column (the indentation). Regression test now
  places the list deep in the document.

- :VimwikiColorize interpolated the colour into the template via an unescaped
  replacement: Lua gsub treats `%` specially, Vim substitute() treats `&`/`\`.
  Lua now uses a function replacement (verbatim); Vim escapes `\&~`.

- render_swapped_table applied table_reduce_last_col before the column swap, so
  a swap involving the last column moved the narrow slot — now clamped after.

- Fixed a stale toc_link_format doc comment (format 1 = [[#anchor]], not a
  full parent path).

Verified-correct by the audit (no change needed): RSS rfc822/cdata/fidelity,
prune_orphan_html reverse-mapping + guards, write_escaped_allowing/match_allowed_tag
(multibyte-safe), substitute_emoji (byte-boundary-safe), resolve_target_uri
create_link/dir_link ordering, all config defaults + From/constructors.

Full rust suite + clippy clean; Neovim 307, Vim 301/18/21.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:48:25 +00:00
gffranco 5d1ac09245 feat(config): P3 config batch — group 4 (valid_html_tags, emoji, color_tag_template)
- valid_html_tags: HTML export now passes the allowlisted inline tags through
  verbatim (b/i/sub/sup/kbd/div/center/strong/em/...) and escapes the rest,
  via a render-time tag-aware escaper (write_escaped_allowing/match_allowed_tag)
  — mirroring upstream's s:safe_html_line regex rather than a parser change.
  `span` is always allowed so colour spans render.
- emoji_enable: render-time `:alias:` -> glyph substitution (curated common
  set), default on; with_emoji builder + substitute_emoji/emoji_for.
- color_tag_template: :VimwikiColorize now wraps via a configurable template
  (g:nuwiki_color_tag_template / setup color_tag_template), splitting on
  __CONTENT__ with __COLORFG__ -> colour; both clients. Colour spans render in
  export via the valid_html_tags span passthrough. Default template reproduces
  the prior `<span style="color:NAME">` output (harness colorize tests green).

Renderer gets with_valid_html_tags + with_emoji; export.rs wires both from
HtmlConfig. Tests: valid_html_tags passthrough + escape, emoji on/off
(html_export.rs). Full rust suite + both harnesses green (Neovim 307,
Vim 301/18/21); clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:30:38 +00:00
gffranco 87d14310fc feat(config): P3 config batch — group 2 (create_link, dir_link, auto_header)
- create_link (default true): server resolve_target_uri now only synthesises a
  missing-page URI when create_link is set; when off, following a link to a
  non-existent page returns no location (no-op).
- dir_link: a [[dir/]] link now resolves to <dir>/<dir_link><ext> when dir_link
  is set, else opens the directory itself. Uses the already-parsed
  LinkTarget.is_directory.
- auto_header (default off): new wiki pages get a `= Stem =` header derived from
  the filename, skipping the wiki index + diary index. Client-side BufNewFile
  autocmd in both clients (init.lua + plugin/nuwiki.vim), with auto_header
  handlers in both command layers (trailing-slash-robust root matching).

Tests: cmd.auto_header_inserts + cmd.auto_header_skips_diary_index (nvim) /
cmd.auto_header_skips_index (vim). Full rust suite + both harnesses green
(Neovim 307, Vim 301/18/21).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:19:37 +00:00
gffranco b2f2fc88bd feat(config): P3 config batch — group 1 (clean config + plumbing)
Config plumbing for the whole config batch (HtmlConfig + WikiConfig fields,
RawWiki deserialization, From/defaults) plus these behaviors:

- rss_name / rss_max_items: HtmlConfig fields (wired into write_rss in the RSS
  group).
- toc_link_format: build_toc_text emits [[#anchor]] (no description) for 1,
  [[#anchor|title]] for 0.
- generated_links_caption: build_links_text emits [[page|FirstHeading]] from a
  page->heading map (ops::page_captions) when enabled.
- table_reduce_last_col: column_widths clamps the last column to width 1 when
  set; threaded through table_align_edit/table_move_column_edit + commands.
- color_dic now ships a populated default palette (red/green/blue/...).
- commentstring aligned to upstream's no-space `%%%s`.
- auto_chdir (default off): :lcd into the owning wiki root on buffer enter;
  both clients (ftplugin.lua setup_auto_chdir + Vim NuwikiAutoChdir augroup),
  with config.wiki_root_for / nuwiki#commands#wiki_root_for resolvers.

Also seeded HtmlConfig fields for later groups (valid_html_tags,
list/text_ignore_newline, emoji_enable, user_htmls, color_tag_template) and
WikiConfig (create_link, dir_link, bullet_types, cycle_bullets).

Tests: toc_link_format, generated_links_caption, table_reduce_last_col,
config defaults + JSON round-trip for every new key. Full rust suite +
both keymap harnesses green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:04:36 +00:00
gffranco 03005d0931 fix(insert): pumvisible() guard on smart_return/smart_shift_return (audit follow-up)
CI / cargo fmt --check (push) Successful in 23s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 28s
CI / editor keymaps (push) Successful in 1m27s
The mappings re-audit confirmed the two new bindings (visual <CR> normalize,
insert <S-CR> multiline item) are correct — column math matches upstream's
s:text_begin, auto-indent handling sound, no parity gaps or regressions.

It flagged one low-severity item shared with the pre-existing smart_return:
neither guarded the completion popup. Upstream maps <CR>/<S-CR> as
`pumvisible() ? '<CR>' : …`. Added that guard to both smart_return and
smart_shift_return (both clients) so a <CR>/<S-CR> accepts the completion
instead of continuing the list when the popup is open.

Left as a documented minor divergence: <S-CR> on an *empty* bullet emits the
aligned no-marker continuation rather than upstream's "blank line above, keep
marker" (niche). Neovim 305, Vim 299/18/21 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:10:07 +00:00
gffranco 724712121d feat(parity): close the entire P3 Mappings section
Both remaining mapping gaps (client-side, both clients):

- Visual <CR>: upstream binds it to NormalizeLinkVisualCR (== visual `+`).
  Added `x <CR>` -> normalize_link(true) (wrap the selection as a wikilink)
  in both clients.

- Insert <S-CR>: upstream's multiline-list-item continuation (VimwikiReturn
  2 2 -> kbd_cr with no new marker). New smart_shift_return (both clients,
  <expr> insert mapping): on a list item returns <CR> + spaces aligning under
  the item text (marker width + 1, +4 for a `[ ] ` checkbox), no marker; off a
  list item, a plain <CR>. Mirrors smart_return's auto-indent handling.

Tests: map.visual_cr_wraps_selection + map.insert_shift_cr_multiline
(test-keymaps.lua, the latter exercising the handler directly since <S-CR>
doesn't round-trip headless feedkeys), and map.visual_cr_wraps_selection +
cr.shift_cr_multiline{,_checkbox} (test-keymaps-vim.vim). Docs (README +
doc/nuwiki.txt) and the gap doc updated. Neovim 305, Vim 299/18/21 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:04:56 +00:00
gffranco 305324c6e9 Merge remote-tracking branch 'origin/calendar-support' into feature-gap
CI / cargo fmt --check (push) Successful in 35s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 39s
CI / editor keymaps (push) Successful in 1m32s
# Conflicts:
#	development/vimwiki-gap.md
2026-06-03 01:46:07 +00:00
gffranco 6deba47106 fix(search): graceful no-match + NuwikiSearch alias (re-audit follow-up)
CI / cargo fmt --check (push) Successful in 28s
CI / cargo clippy (push) Successful in 29s
CI / cargo test (push) Successful in 35s
CI / editor keymaps (push) Successful in 1m25s
Re-audit of the just-landed command work found one real bug I introduced
and a convention gap:

- VimwikiSearch / VWS raised a raw E480 on a no-match search; upstream
  reports it gracefully. Both clients now catch it (try/catch in Vim,
  pcall in Neovim) and emit "nuwiki: no match for <pat>".

- Added a NuwikiSearch alias alongside VimwikiSearch/VWS (every other
  command carries a Nuwiki* form).

Gap-doc note corrected: nuwiki's search populates + opens the location
list (shows all matches) where upstream jumps to the first match — logged
as a minor presentation divergence rather than claiming exact parity.

Config + mappings re-audits came back clean (no new gaps, no regressions;
table-mapping rewire verified non-recursive, gLH/gLL/gLR present).

Tests: cmd.VimwikiSearch_no_match_graceful, surface.{Vimwiki,Nuwiki}Search.
Neovim 301, Vim 294/18/21 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 01:28:44 +00:00
gffranco a11b742fc1 feat(parity): close the entire P3 Commands section
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Successful in 42s
CI / editor keymaps (push) Successful in 1m40s
All six remaining command gaps (client-side; no server change):

- VimwikiRenameFile: bare -> -nargs=? (arg accepted-ignored; rename still
  delegates to the LSP rename prompt). No more E488.

- VimwikiVar / NuwikiVar: get/set the client config (upstream vimwiki#vars#cmd).
  No arg prints config (Vim: g:nuwiki_*; Neovim: resolved setup() opts), one arg
  gets a key, two+ sets it. Global command, both clients.

- VimwikiReturn / NuwikiReturn: command form of the smart <CR> continuation —
  enters insert at EOL and fires the <CR> mapping (return_cmd / vimwiki_return).
  Upstream's mode-flag args accepted but unused.

- VimwikiTableAlignQ vs AlignW: corrected — upstream's gqq/gww are identical on
  a table; the only difference is the non-table fallback (native `normal! gqq`
  vs `gww`). New table_align_or_cmd(cmd) aligns on a table row, else runs
  `normal! <cmd>`. Q/gqq/gq1 -> gqq; W/gww/gw1 -> gww; NuwikiTableAlign -> gqq.

- VimwikiSearch / VWS: scope the lvimgrep to the current wiki's root (resolved
  client-side) over <root>/**/*<ext>, not CWD `**`; empty pattern reuses the
  last search. Both clients.

- VimwikiIndex family: add global Vimwiki/NuwikiIndex + TabIndex entry points
  (plugin/nuwiki.vim + init.lua _setup_global_commands, with -count), so a wiki
  opens from any buffer. Buffer-local copies still shadow inside wiki buffers.

Tests: surface.{Vimwiki,Nuwiki}{Return,Var}, cmd.VimwikiVar_sets_*,
cmd.global_entry_points (both harnesses). Docs (README + doc/nuwiki.txt) and the
gap doc updated. Neovim 299, Vim 291/18/21 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 01:23:07 +00:00
gffranco c161f9d21a feat(parity): P3 quick-wins — ShowVersion, gLH/gLL/gLR, Search/Align nargs
CI / cargo fmt --check (push) Successful in 21s
CI / cargo clippy (push) Successful in 26s
CI / cargo test (push) Successful in 36s
CI / editor keymaps (push) Successful in 1m47s
Closes four low-risk P3 parity items (gap doc updated):

- VimwikiShowVersion / NuwikiShowVersion: new global command (both clients)
  echoing g:nuwiki_version (0.1.0) + the host editor, via
  nuwiki#commands#show_version / commands.show_version.

- gLH / gLL / gLR: upstream's case-variant aliases of gLh/gLl/gLr (dedent /
  indent whole item, renumber all lists) added in both clients.

- VimwikiSearch / VWS: -nargs=1 -> -nargs=* (Vim + Neovim), so a multi-word
  search no longer raises E488 and an empty invocation reuses the last search
  pattern. (The lvimgrep-vs-vimwiki-engine difference stays open.)

- VimwikiTableAlignQ / AlignW / NuwikiTableAlign: bare -> -nargs=?, so
  `:VimwikiTableAlignQ 2` no longer raises E488 (optional column arg is
  accepted-and-ignored; the gqq-align vs gww-align-without-resize split
  stays open).

Docs (doc/nuwiki.txt + README) updated for the command and the aliases.
Tests: surface.{Vimwiki,Nuwiki}ShowVersion, map[n].gL{H,L,R} (both
harnesses), cmd.search_and_tablealign_nargs (vim). Neovim 291, Vim
282/18/21 pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 00:28:51 +00:00
gffranco 9441fe918c fix(parity): close the 2026-06-02 re-audit config/command findings
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Successful in 37s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m36s
Implements the remaining fourth-pass findings (gap doc updated):

- list_margin: rework to upstream's buffer-side meaning. Drop the
  nuwiki-only HTML em-margin (renderer field/method + render_page_html
  param removed) and prepend max(0, list_margin) leading spaces to every
  generated bullet in build_toc_text / build_links_text /
  build_tag_links_text / diary::build_index_body; headings stay at col 0.
  From<RawWiki> derives 0 for markdown wikis when unset. Negatives can't
  resolve 'shiftwidth' server-side, so they collapse to zero indent
  (documented divergence).

- diary_months: per-wiki Vec<String> (default 12 English names), threaded
  into the diary-index month labels; missing/empty slots fall back to the
  English name.

- diary_caption_level: widen u8 -> i8 so vimwiki's -1 (min: -1) parses;
  build_index_body clamps < 0 to base tree level 0.

- VimwikiRemoveDone: regain upstream's -range. All four defs are now
  -bang -range, dispatched via remove_done(bang, range, l1, l2) in both
  clients: ! -> whole buffer, explicit range -> new list_remove_done_range
  ({range:[l1-1,l2-1]}), else current list. Server remove_done_edit gained
  an Option<(u32,u32)> range that filters whole-doc victims by start line.

markdown_header_style is deferred: the generators emit vimwiki syntax only
(caption_line never writes markdown headers), so there's no markdown header
to attach the style to. Logged as the "generated-content is vimwiki-only"
intentional divergence pending a later markdown generated-content effort.

Tests: list_margin indent, markdown list_margin-0 default, diary_months
custom + fallback, negative caption_level clamp/parse, ranged remove-done
(server + both keymap harnesses). 553 lsp/core tests pass; clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 10:50:38 +00:00
gffranco 079e7246ac fix(lists): VimwikiToggleRejectedListItem -range parity (P3)
CI / cargo fmt --check (push) Successful in 7m3s
CI / cargo clippy (push) Successful in 17m14s
CI / cargo test (push) Successful in 31m54s
CI / editor keymaps (push) Successful in 1m51s
Upstream's :VimwikiToggleRejectedListItem is -range and binds glx in
visual mode; nuwiki's four defs were bare, so a ranged invocation raised
E481 and acted cursor-only. Same -range class already fixed for
ToggleListItem/Increment/Decrement -- ToggleRejected was missed.

All four defs (Vim+Neovim x Vimwiki*/Nuwiki*) are now -range, routed
through a new reject_list_item_range(l1, l2) looping over_range in both
clients, mirroring toggle_list_item_range. Visual glx stays cursor-only,
consistent with its <C-Space>/gln/glp siblings.

Also logs the 2026-06-02 re-audit findings in development/vimwiki-gap.md:
RemoveDone lost -range (low pri); config gaps list_margin
semantics/markdown-default, diary_months, markdown_header_style,
diary_caption_level=-1; and the path_html/template_path default-location
divergences. Mappings audit came back clean.

Tests: cmd.reject_list_item_range (test-keymaps.lua),
cmd.VimwikiToggleRejectedListItem_accepts_range (test-keymaps-vim.vim).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 01:02:48 +00:00
gffranco 21f583beef docs(calendar): fix stale comments and integration docs
CI / cargo fmt --check (push) Successful in 15s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Successful in 31s
CI / editor keymaps (push) Successful in 1m21s
- diary.vim: correct calendar_action doc comment (dir is used for split
  direction; only week is unused)
- calendar-vim-integration.md: use real nuwiki var names
  (g:nuwiki_use_calendar / g:nuwiki_no_calendar / g:nuwiki_wikis), document
  auto-detect wiring and calendar-window close behavior; drop the
  non-existent refresh-on-close autocmd
- vimwiki-gap.md: remove stale 'no calendar.vim integration' entry now
  that the integration is implemented
2026-05-31 21:58:06 -03:00
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 2da2168d88 fix(diary): complete the -count wiki selector (repair 874bdd0)
CI / cargo fmt --check (push) Successful in 30s
CI / cargo clippy (push) Successful in 38s
CI / cargo test (push) Successful in 40s
CI / editor keymaps (push) Successful in 1m37s
874bdd0 committed the diary -count feature with several Edits that had
silently no-matched, leaving the tree non-compiling and the clients
inconsistent (CI run 236 failed). This completes it:

- commands.rs: add the `wiki` field to OptionalUriArg (the previous edit
  targeted a wrong struct name) and pass None to the six resolve_diary_wiki
  callers that take no selector (date/list/step paths). Server builds clean.
- autoload/nuwiki/commands.vim + lua/nuwiki/commands.lua: actually thread the
  count through s:diary_open / _diary_open and the diary_today/today_tab/
  yesterday/tomorrow/index handlers (these edits had failed before, so the
  ftplugin defs were calling handlers that ignored/rejected the arg).
- Add the test cases the prior commit referenced but never landed:
  cmd.VimwikiMakeDiaryNote_has_count (test-keymaps.lua) +
  cmd.Vimwiki{MakeDiaryNote,DiaryIndex}_accepts_count (test-keymaps-vim.vim).
- Fix the OptUriArg→OptionalUriArg name in the gap-doc note.

Verified with CI flags: workspace test/clippy/fmt clean; lua 284, vim 272/18/21,
all 0 failed; the three new diary cases pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 22:25:08 +00:00
gffranco f616915581 feat(commands): ChangeSymbolTo -range, GenerateLinks path filter, NuwikiGenerateTags
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 34s
CI / cargo test (push) Successful in 45s
CI / editor keymaps (push) Failing after 1m43s
Closes the three new command gaps from the 2026-05-31 re-audit (all real,
client-side, both clients × Vimwiki/Nuwiki):

- VimwikiChangeSymbolTo / ListChangeSymbolI / NuwikiChangeSymbol are now
  -range -nargs=1 via a new list_change_symbol_range(symbol,l1,l2) helper that
  loops over_range; was -nargs=1 only → E481 on a visual selection.
  ChangeSymbolInListTo/InList stay range-less.
  Bonus: the Neovim VimwikiChangeSymbolInListTo + NuwikiChangeSymbolInList defs
  passed whole_list=false, changing only the current item — corrected to true.

- VimwikiGenerateLinks / NuwikiGenerateLinks are now -nargs=?; with an arg the
  client dispatches the existing server command nuwiki.links.generateForPath
  ({path}) for real subtree-scoped link generation, else nuwiki.links.generate.
  Was bare → E488 on an arg.

- Added NuwikiGenerateTags (Vim + Neovim) mirroring VimwikiGenerateTags, with
  -nargs=? -complete=…tags — closes the :Vimwiki*↔:Nuwiki* alias asymmetry.

Tests: cmd.ChangeSymbolTo_range_sets_marker, cmd.GenerateLinks_accepts_optional_path,
cmd.NuwikiGenerateTags_exists (test-keymaps.lua, 283 pass) +
cmd.VimwikiChangeSymbolTo_accepts_range, cmd.VimwikiGenerateLinks_accepts_path,
cmd.NuwikiGenerateTags_exists (test-keymaps-vim.vim, 272/18/21). fmt clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:48:31 +00:00
gffranco 3b3d8ca782 feat(commands): command-line completion for Goto/Colorize/tag commands (P3)
CI / cargo fmt --check (push) Successful in 37s
CI / cargo clippy (push) Successful in 39s
CI / cargo test (push) Successful in 37s
CI / editor keymaps (push) Successful in 1m29s
Upstream attaches -complete= to several commands; nuwiki had none, so <Tab>
at the : line never completed page/tag/colour names. New
autoload/nuwiki/complete.vim adds pure client-side, synchronous completers
(config + filesystem, never the LSP — so they work under vim-lsp, coc and
Neovim), wired via -complete=customlist into all command contexts:

- nuwiki#complete#pages → VimwikiGoto/NuwikiGoto (glob wiki roots for page names)
- nuwiki#complete#colors → VimwikiColorize/NuwikiColorize (color_dic keys +
  colours already used in buffer color:… spans)
- nuwiki#complete#tags → VimwikiSearchTags/GenerateTagLinks/GenerateTags (+Nuwiki)
  (scan wiki files for :tag: runs — the tag index is server-side and a
  synchronous completer can't query it, so we read the same source it indexes)

Documented divergence: no file completer for VimwikiRenameFile — nuwiki
delegates renaming to the LSP rename request (no filename arg to complete),
unlike upstream which renames itself.

Tests: complete.{pages_globs_and_filters,tags_scans_wiki_files,
colors_from_buffer_spans} (test-keymaps-vim.vim). Both harnesses green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:40:22 +00:00
gffranco efea225240 fix(commands): VimwikiTable default 5 cols (parity) + map audit findings
CI / cargo fmt --check (push) Successful in 36s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m46s
Verification audit of the command-attribute pass confirmed all four changes
(Table, Index/TabIndex, ListChangeLvl, SplitLink) faithful with no regressions,
and surfaced three pre-existing items:

- VimwikiTable no-arg default cols was 3 vs upstream's 5 (rows 2 already
  matched, confirmed against vimwiki#tbl#create). Aligned both backing fns
  (autoload + lua) to default 5 cols.

- Mapped (not fixed here — core <CR> behavior, out of attribute scope):
  the Neovim lua follow_link_or_create double-calls definition() with dead
  code and never wraps a bare word into [[link]] (the Vim side does); and the
  Neovim Split/VSplitLink dispatch to raw definition() instead of
  follow_link_or_create. Both logged in development/vimwiki-gap.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:28:39 +00:00
gffranco 4710ba03c8 feat(commands): VimwikiListChangeLvl -range -nargs=+ (P3 parity)
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 28s
CI / cargo test (push) Successful in 50s
CI / editor keymaps (push) Successful in 1m58s
Upstream is `-range -nargs=+` (change_level(line1, line2, direction,
plus_children)); nuwiki was -nargs=? and range-less, so `:1,3VimwikiListChangeLvl
increase 0` raised E481/E488 and the level change never spanned a selection.

All four defs (Vim+Neovim × Vimwiki+Nuwiki) are now -range -nargs=+ and forward
<line1>, <line2>, <f-args>. list_change_lvl(line1, line2, direction,
[plus_children]) (both clients) parses the required direction + optional subtree
flag and applies the change to every list item in the range via the existing
over_range helper.

Tests: cmd.ListChangeLvl_range_indents (test-keymaps.lua) +
cmd.VimwikiListChangeLvl_accepts_range_and_args (test-keymaps-vim.vim).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:16:10 +00:00
gffranco db9c1c5c11 feat: real range/bang/visual handling for the deferred command gaps
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 53s
CI / editor keymaps (push) Successful in 1m29s
Implements the four items previously left as "needs handler work" — all
fully wired, not cosmetic.

- ToggleListItem / IncrementListItem / DecrementListItem are now -range
  (both clients). The client loops the per-line op over [<line1>,<line2>]
  (toggle_list_item_range / list_cycle_symbol_range). The server's edits are
  version-less single-line WorkspaceEdits, so each line applies independently
  — no server protocol change. `:'<,'>VimwikiToggleListItem` toggles the whole
  selection.
- VimwikiRebuildTags gains -bang: `:…RebuildTags!` passes { all: true } and
  the server (tags_rebuild) re-indexes every configured wiki via
  wikis_snapshot(), not just the current one.
- VimwikiNormalizeLink is -nargs=?: with `1` (upstream's visual flag; the
  x-mode `+` mapping now passes it) it wraps the '< / '> selection as
  [[selection]] (new wrap_visual_as_wikilink in both clients).
- VimwikiCheckLinks is -range: a ranged invocation filters the broken-link
  report to the current buffer's selected lines (client-side filter in
  results_to_qf / check_links).

Tests: cmd.toggle_list_item_range + cmd.normalize_link_visual_wraps_selection
(test-keymaps.lua), normalize.visual_wraps_selection (test-keymaps-vim.vim).
README + doc/nuwiki.txt + gap doc updated. fmt/clippy clean; nvim 274,
vim 266+18, all Rust tests + config-parity green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:57:03 +00:00
gffranco 9de8543385 fix(client): colour-span conceal must skip unallocatable colours
CI / cargo fmt --check (push) Successful in 34s
CI / cargo clippy (push) Successful in 42s
CI / cargo test (push) Successful in 35s
CI / editor keymaps (push) Successful in 1m36s
A literal `<span style="color:…">` in prose (the sample wiki's own colorize
instructions used one, with a `…` ellipsis as the colour) was matched by the
scanner and fed to `highlight guifg=…`, which raised E254 on the un-silenced
guifg line. That aborted the whole nuwiki#colors#refresh(), so on the Vim
clients NO spans got concealed (start-vim) and follow-link crashed mid
FileType autocmd (start-vim-coc). Neovim happened to dodge it depending on
which buffer was scanned.

s:define() now:
- validates the colour (only #hex or an alphabetic name) and skips anything
  else — rgb()/hsl() functions and prose examples no longer create bogus
  conceal regions or errors;
- runs both `highlight` calls under `silent!`, so a valid-format but unknown
  name can't abort the refresh either (tags still conceal, text just isn't
  recoloured).

Also reworded the sample wiki's colorize note so it no longer embeds a
literal span tag.

Test: colorize.skips_unallocatable_colour (vim) — an rgb() span before a real
one: refresh doesn't error, the rgb span isn't concealed, the real span still
is. nvim 269, vim 260+18, all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:09:15 +00:00
gffranco 15ba774a28 feat(client): conceal colour spans down to their coloured text
CI / cargo fmt --check (push) Successful in 25s
CI / cargo clippy (push) Successful in 37s
CI / cargo test (push) Successful in 37s
CI / editor keymaps (push) Successful in 1m48s
A :VimwikiColorize'd word showed the literal
`<span style="color:NAME">TEXT</span>` markup. Now the tags are concealed
and TEXT is painted in NAME, so the buffer shows just the coloured word —
matching the HTML export.

New autoload/nuwiki/colors.vim: nuwiki#colors#refresh() scans the buffer
and, per distinct colour, defines a syntax region that conceals the
`<span …>` / `</span>` tags (concealends) and highlights the body with the
span's actual colour (guifg always; ctermfg for named colours). Idempotent
(clears the group before redefining) and tracked in b:nuwiki_color_seen.

Refresh is driven from three places so it's both correct and immediate:
- syntax/vimwiki.vim: initial pass + re-establish after :colorscheme reload
  (resets the per-buffer cache since :syntax clear drops the regions);
- ftplugin TextChanged/InsertLeave autocmd for spans that arrive via paste/
  undo/external edits;
- colorize() itself calls refresh() right after wrapping, so a freshly
  colorized word conceals instantly (TextChanged doesn't fire reliably mid
  command / in headless ex-mode).

The LSP @vimwikiColor token no longer links to Constant, so in Neovim it
doesn't override the real per-span colour on the concealed text. Pure syntax
+ :highlight — works identically in Vim, coc.nvim, and Neovim, no LSP needed.

Tests: colorize.conceal_hides_tags_shows_text (both harnesses) and
colorize.command_conceals_immediately (vim). Sample wiki's colorize section
notes the conceal. nvim 269, vim 259+18, config-parity green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:01:21 +00:00
gffranco 2facb40265 fix(client): VimwikiColorize accepts a range (no more E481 on selection)
CI / cargo fmt --check (push) Successful in 31s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Successful in 39s
CI / editor keymaps (push) Successful in 1m31s
Selecting text and running :VimwikiColorize {color} raised
"E481: No range allowed" — Vim prepends '<,'> to the command when invoked
from a visual selection, but the command was defined without -range.

All four colorize command defs (Vim + Neovim, Vimwiki* + Nuwiki*) are now
-range and forward the range count to the handler. A ranged (visual)
invocation wraps the '< / '> selection; a bare invocation wraps the cword.
The x-mode <Leader>wc mapping passes the visual flag explicitly.

The Vim-branch colorize() gained selection support (it was cword-only),
matching the Neovim branch, so both clients wrap a visual selection
identically. visual_selection_range() (Lua) no longer gates on
vim.fn.visualmode() — the explicit visual flag now carries that intent, and
the marks check guards unset selections.

Tests: cmd.Colorize_range_wraps_selection (nvim) and
colorize.{command_wraps_cword,visual_wraps_selection,ranged_command_no_error}
(vim). nvim 268, vim 257+18, all green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 15:37:09 +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 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 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 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 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 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 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 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 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 8d11f7daef fix(lsp): send g:nuwiki_wikis to the server so per-wiki roots are known
CI / cargo fmt --check (push) Successful in 34s
CI / cargo clippy (push) Successful in 19s
CI / cargo test (push) Successful in 39s
CI / editor keymaps (push) Successful in 1m46s
The server received only wiki_root (the parent directory) and had no
knowledge of the per-wiki sub-roots configured in g:nuwiki_wikis / the
setup() wikis list. It therefore resolved all links relative to the
parent root, causing every link in a sub-wiki to appear broken.

Vim path (autoload/nuwiki/lsp.vim):
  s:settings() now includes a 'wikis' key when g:nuwiki_wikis is set,
  so the initialization_options and settings payloads carry the full
  per-wiki config (root, diary_rel_path, file_extension, …).

Lua path (lua/nuwiki/lsp.lua):
  Add resolved_opts() which merges vim.g.nuwiki_wikis into
  config.options.wikis when the user configures via VimL rather than
  setup({wikis=…}). init_options(), server_settings(), and the
  pre-0.11 root_dir_for() fallback all use resolved_opts() so the
  server receives the correct per-wiki roots in every code path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:45:04 -03:00
gffranco b3e2a72023 fix(lsp): add coc.nvim dispatch path to all Vim-side LSP calls
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 37s
CI / cargo test (push) Successful in 33s
CI / editor keymaps (push) Successful in 1m18s
Commands were hardwired to vim-lsp (lsp#send_request, :LspDefinition,
:LspReferences, :LspRename). Users running coc.nvim got "vim-lsp not
loaded" on every command despite the plugin correctly detecting coc.

Changes:

autoload/nuwiki/commands.vim
- Add s:has_coc() (checks CocActionAsync exists)
- Add s:coc_wrap() adapter — converts coc's (err, result) callback into
  the vim-lsp notification shape {response:{result:…}} so all existing
  on_notification handlers work unchanged
- s:exec(): try vim-lsp first, fall back to CocActionAsync('runCommand',
  …) with arguments spread as positional params, then error if neither
- Add s:jump_definition() helper — :LspDefinition vs CocActionAsync
- Add nuwiki#commands#backlinks() — :LspReferences vs CocActionAsync
- badd_link(): coc path uses CocAction('getDefinition') to resolve the
  target URI without navigating
- follow_link_or_create(): replace inline :LspDefinition checks with
  s:jump_definition()
- rename_file(): :LspRename vs CocActionAsync('rename')

ftplugin/vimwiki.vim
- VimwikiFollowLink / NuwikiFollowLink: dispatch through
  nuwiki#commands#follow_link_or_create() instead of :LspDefinition
- VimwikiBacklinks / NuwikiBacklinks / VWB: dispatch through
  nuwiki#commands#backlinks() instead of :LspReferences

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:39:27 -03:00
gffranco 2c0cefb5c5 fix(mappings): respect g:nuwiki_wikis in global entry-point helpers
CI / cargo fmt --check (push) Successful in 49s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Successful in 36s
CI / editor keymaps (push) Successful in 1m20s
The global <Leader>ww / diary helpers read g:nuwiki_wiki_root directly,
so they always opened the root-level index.wiki even when the user had
configured per-wiki roots via g:nuwiki_wikis (e.g. personal_wiki.root =
'~/.vimwiki/personal_wiki').

Vim path (autoload): replace the three open_*_path functions with a
shared s:wiki_cfg(n) helper that checks g:nuwiki_wikis[n] first, then
falls back to the scalar g:nuwiki_* vars and built-in defaults.

Lua path (init.lua): replace the separate _wiki_index_path /_diary_path
locals with a unified _wiki_cfg() that checks setup() opts.wikis, then
vim.g.nuwiki_wikis (for users who configure via VimL rather than
setup()), then scalar opts / g: vars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:17:30 -03:00
gffranco ba5c0002ab fix(compat): add vimwiki#vars shim for vimwiki-sync and vim-zettel
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Successful in 25s
CI / editor keymaps (push) Successful in 1m19s
Third-party plugins such as vimwiki-sync and vim-zettel call
vimwiki#vars#get_wikilocal() — a function only provided by the original
vimwiki plugin. Without it, the after/ftplugin loaded by vimwiki-sync
errors on every wiki buffer open:

  E117: Unknown function: vimwiki#vars#get_wikilocal
  E121: Undefined variable: g:zettel_dir

Add autoload/vimwiki/vars.vim with lightweight stubs for the two keys
vimwiki-sync uses ('path', 'is_temporary_wiki'), reading values from
nuwiki's own g:nuwiki_wiki_root / g:nuwiki_file_extension config so
users don't have to duplicate settings. get_global() is stubbed as a
no-op for completeness.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:42:09 -03:00