95645a2b91b8be8a1da977e87816e712d0d45d59
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e75ad6ca89 |
phase 16: diary path conventions + nav commands
Hand-rolled date primitive (no chrono/time dep) plus the diary
command surface that closes the `:VimwikiMakeDiaryNote` ergonomics gap.
nuwiki-core/src/date.rs:
- `DiaryDate { year, month, day }` with strict `YYYY-MM-DD` parsing
(rejects missing zero-padding, alternate separators, whitespace,
impossible calendar dates including the leap-year exceptions).
- `next_day` / `prev_day` via Howard Hinnant's days-from-civil
algorithm — proleptic Gregorian, walks across month/year/leap-day
boundaries.
- `today_utc()` from `SystemTime::now()`. UTC-only by design: a
local-tz implementation would need a tz database we don't ship.
- `Ord` by epoch days, `Display`/`format` round-trip with `parse`.
WikiConfig (P15 resolution):
- `diary_rel_path: String` (default `"diary"`) — mirrors vimwiki's
`g:vimwiki_diary_rel_path`.
- `diary_index: String` (default `"diary"`) — the stem inside the
diary subdir.
- `diary_dir()`, `diary_index_path()`, `diary_path_for(date)` —
centralise the path math so commands stay declarative.
- `RawWiki` (init-options wire format) accepts both fields, so users
with `g:vimwiki_diary_rel_path = "journal"` migrate cleanly.
WorkspaceIndex:
- New `diary_rel_path: Option<String>` field set by `Wiki::new`.
- `IndexedPage.diary_date: Option<DiaryDate>` — populated in `upsert`
when the URI is under `<root>/<diary_rel_path>/` and the stem
parses. `diary_date_for_uri` is the standalone classifier.
- The classifier *requires* the URI to be inside the diary subdir
when a root is known; with no root it accepts any file whose stem
is a date (kept for the ad-hoc test/scratch case).
nuwiki-lsp/src/diary.rs:
- `uri_for_date` / `index_uri` — bridge from `DiaryDate` and config
to `file://` URIs.
- `list_entries(index) → Vec<DiaryEntry>` — ascending, plus
`list_entries_filtered(year, month)` for calendar hooks.
- `next_entry` / `prev_entry` — strict less-than/greater-than
comparison so navigating from a non-diary page (uses today as
pivot) still moves to the right entry.
- `build_index_body` — newest-first, grouped under `== YYYY ==` /
`=== Month ===` subheadings, emits `- [[diary/YYYY-MM-DD]]`. Matches
`:VimwikiDiaryGenerateLinks`.
- `DiaryEntry` JSON shape: `{ date: "YYYY-MM-DD", uri }`. Manual
serde impl so DiaryDate stays serde-free in nuwiki-core.
Commands (9 new):
- `nuwiki.diary.openToday` / `openYesterday` / `openTomorrow` →
`{ uri, date }`.
- `nuwiki.diary.openIndex` → `{ uri }`.
- `nuwiki.diary.generateIndex` → WorkspaceEdit. Three paths:
live document in `backend.documents` → full-doc replace; on-disk
index → read + full-doc replace; missing → CreateFile + insert.
- `nuwiki.diary.next` / `prev` → pivots on the current page's
`diary_date`, falls back to today.
- `nuwiki.diary.listEntries` → optional `year` / `month` filter.
- `nuwiki.diary.openForDate` → strict `YYYY-MM-DD` arg.
All commands accept an optional `uri` to scope to a particular wiki
when multi-wiki lands; the dispatcher falls back to `default_wiki()`.
Tests: 35 new in `phase16_diary.rs` (date parser exhaustively,
calendar arithmetic across boundaries + leaps, path conventions,
upsert classification including the "date filename outside diary dir"
no-op case, list/filter/nav, index body grouping, serde shape, command
list completeness). Total 309 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
||
|
|
f00b0780b8 |
phase 5: renderer trait + HtmlRenderer
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>
|
||
|
|
8f35c0a80c |
phase 2: syntax plugin interface and registry
Add the Lexer/Parser/SyntaxPlugin traits and the SyntaxRegistry per SPEC.md §6.3. - `Lexer<Token>` and `Parser<Token>` are typed building blocks: each syntax owns its own token alphabet (vimwiki today, markdown later). - `TokenStream<T>` is a `Vec<T>` newtype so the eager-vs-lazy choice (§6.6) can flip later without breaking callers. - `SyntaxPlugin` is type-erased: it exposes id / display_name / file_extensions / parse(text) so the registry can hold heterogeneous plugins behind a single trait object. Implementations chain their Lexer + Parser inside parse(). - `SyntaxPlugin: Send + Sync` so the LSP server can stash plugins in an Arc and share them across handler tasks. - `SyntaxRegistry` stores `Arc<dyn SyntaxPlugin>`, supports lookup by id and by file extension, iteration in registration order. Tests cover TokenStream round-trip, full lex→parse chain through a mock plugin, registry lookup hits and misses, and a compile-time assert_send_sync::<SyntaxRegistry>(). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
dc2a952e67 |
phase 1: core AST, spans, and Visitor
Define every node type in SPEC.md §6.4 — Document, 11 block variants, 15 inline variants (including the 4 link-related ones), plus ListItem / DefinitionItem / TableRow / TableCell, ListSymbol, CheckboxState, Keyword, LinkTarget, LinkKind. Every node carries a `Span` (line + column + byte offset, 0-indexed) per §6.5. Visitor: open-recursion trait with default `visit_*` bodies that delegate to `walk_*` free functions. Override at any granularity; call `walk_*` from your override to keep descending. P4 resolved by defining the trait now so Phase 5 (renderer) and Phase 7 (semantic tokens) can plug in without refactoring traversal. P3 resolved: AST string fields are owned `String`. Cow / Arc<str> can be revisited if the parser grows a zero-copy mode. Tests: visitor smoke test builds a heading + paragraph + list + table by hand and asserts exact visit counts (4 blocks, 12 inlines, 9 text leaves, 1 bold, 1 external link, 1 transclusion). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
cf336ee839 |
phase 0: scaffold workspace, plugin layout, and CI
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> |