phase 17: html export commands + extended template vars
HtmlRenderer (nuwiki-core):
- `with_var(k, v)` / `with_vars(map)` add arbitrary `{{key}}`
substitution alongside the existing `{{title}}` / `{{content}}`.
Substitution order: content → title → user vars, so a rendered body
that happens to contain a literal `{{title}}` isn't double-processed.
WikiConfig (nuwiki-lsp/config):
- New `html: HtmlConfig` field with per-wiki export options matching
vimwiki's `g:vimwiki_*` keys: `html_path`, `template_path`,
`template_default`, `template_ext`, `template_date_format`,
`css_name`, `auto_export`, `html_filename_parameterization`,
`exclude_files`. Defaults: `<root>/_html` and `<root>/_templates`.
- `RawWiki` accepts all of the above through `initializationOptions`
(and `workspace/didChangeConfiguration` reload).
New `export` module (pure helpers):
- `output_path_for` — page name → on-disk HTML path. Slugifies only
the final segment when `html_filename_parameterization = true` so
the `diary/` subdir survives.
- `template_for` — primary/fallback path lookup tuple.
- `fallback_template` — minimal in-memory page used when neither
template file exists.
- `format_date` — strftime subset (`%Y`/`%m`/`%d`/`%%`) so we don't
depend on `chrono`/`time`.
- `build_toc_html` — nested `<ul class="toc">` from the doc's
headings, with HTML escaping.
- `compute_root_path` — `../` prefix per directory depth for
stylesheet hrefs in nested pages.
- `is_excluded` — glob matcher supporting `*`, `**`, `?`. Used by
the `allToHtml*` walker against `exclude_files`.
- `render_page_html` — wires the above into HtmlRenderer with
`{{date}}`, `{{root_path}}`, `{{toc}}`, `{{css}}` populated.
- `DEFAULT_CSS` — minimal stylesheet bundled for first-time exports.
New commands:
- `nuwiki.export.currentToHtml` — render the current buffer; honours
`%nohtml` and `%template`; ensures CSS exists.
- `nuwiki.export.allToHtml` — walk the wiki root, skip pages matched
by `exclude_files` and `%nohtml`, and only re-export pages whose
source is newer than the existing HTML (incremental behaviour
matching `:VimwikiAll2HTML`).
- `nuwiki.export.allToHtmlForce` — same but unconditional re-export.
- `nuwiki.export.browse` — alias of currentToHtml that returns the
`file://` URL of the rendered page in the response so the client
can open it.
- `nuwiki.export.rss` — write `<html_path>/rss.xml` listing the
wiki's diary entries newest-first.
- `did_save` handler — runs the auto-export equivalent when the
resolved wiki has `auto_export = true`.
LSP capability:
- `text_document_sync` switched from `Kind(FULL)` to `Options(...)`
so the server can register a `save` notification (otherwise
clients never push `did_save` events).
Tests: 33 new in `phase17_html_export.rs` covering HtmlConfig
defaults + JSON-shape config, the `format_date` subset (including
the `%%` escape and pass-through for unsupported specifiers),
root-path computation, output-path slugification rules, the glob
matcher (single-star, globstar, question-mark, non-match),
template_for primary/fallback selection, render_page_html for the
title/content/date/root_path triad and the override-by-extras flow,
and side-effecting disk paths (write_page creates parents, picks up
on-disk template, RSS is newest-first, ensure_css is idempotent).
Total 357 tests pass; fmt + clippy clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ pub mod config;
|
||||
pub mod diagnostics;
|
||||
pub mod diary;
|
||||
pub mod edits;
|
||||
pub mod export;
|
||||
pub mod index;
|
||||
pub mod nav;
|
||||
pub mod rename;
|
||||
@@ -44,19 +45,21 @@ use tower_lsp::lsp_types::{
|
||||
CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse,
|
||||
DeleteFilesParams, Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams,
|
||||
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
|
||||
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, ExecuteCommandOptions,
|
||||
ExecuteCommandParams, FileOperationFilter, FileOperationPattern, FileOperationPatternKind,
|
||||
FileOperationRegistrationOptions, GotoDefinitionParams, GotoDefinitionResponse, Hover,
|
||||
HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
|
||||
InitializedParams, Location, MarkupContent, MarkupKind, MessageType, OneOf,
|
||||
PositionEncodingKind, ProgressParams, ProgressParamsValue, ProgressToken, Range,
|
||||
ReferenceParams, RenameFilesParams, RenameParams, SemanticTokens, SemanticTokensFullOptions,
|
||||
SemanticTokensOptions, SemanticTokensParams, SemanticTokensRangeParams,
|
||||
SemanticTokensRangeResult, SemanticTokensResult, SemanticTokensServerCapabilities,
|
||||
ServerCapabilities, ServerInfo, SymbolInformation as LspSymbolInformation, SymbolKind,
|
||||
TextDocumentSyncCapability, TextDocumentSyncKind, Url, WorkDoneProgress, WorkDoneProgressBegin,
|
||||
WorkDoneProgressEnd, WorkDoneProgressOptions, WorkspaceEdit,
|
||||
WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities, WorkspaceSymbolParams,
|
||||
DidSaveTextDocumentParams, DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse,
|
||||
ExecuteCommandOptions, ExecuteCommandParams, FileOperationFilter, FileOperationPattern,
|
||||
FileOperationPatternKind, FileOperationRegistrationOptions, GotoDefinitionParams,
|
||||
GotoDefinitionResponse, Hover, HoverContents, HoverParams, HoverProviderCapability,
|
||||
InitializeParams, InitializeResult, InitializedParams, Location, MarkupContent, MarkupKind,
|
||||
MessageType, OneOf, PositionEncodingKind, ProgressParams, ProgressParamsValue, ProgressToken,
|
||||
Range, ReferenceParams, RenameFilesParams, RenameParams, SaveOptions, SemanticTokens,
|
||||
SemanticTokensFullOptions, SemanticTokensOptions, SemanticTokensParams,
|
||||
SemanticTokensRangeParams, SemanticTokensRangeResult, SemanticTokensResult,
|
||||
SemanticTokensServerCapabilities, ServerCapabilities, ServerInfo,
|
||||
SymbolInformation as LspSymbolInformation, SymbolKind, TextDocumentSyncCapability,
|
||||
TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, Url,
|
||||
WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressEnd, WorkDoneProgressOptions,
|
||||
WorkspaceEdit, WorkspaceFileOperationsServerCapabilities, WorkspaceServerCapabilities,
|
||||
WorkspaceSymbolParams,
|
||||
};
|
||||
use tower_lsp::{Client, LanguageServer, LspService, Server};
|
||||
|
||||
@@ -293,8 +296,19 @@ impl LanguageServer for Backend {
|
||||
Ok(InitializeResult {
|
||||
capabilities: ServerCapabilities {
|
||||
position_encoding,
|
||||
text_document_sync: Some(TextDocumentSyncCapability::Kind(
|
||||
TextDocumentSyncKind::FULL,
|
||||
text_document_sync: Some(TextDocumentSyncCapability::Options(
|
||||
TextDocumentSyncOptions {
|
||||
open_close: Some(true),
|
||||
change: Some(TextDocumentSyncKind::FULL),
|
||||
will_save: None,
|
||||
will_save_wait_until: None,
|
||||
// Phase 17 `auto_export` listens on `did_save`. Asking
|
||||
// for `include_text: false` since the document store
|
||||
// already mirrors the live buffer.
|
||||
save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
|
||||
include_text: Some(false),
|
||||
})),
|
||||
},
|
||||
)),
|
||||
document_symbol_provider: Some(OneOf::Left(true)),
|
||||
workspace_symbol_provider: Some(OneOf::Left(true)),
|
||||
@@ -440,6 +454,54 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
}
|
||||
|
||||
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
||||
// Phase 17: when `auto_export` is set on the resolved wiki, run
|
||||
// the equivalent of `nuwiki.export.currentToHtml` after the file
|
||||
// hits disk. Best-effort — failures are logged but do not bubble
|
||||
// to the client. Skips pages with the `%nohtml` directive.
|
||||
let uri = params.text_document.uri.clone();
|
||||
let wiki = match self.wiki_for_uri(&uri) {
|
||||
Some(w) => w,
|
||||
None => return,
|
||||
};
|
||||
if !wiki.config.html.auto_export {
|
||||
return;
|
||||
}
|
||||
let doc = match self.documents.get(&uri) {
|
||||
Some(d) => d,
|
||||
None => return,
|
||||
};
|
||||
if doc.ast.metadata.nohtml {
|
||||
return;
|
||||
}
|
||||
let name = index::page_name_from_uri(&uri, Some(&wiki.config.root));
|
||||
let outcome = commands::export_ops::write_page(&doc.ast, &name, &wiki.config);
|
||||
drop(doc);
|
||||
match outcome {
|
||||
Ok(out) => {
|
||||
let _ = commands::export_ops::ensure_css(&wiki.config);
|
||||
self.client
|
||||
.log_message(
|
||||
MessageType::INFO,
|
||||
format!(
|
||||
"nuwiki: auto-exported {} → {}",
|
||||
name,
|
||||
out.output_path.display()
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
Err(e) => {
|
||||
self.client
|
||||
.log_message(
|
||||
MessageType::ERROR,
|
||||
format!("nuwiki: auto-export of {name} failed — {e}"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn did_close(&self, params: DidCloseTextDocumentParams) {
|
||||
let uri = ¶ms.text_document.uri;
|
||||
self.documents.remove(uri);
|
||||
|
||||
Reference in New Issue
Block a user