Files
nuwiki-rs/crates/nuwiki-core/src/ast/inline.rs
T

189 lines
5.8 KiB
Rust
Raw Normal View History

//! 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,
}
#[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 `<br />`
/// (`= 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::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<InlineNode>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ItalicNode {
pub span: Span,
pub children: Vec<InlineNode>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BoldItalicNode {
pub span: Span,
pub children: Vec<InlineNode>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StrikethroughNode {
pub span: Span,
pub children: Vec<InlineNode>,
}
#[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<InlineNode>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SubscriptNode {
pub span: Span,
pub children: Vec<InlineNode>,
}
#[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
/// `<span style="color:…">` 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<InlineNode>,
}