Docs #1
@@ -1,5 +1,5 @@
|
|||||||
" autoload/nuwiki/commands.vim — Vim-side wrappers around the LSP
|
" autoload/nuwiki/commands.vim — Vim-side wrappers around the LSP
|
||||||
" commands the server advertises (Phase 19).
|
" commands the server advertises.
|
||||||
"
|
"
|
||||||
" Lives in autoload/ so the cost is paid only when a `:Vimwiki*` /
|
" Lives in autoload/ so the cost is paid only when a `:Vimwiki*` /
|
||||||
" `:Nuwiki*` command is actually invoked. Dispatches via vim-lsp's
|
" `:Nuwiki*` command is actually invoked. Dispatches via vim-lsp's
|
||||||
@@ -634,9 +634,9 @@ function! nuwiki#commands#rename_file() abort
|
|||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" ===== §13.1 deferred =====
|
" ===== Deferred =====
|
||||||
|
|
||||||
" §13.1 Cluster A — list rewriters.
|
" Cluster A — list rewriters.
|
||||||
|
|
||||||
" `gl<Space>` — remove done items from the current list only. Passing the
|
" `gl<Space>` — remove done items from the current list only. Passing the
|
||||||
" cursor position scopes the server to the contiguous list block under it.
|
" cursor position scopes the server to the contiguous list block under it.
|
||||||
@@ -737,7 +737,7 @@ function! nuwiki#commands#list_toggle_or_add_checkbox() abort
|
|||||||
let l:new = strpart(l:line, 0, l:end) . '[ ] ' . strpart(l:line, l:end)
|
let l:new = strpart(l:line, 0, l:end) . '[ ] ' . strpart(l:line, l:end)
|
||||||
call setline('.', l:new)
|
call setline('.', l:new)
|
||||||
endfunction
|
endfunction
|
||||||
" §13.1 Cluster B — table rewriters.
|
" Cluster B — table rewriters.
|
||||||
|
|
||||||
function! nuwiki#commands#table_insert(...) abort
|
function! nuwiki#commands#table_insert(...) abort
|
||||||
let l:cols = a:0 >= 1 ? str2nr(a:1) : 3
|
let l:cols = a:0 >= 1 ? str2nr(a:1) : 3
|
||||||
@@ -815,7 +815,7 @@ function! nuwiki#commands#colorize(...) abort
|
|||||||
\ . strpart(l:line, l:right - 1)
|
\ . strpart(l:line, l:right - 1)
|
||||||
call setline('.', l:new)
|
call setline('.', l:new)
|
||||||
endfunction
|
endfunction
|
||||||
" §13.1 Cluster C — link helpers.
|
" Cluster C — link helpers.
|
||||||
|
|
||||||
function! nuwiki#commands#paste_link() abort
|
function! nuwiki#commands#paste_link() abort
|
||||||
let l:p = s:cursor_position()
|
let l:p = s:cursor_position()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
" autoload/nuwiki/lsp.vim — Vim (non-Neovim) LSP client wiring.
|
" autoload/nuwiki/lsp.vim — Vim (non-Neovim) LSP client wiring.
|
||||||
"
|
"
|
||||||
" Spec §7.4 preference order:
|
" Preference order:
|
||||||
" 1. vim-lsp (matoto/vim-lsp)
|
" 1. vim-lsp (matoto/vim-lsp)
|
||||||
" 2. coc.nvim
|
" 2. coc.nvim
|
||||||
" 3. Print an error if neither is available.
|
" 3. Print an error if neither is available.
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
//! Block-level AST nodes and their supporting types.
|
//! Block-level AST nodes and their supporting types.
|
||||||
//!
|
|
||||||
//! See SPEC.md §6.4.
|
|
||||||
|
|
||||||
use super::inline::InlineNode;
|
use super::inline::InlineNode;
|
||||||
use super::span::Span;
|
use super::span::Span;
|
||||||
@@ -17,7 +15,7 @@ pub enum BlockNode {
|
|||||||
DefinitionList(DefinitionListNode),
|
DefinitionList(DefinitionListNode),
|
||||||
Table(TableNode),
|
Table(TableNode),
|
||||||
Comment(CommentNode),
|
Comment(CommentNode),
|
||||||
/// Vimwiki tag line — `:tag1:tag2:`. Phase 12 (v1.1). The placement
|
/// Vimwiki tag line — `:tag1:tag2:`. The placement
|
||||||
/// rule (file / heading / standalone) is captured in `TagScope` so
|
/// rule (file / heading / standalone) is captured in `TagScope` so
|
||||||
/// LSP handlers and renderers can treat the three differently.
|
/// LSP handlers and renderers can treat the three differently.
|
||||||
Tag(TagNode),
|
Tag(TagNode),
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Inline AST nodes.
|
//! Inline AST nodes.
|
||||||
//!
|
//!
|
||||||
//! See SPEC.md §6.4. The link-related variants (`WikiLink`, `ExternalLink`,
|
//! The link-related variants (`WikiLink`, `ExternalLink`,
|
||||||
//! `Transclusion`, `RawUrl`) wrap structs defined in `super::link`.
|
//! `Transclusion`, `RawUrl`) wrap structs defined in `super::link`.
|
||||||
|
|
||||||
use super::link::{ExternalLinkNode, RawUrlNode, TransclusionNode, WikiLinkNode};
|
use super::link::{ExternalLinkNode, RawUrlNode, TransclusionNode, WikiLinkNode};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Link-related AST nodes and supporting types.
|
//! Link-related AST nodes and supporting types.
|
||||||
//!
|
//!
|
||||||
//! See SPEC.md §6.4. All nodes here are inline (`InlineNode` variants);
|
//! All nodes here are inline (`InlineNode` variants);
|
||||||
//! they live in their own module because the link model has enough surface
|
//! they live in their own module because the link model has enough surface
|
||||||
//! area (kinds, anchors, interwiki indirection) to warrant separation.
|
//! area (kinds, anchors, interwiki indirection) to warrant separation.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! AST types for nuwiki documents.
|
//! AST types for nuwiki documents.
|
||||||
//!
|
//!
|
||||||
//! Types here are syntax-agnostic — both vimwiki and (eventually) markdown
|
//! Types here are syntax-agnostic — both vimwiki and (eventually) markdown
|
||||||
//! produce the same node shapes. See SPEC.md §6.4.
|
//! produce the same node shapes.
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod inline;
|
pub mod inline;
|
||||||
@@ -28,7 +28,7 @@ pub use visit::{
|
|||||||
walk_table, walk_table_row, Visitor,
|
walk_table, walk_table_row, Visitor,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Root of an AST. See SPEC.md §6.4.
|
/// Root of an AST.
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct DocumentNode {
|
pub struct DocumentNode {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
@@ -37,8 +37,7 @@ pub struct DocumentNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Document-level placeholders parsed from `%title` / `%nohtml` / `%template`
|
/// Document-level placeholders parsed from `%title` / `%nohtml` / `%template`
|
||||||
/// / `%date` directives, plus the v1.1 file-level tag accumulator
|
/// / `%date` directives, plus the file-level tag accumulator.
|
||||||
/// (SPEC §12.3). See SPEC.md §9.
|
|
||||||
///
|
///
|
||||||
/// New fields are added at the bottom; consumers should construct with
|
/// New fields are added at the bottom; consumers should construct with
|
||||||
/// `..Default::default()` to stay forward-compatible.
|
/// `..Default::default()` to stay forward-compatible.
|
||||||
@@ -48,7 +47,7 @@ pub struct PageMetadata {
|
|||||||
pub nohtml: bool,
|
pub nohtml: bool,
|
||||||
pub template: Option<String>,
|
pub template: Option<String>,
|
||||||
pub date: Option<String>,
|
pub date: Option<String>,
|
||||||
/// File-scope tags parsed by the v1.1 tag lexer (Phase 12). Convenience
|
/// File-scope tags parsed by the tag lexer. Convenience
|
||||||
/// projection of the `BlockNode::Tag` nodes whose scope is `File`.
|
/// projection of the `BlockNode::Tag` nodes whose scope is `File`.
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Source positions and spans carried on every AST node.
|
//! Source positions and spans carried on every AST node.
|
||||||
//!
|
//!
|
||||||
//! See SPEC.md §6.5. Positions are 0-indexed; `column` is a byte offset
|
//! Positions are 0-indexed; `column` is a byte offset
|
||||||
//! within a line; `offset` is a byte offset from the start of the document.
|
//! within a line; `offset` is a byte offset from the start of the document.
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
//! Override any method to short-circuit or augment traversal — call the
|
//! Override any method to short-circuit or augment traversal — call the
|
||||||
//! matching `walk_*` from your override to keep descending.
|
//! matching `walk_*` from your override to keep descending.
|
||||||
//!
|
//!
|
||||||
//! Pattern follows `rustc`'s and `syn`'s visitors. See SPEC.md §6.4 + P4.
|
//! Pattern follows `rustc`'s and `syn`'s visitors.
|
||||||
//!
|
//!
|
||||||
//! Read-only for now. A `VisitorMut` flavor can be added when transformations
|
//! Read-only for now. A `VisitorMut` flavor can be added when transformations
|
||||||
//! become relevant (post-Phase 5).
|
//! become relevant.
|
||||||
|
|
||||||
use super::block::{
|
use super::block::{
|
||||||
BlockNode, BlockquoteNode, CommentNode, DefinitionItemNode, DefinitionListNode, ErrorNode,
|
BlockNode, BlockquoteNode, CommentNode, DefinitionItemNode, DefinitionListNode, ErrorNode,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Date primitives for the diary subsystem (Phase 16).
|
//! Date primitives for the diary subsystem.
|
||||||
//!
|
//!
|
||||||
//! v1.1 deliberately doesn't pull in `chrono` or `time`. The diary feature
|
//! This crate deliberately doesn't pull in `chrono` or `time`. The diary feature
|
||||||
//! only needs `YYYY-MM-DD` parsing/formatting and ±1 day arithmetic, which
|
//! only needs `YYYY-MM-DD` parsing/formatting and ±1 day arithmetic, which
|
||||||
//! is small enough that the dependency cost outweighs the convenience.
|
//! is small enough that the dependency cost outweighs the convenience.
|
||||||
//!
|
//!
|
||||||
@@ -56,7 +56,7 @@ impl DiaryDate {
|
|||||||
format!("{:04}-{:02}-{:02}", self.year, self.month, self.day)
|
format!("{:04}-{:02}-{:02}", self.year, self.month, self.day)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UTC "today" computed from `SystemTime::now()`. Phase 16's diary
|
/// UTC "today" computed from `SystemTime::now()`. The diary
|
||||||
/// commands intentionally use UTC — local-time semantics depend on a
|
/// commands intentionally use UTC — local-time semantics depend on a
|
||||||
/// timezone DB we don't ship and would surprise users crossing DST
|
/// timezone DB we don't ship and would surprise users crossing DST
|
||||||
/// boundaries.
|
/// boundaries.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Core parser, AST, and renderer for nuwiki.
|
//! Core parser, AST, and renderer for nuwiki.
|
||||||
//!
|
//!
|
||||||
//! This crate is editor-independent and must never depend on `nuwiki-lsp` or
|
//! This crate is editor-independent and must never depend on `nuwiki-lsp` or
|
||||||
//! `nuwiki-ls`. See SPEC.md §6.2 for the dependency rules.
|
//! `nuwiki-ls`.
|
||||||
|
|
||||||
pub mod ast;
|
pub mod ast;
|
||||||
pub mod date;
|
pub mod date;
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
//! Walks a `DocumentNode` and writes HTML5 fragments. The renderer is
|
//! Walks a `DocumentNode` and writes HTML5 fragments. The renderer is
|
||||||
//! configured with a link resolver (callback that turns a `LinkTarget` into
|
//! configured with a link resolver (callback that turns a `LinkTarget` into
|
||||||
//! a URL string) and, optionally, an enclosing template with substitution
|
//! a URL string) and, optionally, an enclosing template with substitution
|
||||||
//! placeholders (SPEC.md §6.8 + §12.8).
|
//! placeholders.
|
||||||
//!
|
//!
|
||||||
//! Substitution model: `{{content}}` and `{{title}}` are computed from
|
//! Substitution model: `{{content}}` and `{{title}}` are computed from
|
||||||
//! the rendered body and the document's metadata; `with_var(k, v)` /
|
//! the rendered body and the document's metadata; `with_var(k, v)` /
|
||||||
//! `with_vars(map)` add arbitrary key→value pairs (Phase 17 ships
|
//! `with_vars(map)` add arbitrary key→value pairs (`{{date}}`,
|
||||||
//! `{{date}}`, `{{root_path}}`, `{{toc}}`). Order: `{{content}}` is
|
//! `{{root_path}}`, `{{toc}}`). Order: `{{content}}` is
|
||||||
//! substituted first so a body that happens to contain a literal
|
//! substituted first so a body that happens to contain a literal
|
||||||
//! `{{title}}` isn't itself rewritten in the next pass.
|
//! `{{title}}` isn't itself rewritten in the next pass.
|
||||||
//!
|
//!
|
||||||
@@ -44,7 +44,7 @@ pub struct HtmlRenderer {
|
|||||||
/// `color_dic`-style override: vimwiki colour-tag names → CSS
|
/// `color_dic`-style override: vimwiki colour-tag names → CSS
|
||||||
/// values (`"red"` / `"#ffe119"` / `"oklch(…)"`). Unspecified
|
/// values (`"red"` / `"#ffe119"` / `"oklch(…)"`). Unspecified
|
||||||
/// names fall through to the default `class="color-<name>"`
|
/// names fall through to the default `class="color-<name>"`
|
||||||
/// rendering. Matches SPEC §12.11 `color_dic`.
|
/// rendering. Matches vimwiki's `color_dic`.
|
||||||
colors: HashMap<String, String>,
|
colors: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,7 +662,7 @@ fn table_spans(table: &TableNode) -> Vec<Vec<CellLayout>> {
|
|||||||
|
|
||||||
// ===== Default link resolver =====
|
// ===== Default link resolver =====
|
||||||
|
|
||||||
/// Default `LinkResolver`. Mirrors the conventions in SPEC.md §9:
|
/// Default `LinkResolver`. Mirrors the vimwiki link conventions:
|
||||||
/// wiki pages become `path.html`; interwiki links land in sibling
|
/// wiki pages become `path.html`; interwiki links land in sibling
|
||||||
/// directories; diary entries land under `diary/`; file/local schemes
|
/// directories; diary entries land under `diary/`; file/local schemes
|
||||||
/// use their literal path; anchor-only links collapse to a fragment.
|
/// use their literal path; anchor-only links collapse to a fragment.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Document renderers.
|
//! Document renderers.
|
||||||
//!
|
//!
|
||||||
//! SPEC.md §6.8: a `Renderer` is writer-based, takes a `DocumentNode`, and
|
//! A `Renderer` is writer-based, takes a `DocumentNode`, and
|
||||||
//! emits its representation into any `Write`. Errors propagate through
|
//! emits its representation into any `Write`. Errors propagate through
|
||||||
//! `io::Result` — there's no separate per-renderer error type so the trait
|
//! `io::Result` — there's no separate per-renderer error type so the trait
|
||||||
//! stays object-safe (`Box<dyn Renderer>` is useful for the LSP later).
|
//! stays object-safe (`Box<dyn Renderer>` is useful for the LSP later).
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
//! a `Lexer` produces a `TokenStream`, a `Parser` consumes it and produces a
|
//! a `Lexer` produces a `TokenStream`, a `Parser` consumes it and produces a
|
||||||
//! `DocumentNode`. A `SyntaxPlugin` is the type-erased facade that the LSP
|
//! `DocumentNode`. A `SyntaxPlugin` is the type-erased facade that the LSP
|
||||||
//! layer holds in a `SyntaxRegistry`.
|
//! layer holds in a `SyntaxRegistry`.
|
||||||
//!
|
|
||||||
//! See SPEC.md §6.3 (interface) and §6.6 (lexer strategy).
|
|
||||||
|
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
pub mod vimwiki;
|
pub mod vimwiki;
|
||||||
@@ -15,7 +13,7 @@ pub use registry::SyntaxRegistry;
|
|||||||
use crate::ast::DocumentNode;
|
use crate::ast::DocumentNode;
|
||||||
|
|
||||||
/// Eager token buffer produced by a `Lexer`. The newtype keeps the door
|
/// Eager token buffer produced by a `Lexer`. The newtype keeps the door
|
||||||
/// open to a lazy/streaming variant later (SPEC.md §6.6) without breaking
|
/// open to a lazy/streaming variant later without breaking
|
||||||
/// callers.
|
/// callers.
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
pub struct TokenStream<T> {
|
pub struct TokenStream<T> {
|
||||||
@@ -89,7 +87,7 @@ pub trait Lexer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parser half of a syntax plugin. Consumes a `TokenStream` and produces a
|
/// Parser half of a syntax plugin. Consumes a `TokenStream` and produces a
|
||||||
/// `DocumentNode`. Per SPEC.md §6.7 parsing is resilient — malformed input
|
/// `DocumentNode`. Parsing is resilient — malformed input
|
||||||
/// becomes `BlockNode::Error(ErrorNode)`, never a hard failure.
|
/// becomes `BlockNode::Error(ErrorNode)`, never a hard failure.
|
||||||
pub trait Parser {
|
pub trait Parser {
|
||||||
type Token;
|
type Token;
|
||||||
@@ -104,13 +102,13 @@ pub trait Parser {
|
|||||||
/// hold heterogeneous plugins behind a single trait object.
|
/// hold heterogeneous plugins behind a single trait object.
|
||||||
///
|
///
|
||||||
/// `Send + Sync` is required — the LSP server stores plugins behind an `Arc`
|
/// `Send + Sync` is required — the LSP server stores plugins behind an `Arc`
|
||||||
/// and shares them across request handler tasks (SPEC.md §6.3).
|
/// and shares them across request handler tasks.
|
||||||
pub trait SyntaxPlugin: Send + Sync {
|
pub trait SyntaxPlugin: Send + Sync {
|
||||||
fn id(&self) -> &str;
|
fn id(&self) -> &str;
|
||||||
fn display_name(&self) -> &str;
|
fn display_name(&self) -> &str;
|
||||||
|
|
||||||
/// File extensions associated with this syntax. Each entry includes the
|
/// File extensions associated with this syntax. Each entry includes the
|
||||||
/// leading dot, e.g. `[".wiki"]`. See SPEC.md §6.3.
|
/// leading dot, e.g. `[".wiki"]`.
|
||||||
fn file_extensions(&self) -> &[&str];
|
fn file_extensions(&self) -> &[&str];
|
||||||
|
|
||||||
fn parse(&self, text: &str) -> DocumentNode;
|
fn parse(&self, text: &str) -> DocumentNode;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Registry of syntax plugins. See SPEC.md §6.3.
|
//! Registry of syntax plugins.
|
||||||
//!
|
//!
|
||||||
//! Plugins are stored behind `Arc<dyn SyntaxPlugin>` so the LSP layer can
|
//! Plugins are stored behind `Arc<dyn SyntaxPlugin>` so the LSP layer can
|
||||||
//! hand a cheap clone to per-document tasks without re-locking the registry.
|
//! hand a cheap clone to per-document tasks without re-locking the registry.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Vimwiki lexer.
|
//! Vimwiki lexer.
|
||||||
//!
|
//!
|
||||||
//! Hand-rolled, two-pass per SPEC.md §6.6:
|
//! Hand-rolled, two-pass:
|
||||||
//!
|
//!
|
||||||
//! - **Block pass:** scan the source line by line, recognise structural
|
//! - **Block pass:** scan the source line by line, recognise structural
|
||||||
//! constructs (headings, lists, tables, fences, comments, etc.), emit
|
//! constructs (headings, lists, tables, fences, comments, etc.), emit
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
//! Both passes share one `Vec<VimwikiToken>` so the parser sees a single,
|
//! Both passes share one `Vec<VimwikiToken>` so the parser sees a single,
|
||||||
//! ordered stream. The lexer is permissive: every delimiter is emitted; the
|
//! ordered stream. The lexer is permissive: every delimiter is emitted; the
|
||||||
//! parser pairs them and falls back to literal text on mismatches. Spans are
|
//! parser pairs them and falls back to literal text on mismatches. Spans are
|
||||||
//! stored as 0-indexed byte offsets per SPEC.md §6.5.
|
//! stored as 0-indexed byte offsets.
|
||||||
//!
|
//!
|
||||||
//! Multi-line constructs (`{{{ }}}`, `{{$ }}$`, `%%+ +%%`) flip a
|
//! Multi-line constructs (`{{{ }}}`, `{{$ }}$`, `%%+ +%%`) flip a
|
||||||
//! `BlockMode` so subsequent lines are treated as raw content until the
|
//! `BlockMode` so subsequent lines are treated as raw content until the
|
||||||
@@ -97,13 +97,13 @@ pub enum VimwikiTokenKind {
|
|||||||
CommentMultiClose,
|
CommentMultiClose,
|
||||||
CommentMultiLine(String),
|
CommentMultiLine(String),
|
||||||
|
|
||||||
// ----- Tags (SPEC §12.3) -----
|
// ----- Tags -----
|
||||||
/// `:tag1:tag2:` — colon-delimited tag line. The lexer treats this as
|
/// `:tag1:tag2:` — colon-delimited tag line. The lexer treats this as
|
||||||
/// block-level (must be the entire trimmed content of the line).
|
/// block-level (must be the entire trimmed content of the line).
|
||||||
/// Scope (file / heading / standalone) is the parser's job.
|
/// Scope (file / heading / standalone) is the parser's job.
|
||||||
Tag(Vec<String>),
|
Tag(Vec<String>),
|
||||||
|
|
||||||
// ----- Page placeholders (SPEC §9) -----
|
// ----- Page placeholders -----
|
||||||
PlaceholderTitle(Option<String>),
|
PlaceholderTitle(Option<String>),
|
||||||
PlaceholderNohtml,
|
PlaceholderNohtml,
|
||||||
PlaceholderTemplate(Option<String>),
|
PlaceholderTemplate(Option<String>),
|
||||||
@@ -129,7 +129,7 @@ pub enum VimwikiTokenKind {
|
|||||||
TransclusionSep,
|
TransclusionSep,
|
||||||
RawUrl(String),
|
RawUrl(String),
|
||||||
|
|
||||||
/// Lex error — never aborts the whole document (SPEC §6.7).
|
/// Lex error — never aborts the whole document.
|
||||||
Error(String),
|
Error(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +481,7 @@ impl<'src> LexState<'src> {
|
|||||||
if trimmed.len() < 4 || !trimmed.bytes().all(|b| b == b'-') {
|
if trimmed.len() < 4 || !trimmed.bytes().all(|b| b == b'-') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// SPEC §9: four or more dashes. Allow surrounding whitespace.
|
// Four or more dashes. Allow surrounding whitespace.
|
||||||
let span = Span::new(self.line_start_pos(), self.line_end_pos(line));
|
let span = Span::new(self.line_start_pos(), self.line_end_pos(line));
|
||||||
self.push(VimwikiTokenKind::HorizontalRule, span);
|
self.push(VimwikiTokenKind::HorizontalRule, span);
|
||||||
self.emit_newline(line);
|
self.emit_newline(line);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use crate::ast::DocumentNode;
|
|||||||
use crate::syntax::{Lexer, Parser, SyntaxPlugin};
|
use crate::syntax::{Lexer, Parser, SyntaxPlugin};
|
||||||
|
|
||||||
/// SyntaxPlugin facade: chains `VimwikiLexer` + `VimwikiParser` so the
|
/// SyntaxPlugin facade: chains `VimwikiLexer` + `VimwikiParser` so the
|
||||||
/// registry can hand out a single trait object per SPEC.md §6.3.
|
/// registry can hand out a single trait object.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct VimwikiSyntax;
|
pub struct VimwikiSyntax;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Vimwiki parser.
|
//! Vimwiki parser.
|
||||||
//!
|
//!
|
||||||
//! Hand-rolled recursive-descent over `Vec<VimwikiToken>` (SPEC.md §6.7).
|
//! Hand-rolled recursive-descent over `Vec<VimwikiToken>`.
|
||||||
//! Resilient: never aborts the document. Anything the dispatcher can't
|
//! Resilient: never aborts the document. Anything the dispatcher can't
|
||||||
//! claim becomes a `BlockNode::Error(ErrorNode)` so progress is
|
//! claim becomes a `BlockNode::Error(ErrorNode)` so progress is
|
||||||
//! guaranteed.
|
//! guaranteed.
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
//!
|
//!
|
||||||
//! `[[ ]]` payloads are inspected to choose between `WikiLinkNode`
|
//! `[[ ]]` payloads are inspected to choose between `WikiLinkNode`
|
||||||
//! (the default) and `ExternalLinkNode` (when the target is a URL). The
|
//! (the default) and `ExternalLinkNode` (when the target is a URL). The
|
||||||
//! `LinkTarget` enum follows SPEC §9: leading `/` is root-relative,
|
//! `LinkTarget` enum follows the vimwiki conventions: leading `/` is root-relative,
|
||||||
//! `//` is filesystem-absolute, `wiki<N>:` / `wn.<Name>:` are interwiki,
|
//! `//` is filesystem-absolute, `wiki<N>:` / `wn.<Name>:` are interwiki,
|
||||||
//! `diary:` / `file:` / `local:` are their own kinds, and a trailing `/`
|
//! `diary:` / `file:` / `local:` are their own kinds, and a trailing `/`
|
||||||
//! marks a directory link.
|
//! marks a directory link.
|
||||||
@@ -69,10 +69,10 @@ impl Parser for VimwikiParser {
|
|||||||
struct ParseState<'a> {
|
struct ParseState<'a> {
|
||||||
toks: &'a [VimwikiToken],
|
toks: &'a [VimwikiToken],
|
||||||
pos: usize,
|
pos: usize,
|
||||||
/// Phase 12: count of `HeadingNode`s already pushed to `children`.
|
/// Count of `HeadingNode`s already pushed to `children`.
|
||||||
/// `headings_seen - 1` is the most recent heading's index.
|
/// `headings_seen - 1` is the most recent heading's index.
|
||||||
headings_seen: usize,
|
headings_seen: usize,
|
||||||
/// Phase 12: source line of the most recently-emitted heading.
|
/// Source line of the most recently-emitted heading.
|
||||||
/// `tag_line - last_heading_line ∈ {1, 2}` is a header-scope tag.
|
/// `tag_line - last_heading_line ∈ {1, 2}` is a header-scope tag.
|
||||||
last_heading_line: Option<u32>,
|
last_heading_line: Option<u32>,
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ impl<'a> ParseState<'a> {
|
|||||||
}
|
}
|
||||||
let before = self.pos;
|
let before = self.pos;
|
||||||
let block = self.parse_block();
|
let block = self.parse_block();
|
||||||
// Phase 12: file-scope tags get accumulated into metadata so
|
// File-scope tags get accumulated into metadata so
|
||||||
// callers can read page-level tag membership without walking
|
// callers can read page-level tag membership without walking
|
||||||
// the AST. The TagNode itself stays in `children` so renderers
|
// the AST. The TagNode itself stays in `children` so renderers
|
||||||
// see it too.
|
// see it too.
|
||||||
@@ -280,7 +280,7 @@ impl<'a> ParseState<'a> {
|
|||||||
let inline = parse_inline_seq(&self.toks[inline_start..self.pos]);
|
let inline = parse_inline_seq(&self.toks[inline_start..self.pos]);
|
||||||
self.advance();
|
self.advance();
|
||||||
self.eat_newline();
|
self.eat_newline();
|
||||||
// Record for tag-scope resolution (Phase 12).
|
// Record for tag-scope resolution.
|
||||||
self.headings_seen += 1;
|
self.headings_seen += 1;
|
||||||
self.last_heading_line = Some(span_end.line);
|
self.last_heading_line = Some(span_end.line);
|
||||||
return BlockNode::Heading(HeadingNode {
|
return BlockNode::Heading(HeadingNode {
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ fn registry_can_parse_through_trait_object() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Compile-time check: SyntaxRegistry must be Send + Sync so the LSP server
|
/// Compile-time check: SyntaxRegistry must be Send + Sync so the LSP server
|
||||||
/// can share it across handler tasks (SPEC.md §6.3).
|
/// can share it across handler tasks.
|
||||||
#[test]
|
#[test]
|
||||||
fn registry_is_send_and_sync() {
|
fn registry_is_send_and_sync() {
|
||||||
fn assert_send_sync<T: Send + Sync>() {}
|
fn assert_send_sync<T: Send + Sync>() {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Phase 12 tag tests: lexer + parser + renderer behaviour on
|
//! Tag tests: lexer + parser + renderer behaviour on
|
||||||
//! `:tag:` lines, plus the scope-detection rules.
|
//! `:tag:` lines, plus the scope-detection rules.
|
||||||
|
|
||||||
use nuwiki_core::ast::{BlockNode, TagScope};
|
use nuwiki_core::ast::{BlockNode, TagScope};
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
//! `workspace/executeCommand` dispatcher.
|
//! `workspace/executeCommand` dispatcher.
|
||||||
//!
|
//!
|
||||||
//! Phase 13 introduced the router + `nuwiki.file.delete`. Phase 14 added
|
//! The router handles `nuwiki.file.delete`, the cheap surgical edits —
|
||||||
//! the cheap surgical edits — checkbox toggle/cycle/reject, heading
|
//! checkbox toggle/cycle/reject, heading promote/demote, "next task"
|
||||||
//! promote/demote, "next task" navigation. Phase 15 layers on the
|
//! navigation — and the generation + workspace-query commands:
|
||||||
//! generation + workspace-query commands: `toc.generate`,
|
//! `toc.generate`, `links.generate`, `workspace.checkLinks`,
|
||||||
//! `links.generate`, `workspace.checkLinks`, `workspace.findOrphans`.
|
//! `workspace.findOrphans`.
|
||||||
//!
|
//!
|
||||||
//! Every command resolves to either a `WorkspaceEdit` (the server then
|
//! Every command resolves to either a `WorkspaceEdit` (the server then
|
||||||
//! calls `apply_edit`) or a JSON `Value` (returned to the client) — see
|
//! calls `apply_edit`) or a JSON `Value` (returned to the client) — see
|
||||||
@@ -663,7 +663,7 @@ fn tags_rebuild(backend: &Backend, args: Vec<Value>) -> Result<Option<Value>, St
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Phase 18: multi-wiki picker commands =====
|
// ===== multi-wiki picker commands =====
|
||||||
|
|
||||||
fn wiki_list_all(backend: &Backend) -> Result<Option<Value>, String> {
|
fn wiki_list_all(backend: &Backend) -> Result<Option<Value>, String> {
|
||||||
let wikis = backend.wikis_snapshot();
|
let wikis = backend.wikis_snapshot();
|
||||||
@@ -821,7 +821,7 @@ fn wiki_goto_page(backend: &Backend, args: Vec<Value>) -> Result<Option<Value>,
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== §13.1 Cluster A: list rewriters =====
|
// ===== Cluster A: list rewriters =====
|
||||||
|
|
||||||
fn list_remove_done(backend: &Backend, args: Vec<Value>) -> Result<Option<WorkspaceEdit>, String> {
|
fn list_remove_done(backend: &Backend, args: Vec<Value>) -> Result<Option<WorkspaceEdit>, String> {
|
||||||
let p = parse_optional_uri_arg(args.clone())?;
|
let p = parse_optional_uri_arg(args.clone())?;
|
||||||
@@ -954,7 +954,7 @@ fn list_change_level(backend: &Backend, args: Vec<Value>) -> Result<Option<Works
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== §13.1 Cluster B: table rewriters =====
|
// ===== Cluster B: table rewriters =====
|
||||||
|
|
||||||
fn table_insert(_backend: &Backend, args: Vec<Value>) -> Result<Option<WorkspaceEdit>, String> {
|
fn table_insert(_backend: &Backend, args: Vec<Value>) -> Result<Option<WorkspaceEdit>, String> {
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -1030,7 +1030,7 @@ fn table_move_column(backend: &Backend, args: Vec<Value>) -> Result<Option<Works
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Phase 17: HTML export commands =====
|
// ===== HTML export commands =====
|
||||||
|
|
||||||
fn export_current(
|
fn export_current(
|
||||||
backend: &Backend,
|
backend: &Backend,
|
||||||
@@ -1671,7 +1671,7 @@ pub mod ops {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Phase 15: TOC + Links generation, workspace queries =====
|
// ===== TOC + Links generation, workspace queries =====
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
@@ -1997,7 +1997,7 @@ pub mod ops {
|
|||||||
/// on the diagnostics module's internals.
|
/// on the diagnostics module's internals.
|
||||||
pub use crate::diagnostics::collect_wiki_links;
|
pub use crate::diagnostics::collect_wiki_links;
|
||||||
|
|
||||||
// ===== §13.1 Cluster A: list rewriters =====
|
// ===== Cluster A: list rewriters =====
|
||||||
|
|
||||||
use crate::edits::text_edit_delete;
|
use crate::edits::text_edit_delete;
|
||||||
use nuwiki_core::ast::ListSymbol;
|
use nuwiki_core::ast::ListSymbol;
|
||||||
@@ -2544,7 +2544,7 @@ pub mod ops {
|
|||||||
Some((span, marker_str, i))
|
Some((span, marker_str, i))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== §13.1 Cluster B: table rewriters =====
|
// ===== Cluster B: table rewriters =====
|
||||||
|
|
||||||
use nuwiki_core::ast::{TableNode, TableRowNode};
|
use nuwiki_core::ast::{TableNode, TableRowNode};
|
||||||
|
|
||||||
@@ -2802,7 +2802,7 @@ pub mod ops {
|
|||||||
Some(out)
|
Some(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Phase 16: diary helpers =====
|
// ===== diary helpers =====
|
||||||
|
|
||||||
use crate::edits::op_create;
|
use crate::edits::op_create;
|
||||||
|
|
||||||
@@ -2856,10 +2856,10 @@ pub mod ops {
|
|||||||
b.build()
|
b.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== Tag ops (Phase 12 commands) =====
|
// ===== Tag ops =====
|
||||||
//
|
//
|
||||||
// SPEC §12.3 calls for three tag-driven commands beyond the indexing
|
// Three tag-driven commands beyond the indexing itself.
|
||||||
// that Phase 12 already shipped. `tag_hits` powers `nuwiki.tags.search`;
|
// `tag_hits` powers `nuwiki.tags.search`;
|
||||||
// `tag_links_edit` powers `nuwiki.tags.generateLinks`; the rebuild
|
// `tag_links_edit` powers `nuwiki.tags.generateLinks`; the rebuild
|
||||||
// command is a direct re-walk of the wiki root in the dispatcher.
|
// command is a direct re-walk of the wiki root in the dispatcher.
|
||||||
|
|
||||||
@@ -3048,7 +3048,7 @@ pub mod ops {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disk-touching helpers for the Phase 17 `nuwiki.export.*` commands.
|
/// Disk-touching helpers for the `nuwiki.export.*` commands.
|
||||||
/// Kept out of `ops` because everything here is side-effecting — pure
|
/// Kept out of `ops` because everything here is side-effecting — pure
|
||||||
/// rendering / templating lives in [`crate::export`].
|
/// rendering / templating lives in [`crate::export`].
|
||||||
pub mod export_ops {
|
pub mod export_ops {
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
//! Server-side runtime config.
|
//! Server-side runtime config.
|
||||||
//!
|
//!
|
||||||
//! Populated from `InitializeParams.initialization_options` at boot and
|
//! Populated from `InitializeParams.initialization_options` at boot and
|
||||||
//! refreshed via `workspace/didChangeConfiguration` (P18). v1.0 single-wiki
|
//! refreshed via `workspace/didChangeConfiguration`. Legacy single-wiki
|
||||||
//! shapes are accepted and desugared into the v1.1 `wikis = [...]` form so
|
//! shapes are accepted and desugared into the `wikis = [...]` form so
|
||||||
//! existing setups keep working (P16).
|
//! existing setups keep working.
|
||||||
//!
|
//!
|
||||||
//! Only the fields actually consumed in Phase 11 land here. Phases 13–19
|
//! `WikiConfig` (diary path, html path, template options, …) and the
|
||||||
//! extend `WikiConfig` (diary path, html path, template options, …) and
|
//! top-level `Config` (diagnostic severity, mappings opt-in, …) carry the
|
||||||
//! the top-level `Config` (diagnostic severity, mappings opt-in, …) as
|
//! fields actually consumed by the server.
|
||||||
//! they need them.
|
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@@ -39,9 +38,8 @@ pub struct WikiConfig {
|
|||||||
/// The full path is `<root>/<diary_rel_path>/<diary_index>.wiki`.
|
/// The full path is `<root>/<diary_rel_path>/<diary_index>.wiki`.
|
||||||
pub diary_index: String,
|
pub diary_index: String,
|
||||||
/// `daily`/`weekly`/`monthly`/`yearly`. Picks the date format the
|
/// `daily`/`weekly`/`monthly`/`yearly`. Picks the date format the
|
||||||
/// diary commands use. v1.1 ships `daily`; the others fall through
|
/// diary commands use. `daily` is supported; the others fall through
|
||||||
/// to vimwiki-style names but the commands are no-ops until
|
/// to vimwiki-style names but the commands are no-ops for them.
|
||||||
/// Cluster 4 lands.
|
|
||||||
pub diary_frequency: String,
|
pub diary_frequency: String,
|
||||||
/// First day of the week (`monday`..`sunday`). Used by weekly
|
/// First day of the week (`monday`..`sunday`). Used by weekly
|
||||||
/// diary entry naming.
|
/// diary entry naming.
|
||||||
@@ -74,7 +72,7 @@ pub struct WikiConfig {
|
|||||||
pub nested_syntaxes: std::collections::HashMap<String, String>,
|
pub nested_syntaxes: std::collections::HashMap<String, String>,
|
||||||
/// Rebuild the page's TOC on save when set.
|
/// Rebuild the page's TOC on save when set.
|
||||||
pub auto_toc: bool,
|
pub auto_toc: bool,
|
||||||
/// Phase 17 HTML export options. See SPEC §12.8.
|
/// HTML export options.
|
||||||
pub html: HtmlConfig,
|
pub html: HtmlConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,7 +102,7 @@ pub struct HtmlConfig {
|
|||||||
/// to skip during `allToHtml*`.
|
/// to skip during `allToHtml*`.
|
||||||
pub exclude_files: Vec<String>,
|
pub exclude_files: Vec<String>,
|
||||||
/// `color_dic`: vimwiki colour-tag name → CSS value mapping used
|
/// `color_dic`: vimwiki colour-tag name → CSS value mapping used
|
||||||
/// by the HTML renderer (SPEC §12.11). Empty by default; the
|
/// by the HTML renderer. Empty by default; the
|
||||||
/// renderer falls back to `class="color-<name>"` when a name
|
/// renderer falls back to `class="color-<name>"` when a name
|
||||||
/// isn't in the dict.
|
/// isn't in the dict.
|
||||||
pub color_dic: std::collections::HashMap<String, String>,
|
pub color_dic: std::collections::HashMap<String, String>,
|
||||||
@@ -130,7 +128,7 @@ impl Default for HtmlConfig {
|
|||||||
impl HtmlConfig {
|
impl HtmlConfig {
|
||||||
/// Synthesise default paths under `root` when the user hasn't
|
/// Synthesise default paths under `root` when the user hasn't
|
||||||
/// specified explicit ones. `html_path` defaults to `<root>/_html`
|
/// specified explicit ones. `html_path` defaults to `<root>/_html`
|
||||||
/// and `template_path` to `<root>/_templates` per SPEC §12.8.
|
/// and `template_path` to `<root>/_templates`.
|
||||||
pub fn from_root(root: &Path) -> Self {
|
pub fn from_root(root: &Path) -> Self {
|
||||||
Self {
|
Self {
|
||||||
html_path: root.join("_html"),
|
html_path: root.join("_html"),
|
||||||
@@ -284,7 +282,7 @@ fn default_links_space_char() -> String {
|
|||||||
" ".to_string()
|
" ".to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fill in the v1.1 per-wiki keys that none of the constructors care
|
/// Fill in the per-wiki keys that none of the constructors care
|
||||||
/// about. Centralising the defaults here keeps `empty()`, `from_root()`,
|
/// about. Centralising the defaults here keeps `empty()`, `from_root()`,
|
||||||
/// the legacy single-wiki path, and `From<RawWiki>` in sync.
|
/// the legacy single-wiki path, and `From<RawWiki>` in sync.
|
||||||
fn wiki_defaults() -> WikiDefaults {
|
fn wiki_defaults() -> WikiDefaults {
|
||||||
@@ -329,9 +327,9 @@ impl Config {
|
|||||||
/// Build a `Config` from `InitializeParams`.
|
/// Build a `Config` from `InitializeParams`.
|
||||||
///
|
///
|
||||||
/// Priority for the wikis list:
|
/// Priority for the wikis list:
|
||||||
/// 1. `initialization_options.wikis = [...]` (v1.1 shape)
|
/// 1. `initialization_options.wikis = [...]` (multi-wiki shape)
|
||||||
/// 2. `initialization_options.wiki_root + file_extension + syntax`
|
/// 2. `initialization_options.wiki_root + file_extension + syntax`
|
||||||
/// (v1.0 single-wiki shape, P16)
|
/// (legacy single-wiki shape)
|
||||||
/// 3. `workspace_folders[0]` if present
|
/// 3. `workspace_folders[0]` if present
|
||||||
/// 4. deprecated `root_uri` if present
|
/// 4. deprecated `root_uri` if present
|
||||||
/// 5. empty list (server stays alive but won't index anything)
|
/// 5. empty list (server stays alive but won't index anything)
|
||||||
@@ -445,7 +443,7 @@ mod opt_bool_or_int {
|
|||||||
#[serde(default, rename_all = "snake_case")]
|
#[serde(default, rename_all = "snake_case")]
|
||||||
struct InitOptions {
|
struct InitOptions {
|
||||||
wikis: Option<Vec<RawWiki>>,
|
wikis: Option<Vec<RawWiki>>,
|
||||||
// v1.0 single-wiki compat fields
|
// legacy single-wiki compat fields
|
||||||
wiki_root: Option<String>,
|
wiki_root: Option<String>,
|
||||||
file_extension: Option<String>,
|
file_extension: Option<String>,
|
||||||
syntax: Option<String>,
|
syntax: Option<String>,
|
||||||
@@ -466,7 +464,7 @@ struct RawWiki {
|
|||||||
diary_rel_path: Option<String>,
|
diary_rel_path: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
diary_index: Option<String>,
|
diary_index: Option<String>,
|
||||||
// Phase 17 HTML keys — all optional, fall back to per-root defaults.
|
// HTML keys — all optional, fall back to per-root defaults.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
html_path: Option<String>,
|
html_path: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -487,7 +485,7 @@ struct RawWiki {
|
|||||||
exclude_files: Option<Vec<String>>,
|
exclude_files: Option<Vec<String>>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
color_dic: Option<std::collections::HashMap<String, String>>,
|
color_dic: Option<std::collections::HashMap<String, String>>,
|
||||||
// v1.1 per-wiki keys mirroring vimwiki globals. All optional so
|
// per-wiki keys mirroring vimwiki globals. All optional so
|
||||||
// existing single-wiki configs migrate without touching anything.
|
// existing single-wiki configs migrate without touching anything.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
index: Option<String>,
|
index: Option<String>,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Diagnostic sources beyond parse-time `ErrorNode`s.
|
//! Diagnostic sources beyond parse-time `ErrorNode`s.
|
||||||
//!
|
//!
|
||||||
//! Phase 15 lands the `nuwiki.link` source — broken-link warnings. Walks
|
//! The `nuwiki.link` source emits broken-link warnings. Walks
|
||||||
//! every `WikiLinkNode` in the AST and emits one diagnostic per:
|
//! every `WikiLinkNode` in the AST and emits one diagnostic per:
|
||||||
//! - `Wiki` target that doesn't resolve to a page in the workspace index,
|
//! - `Wiki` target that doesn't resolve to a page in the workspace index,
|
||||||
//! - `Wiki`/`AnchorOnly` target with an anchor that matches neither a
|
//! - `Wiki`/`AnchorOnly` target with an anchor that matches neither a
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
//! - `File`/`Local` target whose resolved path doesn't exist on disk.
|
//! - `File`/`Local` target whose resolved path doesn't exist on disk.
|
||||||
//!
|
//!
|
||||||
//! Interwiki, Diary, Raw, and external links are not diagnosed —
|
//! Interwiki, Diary, Raw, and external links are not diagnosed —
|
||||||
//! interwiki resolution is Phase 18's job, diary is Phase 16's, and we
|
//! interwiki resolution and diary handling live elsewhere, and we
|
||||||
//! don't fetch URLs.
|
//! don't fetch URLs.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -332,8 +332,7 @@ fn resolve_file_path(
|
|||||||
index.root.as_ref().map(|r| r.join(&candidate))
|
index.root.as_ref().map(|r| r.join(&candidate))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `nuwiki.link` diagnostics for a single document. Phase 11 left this as
|
/// `nuwiki.link` diagnostics for a single document.
|
||||||
/// a stub; Phase 15 fills it in.
|
|
||||||
pub fn link_health(
|
pub fn link_health(
|
||||||
ast: &DocumentNode,
|
ast: &DocumentNode,
|
||||||
text: &str,
|
text: &str,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Diary subsystem (Phase 16) — pure helpers used by the
|
//! Diary subsystem — pure helpers used by the
|
||||||
//! `nuwiki.diary.*` `executeCommand` handlers.
|
//! `nuwiki.diary.*` `executeCommand` handlers.
|
||||||
//!
|
//!
|
||||||
//! All path/URI math lives here so the command dispatcher stays a thin
|
//! All path/URI math lives here so the command dispatcher stays a thin
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Helpers for building `WorkspaceEdit`s.
|
//! Helpers for building `WorkspaceEdit`s.
|
||||||
//!
|
//!
|
||||||
//! Phase 13+ (rename, list/table edits, link/TOC generation) all return
|
//! Rename, list/table edits, and link/TOC generation all return
|
||||||
//! `WorkspaceEdit`. The builder centralises the byte-offset → LSP-position
|
//! `WorkspaceEdit`. The builder centralises the byte-offset → LSP-position
|
||||||
//! conversion (UTF-8 or UTF-16, matching whatever was negotiated in
|
//! conversion (UTF-8 or UTF-16, matching whatever was negotiated in
|
||||||
//! `initialize`) so each call site doesn't have to redo it.
|
//! `initialize`) so each call site doesn't have to redo it.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! HTML export — pure helpers shared by the Phase 17 `nuwiki.export.*`
|
//! HTML export — pure helpers shared by the `nuwiki.export.*`
|
||||||
//! command handlers. Side-effecting work (reading the template from
|
//! command handlers. Side-effecting work (reading the template from
|
||||||
//! disk, writing the rendered HTML, copying CSS) lives in `commands.rs`
|
//! disk, writing the rendered HTML, copying CSS) lives in `commands.rs`
|
||||||
//! since the dispatcher is the one with a `&Backend` and async context.
|
//! since the dispatcher is the one with a `&Backend` and async context.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! `textDocument/foldingRange` provider — SPEC §12.10 / P14.
|
//! `textDocument/foldingRange` provider.
|
||||||
//!
|
//!
|
||||||
//! Strategy: one fold per heading (line → line before the next heading
|
//! Strategy: one fold per heading (line → line before the next heading
|
||||||
//! of same-or-higher level) plus one fold per top-level list block.
|
//! of same-or-higher level) plus one fold per top-level list block.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Workspace index — the in-memory model of every `.wiki` page nuwiki
|
//! Workspace index — the in-memory model of every `.wiki` page nuwiki
|
||||||
//! has seen, used to power Phase 8 nav features.
|
//! has seen, used to power nav features.
|
||||||
//!
|
//!
|
||||||
//! Per P8 (SPEC §11), the initial scan runs as a background tokio task so
|
//! The initial scan runs as a background tokio task so
|
||||||
//! the server stays responsive on startup. Per-document updates land here
|
//! the server stays responsive on startup. Per-document updates land here
|
||||||
//! synchronously via `upsert` whenever the LSP backend re-parses on
|
//! synchronously via `upsert` whenever the LSP backend re-parses on
|
||||||
//! `didOpen` / `didChange`.
|
//! `didOpen` / `didChange`.
|
||||||
@@ -29,10 +29,10 @@ pub struct IndexedPage {
|
|||||||
pub title: Option<String>,
|
pub title: Option<String>,
|
||||||
pub headings: Vec<HeadingInfo>,
|
pub headings: Vec<HeadingInfo>,
|
||||||
pub outgoing: Vec<OutgoingLink>,
|
pub outgoing: Vec<OutgoingLink>,
|
||||||
/// Phase 12: every `TagNode` on the page. `tags_by_name` on the
|
/// Every `TagNode` on the page. `tags_by_name` on the
|
||||||
/// containing `WorkspaceIndex` is the reverse map.
|
/// containing `WorkspaceIndex` is the reverse map.
|
||||||
pub tags: Vec<TagInfo>,
|
pub tags: Vec<TagInfo>,
|
||||||
/// Phase 16: `Some(date)` when this page is a *daily* diary entry —
|
/// `Some(date)` when this page is a *daily* diary entry —
|
||||||
/// stem parses as `YYYY-MM-DD`. Equivalent to
|
/// stem parses as `YYYY-MM-DD`. Equivalent to
|
||||||
/// `diary_period.and_then(|p| if let DiaryPeriod::Day(d) = p { Some(d) } else { None })`.
|
/// `diary_period.and_then(|p| if let DiaryPeriod::Day(d) = p { Some(d) } else { None })`.
|
||||||
/// Kept as its own field for back-compat with prev/next/list callers
|
/// Kept as its own field for back-compat with prev/next/list callers
|
||||||
@@ -97,7 +97,7 @@ pub struct Backlink {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A tag's location, used by `WorkspaceIndex::tags_for` for cross-page
|
/// A tag's location, used by `WorkspaceIndex::tags_for` for cross-page
|
||||||
/// `[[Page#tag]]` resolution and `nuwiki.tags.search` (Phase 13).
|
/// `[[Page#tag]]` resolution and `nuwiki.tags.search`.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TagOccurrence {
|
pub struct TagOccurrence {
|
||||||
pub uri: Url,
|
pub uri: Url,
|
||||||
@@ -108,7 +108,7 @@ pub struct TagOccurrence {
|
|||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct WorkspaceIndex {
|
pub struct WorkspaceIndex {
|
||||||
pub root: Option<PathBuf>,
|
pub root: Option<PathBuf>,
|
||||||
/// Phase 16: subdir relative to `root` that holds diary entries.
|
/// Subdir relative to `root` that holds diary entries.
|
||||||
/// Mirrors `WikiConfig::diary_rel_path`; stored here so `upsert` can
|
/// Mirrors `WikiConfig::diary_rel_path`; stored here so `upsert` can
|
||||||
/// classify each indexed URI without a back-reference to the config.
|
/// classify each indexed URI without a back-reference to the config.
|
||||||
pub diary_rel_path: Option<String>,
|
pub diary_rel_path: Option<String>,
|
||||||
@@ -119,9 +119,9 @@ pub struct WorkspaceIndex {
|
|||||||
pub pages_by_uri: HashMap<Url, IndexedPage>,
|
pub pages_by_uri: HashMap<Url, IndexedPage>,
|
||||||
pub pages_by_name: HashMap<String, Url>,
|
pub pages_by_name: HashMap<String, Url>,
|
||||||
pub backlinks: HashMap<String, Vec<Backlink>>,
|
pub backlinks: HashMap<String, Vec<Backlink>>,
|
||||||
/// Phase 12: tag name → every occurrence across the workspace. Used by
|
/// Tag name → every occurrence across the workspace. Used by
|
||||||
/// the v1.1 nav handlers (tag-as-anchor `definition`, `workspace/symbol`
|
/// the nav handlers (tag-as-anchor `definition`, `workspace/symbol`
|
||||||
/// inclusion) and the Phase 13 `nuwiki.tags.search` command.
|
/// inclusion) and the `nuwiki.tags.search` command.
|
||||||
pub tags_by_name: HashMap<String, Vec<TagOccurrence>>,
|
pub tags_by_name: HashMap<String, Vec<TagOccurrence>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ impl WorkspaceIndex {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 12: maintain the reverse tag map alongside the per-page list.
|
// Maintain the reverse tag map alongside the per-page list.
|
||||||
for tag in &page.tags {
|
for tag in &page.tags {
|
||||||
self.tags_by_name
|
self.tags_by_name
|
||||||
.entry(tag.name.clone())
|
.entry(tag.name.clone())
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
//! LSP protocol bridge for nuwiki.
|
//! LSP protocol bridge for nuwiki.
|
||||||
//!
|
//!
|
||||||
//! `tower-lsp` server that maintains a `DocumentStore` (SPEC §6.10) plus a
|
//! `tower-lsp` server that maintains a `DocumentStore` plus a
|
||||||
//! workspace-wide index (SPEC §6.10 + P8) and exposes every LSP method
|
//! workspace-wide index and exposes every LSP method:
|
||||||
//! through Phase 8:
|
|
||||||
//!
|
//!
|
||||||
//! - `initialize` / `initialized` / `shutdown`
|
//! - `initialize` / `initialized` / `shutdown`
|
||||||
//! - `textDocument/didOpen`, `didChange` (full sync), `didClose`
|
//! - `textDocument/didOpen`, `didChange` (full sync), `didClose`
|
||||||
@@ -14,12 +13,12 @@
|
|||||||
//! - `workspace/symbol` — search across indexed headings
|
//! - `workspace/symbol` — search across indexed headings
|
||||||
//!
|
//!
|
||||||
//! Position encoding is negotiated as UTF-8 when the client supports LSP
|
//! Position encoding is negotiated as UTF-8 when the client supports LSP
|
||||||
//! 3.17+ encodings (SPEC §6.11). When the client only supports the legacy
|
//! 3.17+ encodings. When the client only supports the legacy
|
||||||
//! UTF-16 default, positions get translated through a per-line lookup.
|
//! UTF-16 default, positions get translated through a per-line lookup.
|
||||||
//!
|
//!
|
||||||
//! Re-parses are full per change (incremental parsing deferred post-v1).
|
//! Re-parses are full per change (no incremental parsing).
|
||||||
//! The workspace scan runs in a background tokio task spawned from
|
//! The workspace scan runs in a background tokio task spawned from
|
||||||
//! `initialize` (P8) with `window/workDoneProgress` begin/end events.
|
//! `initialize` with `window/workDoneProgress` begin/end events.
|
||||||
|
|
||||||
pub mod commands;
|
pub mod commands;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
@@ -111,8 +110,8 @@ pub async fn run_stdio() -> io::Result<()> {
|
|||||||
struct DocumentState {
|
struct DocumentState {
|
||||||
text: String,
|
text: String,
|
||||||
ast: DocumentNode,
|
ast: DocumentNode,
|
||||||
/// Last version we observed from the client. Held per SPEC §6.10 even
|
/// Last version we observed from the client. Held even
|
||||||
/// though the foundation phase doesn't yet need to read it back.
|
/// though nothing reads it back yet.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
version: i32,
|
version: i32,
|
||||||
}
|
}
|
||||||
@@ -124,8 +123,8 @@ struct Backend {
|
|||||||
/// True after `initialize` if the client opted into UTF-8 encoding.
|
/// True after `initialize` if the client opted into UTF-8 encoding.
|
||||||
/// Otherwise we keep the LSP 3.16 default of UTF-16.
|
/// Otherwise we keep the LSP 3.16 default of UTF-16.
|
||||||
use_utf8: Arc<AtomicBool>,
|
use_utf8: Arc<AtomicBool>,
|
||||||
/// Per-wiki state (P11). Vector holds one entry until Phase 18
|
/// Per-wiki state. Vector holds one entry until multi-wiki
|
||||||
/// (multi-wiki) lets users add more.
|
/// support lets users add more.
|
||||||
wikis: Arc<RwLock<Vec<Wiki>>>,
|
wikis: Arc<RwLock<Vec<Wiki>>>,
|
||||||
/// Server config received via `initializationOptions` and refreshed
|
/// Server config received via `initializationOptions` and refreshed
|
||||||
/// via `workspace/didChangeConfiguration` (P18).
|
/// via `workspace/didChangeConfiguration` (P18).
|
||||||
@@ -151,7 +150,7 @@ impl Backend {
|
|||||||
wikis.iter().find(|w| w.id == id).cloned()
|
wikis.iter().find(|w| w.id == id).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Used by Phase 15 workspace-level commands (`checkLinks`, `findOrphans`)
|
/// Used by workspace-level commands (`checkLinks`, `findOrphans`)
|
||||||
/// when no URI is supplied — they fall back to the first registered wiki.
|
/// when no URI is supplied — they fall back to the first registered wiki.
|
||||||
fn default_wiki(&self) -> Option<Wiki> {
|
fn default_wiki(&self) -> Option<Wiki> {
|
||||||
let wikis = self.wikis.read().ok()?;
|
let wikis = self.wikis.read().ok()?;
|
||||||
@@ -290,7 +289,7 @@ impl Backend {
|
|||||||
|
|
||||||
/// Return live state if `uri` is open in the editor, otherwise read
|
/// Return live state if `uri` is open in the editor, otherwise read
|
||||||
/// the file from disk and re-parse. The closed-doc path is used by
|
/// the file from disk and re-parse. The closed-doc path is used by
|
||||||
/// cross-document operations (Phase 13 `workspace/rename` and link
|
/// cross-document operations (`workspace/rename` and link
|
||||||
/// health) that need accurate spans + text without trusting cached
|
/// health) that need accurate spans + text without trusting cached
|
||||||
/// `WorkspaceIndex` data.
|
/// `WorkspaceIndex` data.
|
||||||
pub async fn read_or_open(&self, uri: &Url) -> io::Result<DocSnapshot> {
|
pub async fn read_or_open(&self, uri: &Url) -> io::Result<DocSnapshot> {
|
||||||
@@ -318,7 +317,7 @@ impl Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn update_document(&self, uri: Url, text: String, version: i32) {
|
async fn update_document(&self, uri: Url, text: String, version: i32) {
|
||||||
// For Phase 6 we always treat unknown buffers as vimwiki — if/when
|
// We always treat unknown buffers as vimwiki — if/when
|
||||||
// multi-syntax dispatch lands the pick should follow the URI's ext.
|
// multi-syntax dispatch lands the pick should follow the URI's ext.
|
||||||
let plugin = match self.registry.get("vimwiki") {
|
let plugin = match self.registry.get("vimwiki") {
|
||||||
Some(p) => p,
|
Some(p) => p,
|
||||||
@@ -373,7 +372,7 @@ impl Backend {
|
|||||||
#[tower_lsp::async_trait]
|
#[tower_lsp::async_trait]
|
||||||
impl LanguageServer for Backend {
|
impl LanguageServer for Backend {
|
||||||
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
|
async fn initialize(&self, params: InitializeParams) -> LspResult<InitializeResult> {
|
||||||
// SPEC §6.11: prefer UTF-8 if the client advertises support.
|
// Prefer UTF-8 if the client advertises support.
|
||||||
let supports_utf8 = params
|
let supports_utf8 = params
|
||||||
.capabilities
|
.capabilities
|
||||||
.general
|
.general
|
||||||
@@ -404,7 +403,7 @@ impl LanguageServer for Backend {
|
|||||||
change: Some(TextDocumentSyncKind::FULL),
|
change: Some(TextDocumentSyncKind::FULL),
|
||||||
will_save: None,
|
will_save: None,
|
||||||
will_save_wait_until: None,
|
will_save_wait_until: None,
|
||||||
// Phase 17 `auto_export` listens on `did_save`. Asking
|
// `auto_export` listens on `did_save`. Asking
|
||||||
// for `include_text: false` since the document store
|
// for `include_text: false` since the document store
|
||||||
// already mirrors the live buffer.
|
// already mirrors the live buffer.
|
||||||
save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
|
save: Some(TextDocumentSyncSaveOptions::SaveOptions(SaveOptions {
|
||||||
@@ -502,9 +501,9 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
|
async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
|
||||||
// Apply the new settings to `Config`. Re-indexing on root changes
|
// Apply the new settings to `Config`. Re-indexing on root changes
|
||||||
// is out of scope for Phase 11; for now we just accept the new
|
// is out of scope here; for now we just accept the new
|
||||||
// values so later commands see the right diagnostic settings etc.
|
// values so later commands see the right diagnostic settings etc.
|
||||||
// Phase 18 (multi-wiki) revisits this with proper rebuild semantics.
|
// Multi-wiki support revisits this with proper rebuild semantics.
|
||||||
let mut cfg = self.config.write().expect("config lock poisoned");
|
let mut cfg = self.config.write().expect("config lock poisoned");
|
||||||
cfg.apply_change(¶ms.settings);
|
cfg.apply_change(¶ms.settings);
|
||||||
}
|
}
|
||||||
@@ -530,7 +529,7 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn did_rename_files(&self, params: RenameFilesParams) {
|
async fn did_rename_files(&self, params: RenameFilesParams) {
|
||||||
// Phase 13 advertises this capability so the editor pushes
|
// We advertise this capability so the editor pushes
|
||||||
// out-of-band renames (file explorer drags, `:VimwikiRenameFile`
|
// out-of-band renames (file explorer drags, `:VimwikiRenameFile`
|
||||||
// when the client opts to surface the new URI). Update the index
|
// when the client opts to surface the new URI). Update the index
|
||||||
// so backlinks/headings track the new URI without waiting for
|
// so backlinks/headings track the new URI without waiting for
|
||||||
@@ -576,7 +575,7 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
||||||
// Phase 17: when `auto_export` is set on the resolved wiki, run
|
// When `auto_export` is set on the resolved wiki, run
|
||||||
// the equivalent of `nuwiki.export.currentToHtml` after the file
|
// the equivalent of `nuwiki.export.currentToHtml` after the file
|
||||||
// hits disk. Best-effort — failures are logged but do not bubble
|
// hits disk. Best-effort — failures are logged but do not bubble
|
||||||
// to the client. Skips pages with the `%nohtml` directive.
|
// to the client. Skips pages with the `%nohtml` directive.
|
||||||
@@ -934,7 +933,7 @@ impl LanguageServer for Backend {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Phase 12: surface tags too, named as `:tag:` so editors
|
// Surface tags too, named as `:tag:` so editors
|
||||||
// can distinguish them from headings at a glance.
|
// can distinguish them from headings at a glance.
|
||||||
for t in &page.tags {
|
for t in &page.tags {
|
||||||
if q.is_empty() || t.name.to_lowercase().contains(&q) {
|
if q.is_empty() || t.name.to_lowercase().contains(&q) {
|
||||||
@@ -1121,9 +1120,9 @@ fn utf16_column(text: &str, line: u32, byte_col: u32) -> u32 {
|
|||||||
.sum::<usize>() as u32
|
.sum::<usize>() as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Backward-compat wrapper kept for v1.0 test surface. New call sites
|
/// Backward-compat wrapper kept for the legacy test surface. New call sites
|
||||||
/// should use `collect_diagnostics` so they can opt into link-health
|
/// should use `collect_diagnostics` so they can opt into link-health
|
||||||
/// (Phase 15) and other sources.
|
/// and other sources.
|
||||||
pub fn ast_diagnostics(ast: &DocumentNode, text: &str, utf8: bool) -> Vec<Diagnostic> {
|
pub fn ast_diagnostics(ast: &DocumentNode, text: &str, utf8: bool) -> Vec<Diagnostic> {
|
||||||
collect_diagnostics(
|
collect_diagnostics(
|
||||||
ast,
|
ast,
|
||||||
@@ -1136,12 +1135,12 @@ pub fn ast_diagnostics(ast: &DocumentNode, text: &str, utf8: bool) -> Vec<Diagno
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Composable diagnostic collector (P11 §12.2.4).
|
/// Composable diagnostic collector.
|
||||||
///
|
///
|
||||||
/// Sources, each gated by `cfg`:
|
/// Sources, each gated by `cfg`:
|
||||||
///
|
///
|
||||||
/// 1. Parse errors (always on).
|
/// 1. Parse errors (always on).
|
||||||
/// 2. Broken-link warnings (Phase 15) — emitted when an index, source
|
/// 2. Broken-link warnings — emitted when an index, source
|
||||||
/// page name, and `link_severity != Off` are all available.
|
/// page name, and `link_severity != Off` are all available.
|
||||||
pub fn collect_diagnostics(
|
pub fn collect_diagnostics(
|
||||||
ast: &DocumentNode,
|
ast: &DocumentNode,
|
||||||
@@ -1317,8 +1316,8 @@ fn keyword_str(k: nuwiki_core::ast::Keyword) -> &'static str {
|
|||||||
|
|
||||||
// ===== Workspace + navigation helpers =====
|
// ===== Workspace + navigation helpers =====
|
||||||
//
|
//
|
||||||
// `workspace_root_from_params` lived here through Phase 10; Phase 11 lifted
|
// `workspace_root_from_params` logic now lives in
|
||||||
// that logic into `Config::from_init_params` (see `config.rs`) so workspace
|
// `Config::from_init_params` (see `config.rs`) so workspace
|
||||||
// folders and `root_uri` are handled alongside `initializationOptions`.
|
// folders and `root_uri` are handled alongside `initializationOptions`.
|
||||||
|
|
||||||
/// Background workspace scan (P8). Walks every `.wiki` file under `root`,
|
/// Background workspace scan (P8). Walks every `.wiki` file under `root`,
|
||||||
@@ -1469,7 +1468,7 @@ fn heading_range_in(idx: &WorkspaceIndex, uri: &Url, anchor: &str, _utf8: bool)
|
|||||||
if let Some(h) = page.find_heading_by_anchor(anchor) {
|
if let Some(h) = page.find_heading_by_anchor(anchor) {
|
||||||
return Some(span_to_lsp_range_no_text(&h.span));
|
return Some(span_to_lsp_range_no_text(&h.span));
|
||||||
}
|
}
|
||||||
// Phase 12: tags-as-anchors. `[[Page#tag-name]]` lands on the
|
// Tags-as-anchors. `[[Page#tag-name]]` lands on the
|
||||||
// matching `TagNode` on the target page.
|
// matching `TagNode` on the target page.
|
||||||
let t = page.tags.iter().find(|t| t.name == anchor)?;
|
let t = page.tags.iter().find(|t| t.name == anchor)?;
|
||||||
Some(span_to_lsp_range_no_text(&t.span))
|
Some(span_to_lsp_range_no_text(&t.span))
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Helpers shared by the Phase 8 navigation handlers (definition,
|
//! Helpers shared by the navigation handlers (definition,
|
||||||
//! references, hover, completion).
|
//! references, hover, completion).
|
||||||
//!
|
//!
|
||||||
//! Two responsibilities:
|
//! Two responsibilities:
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
//! `textDocument/rename` — server-side rename with cross-document link
|
//! `textDocument/rename` — server-side rename with cross-document link
|
||||||
//! rewriting.
|
//! rewriting.
|
||||||
//!
|
//!
|
||||||
//! Phase 13 implementation:
|
//! Implementation:
|
||||||
//! 1. Resolve the rename target — cursor on a wikilink renames the linked
|
//! 1. Resolve the rename target — cursor on a wikilink renames the linked
|
||||||
//! page; otherwise rename the current file.
|
//! page; otherwise rename the current file.
|
||||||
//! 2. Build the new URI from the wiki root + the user-supplied path.
|
//! 2. Build the new URI from the wiki root + the user-supplied path.
|
||||||
//! 3. For every page in the wiki's index with an outgoing link to the
|
//! 3. For every page in the wiki's index with an outgoing link to the
|
||||||
//! old page name, read its source via the Phase 11 closed-doc loader
|
//! old page name, read its source via the closed-doc loader
|
||||||
//! so cached spans are validated against current text, then emit a
|
//! so cached spans are validated against current text, then emit a
|
||||||
//! `TextEdit` that swaps the `[[old]]` for `[[new]]` while preserving
|
//! `TextEdit` that swaps the `[[old]]` for `[[new]]` while preserving
|
||||||
//! anchors and descriptions.
|
//! anchors and descriptions.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
//! Semantic-token emission for the vimwiki AST.
|
//! Semantic-token emission for the vimwiki AST.
|
||||||
//!
|
//!
|
||||||
//! Token-type scheme settled in P7: custom `vimwiki*` types plus
|
//! Token-type scheme: custom `vimwiki*` types plus
|
||||||
//! `level1`..`level6` + `centered` modifiers (SPEC §11). Default highlight
|
//! `level1`..`level6` + `centered` modifiers.
|
||||||
//! groups for these arrive in Phase 9.
|
|
||||||
//!
|
//!
|
||||||
//! Pipeline:
|
//! Pipeline:
|
||||||
//!
|
//!
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! `Wiki` aggregate — per-wiki state, even when there's only one.
|
//! `Wiki` aggregate — per-wiki state, even when there's only one.
|
||||||
//!
|
//!
|
||||||
//! v1.0 shipped with a single `Arc<RwLock<WorkspaceIndex>>` on `Backend`.
|
//! The single `Arc<RwLock<WorkspaceIndex>>` on `Backend` is lifted into a
|
||||||
//! Phase 11 lifts that into a `Wiki` aggregate so Phase 18 (multi-wiki)
|
//! `Wiki` aggregate so multi-wiki support only changes config shape, not
|
||||||
//! only changes config shape, not data flow. Phases 12–17 always operate
|
//! data flow. Handlers always operate on a `Wiki`; in practice the `wikis`
|
||||||
//! on a `Wiki`; in practice the `wikis` `Vec` has one entry until 18.
|
//! `Vec` has one entry until multi-wiki support lands.
|
||||||
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//! §13.1 §13.3 — `color_dic` → `HtmlRenderer` integration.
|
//! `color_dic` → `HtmlRenderer` integration.
|
||||||
//!
|
//!
|
||||||
//! Drives the renderer directly so we don't have to spin up the full
|
//! Drives the renderer directly so we don't have to spin up the full
|
||||||
//! export pipeline. The end-to-end path (export command → renderer
|
//! export pipeline. The end-to-end path (export command → renderer
|
||||||
//! with `cfg.color_dic` plumbed through) is integration-tested via the
|
//! with `cfg.color_dic` plumbed through) is integration-tested via the
|
||||||
//! Phase 17 export tests.
|
//! export tests.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Command-coverage suite: at least one behavioural test for every
|
//! Command-coverage suite: at least one behavioural test for every
|
||||||
//! documented `:Nuwiki*` / `:Vimwiki*` command (see `doc/nuwiki.txt`
|
//! documented `:Nuwiki*` / `:Vimwiki*` command (see the COMMANDS section
|
||||||
//! §5 COMMANDS).
|
//! of `doc/nuwiki.txt`).
|
||||||
//!
|
//!
|
||||||
//! The user-facing commands funnel through `workspace/executeCommand`
|
//! The user-facing commands funnel through `workspace/executeCommand`
|
||||||
//! into the Backend dispatcher, or — for follow/backlinks/rename — through
|
//! into the Backend dispatcher, or — for follow/backlinks/rename — through
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//! Phase 13: pure-function tests for the rename helper + the file-delete
|
//! Pure-function tests for the rename helper + the file-delete
|
||||||
//! command. The async `Backend::rename` end-to-end (with `read_or_open`
|
//! command. The async `Backend::rename` end-to-end (with `read_or_open`
|
||||||
//! hitting disk) is exercised by Phase 14+ integration but covered here
|
//! hitting disk) is exercised by integration tests but covered here
|
||||||
//! at the building-block level — same logic, no async client.
|
//! at the building-block level — same logic, no async client.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
@@ -95,7 +95,7 @@ async fn file_delete_returns_workspace_edit_with_delete_op() {
|
|||||||
//
|
//
|
||||||
// Easier: shape the test as an integration test over the public
|
// Easier: shape the test as an integration test over the public
|
||||||
// module surface — call `commands::execute` once we expose a thin
|
// module surface — call `commands::execute` once we expose a thin
|
||||||
// builder. For Phase 13 we test the JSON-shaped contract using the
|
// builder. We test the JSON-shaped contract using the
|
||||||
// edits module directly, since that's what `file_delete` returns.
|
// edits module directly, since that's what `file_delete` returns.
|
||||||
let uri = Url::parse("file:///tmp/note.wiki").unwrap();
|
let uri = Url::parse("file:///tmp/note.wiki").unwrap();
|
||||||
let mut b = nuwiki_lsp::edits::WorkspaceEditBuilder::new();
|
let mut b = nuwiki_lsp::edits::WorkspaceEditBuilder::new();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! §13.1 Cluster A — list rewriters: `removeDone`, `renumber`,
|
//! Cluster A — list rewriters: `removeDone`, `renumber`,
|
||||||
//! `changeSymbol`, `changeLevel`.
|
//! `changeSymbol`, `changeLevel`.
|
||||||
|
|
||||||
use nuwiki_core::ast::ListSymbol;
|
use nuwiki_core::ast::ListSymbol;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! §13.1 Cluster B — table rewriters: `insert`, `align`, `moveColumn`.
|
//! Cluster B — table rewriters: `insert`, `align`, `moveColumn`.
|
||||||
|
|
||||||
use nuwiki_core::syntax::vimwiki::VimwikiSyntax;
|
use nuwiki_core::syntax::vimwiki::VimwikiSyntax;
|
||||||
use nuwiki_core::syntax::SyntaxPlugin;
|
use nuwiki_core::syntax::SyntaxPlugin;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Phase 19: LSP folding-range provider.
|
//! LSP folding-range provider.
|
||||||
//!
|
//!
|
||||||
//! Drives `folding::folding_ranges` directly. The handler wiring is
|
//! Drives `folding::folding_ranges` directly. The handler wiring is
|
||||||
//! shallow — it just calls into this function — so we verify the
|
//! shallow — it just calls into this function — so we verify the
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Tests for the pure helpers that the LSP backend uses to translate
|
//! Tests for the pure helpers that the LSP backend uses to translate
|
||||||
//! between `nuwiki-core` ASTs and the LSP wire format. The async
|
//! between `nuwiki-core` ASTs and the LSP wire format. The async
|
||||||
//! request-handling loop is exercised at integration time in Phase 8+
|
//! request-handling loop is exercised at integration time by the
|
||||||
//! (navigation), so this file stays focused on conversion correctness.
|
//! navigation tests, so this file stays focused on conversion correctness.
|
||||||
|
|
||||||
use nuwiki_core::ast::{
|
use nuwiki_core::ast::{
|
||||||
inline::InlineNode, BlockNode, BlockquoteNode, DocumentNode, ErrorNode, ParagraphNode,
|
inline::InlineNode, BlockNode, BlockquoteNode, DocumentNode, ErrorNode, ParagraphNode,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Phase 17: HTML export commands.
|
//! HTML export commands.
|
||||||
//!
|
//!
|
||||||
//! Drives the pure `export::*` helpers directly. The side-effecting
|
//! Drives the pure `export::*` helpers directly. The side-effecting
|
||||||
//! `export_ops::write_page` / `write_rss` paths get exercised via a
|
//! `export_ops::write_page` / `write_rss` paths get exercised via a
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Phase 15: link-health diagnostics + TOC/links/orphans queries.
|
//! Link-health diagnostics + TOC/links/orphans queries.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
//! Phase 18: multi-wiki — interwiki resolution + picker commands.
|
//! Multi-wiki — interwiki resolution + picker commands.
|
||||||
//!
|
//!
|
||||||
//! The cross-wiki resolver methods on `Backend` are exercised indirectly
|
//! The cross-wiki resolver methods on `Backend` are exercised indirectly
|
||||||
//! through `tower_lsp::LspService`'s test harness via a synthetic
|
//! through `tower_lsp::LspService`'s test harness via a synthetic
|
||||||
//! `Backend` (the same pattern Phase 13 used). To keep this test suite
|
//! `Backend`. To keep this test suite
|
||||||
//! lightweight we drive the *pure* resolution primitives — the parser
|
//! lightweight we drive the *pure* resolution primitives — the parser
|
||||||
//! producing `LinkKind::Interwiki` targets, then the `WorkspaceIndex`
|
//! producing `LinkKind::Interwiki` targets, then the `WorkspaceIndex`
|
||||||
//! lookup-by-name — and verify the multi-wiki config shape end-to-end.
|
//! lookup-by-name — and verify the multi-wiki config shape end-to-end.
|
||||||
@@ -80,7 +80,7 @@ fn multi_wiki_config_loads_two_entries() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn v1_0_single_root_still_works_alongside_multi_wiki_shape() {
|
fn v1_0_single_root_still_works_alongside_multi_wiki_shape() {
|
||||||
// Single-wiki shorthand is the v1.0 form; ensure it still desugars.
|
// Single-wiki shorthand is the legacy form; ensure it still desugars.
|
||||||
let cfg = config_from_json(serde_json::json!({
|
let cfg = config_from_json(serde_json::json!({
|
||||||
"wiki_root": "/tmp/legacy",
|
"wiki_root": "/tmp/legacy",
|
||||||
}));
|
}));
|
||||||
@@ -135,7 +135,7 @@ fn nested_root_picks_longest_prefix_match() {
|
|||||||
|
|
||||||
// ===== Cross-wiki resolution (uses WorkspaceIndex per wiki) =====
|
// ===== Cross-wiki resolution (uses WorkspaceIndex per wiki) =====
|
||||||
|
|
||||||
/// Verify that the cross-wiki resolution semantics match the SPEC:
|
/// Verify the cross-wiki resolution semantics:
|
||||||
/// `[[wiki1:Page]]` looks up `Page` in the *second* wiki's index, not
|
/// `[[wiki1:Page]]` looks up `Page` in the *second* wiki's index, not
|
||||||
/// the source wiki's. The pure-data check here mirrors what
|
/// the source wiki's. The pure-data check here mirrors what
|
||||||
/// `Backend::resolve_target_uri` does.
|
/// `Backend::resolve_target_uri` does.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! Tests for the Phase 8 navigation pieces: WorkspaceIndex queries,
|
//! Tests for the navigation pieces: WorkspaceIndex queries,
|
||||||
//! page-name derivation, anchor slugify, position lookup, and link
|
//! page-name derivation, anchor slugify, position lookup, and link
|
||||||
//! resolution. The async LSP handlers are exercised through these
|
//! resolution. The async LSP handlers are exercised through these
|
||||||
//! helpers; end-to-end JSON-RPC drive lives in a follow-up phase.
|
//! helpers; end-to-end JSON-RPC drive lives elsewhere.
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|||||||
@@ -95,10 +95,10 @@ else
|
|||||||
call s:record(0, 'cmd.NuwikiIndex_defined', ':NuwikiIndex not registered')
|
call s:record(0, 'cmd.NuwikiIndex_defined', ':NuwikiIndex not registered')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
" ===== Documented command surface (doc/nuwiki.txt §5) =====
|
" ===== Documented command surface =====
|
||||||
" Every command must be reachable in BOTH the canonical :Nuwiki* form and
|
" Every command must be reachable in BOTH the canonical :Nuwiki* form and
|
||||||
" the vimwiki-compatible :Vimwiki* form. The suffixes below are the §5 list
|
" the vimwiki-compatible :Vimwiki* form. The suffixes below are the command
|
||||||
" minus the prefix. :NuwikiInstall is the one exception — it's a nuwiki-only
|
" list minus the prefix. :NuwikiInstall is the one exception — it's a nuwiki-only
|
||||||
" maintenance command with no :Vimwiki* alias, so it's checked separately.
|
" maintenance command with no :Vimwiki* alias, so it's checked separately.
|
||||||
|
|
||||||
let s:both_forms = [
|
let s:both_forms = [
|
||||||
@@ -151,7 +151,7 @@ call s:record(
|
|||||||
\ 'invoke.VimwikiNextLink_jumps_to_first_link',
|
\ 'invoke.VimwikiNextLink_jumps_to_first_link',
|
||||||
\ 'col=' . col('.'))
|
\ 'col=' . col('.'))
|
||||||
|
|
||||||
" ===== Documented mapping surface (doc §6/§7/§8) =====
|
" ===== Documented mapping surface =====
|
||||||
" Every documented mapping must resolve to a buffer-local mapping in the
|
" Every documented mapping must resolve to a buffer-local mapping in the
|
||||||
" mode(s) the doc lists it under. The mouse group is opt-in — the shell
|
" mode(s) the doc lists it under. The mouse group is opt-in — the shell
|
||||||
" wrapper sets g:nuwiki_mouse_mappings before the ftplugin loads.
|
" wrapper sets g:nuwiki_mouse_mappings before the ftplugin loads.
|
||||||
|
|||||||
@@ -123,10 +123,10 @@ vim.defer_fn(function()
|
|||||||
end
|
end
|
||||||
record(true, 'lsp.attached', 'client=' .. client.name)
|
record(true, 'lsp.attached', 'client=' .. client.name)
|
||||||
|
|
||||||
-- ===== Documented command surface (doc/nuwiki.txt §5) =====
|
-- ===== Documented command surface =====
|
||||||
-- Every command must be reachable in BOTH the canonical :Nuwiki* form
|
-- Every command must be reachable in BOTH the canonical :Nuwiki* form
|
||||||
-- and the vimwiki-compatible :Vimwiki* form. The suffixes below are the
|
-- and the vimwiki-compatible :Vimwiki* form. The suffixes below are the
|
||||||
-- §5 list minus the prefix. :NuwikiInstall is the one exception — a
|
-- command list minus the prefix. :NuwikiInstall is the one exception — a
|
||||||
-- nuwiki-only maintenance command with no :Vimwiki* alias.
|
-- nuwiki-only maintenance command with no :Vimwiki* alias.
|
||||||
local both_forms = {
|
local both_forms = {
|
||||||
'Index', 'TabIndex', 'UISelect', 'Goto', 'FollowLink', 'Backlinks',
|
'Index', 'TabIndex', 'UISelect', 'Goto', 'FollowLink', 'Backlinks',
|
||||||
@@ -169,7 +169,7 @@ vim.defer_fn(function()
|
|||||||
record(ok, 'invoke.next_prev_link_cursor_movement', ok and '' or tostring(err))
|
record(ok, 'invoke.next_prev_link_cursor_movement', ok and '' or tostring(err))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ===== Documented mapping surface (doc §6/§7/§8) =====
|
-- ===== Documented mapping surface =====
|
||||||
-- Every documented mapping must resolve to a buffer-local mapping in
|
-- Every documented mapping must resolve to a buffer-local mapping in
|
||||||
-- the mode(s) the doc lists it under. `modes` is a string of mode
|
-- the mode(s) the doc lists it under. `modes` is a string of mode
|
||||||
-- letters: n/x/o/i. The mouse group is opt-in — the harness enables it
|
-- letters: n/x/o/i. The mouse group is opt-in — the harness enables it
|
||||||
@@ -179,36 +179,36 @@ vim.defer_fn(function()
|
|||||||
return next(d) ~= nil and d.buffer == 1
|
return next(d) ~= nil and d.buffer == 1
|
||||||
end
|
end
|
||||||
local mapping_surface = {
|
local mapping_surface = {
|
||||||
-- §6 Links
|
-- Links
|
||||||
{ '<CR>', 'n' }, { '<S-CR>', 'n' }, { '<C-CR>', 'n' }, { '<C-S-CR>', 'n' },
|
{ '<CR>', 'n' }, { '<S-CR>', 'n' }, { '<C-CR>', 'n' }, { '<C-S-CR>', 'n' },
|
||||||
{ '<BS>', 'n' }, { '<Tab>', 'n' }, { '<S-Tab>', 'n' }, { '+', 'nx' },
|
{ '<BS>', 'n' }, { '<Tab>', 'n' }, { '<S-Tab>', 'n' }, { '+', 'nx' },
|
||||||
-- §6 Lists
|
-- Lists
|
||||||
{ '<C-Space>', 'nx' }, { '<C-@>', 'nx' }, { '<Nul>', 'nx' },
|
{ '<C-Space>', 'nx' }, { '<C-@>', 'nx' }, { '<Nul>', 'nx' },
|
||||||
{ 'gnt', 'n' }, { 'gln', 'nx' }, { 'glp', 'nx' }, { 'glx', 'nx' },
|
{ 'gnt', 'n' }, { 'gln', 'nx' }, { 'glp', 'nx' }, { 'glx', 'nx' },
|
||||||
{ 'glh', 'n' }, { 'gll', 'n' }, { 'gLh', 'n' }, { 'gLl', 'n' },
|
{ 'glh', 'n' }, { 'gll', 'n' }, { 'gLh', 'n' }, { 'gLl', 'n' },
|
||||||
{ 'glr', 'n' }, { 'gLr', 'n' }, { 'gl<Space>', 'n' }, { 'gL<Space>', 'n' },
|
{ 'glr', 'n' }, { 'gLr', 'n' }, { 'gl<Space>', 'n' }, { 'gL<Space>', 'n' },
|
||||||
{ 'o', 'n' }, { 'O', 'n' },
|
{ 'o', 'n' }, { 'O', 'n' },
|
||||||
-- §6 Headers
|
-- Headers
|
||||||
{ '=', 'n' }, { '-', 'n' }, { ']]', 'n' }, { '[[', 'n' },
|
{ '=', 'n' }, { '-', 'n' }, { ']]', 'n' }, { '[[', 'n' },
|
||||||
{ ']=', 'n' }, { '[=', 'n' }, { ']u', 'n' }, { '[u', 'n' },
|
{ ']=', 'n' }, { '[=', 'n' }, { ']u', 'n' }, { '[u', 'n' },
|
||||||
-- §6 Tables
|
-- Tables
|
||||||
{ 'gqq', 'n' }, { 'gq1', 'n' }, { 'gww', 'n' }, { 'gw1', 'n' },
|
{ 'gqq', 'n' }, { 'gq1', 'n' }, { 'gww', 'n' }, { 'gw1', 'n' },
|
||||||
{ '<A-Left>', 'n' }, { '<A-Right>', 'n' },
|
{ '<A-Left>', 'n' }, { '<A-Right>', 'n' },
|
||||||
-- §6 Wiki / diary / export
|
-- Wiki / diary / export
|
||||||
{ '<Leader>ww', 'n' }, { '<Leader>ws', 'n' }, { '<Leader>wi', 'n' },
|
{ '<Leader>ww', 'n' }, { '<Leader>ws', 'n' }, { '<Leader>wi', 'n' },
|
||||||
{ '<Leader>w<Leader>w', 'n' }, { '<Leader>w<Leader>y', 'n' },
|
{ '<Leader>w<Leader>w', 'n' }, { '<Leader>w<Leader>y', 'n' },
|
||||||
{ '<Leader>w<Leader>t', 'n' }, { '<Leader>w<Leader>i', 'n' },
|
{ '<Leader>w<Leader>t', 'n' }, { '<Leader>w<Leader>i', 'n' },
|
||||||
{ '<Leader>wn', 'n' }, { '<Leader>wd', 'n' }, { '<Leader>wr', 'n' },
|
{ '<Leader>wn', 'n' }, { '<Leader>wd', 'n' }, { '<Leader>wr', 'n' },
|
||||||
{ '<Leader>wh', 'n' }, { '<Leader>whh', 'n' }, { '<Leader>wha', 'n' },
|
{ '<Leader>wh', 'n' }, { '<Leader>whh', 'n' }, { '<Leader>wha', 'n' },
|
||||||
{ '<Leader>wc', 'nx' }, { '<C-Down>', 'n' }, { '<C-Up>', 'n' },
|
{ '<Leader>wc', 'nx' }, { '<C-Down>', 'n' }, { '<C-Up>', 'n' },
|
||||||
-- §6 Mouse (opt-in)
|
-- Mouse (opt-in)
|
||||||
{ '<2-LeftMouse>', 'n' }, { '<S-2-LeftMouse>', 'n' }, { '<C-2-LeftMouse>', 'n' },
|
{ '<2-LeftMouse>', 'n' }, { '<S-2-LeftMouse>', 'n' }, { '<C-2-LeftMouse>', 'n' },
|
||||||
{ '<MiddleMouse>', 'n' }, { '<RightMouse>', 'n' },
|
{ '<MiddleMouse>', 'n' }, { '<RightMouse>', 'n' },
|
||||||
-- §7 Text objects (operator-pending + visual)
|
-- Text objects (operator-pending + visual)
|
||||||
{ 'ah', 'ox' }, { 'ih', 'ox' }, { 'aH', 'ox' }, { 'iH', 'ox' },
|
{ 'ah', 'ox' }, { 'ih', 'ox' }, { 'aH', 'ox' }, { 'iH', 'ox' },
|
||||||
{ 'al', 'ox' }, { 'il', 'ox' }, { 'a\\', 'ox' }, { 'i\\', 'ox' },
|
{ 'al', 'ox' }, { 'il', 'ox' }, { 'a\\', 'ox' }, { 'i\\', 'ox' },
|
||||||
{ 'ac', 'ox' }, { 'ic', 'ox' },
|
{ 'ac', 'ox' }, { 'ic', 'ox' },
|
||||||
-- §8 Insert-mode
|
-- Insert-mode
|
||||||
{ '<CR>', 'i' }, { '<Tab>', 'i' }, { '<S-Tab>', 'i' },
|
{ '<CR>', 'i' }, { '<Tab>', 'i' }, { '<S-Tab>', 'i' },
|
||||||
{ '<C-D>', 'i' }, { '<C-T>', 'i' },
|
{ '<C-D>', 'i' }, { '<C-T>', 'i' },
|
||||||
{ '<C-L><C-J>', 'i' }, { '<C-L><C-K>', 'i' }, { '<C-L><C-M>', 'i' },
|
{ '<C-L><C-J>', 'i' }, { '<C-L><C-K>', 'i' }, { '<C-L><C-M>', 'i' },
|
||||||
@@ -424,7 +424,7 @@ vim.defer_fn(function()
|
|||||||
wait_ms = 200,
|
wait_ms = 200,
|
||||||
})
|
})
|
||||||
|
|
||||||
-- ===== Link helpers (§13.1 Cluster C) =====
|
-- ===== Link helpers (Cluster C) =====
|
||||||
|
|
||||||
run('links.normalize_via_+', {
|
run('links.normalize_via_+', {
|
||||||
lines = { 'MyPage rest' },
|
lines = { 'MyPage rest' },
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
" ftplugin/nuwiki.vim — per-buffer settings + Phase 19 command/keymap glue.
|
" ftplugin/nuwiki.vim — per-buffer settings + command/keymap glue.
|
||||||
"
|
"
|
||||||
" Wires up:
|
" Wires up:
|
||||||
" - basic edit-time options (commentstring, suffixesadd, …)
|
" - basic edit-time options (commentstring, suffixesadd, …)
|
||||||
" - every `:Vimwiki*` / `:Nuwiki*` command from SPEC §12.10 that has a
|
" - every `:Vimwiki*` / `:Nuwiki*` command that has a
|
||||||
" server-side counterpart (deferred §13.1 commands stub out with a
|
" server-side counterpart (deferred commands stub out with a
|
||||||
" "not yet implemented" notification)
|
" "not yet implemented" notification)
|
||||||
" - on Neovim: keymaps + text objects + folding via
|
" - on Neovim: keymaps + text objects + folding via
|
||||||
" `nuwiki.ftplugin.attach()`
|
" `nuwiki.ftplugin.attach()`
|
||||||
@@ -78,7 +78,7 @@ if !has('nvim')
|
|||||||
command! -buffer -nargs=? VimwikiSearchTags call nuwiki#commands#tags_search(<q-args>)
|
command! -buffer -nargs=? VimwikiSearchTags call nuwiki#commands#tags_search(<q-args>)
|
||||||
command! -buffer -nargs=? VimwikiGenerateTagLinks call nuwiki#commands#tags_generate_links(<q-args>)
|
command! -buffer -nargs=? VimwikiGenerateTagLinks call nuwiki#commands#tags_generate_links(<q-args>)
|
||||||
|
|
||||||
" §13.1 deferred stubs.
|
" Deferred stubs.
|
||||||
command! -buffer -nargs=1 VimwikiTable call nuwiki#commands#table_insert()
|
command! -buffer -nargs=1 VimwikiTable call nuwiki#commands#table_insert()
|
||||||
command! -buffer VimwikiTableMoveColumnLeft call nuwiki#commands#table_move_left()
|
command! -buffer VimwikiTableMoveColumnLeft call nuwiki#commands#table_move_left()
|
||||||
command! -buffer VimwikiTableMoveColumnRight call nuwiki#commands#table_move_right()
|
command! -buffer VimwikiTableMoveColumnRight call nuwiki#commands#table_move_right()
|
||||||
@@ -115,7 +115,7 @@ if !has('nvim')
|
|||||||
command! -buffer -nargs=? NuwikiGenerateTagLinks call nuwiki#commands#tags_generate_links(<q-args>)
|
command! -buffer -nargs=? NuwikiGenerateTagLinks call nuwiki#commands#tags_generate_links(<q-args>)
|
||||||
command! -buffer -nargs=1 NuwikiGoto call nuwiki#commands#wiki_goto_page(<q-args>)
|
command! -buffer -nargs=1 NuwikiGoto call nuwiki#commands#wiki_goto_page(<q-args>)
|
||||||
|
|
||||||
" Canonical :Nuwiki* names that mirror the documented surface (doc §5).
|
" Canonical :Nuwiki* names that mirror the documented surface.
|
||||||
" The older :Nuwiki* spellings above stay defined for back-compat.
|
" The older :Nuwiki* spellings above stay defined for back-compat.
|
||||||
command! -buffer NuwikiNextLink call nuwiki#commands#link_next()
|
command! -buffer NuwikiNextLink call nuwiki#commands#link_next()
|
||||||
command! -buffer NuwikiPrevLink call nuwiki#commands#link_prev()
|
command! -buffer NuwikiPrevLink call nuwiki#commands#link_prev()
|
||||||
@@ -331,7 +331,7 @@ command! -buffer VimwikiRebuildTags lua require('nuwiki.commands').
|
|||||||
command! -buffer -nargs=? VimwikiSearchTags lua require('nuwiki.commands').tags_search(<q-args>)
|
command! -buffer -nargs=? VimwikiSearchTags lua require('nuwiki.commands').tags_search(<q-args>)
|
||||||
command! -buffer -nargs=? VimwikiGenerateTagLinks lua require('nuwiki.commands').tags_generate_links(<q-args>)
|
command! -buffer -nargs=? VimwikiGenerateTagLinks lua require('nuwiki.commands').tags_generate_links(<q-args>)
|
||||||
|
|
||||||
" §13.1 deferred — stubs notify the user instead of erroring.
|
" Deferred — stubs notify the user instead of erroring.
|
||||||
command! -buffer -nargs=1 VimwikiTable lua require('nuwiki.commands').table_insert()
|
command! -buffer -nargs=1 VimwikiTable lua require('nuwiki.commands').table_insert()
|
||||||
command! -buffer VimwikiTableMoveColumnLeft lua require('nuwiki.commands').table_move_column_left()
|
command! -buffer VimwikiTableMoveColumnLeft lua require('nuwiki.commands').table_move_column_left()
|
||||||
command! -buffer VimwikiTableMoveColumnRight lua require('nuwiki.commands').table_move_column_right()
|
command! -buffer VimwikiTableMoveColumnRight lua require('nuwiki.commands').table_move_column_right()
|
||||||
@@ -370,7 +370,7 @@ command! -buffer -nargs=? NuwikiSearchTags lua require('nuwiki.commands'
|
|||||||
command! -buffer -nargs=? NuwikiGenerateTagLinks lua require('nuwiki.commands').tags_generate_links(<q-args>)
|
command! -buffer -nargs=? NuwikiGenerateTagLinks lua require('nuwiki.commands').tags_generate_links(<q-args>)
|
||||||
command! -buffer -nargs=1 NuwikiGoto lua require('nuwiki.commands').wiki_goto_page(<q-args>)
|
command! -buffer -nargs=1 NuwikiGoto lua require('nuwiki.commands').wiki_goto_page(<q-args>)
|
||||||
|
|
||||||
" Canonical :Nuwiki* names that mirror the documented surface (doc §5).
|
" Canonical :Nuwiki* names that mirror the documented surface.
|
||||||
" The older :Nuwiki* spellings above stay defined for back-compat.
|
" The older :Nuwiki* spellings above stay defined for back-compat.
|
||||||
command! -buffer NuwikiNextLink lua require('nuwiki.commands').link_next()
|
command! -buffer NuwikiNextLink lua require('nuwiki.commands').link_next()
|
||||||
command! -buffer NuwikiPrevLink lua require('nuwiki.commands').link_prev()
|
command! -buffer NuwikiPrevLink lua require('nuwiki.commands').link_prev()
|
||||||
@@ -384,5 +384,5 @@ command! -buffer Nuwiki2HTML lua require('nuwiki.command
|
|||||||
command! -buffer Nuwiki2HTMLBrowse lua require('nuwiki.commands').export_browse()
|
command! -buffer Nuwiki2HTMLBrowse lua require('nuwiki.commands').export_browse()
|
||||||
command! -buffer -bang NuwikiAll2HTML execute (<bang>0 ? "lua require('nuwiki.commands').export_all_force()" : "lua require('nuwiki.commands').export_all()")
|
command! -buffer -bang NuwikiAll2HTML execute (<bang>0 ? "lua require('nuwiki.commands').export_all_force()" : "lua require('nuwiki.commands').export_all()")
|
||||||
|
|
||||||
" Phase 19 buffer attach: keymaps + text objects + folding.
|
" Buffer attach: keymaps + text objects + folding.
|
||||||
lua require('nuwiki.ftplugin').attach(0)
|
lua require('nuwiki.ftplugin').attach(0)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- lua/nuwiki/commands.lua — Lua wrappers around every `nuwiki.*`
|
-- lua/nuwiki/commands.lua — Lua wrappers around every `nuwiki.*`
|
||||||
-- `workspace/executeCommand` the server advertises. Phase 19 plumbing.
|
-- `workspace/executeCommand` the server advertises.
|
||||||
--
|
--
|
||||||
-- Each public function is named after its `:Vimwiki*` / `:Nuwiki*`
|
-- Each public function is named after its `:Vimwiki*` / `:Nuwiki*`
|
||||||
-- counterpart so `ftplugin/nuwiki.vim` can wire them up declaratively.
|
-- counterpart so `ftplugin/nuwiki.vim` can wire them up declaratively.
|
||||||
@@ -468,7 +468,7 @@ function M.rename_file()
|
|||||||
vim.lsp.buf.rename()
|
vim.lsp.buf.rename()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ===== Deferred placeholders (§13.1) =====
|
-- ===== Deferred placeholders =====
|
||||||
--
|
--
|
||||||
-- These commands aren't implemented on the server yet. Stub them so the
|
-- These commands aren't implemented on the server yet. Stub them so the
|
||||||
-- `:Vimwiki*` compat surface exists and users get a clear message
|
-- `:Vimwiki*` compat surface exists and users get a clear message
|
||||||
@@ -483,7 +483,7 @@ local function _not_yet(name)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- §13.1 Cluster A — list rewriters.
|
-- Cluster A — list rewriters.
|
||||||
|
|
||||||
-- `gl<Space>` — remove done items from the current list only. Passing the
|
-- `gl<Space>` — remove done items from the current list only. Passing the
|
||||||
-- cursor position scopes the server to the contiguous list block under it.
|
-- cursor position scopes the server to the contiguous list block under it.
|
||||||
@@ -891,7 +891,7 @@ function M.smart_shift_tab()
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- §13.1 Cluster B — table rewriters.
|
-- Cluster B — table rewriters.
|
||||||
|
|
||||||
function M.table_insert(cols, rows)
|
function M.table_insert(cols, rows)
|
||||||
cols = tonumber(cols) or 3
|
cols = tonumber(cols) or 3
|
||||||
@@ -974,7 +974,7 @@ function M.colorize(color)
|
|||||||
vim.api.nvim_buf_set_lines(0, row - 1, row, false, { new_line })
|
vim.api.nvim_buf_set_lines(0, row - 1, row, false, { new_line })
|
||||||
end
|
end
|
||||||
|
|
||||||
-- §13.1 Cluster C — link helpers.
|
-- Cluster C — link helpers.
|
||||||
|
|
||||||
function M.paste_link()
|
function M.paste_link()
|
||||||
exec('nuwiki.link.pasteWikilink', pos_args())
|
exec('nuwiki.link.pasteWikilink', pos_args())
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
-- lua/nuwiki/config.lua — defaults and user-config merge.
|
-- lua/nuwiki/config.lua — defaults and user-config merge.
|
||||||
--
|
--
|
||||||
-- Schema mirrors SPEC §7.5 + §12.11. Keep this aligned with
|
-- Keep this aligned with `doc/nuwiki.txt`.
|
||||||
-- `doc/nuwiki.txt`.
|
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
M.defaults = {
|
M.defaults = {
|
||||||
-- v1.0 single-wiki shorthand. The server desugars these into a
|
-- Single-wiki shorthand. The server desugars these into a
|
||||||
-- one-entry `wikis = {…}` list. Use either this or the multi-wiki
|
-- one-entry `wikis = {…}` list. Use either this or the multi-wiki
|
||||||
-- form below; if both are set, `wikis` wins.
|
-- form below; if both are set, `wikis` wins.
|
||||||
wiki_root = '~/vimwiki',
|
wiki_root = '~/vimwiki',
|
||||||
@@ -14,12 +13,12 @@ M.defaults = {
|
|||||||
syntax = 'vimwiki',
|
syntax = 'vimwiki',
|
||||||
log_level = 'warn',
|
log_level = 'warn',
|
||||||
|
|
||||||
-- v1.1 multi-wiki shape. Each entry honours every per-wiki key the
|
-- Multi-wiki shape. Each entry honours every per-wiki key the
|
||||||
-- server understands. Leave nil to fall through to the shorthand
|
-- server understands. Leave nil to fall through to the shorthand
|
||||||
-- above.
|
-- above.
|
||||||
--
|
--
|
||||||
-- Per-wiki keys (all optional, server defaults documented in SPEC
|
-- Per-wiki keys (all optional, server defaults documented in
|
||||||
-- §12.11):
|
-- `doc/nuwiki.txt`):
|
||||||
-- name, root, file_extension, syntax, index
|
-- name, root, file_extension, syntax, index
|
||||||
-- diary_rel_path, diary_index, diary_frequency,
|
-- diary_rel_path, diary_index, diary_frequency,
|
||||||
-- diary_start_week_day, diary_caption_level, diary_sort,
|
-- diary_start_week_day, diary_caption_level, diary_sort,
|
||||||
@@ -31,7 +30,7 @@ M.defaults = {
|
|||||||
-- links_space_char, nested_syntaxes
|
-- links_space_char, nested_syntaxes
|
||||||
wikis = nil,
|
wikis = nil,
|
||||||
|
|
||||||
-- Phase 19: per-buffer glue. Each subgroup mirrors vimwiki's
|
-- Per-buffer glue. Each subgroup mirrors vimwiki's
|
||||||
-- `g:vimwiki_key_mappings` shape and can be flipped off
|
-- `g:vimwiki_key_mappings` shape and can be flipped off
|
||||||
-- independently to suppress that group of keymaps.
|
-- independently to suppress that group of keymaps.
|
||||||
mappings = {
|
mappings = {
|
||||||
@@ -47,7 +46,7 @@ M.defaults = {
|
|||||||
mouse = false, -- <2-LeftMouse>, <RightMouse>, … (opt-in)
|
mouse = false, -- <2-LeftMouse>, <RightMouse>, … (opt-in)
|
||||||
},
|
},
|
||||||
|
|
||||||
-- Phase 19: folding. `'lsp'` uses the server's `foldingRange`
|
-- Folding. `'lsp'` uses the server's `foldingRange`
|
||||||
-- provider (Neovim 0.11+ wires it automatically). `'expr'` falls
|
-- provider (Neovim 0.11+ wires it automatically). `'expr'` falls
|
||||||
-- back to a regex-based `foldexpr`. `'off'` skips folding setup.
|
-- back to a regex-based `foldexpr`. `'off'` skips folding setup.
|
||||||
folding = 'lsp',
|
folding = 'lsp',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- lua/nuwiki/ftplugin.lua — per-buffer hookup invoked by
|
-- lua/nuwiki/ftplugin.lua — per-buffer hookup invoked by
|
||||||
-- `ftplugin/nuwiki.vim`. Centralises the Lua side of Phase 19 so the
|
-- `ftplugin/nuwiki.vim`. Centralises the Lua side of the per-buffer
|
||||||
-- VimL ftplugin stays a one-liner regardless of which subgroups are
|
-- glue so the VimL ftplugin stays a one-liner regardless of which subgroups are
|
||||||
-- enabled.
|
-- enabled.
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
-- lua/nuwiki/health.lua — `:checkhealth nuwiki` target.
|
-- lua/nuwiki/health.lua — `:checkhealth nuwiki` target.
|
||||||
--
|
--
|
||||||
-- v1.0 checks per SPEC §7.6 (binary, version, filetype, LSP attach).
|
-- Core checks: binary, version, filetype, LSP attach.
|
||||||
-- v1.1 §12.10 additions: registered LSP commands, indexed wikis, HTML
|
-- Additional checks: registered LSP commands, indexed wikis, HTML
|
||||||
-- output path writability, default keymaps installed.
|
-- output path writability, default keymaps installed.
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
-- require('nuwiki').install() — install / build the language server
|
-- require('nuwiki').install() — install / build the language server
|
||||||
-- require('nuwiki').health() — :checkhealth nuwiki target
|
-- require('nuwiki').health() — :checkhealth nuwiki target
|
||||||
--
|
--
|
||||||
-- Per SPEC §6.2/§6.3 this layer is wiring only. Everything substantive
|
-- This layer is wiring only. Everything substantive
|
||||||
-- happens in the Rust language server.
|
-- happens in the Rust language server.
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
-- lua/nuwiki/install.lua — install / build the `nuwiki-ls` binary.
|
-- lua/nuwiki/install.lua — install / build the `nuwiki-ls` binary.
|
||||||
--
|
--
|
||||||
-- Strategy (matches SPEC §7.2):
|
-- Strategy:
|
||||||
-- 1. Look for `bin/nuwiki-ls[.exe]` next to the plugin.
|
-- 1. Look for `bin/nuwiki-ls[.exe]` next to the plugin.
|
||||||
-- 2. If missing or `g:nuwiki_build_from_source` is set, build with cargo.
|
-- 2. If missing or `g:nuwiki_build_from_source` is set, build with cargo.
|
||||||
-- 3. Otherwise try to download a release asset for the current target.
|
-- 3. Otherwise try to download a release asset for the current target.
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
-- wiki_prefix = true, -- <Leader>ww, <Leader>ws, <Leader>wi, …
|
-- wiki_prefix = true, -- <Leader>ww, <Leader>ws, <Leader>wi, …
|
||||||
-- }
|
-- }
|
||||||
--
|
--
|
||||||
-- Mappings that point at §13.1-deferred commands stub out to a
|
-- Mappings that point at deferred commands stub out to a
|
||||||
-- "not yet implemented" notification — better than a silent no-op
|
-- "not yet implemented" notification — better than a silent no-op
|
||||||
-- so users get parity *signalling*, not just parity in lhs.
|
-- so users get parity *signalling*, not just parity in lhs.
|
||||||
|
|
||||||
@@ -133,7 +133,7 @@ local function open_above_with_bullet()
|
|||||||
vim.api.nvim_feedkeys('O' .. prefix, 'n', false)
|
vim.api.nvim_feedkeys('O' .. prefix, 'n', false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- ===== Stub for deferred §13.1 commands =====
|
-- ===== Stub for deferred commands =====
|
||||||
|
|
||||||
local function deferred(name)
|
local function deferred(name)
|
||||||
return function()
|
return function()
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
" their config (lazy.nvim does this automatically through `opts`). On Vim
|
" their config (lazy.nvim does this automatically through `opts`). On Vim
|
||||||
" we start the LSP client when a vimwiki buffer is loaded.
|
" we start the LSP client when a vimwiki buffer is loaded.
|
||||||
"
|
"
|
||||||
" Per SPEC §6.2 / §6.3 this file contains wiring only — all logic lives in
|
" This file contains wiring only — all logic lives in
|
||||||
" the Rust language server.
|
" the Rust language server.
|
||||||
|
|
||||||
if exists('g:loaded_nuwiki')
|
if exists('g:loaded_nuwiki')
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
" syntax/vimwiki.vim — default highlight groups for nuwiki.
|
" syntax/vimwiki.vim — default highlight groups for nuwiki.
|
||||||
"
|
"
|
||||||
" Two responsibilities:
|
" Two responsibilities:
|
||||||
" 1. Default `@vimwiki*` highlight groups for the LSP semantic tokens
|
" 1. Default `@vimwiki*` highlight groups for the LSP semantic tokens.
|
||||||
" (SPEC §11 P7). Loaded eagerly so the very first hover/highlight
|
" Loaded eagerly so the very first hover/highlight
|
||||||
" cycle after server startup already has colours.
|
" cycle after server startup already has colours.
|
||||||
" 2. A small regex-based fallback so static highlighting works on
|
" 2. A small regex-based fallback so static highlighting works on
|
||||||
" buffers where the LSP isn't running yet.
|
" buffers where the LSP isn't running yet.
|
||||||
|
|||||||
Reference in New Issue
Block a user