//! Inline AST nodes. //! //! The link-related variants (`WikiLink`, `ExternalLink`, //! `Transclusion`, `RawUrl`) wrap structs defined in `super::link`. use super::link::{ExternalLinkNode, RawUrlNode, TransclusionNode, WikiLinkNode}; use super::span::Span; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Keyword { Todo, Done, Started, Fixme, Fixed, Xxx, Stopped, } impl Keyword { /// The literal source text of the keyword (e.g. `"TODO"`). Used both for /// the rendered span and for anchor/id derivation via [`inline_text`]. pub fn label(self) -> &'static str { match self { Keyword::Todo => "TODO", Keyword::Done => "DONE", Keyword::Started => "STARTED", Keyword::Fixme => "FIXME", Keyword::Fixed => "FIXED", Keyword::Xxx => "XXX", Keyword::Stopped => "STOPPED", } } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum InlineNode { Text(TextNode), Bold(BoldNode), Italic(ItalicNode), BoldItalic(BoldItalicNode), Strikethrough(StrikethroughNode), Code(CodeNode), Superscript(SuperscriptNode), Subscript(SubscriptNode), MathInline(MathInlineNode), Keyword(KeywordNode), Color(ColorNode), WikiLink(WikiLinkNode), ExternalLink(ExternalLinkNode), Transclusion(TransclusionNode), RawUrl(RawUrlNode), /// A soft line break joining two content lines within a paragraph or /// list item. Consumers treat it as whitespace; the HTML renderer emits /// a space (vimwiki `*_ignore_newline = 1`, the default) or `
` /// (`= 0`). SoftBreak(SoftBreakNode), } impl InlineNode { /// The source [`Span`] this node covers. Every variant wraps a node with /// its own `span` field; this is the one place that maps variant → span. pub fn span(&self) -> Span { match self { InlineNode::Text(n) => n.span, InlineNode::Bold(n) => n.span, InlineNode::Italic(n) => n.span, InlineNode::BoldItalic(n) => n.span, InlineNode::Strikethrough(n) => n.span, InlineNode::Code(n) => n.span, InlineNode::Superscript(n) => n.span, InlineNode::Subscript(n) => n.span, InlineNode::MathInline(n) => n.span, InlineNode::Keyword(n) => n.span, InlineNode::Color(n) => n.span, InlineNode::WikiLink(n) => n.span, InlineNode::ExternalLink(n) => n.span, InlineNode::Transclusion(n) => n.span, InlineNode::RawUrl(n) => n.span, InlineNode::SoftBreak(n) => n.span, } } } /// Concatenate the plain-text content of a run of inlines, descending into /// formatting containers. Links/external links use their description, falling /// back to the target path / URL. This is the canonical heading/anchor text — /// the HTML renderer uses it for heading `id`s and the LSP for anchor matching, /// so the two never drift. pub fn inline_text(inlines: &[InlineNode]) -> String { let mut out = String::new(); push_inline_text(inlines, &mut out); out.trim().to_string() } fn push_inline_text(inlines: &[InlineNode], out: &mut String) { for n in inlines { match n { InlineNode::Text(t) => out.push_str(&t.content), InlineNode::Bold(b) => push_inline_text(&b.children, out), InlineNode::Italic(i) => push_inline_text(&i.children, out), InlineNode::BoldItalic(bi) => push_inline_text(&bi.children, out), InlineNode::Strikethrough(s) => push_inline_text(&s.children, out), InlineNode::Code(c) => out.push_str(&c.content), InlineNode::Superscript(s) => push_inline_text(&s.children, out), InlineNode::Subscript(s) => push_inline_text(&s.children, out), InlineNode::Color(c) => push_inline_text(&c.children, out), InlineNode::Keyword(k) => out.push_str(k.keyword.label()), InlineNode::WikiLink(w) => match &w.description { Some(d) => push_inline_text(d, out), None => { if let Some(p) = &w.target.path { out.push_str(p); } } }, InlineNode::ExternalLink(e) => match &e.description { Some(d) => push_inline_text(d, out), None => out.push_str(&e.url), }, _ => {} } } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TextNode { pub span: Span, pub content: String, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct SoftBreakNode { pub span: Span, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct BoldNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct ItalicNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct BoldItalicNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct StrikethroughNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct CodeNode { pub span: Span, pub content: String, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct SuperscriptNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct SubscriptNode { pub span: Span, pub children: Vec, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct MathInlineNode { pub span: Span, pub content: String, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct KeywordNode { pub span: Span, pub keyword: Keyword, } /// A named colour span (`color` is a `color_dic` key, `children` the wrapped /// inline content). /// /// This is an **extension point**, not a node the vimwiki parser emits: in /// the editor, colour comes from the `:Colorize` family writing literal /// `` markup, which round-trips through export via /// `valid_html_tags`. `ColorNode` exists so an alternate syntax (or a future /// `[color]` markup) can feed the renderer's `color_dic` / `color_tag_template` /// path directly; see `HtmlRenderer::render_color`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ColorNode { pub span: Span, pub color: String, pub children: Vec, }