Files
nuwiki/crates
gffranco ce3ac8c4f8
CI / cargo fmt --check (push) Successful in 23s
CI / cargo clippy (push) Successful in 1m29s
CI / cargo test (push) Successful in 1m40s
phase 15: link health + TOC/links/orphans
New diagnostics module hosts the `nuwiki.link` source: walks every
`WikiLinkNode` and emits a diagnostic per broken target. Severity is
gated by `cfg.link_severity` (off | hint | warn | error). Wired into
`collect_diagnostics` so didOpen/didChange publish link diagnostics
alongside parse errors.

Classification (`BrokenLinkKind`):
- `Wiki` target missing from the workspace index → MissingPage.
- `Wiki`/`AnchorOnly` target with an anchor that matches neither a
  heading nor a `:tag:` on the resolved page → MissingAnchor.
- `file:` / `local:` target whose resolved path isn't on disk →
  MissingFile. Absolute paths are used as-is; relative paths resolve
  against the source URI's parent directory, falling back to the wiki
  root.
- Interwiki/Diary/Raw/external — never diagnosed.

`classify_link` works off a `&WikiLinkNode`; `classify_outgoing` does
the same job from a cached `IndexedPage.outgoing` entry so the
workspace-wide checker doesn't need to re-parse every page.

New commands:
- `nuwiki.toc.generate` — generate `= Contents =` heading + nested
  list of `[[#anchor|Title]]` entries from current headings. Replaces
  any existing h1 "Contents" + its immediate following list
  case-insensitively; inserts at line 0 otherwise. Skips the TOC's
  own heading so re-generation is idempotent.
- `nuwiki.links.generate` — equivalent of `:VimwikiGenerateLinks`.
  Flat alphabetical list under `= Generated Links =`, excludes the
  current page.
- `nuwiki.workspace.checkLinks` — returns `Vec<BrokenLinkEntry>` with
  `{ uri, range, kind, message }` per broken link across the wiki.
  Open documents use live text for range conversion; closed pages
  fall back to the stored span coords. Accepts an optional `uri` to
  pick a specific wiki; defaults to the first registered one.
- `nuwiki.workspace.findOrphans` — returns `Vec<{ uri, name }>` for
  every indexed page with no incoming links. Sorted alphabetically.

Pure ops (`commands::ops`):
- `build_toc_text(items, heading_name)` — formats the TOC body with
  2-space indent per level, anchors via `index::slugify`.
- `build_links_text(pages, heading_name, exclude)` — formats the flat
  list.
- `find_section_range(ast, heading_name)` — locates an existing h1
  section (heading + immediate following list) by case-insensitive
  title match.
- `toc_edit` / `links_edit` — full edit producers.
- `collect_workspace_broken_links` / `find_orphans`.

Plumbing:
- `collect_diagnostics` gains a `uri: Option<&Url>` parameter so
  link-health can resolve `file:` / `local:` relatives. `ast_diagnostics`
  back-compat wrapper preserved.
- `Backend::default_wiki` is no longer `#[allow(dead_code)]`.

Tests: 31 new in `phase15_link_health.rs` covering severity mapping,
classification on every kind/anchor case, TOC nesting, links exclusion,
section-range case-insensitivity, idempotent re-generation, orphan
detection, and the COMMANDS list. The Phase 11 stub test was rewritten
into a real "no index = no diagnostics" assertion. All 274 tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 20:49:32 +00:00
..