Commit Graph

249 Commits

Author SHA1 Message Date
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 a9208db2e4 fix(lua): recognize spaceless headings (==Heading==) in client helpers
CI / editor tests (push) Successful in 44s
The shared heading_level helper still required spaces around the title, so
the client-side regex paths — fold fallback, text objects (ah/ih), and
heading navigation (]] etc.) — skipped spaceless headings, even though the
server (and HTML export) accept them since the #7 fix. Rewrite to match the
lexer: balanced leading/trailing `=` runs (cap 6) around a non-empty title,
spaced or spaceless. Marker-only (`======`), unbalanced (`==a==b`, `==> x`)
and 7+ runs still return 0.

Verified against an 18-case battery + full editor harness (10/10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:28:37 +00:00
gffranco ac1db1af1d refactor(lua): extract shared heading_level/is_table_row; dedup wiki_list in search
CI / editor tests (push) Successful in 48s
Pulls the duplicated heading_level (keymaps/folding/textobjects) and
is_table_row (commands/textobjects) helpers into lua/nuwiki/util.lua. The
copies had drifted — only folding capped level >6 and only commands
nil-guarded is_table_row; the unified versions keep both (cap + nil-safe),
which also aligns the keymap/textobject paths with the server's level cap.
search() now resolves config.wiki_list() once instead of twice.

No behaviour change beyond the >6 cap now applying everywhere. Full editor
harness green (10/10).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:19:21 +00:00
gffranco 101cb1cea9 ci(release): automate plugin releases via workflow_dispatch
CI / editor tests (push) Successful in 41s
Adds a dispatch-triggered Release workflow for the plugin: enter a version
in the Actions UI and it stamps g:nuwiki_version in plugin/nuwiki.vim,
sanity-checks the plugin still sources (+ exposes the stamped version),
commits chore(release): X.Y.Z, pushes the vX.Y.Z tag, and publishes a Gitea
release with notes. No binary build — the plugin ships none; nuwiki-ls is
fetched from nuwiki-rs at install time. Dispatch-only so the self-created
tag doesn't double-fire.

Updates ONBOARDING (tree, CI/CD, "Releasing the plugin") to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 16:26:28 +00:00
gffranco 3859d3b0e7 chore(release): 0.5.0 — first plugin-only release
CI / editor tests (push) Successful in 47s
First tagged release after extracting the Rust server to nuwiki-rs. The
plugin and nuwiki-ls now version independently; 0.5.0 marks the split.
Bumps g:nuwiki_version (surfaced by :NuwikiShowVersion).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.5.0
2026-06-24 16:20:41 +00:00
gffranco 1c299f4ae5 Merge pull request 'Remove Rust code, delegate LSP server to nuwiki-rs' (#8) from rust-removal into main
CI / editor tests (push) Successful in 45s
Reviewed-on: #8
2026-06-24 16:13:48 +00:00
gffranco 4e4f39dbf2 docs(readme): prominent callout that the Rust server lives in nuwiki-rs
CI / editor tests (pull_request) Successful in 44s
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:51:22 +00:00
gffranco 62a3d49649 docs(onboarding): drop stale release.yaml from tree, note manual plugin release
CI / editor tests (pull_request) Successful in 50s
The repo-structure diagram still listed `.gitea/workflows/release.yaml` and
described `ci.yaml` as "Lint, test, fmt check" — both stale after the Rust
split (release.yaml moved to nuwiki-rs; ci.yaml now runs the editor
harnesses). Fix the diagram and add a short "Releasing the plugin" note,
since version stamping is now manual (set-version.sh left with the crates).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 15:42:27 +00:00
gffranco 71b47393af Fix CI: remove Rust-only jobs, replace with editor test suite
CI / editor tests (pull_request) Successful in 41s
- Remove fmt, clippy, cargo-test jobs (no Cargo.toml in this repo)
- Consolidate editor tests into a single  job
- Remove release.yaml (binary build/release is in nuwiki-rs)
- Update ONBOARDING.md repo structure diagram
- Run CI on both Neovim 0.11 and latest
2026-06-24 13:16:10 +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
gitea-actions 0df72e52ea chore(release): 0.4.2
CI / cargo fmt --check (push) Successful in 50s
CI / cargo clippy (push) Successful in 50s
CI / cargo test (push) Successful in 1m7s
CI / editor keymaps (push) Successful in 1m11s
2026-06-24 01:16:07 +00:00
gffranco 4a717bcb31 fix(parser): accept spaceless headings (==Heading==) like vimwiki (#7)
CI / cargo clippy (push) Successful in 34s
CI / cargo fmt --check (push) Successful in 39s
CI / cargo test (push) Successful in 1m4s
CI / editor keymaps (push) Successful in 1m12s
The heading lexer required a space after the opening `=`s, so `==Heading==`
fell through to a literal `<p>==Heading==</p>` instead of an `<h2>`. vimwiki
accepts the spaceless form, so a migrated page whose top heading was
`==TODO==` (or `====Progress====`) rendered with no heading at all.

Drop the space-after-marker requirement; the existing title trim already
handles one optional space per side, so both `== H ==` and `==H==` work.
Marker-only (`======`) and unbalanced (`==a==b`, `==> x`) lines still stay
paragraphs (title-present + matching trailing-`=` checks). Regression tests
added at the lexer and renderer levels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 01:09:46 +00:00
gitea-actions f1494dd2fc chore(release): 0.4.1
CI / cargo clippy (push) Successful in 50s
CI / cargo fmt --check (push) Successful in 55s
CI / cargo test (push) Successful in 1m4s
CI / editor keymaps (push) Successful in 1m40s
2026-06-24 00:10:33 +00:00
gffranco a653903dba fix(html): keyword-in-heading badge + brackets in wikilink descriptions
CI / cargo clippy (push) Successful in 38s
CI / cargo fmt --check (push) Successful in 40s
CI / cargo test (push) Successful in 1m0s
CI / editor keymaps (push) Successful in 1m30s
Two export bugs reported against v0.4.0:

1. A heading whose text is a keyword (`== TODO ==`) rendered as an inline
   `<span class="todo">` badge with an *empty* id, instead of a styled
   header. Root causes: `inline_text` dropped Keyword nodes (so the heading
   id/anchor came out empty, or `My TODO list` → `My  list`), and the
   keyword span was emitted inside the heading. Fix: `inline_text` now
   includes the keyword's literal text (via new `Keyword::label()`), and
   heading content renders keywords as plain text (flatten_keywords) so a
   keyword title looks like a header. Keyword badges still render in body
   text as before.

2. A `]` inside a wikilink description (e.g. `[[page|Task [B-1]]]`) closed
   the link at the first `]]`, truncating the description and leaking a
   stray `]`. The close-scan is now bracket-aware (find_wikilink_close):
   inner `[ ]` pairs are balanced so the link closes at the correct `]]`.

Added regression tests for both.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 23:59:35 +00:00
gffranco e0f806d307 style: rustfmt the issue #6 parity changes
CI / cargo fmt --check (push) Successful in 54s
CI / cargo test (push) Successful in 1m18s
CI / cargo clippy (push) Successful in 1m24s
CI / editor keymaps (push) Successful in 1m42s
`cargo fmt --check` (CI) flagged the hand-formatted destructure in the
lexer test and an over-long line in export.rs. No behavior change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 15:30:04 +00:00
gitea-actions 5dd02e7b9e chore(release): 0.4.0
CI / cargo fmt --check (push) Failing after 39s
CI / cargo clippy (push) Successful in 47s
CI / cargo test (push) Successful in 57s
CI / editor keymaps (push) Successful in 1m48s
2026-06-23 14:10:16 +00:00
gffranco 8fdfa9e256 fix(html): match vimwiki export output for headings, code fences, tags, anchors (#6)
CI / cargo fmt --check (push) Failing after 42s
CI / cargo clippy (push) Successful in 1m6s
CI / cargo test (push) Successful in 1m14s
CI / editor keymaps (push) Successful in 1m31s
Exported HTML diverged from upstream vimwiki's :VimwikiAll2HTML in ways
that break custom templates' CSS/JS and deep-links. Bring the renderer to
parity on the structural differences reported in #6:

1. Headings — restore vimwiki's structure: a wrapping
   `<div id="{hierarchical}">` (parent anchors joined by `-`),
   `class="header"`, and an in-heading `<a href="#{hierarchical}">`
   self-link. The flat `id` stays on the heading element so intra-page
   TOC / `#anchor` links keep resolving. Ancestor path is tracked while
   walking top-level headings.

2. Code fences — emit vimwiki's `<pre {raw-attrs}>` (the verbatim text
   after `{{{`, e.g. `<pre python>`) instead of
   `<pre><code class="language-X">`, so highlighters wired for vimwiki
   output apply. The fence text is now preserved verbatim through the
   lexer/AST (PreformattedNode.attrs).

3. Inline tag ids — drop the `tag-` prefix (`id="wiki"`, not
   `id="tag-wiki"`). Also fixes a real inconsistency: the LSP validated
   `[[Page#wiki]]` as resolvable while the HTML emitted `id="tag-wiki"`,
   so the exported link was broken.

4. Same-page anchors — `[[#Section]]` resolves to `index.html#Section`
   (current page filename + fragment), matching vimwiki, rather than a
   bare `#Section`.

Also ship vimwiki's stock style.css verbatim as the default stylesheet
(was a ~7-line minimal one) so exports look identical out of the box and
the `.header`/`.tag`/`.toc`/`done*` classes are styled.

Centered headings and the `<div class="toc">` Contents wrapper keep their
existing form. Tests updated across core + lsp to the new output; added
coverage for hierarchical ids, raw fence attrs, bare tag ids, and the
same-page anchor filename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 14:04:14 +00:00
gffranco 59494e9a86 ci(release): stamp the version in-pipeline from a dispatch input
CI / cargo fmt --check (push) Successful in 54s
CI / cargo clippy (push) Successful in 58s
CI / cargo test (push) Successful in 1m8s
CI / editor keymaps (push) Successful in 1m44s
Releasing no longer means hand-editing the version across four files. The
Release workflow is now workflow_dispatch with a `version` input:

  Actions → Run workflow → version: 0.4.0

A new `prepare` job validates the semver, runs scripts/set-version.sh to
stamp it into the workspace Cargo.toml, the two internal path-dep pins,
g:nuwiki_version, and the Cargo.lock entries for our crates; gates on
`cargo test --workspace`; then commits "chore(release): X.Y.Z" and pushes
the vX.Y.Z tag. The build matrix and release job run off that freshly
pushed tag (checkout ref = the new tag), so the tagged commit carries the
real version.

scripts/set-version.sh is the single source of truth for where the version
lives — run it locally with the same arg to bump by hand. It patches
Cargo.lock via awk so it needs no cargo/toolchain.

The push:tags trigger is removed (the pipeline now creates the tag itself,
so a tag-push trigger would double-fire).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:06:33 +00:00
gffranco e1d734a434 chore(release): 0.3.0
CI / cargo fmt --check (push) Successful in 43s
CI / cargo clippy (push) Successful in 48s
CI / cargo test (push) Successful in 1m26s
Release / build aarch64-unknown-linux-musl (push) Successful in 1m26s
Release / build x86_64-unknown-linux-gnu (push) Successful in 57s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m5s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m8s
Release / gitea release (push) Successful in 26s
CI / editor keymaps (push) Successful in 1m51s
Bump workspace + internal dep pins + g:nuwiki_version to 0.3.0.

Since 0.2.0:
- feat(config): read g:vimwiki_list on Neovim too (lazy.nvim drop-in)
- feat(config): bridge setup({wikis}) to g:nuwiki_wikis for VimL plugins
- feat(calendar): diary into the current wiki, not always the first
- fix(lsp): resolve links from anywhere in the link, including the title
- fix(install): resolve release asset via Gitea API / direct URL; guard
  download_bin.vim against 'compatible' mode

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:35:09 +00:00
gffranco fcce5621a0 fix(lsp): resolve links from anywhere in the link, including the title
CI / cargo fmt --check (push) Successful in 1m1s
CI / cargo clippy (push) Successful in 1m6s
CI / cargo test (push) Successful in 1m16s
CI / editor keymaps (push) Successful in 1m41s
goto-definition (and references/hover) reported "No locations found" when
the cursor was on the *description* of a piped link — `[[target|Title]]`
or `[desc](url)` — and only worked over the target/location part.

find_in_inlines descended into the link's description and returned its
inner Text node, so the nav handlers saw a Text (not a WikiLink/
ExternalLink) and bailed. A link is an atomic unit for navigation: stop
descending into descriptions so a hit anywhere in the link returns the
link node itself. Adds a navigation regression test for cursor-on-title.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:29:41 +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 0d1a73a3ba feat(config): bridge setup({wikis}) to g:nuwiki_wikis for VimL plugins
CI / cargo fmt --check (push) Failing after 11s
CI / cargo clippy (push) Successful in 32s
CI / cargo test (push) Successful in 57s
CI / editor keymaps (push) Successful in 1m33s
A Lua-only `setup({ wikis = … })` config is invisible to the VimL side:
the buffer helpers in autoload/nuwiki/config.vim and the vimwiki compat
shim (autoload/vimwiki/vars.vim, which third-party plugins like
vimwiki-sync call via vimwiki#vars#get_wikilocal) read g:nuwiki_wikis. So
a Neovim user who migrated from g:vimwiki_list to native setup({wikis})
would break vimwiki-sync (it'd resolve the default ~/vimwiki).

setup() now publishes the resolved wiki list to g:nuwiki_wikis when the
config came from setup() opts (the only case VimL can't already see —
g:nuwiki_wikis / g:vimwiki_list configs are global and visible). Verified
vimwiki#vars#get_wikilocal('path', n) returns the right roots from a
pure-Lua setup() config. test-global-shorthand grows two bridge checks
(17). Goldens + keymap harnesses green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 01:34:24 +00:00
gffranco e6f400b370 feat(config): read g:vimwiki_list on Neovim too (lazy.nvim drop-in)
CI / cargo fmt --check (push) Successful in 2m12s
CI / cargo clippy (push) Successful in 2m18s
CI / cargo test (push) Successful in 2m24s
CI / editor keymaps (push) Successful in 1m27s
The g:vimwiki_* drop-in was Vim-only: the Lua client read only setup()
opts and g:nuwiki_wikis, so swapping the vimwiki plugin for nuwiki under
lazy.nvim left the existing vim.g.vimwiki_list ignored and nuwiki pointed
at the default ~/vimwiki.

Mirror autoload/nuwiki/config.vim on the Lua side: config.lua now owns a
single resolver (M.wikis / M.scalar_globals) that resolves setup() wikis →
g:nuwiki_wikis → g:vimwiki_list (translating path->root, path_html->html_path,
template_*, ext->file_extension, …) and folds the global scalar defaults
(g:vimwiki_* then g:nuwiki_* then explicit setup() values; built-in defaults
excluded). lsp.lua (payload) and wiki_cfg/wiki_list (buffer commands) both
route through it, so the server and <Leader>ww agree on the wikis.

Extends test-global-shorthand (15 checks) with the vimwiki_list drop-in +
buffer-command resolution + native-wins cases. Config-parity goldens and
keymap harnesses green on both clients. README + known-issues updated
(drop-in is now Vim & Neovim).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 01:15:43 +00:00
gffranco 96d53dddf3 fix(install): guard download_bin.vim against 'compatible' mode (dein build)
CI / cargo fmt --check (push) Successful in 33s
CI / cargo clippy (push) Successful in 36s
CI / cargo test (push) Successful in 46s
CI / editor keymaps (push) Successful in 1m49s
The dein/vim-plug build hook runs `vim -e -s -c "source scripts/download_bin.vim"`,
which starts in 'compatible' mode. There, leading-backslash line
continuations aren't recognised, so the multi-line URL string raised
`E10: \ should be followed by /, ? or &`. The install actually succeeded
(binary downloaded), but in silent Ex mode any emitted error makes Vim
exit non-zero — so dein reported a build failure. :NuwikiInstall worked
because a normal session is 'nocompatible'.

Wrap the script in the standard `let s:cpo_save = &cpo | set cpo&vim` …
`let &cpo = s:cpo_save` guard so line continuations parse regardless of
how it's invoked. Verified the exact dein command now exits 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 02:05:56 +00:00
gffranco c144cbbb55 fix(install): simplify to the direct /releases/download/latest/ URL
CI / cargo fmt --check (push) Successful in 1m10s
CI / cargo clippy (push) Successful in 1m13s
CI / cargo test (push) Successful in 1m21s
CI / editor keymaps (push) Successful in 1m42s
Gitea serves the most recent release under the `latest` tag segment, so
`/releases/download/latest/nuwiki-ls-<target>.tar.gz` resolves directly
(HTTP 200) — no API lookup or JSON parsing needed. Drop the
latest_asset_url helpers added in the previous fix; the only real bug was
the URL shape (`/releases/latest/download/` is GitHub-only and 404s on
Gitea). Both install paths now just pick the target and build the URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 01:46:18 +00:00
gffranco 3c1ed48a2f fix(install): resolve release asset via Gitea API (download was always 404)
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 40s
CI / cargo test (push) Successful in 46s
CI / editor keymaps (push) Successful in 1m40s
:NuwikiInstall always fell back to building from source because the
download URL used the GitHub-only shape
`/releases/latest/download/<asset>`, which this Gitea instance serves as
404. The release assets exist and are correctly named; only the URL was
wrong.

Resolve the asset's real download URL through the Gitea API instead: GET
`/api/v1/repos/.../releases/latest`, find the asset matching
`nuwiki-ls-<target>.tar.gz`, and download its `browser_download_url`
(`/releases/download/<tag>/<asset>`, which returns 200). Fixed in both
install paths — lua/nuwiki/install.lua (vim.json.decode) and
scripts/download_bin.vim (json_decode). Verified end to end: resolve →
download → extract → executable binary.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 01:41:21 +00:00
gffranco 34a0607e7a chore(release): bump version to 0.2.0
CI / cargo fmt --check (push) Successful in 48s
CI / cargo clippy (push) Successful in 1m0s
CI / cargo test (push) Successful in 1m28s
Release / build aarch64-unknown-linux-musl (push) Successful in 1m33s
Release / build x86_64-unknown-linux-gnu (push) Successful in 1m0s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m2s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m1s
Release / gitea release (push) Successful in 26s
CI / editor keymaps (push) Successful in 1m45s
Minor feature release over v0.1.0 (124 commits). Highlights:
- Drop-in upstream g:vimwiki_list / g:vimwiki_* config (Vim) + global
  per-wiki defaults shorthand (both clients)
- Programmatic coc.nvim registration (no hand-written coc-settings.json)
- :NuwikiTOC / GenerateLinks / GenerateTagLinks insert at the cursor line;
  scoped :NuwikiGenerateLinks <path> wired up
- HTML export: heading id anchors, div.toc wrapper, section numbering;
  toc_header/level honoured in single-wiki config
- The full P3 config/command/mapping parity batch + R1–R18 review fixes

Bumps the workspace version, the inter-crate path-dep requirements, and
g:nuwiki_version.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 01:29:29 +00:00
gffranco daee0f1902 feat(links): wire up nuwiki.links.generateForPath (scoped GenerateLinks)
CI / cargo fmt --check (push) Successful in 24s
CI / cargo clippy (push) Successful in 30s
CI / cargo test (push) Successful in 40s
CI / editor keymaps (push) Successful in 1m22s
`:VimwikiGenerateLinks <path>` sent nuwiki.links.generateForPath, but the
server had no handler and didn't advertise it — so the scoped form was
silently dropped (only the no-arg form worked). Register and implement it.

The page list is scoped via page_in_scope: a plain `path` is a directory
subtree (the page itself or anything under `<path>/`); a `path` with `*`/`?`
is a glob pattern (upstream's behaviour), matched with export::glob_match
(now pub(crate)). links_generate and the scoped form share
links_generate_scoped, so cursor-line insertion + captions + config all
apply uniformly.

Tests: page_in_scope unit tests (subtree, glob, prefix-sibling, trailing
slash) + COMMANDS advertises both generate forms. 580 passed, clippy
clean, harnesses green. Doc updated with the [path] argument.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 01:23:42 +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 346e8b0de6 dev: NUWIKI_DEV_VIMWIKI mode to dogfood the g:vimwiki_* drop-in
CI / cargo fmt --check (push) Successful in 25s
CI / cargo clippy (push) Successful in 30s
CI / cargo test (push) Successful in 38s
CI / editor keymaps (push) Successful in 1m31s
The start-vim-coc.sh harness runs `vim --clean`, so it never sources a
user's real vimwiki.vim — meaning g:vimwiki_list is never set and the
g:vimwiki_* drop-in translation was impossible to exercise through the
harness (you'd only ever test the native g:nuwiki_* path).

Add NUWIKI_DEV_VIMWIKI=1: instead of g:nuwiki_*, the generated vimrc
configures an upstream g:vimwiki_list (+ g:vimwiki_toc_header_level etc.)
pointing at the scratch wiki, so nuwiki's client-side translation +
programmatic coc registration run on a real vimwiki-shaped config.

    NUWIKI_DEV_VIMWIKI=1 ./development/start-vim-coc.sh

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 00:26:11 +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 a4643bdacb dev: exercise toc_header_level + html numbering in the dev launchers
CI / cargo fmt --check (push) Successful in 36s
CI / cargo clippy (push) Successful in 45s
CI / cargo test (push) Successful in 51s
CI / editor keymaps (push) Successful in 1m47s
The start-* dev harnesses generated a minimal server config (wiki_root,
file_extension, syntax, log_level, diagnostic) with no display settings,
so :NuwikiTOC always produced `= Contents =` (level 1) regardless of code
changes — there was no way to test toc_header_level/html numbering through
the harness. The coc launcher is worst: coc reads coc-settings.json
verbatim, bypassing the Vim client's config translation entirely.

Set toc_header_level=2, html_header_numbering=2, html_header_numbering_sym
in all three launchers:
- start-vim-coc.sh: into the generated coc-settings.json (init + settings).
- start-vim.sh: as g:nuwiki_* globals (folded into the wiki via the global
  shorthand).
- start-nvim.sh: in the setup() table.

Now the dev wiki demonstrates the settings: :NuwikiTOC writes
`== Contents ==` and HTML export numbers headings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 23:50:40 +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 fc0d74bfbe fix(toc): honour toc_header/level in single-wiki config + div.toc on export
CI / cargo fmt --check (push) Successful in 48s
CI / cargo clippy (push) Successful in 54s
CI / cargo test (push) Successful in 1m4s
CI / editor keymaps (push) Successful in 1m38s
Two TOC bugs:

1. :NuwikiTOC ignored toc_header / toc_header_level (always "= Contents ="
   at level 1). In the single-wiki shorthand the server only carried
   file_extension/syntax from the top-level options into the synthesized
   wiki; every other per-wiki key (toc_header, toc_header_level,
   links_header, html_path, auto_export, …) was dropped. Add
   single_wiki_from_value(), which re-parses the top-level object as a full
   RawWiki (injecting root from wiki_root), so all per-wiki keys set at the
   top level flow through. Wired into from_init_params + apply_change.

2. HTML export put class="toc" on the <hN> element, but upstream vimwiki
   (and its stylesheet) wraps the TOC heading in <div class="toc">…</div>.
   render_heading now emits the div wrapper, matching upstream so .toc CSS
   applies. With (1) fixed, detection also works for a custom toc_header.

Tests: single_wiki_shorthand_honours_top_level_per_wiki_keys (config),
toc_header_heading_wrapped_in_div_toc + non_toc_heading_not_wrapped
(renderer). Full suite 572 passed; clippy clean; config-parity goldens
match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 22:56:17 +00:00
gffranco f3d8af5f23 fix: TOC header class, numbering config, and Lua defaults
CI / cargo fmt --check (push) Successful in 31s
CI / cargo clippy (push) Successful in 31s
CI / cargo test (push) Successful in 36s
Release / build x86_64-unknown-linux-gnu (push) Successful in 53s
Release / build aarch64-unknown-linux-musl (push) Successful in 1m6s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m8s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m47s
Release / gitea release (push) Successful in 28s
CI / editor keymaps (push) Successful in 1m39s
- Add class='toc' to TOC heading in HTML export by detecting the
  configured toc_header value in render_heading()
- Add toc_header field to HtmlRenderer and wire it through
  render_page_html()
- Add toc_header, toc_header_level, html_header_numbering, and
  html_header_numbering_sym to Lua config defaults so users can
  discover and set them
2026-06-04 00:39:20 -03:00
gffranco bd729d513d fix: include release notes in Gitea API payload
CI / cargo fmt --check (push) Successful in 29s
CI / cargo clippy (push) Successful in 35s
CI / cargo test (push) Successful in 52s
Release / build x86_64-unknown-linux-gnu (push) Successful in 50s
Release / build x86_64-unknown-linux-musl (push) Successful in 56s
CI / editor keymaps (push) Successful in 1m55s
Release / build aarch64-unknown-linux-gnu (push) Successful in 3m16s
Release / build aarch64-unknown-linux-musl (push) Successful in 4m23s
Release / gitea release (push) Successful in 1m40s
The release notes were generated but never passed to the Gitea API.
Also suppress the expected 404 from the idempotency check.
2026-06-03 22:43:53 -03:00
gffranco 5a936e4f96 fix: add checkout step to release job for git commands
CI / cargo fmt --check (push) Successful in 49s
CI / cargo clippy (push) Successful in 49s
CI / cargo test (push) Successful in 58s
Release / build aarch64-unknown-linux-musl (push) Successful in 1m2s
Release / build x86_64-unknown-linux-gnu (push) Successful in 1m36s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m34s
CI / editor keymaps (push) Successful in 1m44s
Release / build aarch64-unknown-linux-gnu (push) Successful in 2m27s
Release / gitea release (push) Successful in 1m51s
download-artifact@v3 doesn't create a git repo, so git log/git tag
fail in the release notes generation step.
2026-06-03 22:33:08 -03:00
gffranco 32cf4508de fix: use artifact v3 for GHES compatibility
CI / cargo fmt --check (push) Successful in 38s
CI / cargo clippy (push) Successful in 34s
CI / cargo test (push) Successful in 32s
Release / build aarch64-unknown-linux-musl (push) Successful in 3m31s
Release / build x86_64-unknown-linux-gnu (push) Successful in 57s
Release / build aarch64-unknown-linux-gnu (push) Successful in 1m8s
Release / build x86_64-unknown-linux-musl (push) Successful in 1m3s
CI / editor keymaps (push) Successful in 1m22s
Release / gitea release (push) Failing after 14s
Gitea (GHES) does not support upload-artifact@v4+.
Revert to v3 which is supported on GHES.
2026-06-03 22:17:31 -03:00
gffranco 7e757c6a7f release: fix restore-keys pipe syntax for Gitea 2026-06-03 22:17:31 -03:00
gffranco a5a07768d6 ci: fix restore-keys to use pipe syntax (Gitea doesn't support YAML list) 2026-06-03 22:17:31 -03:00
gffranco 08ebdc6f9b ci: remove concurrency key (not supported by Gitea Actions) 2026-06-03 22:17:31 -03:00
gffranco b77f4b1ced ci: improve workflows with stricter checks, caching, and idempotency
CI improvements:
- Add concurrency group to cancel redundant runs on same branch
- Add cargo cache to fmt job (was missing entirely)
- Add timeout-minutes: 5 to fast jobs (fmt, clippy, test) for fail-fast
- Change RUST_BACKTRACE from 'short' to '1' for better CI debuggability
- Pin push trigger to branches: [main] to avoid double-firing on PR merges
- Test minimum supported Neovim (0.11.0) instead of latest patch (0.11.3)

Release improvements:
- Enforce RUSTFLAGS: -D warnings at env level and in matrix rustflags
- Remove redundant checkout in release job (only needs env vars)
- Add release notes generation from git log since previous tag
- Make release creation idempotent (check if release exists before creating)
- Fix release_id not being set when release already exists
2026-06-03 22:17:31 -03:00
gffranco c9d75aeb1f Ci improvements (#5)
CI / cargo fmt --check (push) Successful in 42s
CI / cargo clippy (push) Successful in 34s
CI / cargo test (push) Successful in 38s
CI / editor keymaps (push) Successful in 1m32s
Reviewed-on: #5
Co-authored-by: Gabriel Fróes Franco <gffranco@gmail.com>
Co-committed-by: Gabriel Fróes Franco <gffranco@gmail.com>
2026-06-04 01:06:53 +00:00
gffranco cd4bfffef9 Ci improvements (#4)
CI / cargo fmt --check (push) Successful in 31s
CI / cargo clippy (push) Successful in 49s
CI / cargo test (push) Successful in 32s
CI / editor keymaps (push) Successful in 1m34s
Reviewed-on: #4
2026-06-03 23:52:46 +00:00
gffranco 0de2bd9d68 revert e530b3d623
CI / cargo fmt --check (push) Successful in 18s
CI / cargo clippy (push) Successful in 26s
CI / cargo test (push) Successful in 35s
CI / editor keymaps (push) Successful in 1m24s
revert Ci improvements (#3)

Reviewed-on: #3
2026-06-03 23:36:11 +00:00
gffranco e530b3d623 Ci improvements (#3)
Reviewed-on: #3
2026-06-03 23:35:10 +00:00
gffranco a2ca49a64c docs: fix README.md stale documentation and incorrect mappings
CI / cargo fmt --check (push) Successful in 51s
CI / cargo clippy (push) Successful in 27s
CI / cargo test (push) Successful in 33s
CI / editor keymaps (push) Successful in 1m35s
- Add NuwikiSearch to commands table (was incorrectly marked as Vimwiki-only)
- Add missing Vim globals: g:nuwiki_no_calendar, g:nuwiki_auto_chdir, g:nuwiki_auto_header
- Fix Plain Vim manual install to use cp instead of symlink (aligns with install() helper)
- Add missing <S-CR> insert-mode keymap to Lists keymaps table
- Clean up diary frequency section line wrapping
2026-06-03 19:51:27 -03:00
gffranco 5090ab1be0 docs: retire development/vimwiki-gap.md
CI / editor keymaps (push) Successful in 1m27s
CI / cargo fmt --check (push) Successful in 27s
CI / cargo clippy (push) Successful in 54s
CI / cargo test (push) Successful in 46s
The parity work stream is complete. Forward-looking content (vimwiki
divergences, deferred features, internal deferrals) now lives in the
single source of truth, known-issues.md; the historical per-item
tracking log is preserved in git history. No code or docs reference the
file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:31:29 +00:00