Commit Graph

112 Commits

Author SHA1 Message Date
gffranco 21b485c91b Remove stale phase/spec references from code comments
Strip "Phase N", "SPEC §X.Y", and "v1.x" planning labels from comments
across the Rust crates and editor layers now that those tracking
artifacts are gone. Comments only — no logic, strings, or test names
touched.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 15:25:51 -03:00
gffranco 5135840f05 Move SPEC.md to development/ and rewrite as a technical reference
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Successful in 28s
CI / cargo fmt --check (pull_request) Successful in 17s
CI / cargo clippy (pull_request) Successful in 36s
CI / cargo test (pull_request) Successful in 35s
CI / editor keymaps (push) Successful in 1m23s
CI / editor keymaps (pull_request) Successful in 1m35s
The old spec was a 1426-line forward-looking planning doc (phases,
pending decisions, design sketches for work now shipped). Replace it
with a concise 339-line reference describing the implemented system —
architecture, AST, LSP capabilities + executeCommand surface, editor
integration, config schema, keymaps, syntax support, and CI — verified
against the current code. Lives under development/ with the other
developer-only docs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 15:07:59 -03:00
gffranco edab2ec578 Extract shared launcher logic into development/_common.sh
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 28s
CI / cargo test (push) Successful in 31s
CI / cargo fmt --check (pull_request) Successful in 16s
CI / cargo clippy (pull_request) Successful in 29s
CI / cargo test (pull_request) Successful in 46s
CI / editor keymaps (push) Successful in 1m38s
CI / editor keymaps (pull_request) Successful in 1m40s
start-nvim.sh and start-vim.sh now source a common helper for the
paths, log(), ensure_binary(), and the sample-wiki fixture, so the
fixture lives in exactly one place instead of being duplicated across
both launchers. Each launcher keeps only its editor-specific config
generation. start-nvim.sh also gains the NUWIKI_DEV_NO_SEED guard for
parity with start-vim.sh.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:59:06 -03:00
gffranco 1753292814 Replace test-personal-wiki.sh with a generated sample wiki
CI / cargo fmt --check (push) Successful in 25s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 35s
CI / cargo fmt --check (pull_request) Successful in 20s
CI / cargo clippy (pull_request) Successful in 22s
CI / cargo test (pull_request) Successful in 26s
CI / editor keymaps (push) Successful in 1m36s
CI / editor keymaps (pull_request) Successful in 1m23s
Drop the personal-wiki launcher in favor of a self-contained fixture
the dev launchers seed into the cache. write_sample_wiki() lays down
one page per vimwiki feature group (syntax, lists, tables, links,
notes, diary) so every plugin feature has something to exercise
without touching a real wiki.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:50:58 -03:00
gffranco 088cf81bcb Group keymap harnesses under development/tests/
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 34s
CI / cargo test (push) Successful in 35s
CI / cargo fmt --check (pull_request) Successful in 32s
CI / cargo clippy (pull_request) Successful in 28s
CI / cargo test (pull_request) Successful in 33s
CI / editor keymaps (push) Successful in 1m27s
CI / editor keymaps (pull_request) Successful in 1m27s
Move the four keymap-harness files (test-keymaps.sh/.lua and the Vim
counterparts) into a development/tests/ subfolder so they sit apart from
the interactive dev launchers. REPO_ROOT now climbs two levels, and the
internal source paths, CI invocations, and ONBOARDING tree are updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:40:37 -03:00
gffranco f32f54e8a8 Move development scripts into development/, leaving scripts/ plugin-only
CI / cargo fmt --check (push) Successful in 32s
CI / cargo clippy (push) Successful in 25s
CI / cargo test (push) Successful in 33s
CI / cargo fmt --check (pull_request) Successful in 30s
CI / cargo clippy (pull_request) Successful in 49s
CI / cargo test (pull_request) Successful in 43s
CI / editor keymaps (push) Successful in 1m49s
CI / editor keymaps (pull_request) Successful in 1m36s
The root scripts/ folder now holds only download_bin.vim, the one script
the plugin itself invokes (via :NuwikiInstall and the vim-plug/Dein build
hooks). All developer tooling — the start-nvim/start-vim launchers,
test-personal-wiki, the keymap harnesses, and syntax-diag — moves to
development/ alongside the developer docs.

REPO_ROOT resolution is unchanged (development/ sits one level under root,
same as scripts/ did); only the explicit cross-references, the CI harness
invocations, the README launcher paths, and the ONBOARDING structure tree
are updated.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:35:24 -03:00
gffranco dd92e070d0 Move developer docs into development/ to declutter root
CI / cargo fmt --check (push) Successful in 24s
CI / cargo clippy (push) Successful in 18s
CI / cargo test (push) Successful in 46s
CI / cargo fmt --check (pull_request) Successful in 24s
CI / cargo clippy (pull_request) Successful in 23s
CI / cargo test (pull_request) Successful in 29s
CI / editor keymaps (push) Successful in 1m25s
CI / editor keymaps (pull_request) Successful in 1m23s
Relocate ONBOARDING.md and nuwiki-architecture.html out of the repo root
into a dedicated development/ folder. No file references either path, so
the move is self-contained.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:29:23 -03:00
gffranco 9ec1adb3e7 Move dev launcher scripts into scripts/ to declutter root
CI / cargo fmt --check (push) Successful in 37s
CI / cargo clippy (push) Successful in 35s
CI / cargo test (push) Successful in 32s
CI / cargo fmt --check (pull_request) Successful in 29s
CI / cargo clippy (pull_request) Successful in 46s
CI / cargo test (pull_request) Successful in 52s
CI / editor keymaps (push) Successful in 1m36s
CI / editor keymaps (pull_request) Successful in 1m21s
Relocate start-nvim.sh, start-vim.sh and test-personal-wiki.sh from the
repo root into scripts/, alongside the existing test harnesses. Each
script now derives REPO_ROOT one level up, test-personal-wiki.sh execs
the moved start-vim.sh, and the README + usage comments point at the new
paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 14:09:06 -03:00
gffranco 21ac9deb23 Fix glp backward cycle and gl<Space> current-list scope
CI / cargo fmt --check (push) Successful in 16s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 37s
CI / cargo fmt --check (pull_request) Successful in 34s
CI / cargo clippy (pull_request) Successful in 36s
CI / cargo test (pull_request) Successful in 38s
CI / editor keymaps (push) Successful in 1m23s
CI / editor keymaps (pull_request) Successful in 1m31s
Two documented checkbox-list mappings did not match their spec:

- `glp` dispatched the same forward-only cycleCheckbox as `gln`, so it
  could never "cycle the checkbox state backward". Add `ops::cycle_state_back`
  and thread a `reverse` flag through the dispatcher and both clients.

- `gl<Space>` and `gL<Space>` both swept the whole buffer, making them
  identical. `gl<Space>` now passes the cursor position and the server
  scopes deletion to the contiguous list block under the cursor (current
  list, cascading into sublists); `gL<Space>` stays whole-buffer.

Adds Rust unit tests, plus behavioral and full-registration coverage for
the documented mapping surface in the Neovim and Vim keymap harnesses, and
a command-coverage suite mirroring the doc's command groups.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-30 13:56:28 -03:00
gffranco 93725501dc Fix help file: correct vimwiki-compatible form and ensure all commands are in Nuwiki form
CI / cargo fmt --check (push) Successful in 23s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Successful in 42s
CI / editor keymaps (push) Successful in 1m23s
CI / cargo fmt --check (pull_request) Successful in 56s
CI / cargo clippy (pull_request) Successful in 25s
CI / cargo test (pull_request) Successful in 1m13s
CI / editor keymaps (pull_request) Successful in 1m18s
2026-05-30 12:51:01 +00:00
gffranco 8bf20b412d Update help file to show Nuwiki commands for brevity and change all command listings to Nuwiki form
CI / cargo fmt --check (push) Successful in 1m3s
CI / cargo clippy (push) Successful in 49s
CI / cargo test (push) Successful in 46s
CI / editor keymaps (push) Successful in 1m22s
2026-05-30 12:43:47 +00:00
gffranco 7fc5b8e5bf Update help file to show Nuwiki commands for brevity
CI / cargo fmt --check (push) Successful in 18s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 26s
CI / editor keymaps (push) Successful in 1m11s
2026-05-30 12:02:29 +00:00
gffranco 753d9e28ea Update help file to clarify nuwiki and vimwiki command forms
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m21s
2026-05-30 03:35:15 +00:00
gffranco a1a0c44a62 Add onboarding documentation and architecture diagram
CI / cargo fmt --check (push) Successful in 1m5s
CI / cargo clippy (push) Successful in 33s
CI / cargo test (push) Successful in 45s
CI / editor keymaps (push) Successful in 1m21s
2026-05-30 02:43:03 +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
gffranco 859d69e4fe fix(lsp): match wikilink anchors against raw heading text
`[[Page#Heading text]]` parsed the anchor as the literal string after
`#`, but the index stored each heading's anchor as `slugify(title)`.
The two never compared equal, so every TOC-style anchor link
(`[[#1. Topologia atual]]`, etc.) was reported as a broken anchor and
goto-definition couldn't jump to the right heading either.

Slugify the request before comparing — `slugify` is idempotent on
valid-slug input, so the same code path now accepts both
`[[Page#some-heading]]` (slug form) and `[[Page#Some Heading]]`
(raw text). Applied to diagnostics, navigation, and hover preview so
all three agree on what "an anchor matches a heading" means.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:29:30 -03:00
gffranco c66431cde3 chore(scripts): add syntax-diag helper for debugging highlight issues
Drop-in script that dumps the syntax/highlight state of the current
buffer to /tmp/nuwiki-syndiag.log: filetype, b:current_syntax, every
syntax/vimwiki.vim found in &runtimepath (to spot duplicates from
other plugins), the resolved attributes of our highlight groups +
the colorscheme groups they link to, a per-segment synID walk over
the buffer, and the filtered scriptnames list. Useful when a user
reports broken highlighting and we need to know whether the patterns
aren't matching, the colorscheme is the culprit, or another plugin's
syntax/vimwiki.vim is winning the runtimepath race.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:04:03 -03:00
gffranco 04cbe67882 fix(syntax): per-level heading groups so colorschemes differentiate H1-H6
The single `nuwikiHeading` group linked all six levels to `Title`.
Many popular colorschemes (codedark, gruvbox-light, etc.) define
`Title` as bold-only with no foreground colour, so every heading
rendered as bold text in the Normal colour — H1 and H3 were
visually identical.

Split into nuwikiHeading1..6 and link each to a different accent
(Title / Function / Identifier / Type / Constant / PreProc), matching
the per-level Tree-sitter mapping the Neovim semantic-token side
already uses. The patterns are mutually exclusive: each requires
whitespace immediately after the equals run, so `=` won't match `==`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:03:51 -03:00
gffranco cf48242542 fix(syntax): target colorscheme-stable groups for inline markup
Two undefined targets meant bold/italic rendered as plain text:

  - Neovim semantic-token defaults linked @vimwikiBold/Italic to
    markdownBold/Italic, which only exist when the markdown treesitter
    parser is loaded — never in a .wiki buffer. Use @markup.strong /
    @markup.italic / @markup.raw.inline / @markup.link instead; those
    are defined by every nvim 0.9+ colorscheme.
  - The static fallback linked nuwikiBold/Italic to Bold/Italic groups
    that exist in Neovim but not plain Vim, so on Vim the links
    resolved to nothing. Define them with explicit gui=bold/italic
    attributes so they render correctly everywhere.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:02:32 -03:00
gffranco 8f8c2884a9 feat(vim): conceal markup delimiters when conceallevel=2
Set conceallevel=2 and concealcursor=nc on .wiki buffers and add
contained `conceal` matches for the bold/italic/code delimiters and
wikilink brackets/targets. The static-fallback rendering now mirrors
what the Neovim semantic-token path produced: *bold* shows as bold
without the asterisks, [[target|desc]] shows just "desc", etc.

The wikilink region uses an inline `contains=` (no line continuation)
because some Vim builds (notably Homebrew Vim 9.2) raise E10 on the
leading `\\ contains=...` form even though the surrounding region
parses fine.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:01:55 -03:00
gffranco 2aec51274e fix(lsp): resolve wikilinks that include the .wiki file extension
`[[page.wiki]]` should resolve identically to `[[page]]`. The index
keyed by stem, so links with the explicit extension fell through to
the broken-link diagnostic and to-page navigation failed. Strip the
configured extension before every page lookup (resolve, definition,
backlinks, classify_link, classify_outgoing) and stop appending the
extension in synthesise_page_uri when it's already present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 22:00:03 -03:00
gffranco 6ae642dbee fix(config): unwrap {nuwiki:{...}} wrapper in apply_change
CI / cargo fmt --check (push) Failing after 22s
CI / cargo clippy (push) Successful in 21s
CI / cargo test (push) Successful in 29s
CI / editor keymaps (push) Successful in 1m19s
workspace/didChangeConfiguration settings from Neovim arrive as
{ "nuwiki": { "wiki_root": ..., "wikis": [...] } } because
server_settings() in lua/nuwiki/lsp.lua wraps the payload in a
"nuwiki" namespace key.  apply_change() was trying to deserialise that
outer object directly as InitOptions, which always succeeded but produced
all-None fields (unknown keys are ignored by serde), so every settings
refresh was silently dropped.

Unwrap single-key objects whose value is also an object before
deserialising — this handles the Neovim wrapper while keeping the flat
VimL shape working unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:24:51 -03:00
gffranco 02172d9e25 fix(config): accept integer 0/1 for bool fields in RawWiki deserialization
CI / cargo fmt --check (push) Successful in 31s
CI / cargo clippy (push) Successful in 39s
CI / cargo test (push) Successful in 36s
CI / editor keymaps (push) Successful in 1m25s
VimL dicts serialise boolean-like settings such as `auto_export = 1`
as the JSON number `1`, not `true`.  serde's default `Option<bool>`
deserializer rejects that, causing `serde_json::from_value::<InitOptions>`
to return `Err` and `.ok()` to silently discard the entire wikis list.
The server then fell back to `wiki_root` and could not resolve per-wiki
links, marking every cross-wiki link as broken.

Add an `opt_bool_or_int` serde helper that accepts JSON bool, `null`,
or integers (0 = false, non-zero = true), and apply it to `auto_export`,
`html_filename_parameterization`, `listsyms_propagate`, and `auto_toc`
in `RawWiki`.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 23:03:36 -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 b7d2a6c61a fix(install): resolve plugin root at source time to fix NuwikiInstall E484
CI / cargo fmt --check (push) Successful in 21s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 45s
CI / editor keymaps (push) Successful in 1m24s
<sfile> inside a command! body is evaluated at execution time, not at
definition time. When :NuwikiInstall is run interactively there is no
sourcing context, so <sfile> expands to nothing and the path collapses
to ./scripts/download_bin.vim — producing E484.

Fix: capture the path in s:plugin_root immediately while the script is
being sourced, then reference that variable from the command body.

Also hoist :NuwikiInstall above the early `finish` for Neovim so the
command is available in both editors (Neovim routes to
require('nuwiki.install').install(), Vim sources the download script).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 22:21:50 -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
gffranco 9354e1c176 fix(mappings): add global entry-point keymaps and correct <Leader>ww/<Leader>wt targets
CI / cargo fmt --check (push) Successful in 17s
CI / cargo clippy (push) Successful in 31s
CI / cargo test (push) Successful in 53s
CI / editor keymaps (push) Successful in 1m30s
Two bugs prevented keymaps from working after a Dein install:

1. <Leader>ww and <Leader>wt both called diary_today() instead of
   wiki_index() / wiki_tab_index(). Fixed in keymaps.lua (Neovim)
   and ftplugin/vimwiki.vim (Vim).

2. All keymaps were buffer-local (<buffer> / buffer=bufnr), so they
   only activated inside an already-open .wiki file. Users had no way
   to reach the wiki from any other buffer, making the plugin appear
   broken on a fresh Vim start.

   Fix: register the eight <Leader>w* entry-point mappings globally —
   in setup() for Neovim (lua/nuwiki/init.lua) and in plugin/nuwiki.vim
   for plain Vim. To avoid a chicken-and-egg LSP dependency, the global
   mappings open files directly from config (wiki_root, diary_rel_path,
   file_extension); the LSP auto-starts via the FileType autocmd once
   the vimwiki buffer loads. Three new autoload helpers
   (open_wiki_path, open_diary_path, open_diary_index_path) provide the
   LSP-free path for the Vim side.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 21:34:41 -03:00
gffranco a8b89a5303 fix(vim): correct broken symlink, missing commands, and bad VimwikiGoBackLink
CI / cargo fmt --check (push) Successful in 40s
CI / cargo clippy (push) Successful in 49s
CI / cargo test (push) Successful in 30s
CI / editor keymaps (push) Successful in 1m16s
- README: fix plain-Vim install symlink (ln -s target/… → ln -s ../target/…
  so the relative path resolves correctly from inside bin/)
- ftplugin: add missing VimwikiUISelect command on the Vim path
- ftplugin: add missing -count attribute on NuwikiTabIndex (Vim path)
- ftplugin: fix VimwikiGoBackLink (Neovim path) — `normal!` with no
  argument causes E471; use `execute "normal! \<C-o>"` instead

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 20:40:20 -03:00
gffranco 8cff0c2d68 ci(release): add libc6-dev-arm64-cross for aarch64-gnu builds
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 22s
CI / cargo test (push) Successful in 35s
CI / editor keymaps (push) Successful in 2m4s
Release / build x86_64-unknown-linux-gnu (push) Successful in 34s
Release / build aarch64-unknown-linux-musl (push) Successful in 55s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m50s
Release / build x86_64-unknown-linux-musl (push) Successful in 47s
Release / gitea release (push) Successful in 37s
gcc-aarch64-linux-gnu ships the cross compiler and binutils only;
linking glibc binaries also requires the target libc crt/dev files
(Scrt1.o, crti.o), which live in libc6-dev-arm64-cross.

Without it, the aarch64-unknown-linux-gnu build failed with:
  ld: cannot find Scrt1.o: No such file or directory
  ld: cannot find crti.o: No such file or directory

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:27:57 +00:00
gffranco f2fd7d6376 ci(release): pin upload/download-artifact to v3 for Gitea Actions
CI / cargo fmt --check (push) Successful in 22s
CI / cargo clippy (push) Successful in 19s
CI / cargo test (push) Successful in 31s
Release / build x86_64-unknown-linux-gnu (push) Successful in 1m34s
Release / build aarch64-unknown-linux-musl (push) Successful in 55s
Release / build aarch64-unknown-linux-gnu (push) Failing after 57s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m16s
Release / gitea release (push) Has been skipped
CI / editor keymaps (push) Successful in 1m24s
actions/upload-artifact@v4 and download-artifact@v4 require the
GitHub-hosted artifact backend, which Gitea Actions does not
implement (GHESNotSupportedError on v2.0.0+). Downgrade to v3.

v3's download-artifact has no merge-multiple flag and nests each
artifact under artifacts/<artifact-name>/, so the upload loop now
recurses to find the archives.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 22:55:31 +00:00
gffranco 82ff454e0f ci(release): drop cross, use rustup targets + native linkers
CI / cargo fmt --check (push) Successful in 22s
CI / cargo clippy (push) Successful in 23s
CI / cargo test (push) Failing after 16m25s
Release / build x86_64-unknown-linux-gnu (push) Failing after 56s
Release / build aarch64-unknown-linux-musl (push) Failing after 56s
Release / build aarch64-unknown-linux-gnu (push) Failing after 1m43s
Release / build x86_64-unknown-linux-musl (push) Failing after 1m31s
Release / gitea release (push) Has been skipped
CI / editor keymaps (push) Has been cancelled
cross spawns Docker containers per target, but the Gitea Actions
runner doesn't expose /var/run/docker.sock, so every cross-compile
step died with "Cannot connect to the Docker daemon".

Replace with rustup-installed targets plus per-target linker setup:
  - x86_64-gnu: native (no extra deps)
  - aarch64-gnu: gcc-aarch64-linux-gnu from apt
  - x86_64-musl: musl-tools from apt
  - aarch64-musl: rust-lld + link-self-contained (rust-std bundles
    musl libc.a + crt, no apt deps needed)

Verified aarch64-musl path locally: produces a statically linked
ARM aarch64 ELF.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 22:29:36 +00:00
gffranco 3ef70e4b7c test(lsp): update find_section_range test for any-level heading match
CI / cargo fmt --check (push) Successful in 56s
CI / cargo clippy (push) Successful in 1m5s
CI / cargo test (push) Successful in 1m2s
CI / editor keymaps (push) Successful in 2m4s
The prior commit (e849ac9) intentionally removed the h1-only
restriction from find_section_range so VimwikiGenerateTagLinks stays
idempotent under non-h1 tag headings. The accompanying test still
asserted the old h1-only behavior and now fails.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:34:01 +00:00
gffranco ab6b5f6d73 style(lsp): apply rustfmt to commands.rs
CI / cargo fmt --check (push) Successful in 22s
CI / cargo clippy (push) Successful in 24s
CI / cargo test (push) Failing after 31s
CI / editor keymaps (push) Has been skipped
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 19:27:22 +00:00
gffranco e849ac9d1f Fix duplicate tag links for VimwikiGenerateTagLinks by extending section range detection and allowing any heading level
Release / build aarch64-unknown-linux-gnu (push) Failing after 32s
Release / build aarch64-unknown-linux-musl (push) Failing after 16m23s
Release / build x86_64-unknown-linux-gnu (push) Failing after 55s
Release / build x86_64-unknown-linux-musl (push) Failing after 55s
Release / gitea release (push) Has been skipped
CI / cargo fmt --check (push) Failing after 37s
CI / cargo clippy (push) Successful in 26s
CI / cargo test (push) Failing after 33s
CI / editor keymaps (push) Has been skipped
2026-05-14 16:11:29 -03:00
gffranco 8fa85988d8 style(lists): apply rustfmt to checkbox propagation
CI / cargo fmt --check (push) Successful in 18s
CI / cargo clippy (push) Successful in 35s
CI / cargo test (push) Successful in 43s
CI / editor keymaps (push) Successful in 1m16s
CI's cargo fmt --check flagged a few lines from the propagation patch
that exceeded the wrapping threshold. No behavioural change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:24:35 +00:00