Commit Graph

55 Commits

Author SHA1 Message Date
gffranco f00b0780b8 phase 5: renderer trait + HtmlRenderer
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Successful in 50s
CI / cargo test (push) Successful in 57s
Writer-based `Renderer` trait per SPEC §6.8: object-safe
`fn render(&self, doc, w: &mut dyn Write) -> io::Result<()>` plus
a `render_to_string` convenience. Object safety preserves the option
of `Box<dyn Renderer>` in the LSP layer.

`HtmlRenderer` builder accepts a link-resolution callback (via
`with_link_resolver`) and an optional enclosing template (via
`with_template`). The default resolver mirrors §9: Wiki paths become
`path.html`, interwiki links land in sibling directories
(`../wiki<N>/` / `../wn-<Name>/`), diary entries under `diary/`,
file/local schemes use literal paths, anchor-only collapses to `#`.

Renderer covers every AST node:

- block: heading (centered class), paragraph, hr, blockquote,
  preformatted (`language-<lang>` class), math block (with
  `\begin{env}` when an environment is set), lists (ordered/unordered
  + every checkbox state, plus recursive sublists), definition list
  (dt/dd), table (thead/tbody driven by `is_header`, col/row span as
  CSS class hooks), comment (as HTML comment with `--` neutralised),
  error node (as `.parse-error` span)
- inline: strong/em + bold-italic, `<del>`, code (escaped),
  `<sup>`/`<sub>`, math-inline wrapped in `\(...\)`, keyword spans
  with per-keyword class, color spans, wikilink/external/raw URL
  anchors, transclusion as `<img>` with sorted attrs

HTML escaping is byte-level (`<>&"'`). Template substitution replaces
`{{content}}` first so a body containing the literal `{{title}}`
isn't double-substituted.

Tests (31 new) cover every block + inline construct, every LinkKind
through the default resolver, HTML escaping, custom resolver
override, template with/without title metadata, and an end-to-end
smoke test on a multi-construct document.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 19:12:01 +00:00
gffranco 294963b517 phase 4: vimwiki parser + SyntaxPlugin glue
CI / cargo fmt --check (push) Successful in 19s
CI / cargo clippy (push) Successful in 1m2s
CI / cargo test (push) Successful in 1m5s
Hand-rolled recursive-descent parser over the token stream. Resilient
per SPEC §6.7: never aborts the document — anything the dispatcher
can't claim becomes BlockNode::Error and the cursor advances, and
unterminated wikilinks / transclusions / delimiters fall back to
literal text.

Block coverage: every variant from §6.4 — Heading (with inline
content between markers), Paragraph (with soft-break across single
newlines), HorizontalRule, Blockquote (both `>` and 4-space styles),
Preformatted, MathBlock, List (with checkbox + indent-driven
sublists), DefinitionList, Table (header separator promotes the
preceding row, plus colspan / rowspan cells), Comment (single +
multiline), and page-level placeholders threaded into PageMetadata.

Inline pairing: left-to-right scan with "find next matching delim",
recursing on the slice between. `_*x*_` becomes Italic(Bold(...)),
mirroring SPEC §6.7 precedence. URLs inside `[[ ]]` route to
ExternalLinkNode; everything else to WikiLinkNode with full
LinkTarget classification (Wiki / Interwiki numbered + named /
Diary / File / Local / AnchorOnly / directory / root-relative /
filesystem-absolute).

VimwikiSyntax registers as id="vimwiki", extensions=[".wiki"] and
chains lexer + parser inside SyntaxPlugin::parse.

SPEC §4 updated to record the hand-rolled choice with rationale —
span-bearing tokens fit awkwardly into winnow/nom combinator style;
resilience and recovery are explicit in code instead.

Tests (41 new parser cases) cover every AST construct, inline
precedence including nested bold/italic, every LinkKind, transclusion
alt + attrs, resilience on unterminated links, and end-to-end
SyntaxRegistry dispatch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:58:41 +00:00
gffranco 942dbe2aa8 phase 3: vimwiki lexer
CI / cargo fmt --check (push) Successful in 22s
CI / cargo clippy (push) Successful in 53s
CI / cargo test (push) Successful in 1m2s
Two-pass, hand-rolled lexer per SPEC.md §6.6. Block pass walks the
source line by line; recognised constructs emit structural tokens, and
text-bearing line content is fed to the inline pass. Multi-line fences
(`{{{ }}}`, `{{$ }}$`, `%%+ +%%`) flip a `BlockMode` so subsequent
lines are captured raw until the matching closer.

VimwikiToken covers the full §9 feature checklist:

- headings (1–6, centered), horizontal rule, blockquotes (`>` and
  4-space), all list-marker variants, checkboxes, definition `::`,
  tables (separators, header row, col/row span)
- preformatted fences (with language + key=val attrs), math blocks
  (with `%env%`), single- and multi-line comments, all four
  page placeholders (%title / %nohtml / %template / %date)
- inline: bold/italic/strike/super/sub delimiters, inline code, inline
  math, all six keywords (word-bounded), wikilinks (with description
  separator), transclusions (with attr separator), raw URLs across
  http(s)/ftp/mailto/file schemes — trailing sentence punctuation
  stripped from URL spans

Spans are byte-accurate per SPEC §6.5 (0-indexed line + byte column +
absolute byte offset). The lexer is permissive: every delimiter is
emitted, the parser will pair them and fall back to literal text on
mismatches.

Tests (41 cases) cover one example per construct in §9, plus
word-boundary keyword detection, list-vs-bold disambiguation,
indent-vs-list disambiguation, and a span-correctness check.

README now carries a per-phase status table; SPEC top-line status
advanced to Phase 3 complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 17:20:46 +00:00
gffranco cf336ee839 phase 0: scaffold workspace, plugin layout, and CI
CI / cargo fmt --check (push) Successful in 46s
CI / cargo clippy (push) Successful in 1m33s
CI / cargo test (push) Successful in 53s
Lay down the empty repo skeleton defined in SPEC.md §5 so subsequent
phases have somewhere to land:

- Cargo workspace (resolver v2, edition 2021, MSRV 1.83) with
  nuwiki-core, nuwiki-lsp, nuwiki-ls — dep direction matches §6.2.
- Vim/Neovim plugin directory layout (plugin, lua, autoload, ftdetect,
  ftplugin, syntax, doc, scripts) with header-only stubs.
- .gitea/workflows/ci.yaml running fmt, clippy -D warnings, and tests
  pinned to Rust 1.83.
- README, dual MIT/Apache-2.0 license texts, .gitignore.

Verified: cargo check / fmt --check / clippy / test all clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 16:01:58 +00:00
gffranco cf751383e1 initial setup 2026-05-10 14:53:58 +00:00