Skip to main content

markdown_syntax/
ast.rs

1//! The owned Markdown AST that [`parse()`](crate::parse()) produces and
2//! `Document::to_markdown`/`to_html` consume. Every node carries a [`NodeMeta`]
3//! with an optional source [`Span`]. [`Block`] and [`Inline`] are the two node
4//! enums; everything else is a concrete node struct or a small enum describing
5//! a node's variant.
6
7use alloc::{string::String, sync::Arc, vec::Vec};
8
9use crate::diagnostic::Diagnostic;
10use crate::parse::Definitions;
11use crate::span::Span;
12
13/// Metadata attached to every AST node; currently just the source span.
14#[derive(Clone, Debug, Default, Eq, PartialEq)]
15pub struct NodeMeta {
16    /// The node's source location, or `None` for hand-built nodes.
17    pub span: Option<Span>,
18}
19
20impl NodeMeta {
21    /// Wrap an optional [`Span`] into a [`NodeMeta`].
22    pub const fn new(span: Option<Span>) -> Self {
23        Self { span }
24    }
25}
26
27/// The root of a document: a sequence of top-level [`Block`]s, and for a
28/// parsed document the source it was parsed from.
29///
30/// The source is the only truth: writing a parsed document back with
31/// [`Document::to_markdown`] reuses the source of every top-level block that
32/// did not change. Only [`parse()`](crate::parse()) gives a document a
33/// source; [`Document::without_source`] drops it, and a document without one
34/// is written as canonical Markdown.
35#[derive(Clone, Debug, Default, Eq, PartialEq)]
36pub struct Document {
37    /// Node metadata (source span).
38    pub meta: NodeMeta,
39    /// The document's top-level blocks, in source order.
40    pub children: Vec<Block>,
41    /// The input `parse` read, shared by clones.
42    source: Option<Arc<str>>,
43}
44
45impl Document {
46    /// A document holding `children` and no source.
47    pub fn new(children: Vec<Block>) -> Self {
48        Self {
49            meta: NodeMeta::default(),
50            children,
51            source: None,
52        }
53    }
54
55    /// The document `parse` returns: `children` read from `source`.
56    pub(crate) fn parsed(meta: NodeMeta, children: Vec<Block>, source: &str) -> Self {
57        Self {
58            meta,
59            children,
60            source: Some(Arc::from(source)),
61        }
62    }
63
64    /// The input this document was parsed from, or `None` for a document
65    /// built by hand or returned by [`Document::without_source`].
66    pub fn source(&self) -> Option<&str> {
67        self.source.as_deref()
68    }
69
70    /// This document without its source; writing it gives canonical
71    /// Markdown.
72    #[must_use]
73    pub fn without_source(mut self) -> Self {
74        self.source = None;
75        self
76    }
77}
78
79impl From<Vec<Block>> for Document {
80    fn from(children: Vec<Block>) -> Self {
81        Self::new(children)
82    }
83}
84
85/// Child indices from a document down to one node. A list's children are its
86/// items, a table's are its rows, and a row's are its cells; every other
87/// container's are its blocks.
88#[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
89pub struct NodePath(pub Vec<usize>);
90
91impl From<Vec<usize>> for NodePath {
92    fn from(indices: Vec<usize>) -> Self {
93        Self(indices)
94    }
95}
96
97impl From<&[usize]> for NodePath {
98    fn from(indices: &[usize]) -> Self {
99        Self(indices.into())
100    }
101}
102
103impl<const N: usize> From<[usize; N]> for NodePath {
104    fn from(indices: [usize; N]) -> Self {
105        Self(indices.into())
106    }
107}
108
109/// A block-level node: the building blocks of a document's vertical structure.
110#[derive(Clone, Debug, Eq, PartialEq)]
111pub enum Block {
112    /// A paragraph of inline content.
113    Paragraph(Paragraph),
114    /// An ATX (`# h`) or setext (underlined) heading.
115    Heading(Heading),
116    /// A thematic break / horizontal rule: `---`, `***`, or `___`.
117    ThematicBreak(ThematicBreak),
118    /// A block quote: lines prefixed with `> `.
119    BlockQuote(BlockQuote),
120    /// A GFM alert / admonition: `> [!NOTE]` etc.
121    Alert(Alert),
122    /// A bullet or ordered list.
123    List(List),
124    /// A fenced (```` ``` ````) or indented code block.
125    CodeBlock(CodeBlock),
126    /// A raw HTML block.
127    HtmlBlock(HtmlBlock),
128    /// A structured raw HTML container whose interior is parsed as Markdown.
129    HtmlContainer(HtmlContainer),
130    /// A link reference definition: `[label]: url "title"`.
131    Definition(Definition),
132    /// A footnote definition: `[^id]: text`.
133    FootnoteDefinition(FootnoteDefinition),
134    /// A GFM pipe table.
135    Table(Table),
136    /// A display math block: `$$ … $$`.
137    MathBlock(MathBlock),
138    /// A leading frontmatter block (`---` YAML or `+++` TOML).
139    Frontmatter(Frontmatter),
140    /// A leaf directive: `::name[label]{attrs}`.
141    LeafDirective(LeafDirective),
142    /// A container directive: `:::name … :::`.
143    ContainerDirective(ContainerDirective),
144    /// A blank line beyond the one that separates two blocks.
145    BlankLine(BlankLine),
146}
147
148/// A paragraph: a run of inline content. Source: any plain text line(s).
149#[derive(Clone, Debug, Eq, PartialEq)]
150pub struct Paragraph {
151    /// Node metadata (source span).
152    pub meta: NodeMeta,
153    /// The paragraph's inline content.
154    pub children: Vec<Inline>,
155    /// The text the content was built from, by [`Paragraph::with_source`].
156    pub(crate) source: Option<String>,
157}
158
159/// A heading. Source: `# Title` (ATX) or `Title\n===` (setext).
160#[derive(Clone, Debug, Eq, PartialEq)]
161pub struct Heading {
162    /// Node metadata (source span).
163    pub meta: NodeMeta,
164    /// Heading level, 1..=6.
165    pub depth: u8,
166    /// Whether the heading used ATX or setext syntax.
167    pub kind: HeadingKind,
168    /// The heading's inline content.
169    pub children: Vec<Inline>,
170    /// The text the content was built from, by [`Heading::with_source`].
171    pub(crate) source: Option<String>,
172}
173
174/// Which heading syntax produced a [`Heading`].
175#[derive(Clone, Copy, Debug, Eq, PartialEq)]
176pub enum HeadingKind {
177    /// ATX heading: `# Title` … `###### Title`.
178    Atx,
179    /// Setext heading: `Title` underlined with `===` (level 1) or `---` (level 2).
180    Setext,
181}
182
183/// A blank line the source holds beyond the one that separates two blocks:
184/// K blank lines between two blocks are one separator and K − 1 of these.
185/// Before a document's first block, at the start or end of a block quote,
186/// alert, HTML container, or container directive, and after a document's
187/// last line, each blank line is one. Source: a line holding only
188/// whitespace and its containers' `>` markers.
189#[derive(Clone, Debug, Default, Eq, PartialEq)]
190pub struct BlankLine {
191    /// Node metadata (source span): the line with its line ending.
192    pub meta: NodeMeta,
193}
194
195/// A thematic break / horizontal rule. Source: `---`, `***`, or `___`.
196#[derive(Clone, Debug, Eq, PartialEq)]
197pub struct ThematicBreak {
198    /// Node metadata (source span).
199    pub meta: NodeMeta,
200    /// Which character formed the break.
201    pub marker: ThematicBreakMarker,
202}
203
204/// The character used to draw a [`ThematicBreak`].
205#[derive(Clone, Copy, Debug, Eq, PartialEq)]
206pub enum ThematicBreakMarker {
207    /// Dashes: `---`.
208    Dash,
209    /// Asterisks: `***`.
210    Asterisk,
211    /// Underscores: `___`.
212    Underscore,
213}
214
215/// A block quote: content prefixed with `> `.
216#[derive(Clone, Debug, Eq, PartialEq)]
217pub struct BlockQuote {
218    /// Node metadata (source span).
219    pub meta: NodeMeta,
220    /// The quoted block content.
221    pub children: Vec<Block>,
222}
223
224/// A GFM alert / admonition. Source: `> [!NOTE]` followed by quoted content.
225#[derive(Clone, Debug, Eq, PartialEq)]
226pub struct Alert {
227    /// Node metadata (source span).
228    pub meta: NodeMeta,
229    /// The alert severity / type.
230    pub kind: AlertKind,
231    /// An optional custom title following the `[!KIND]` marker.
232    pub title: Option<String>,
233    /// The alert's block content.
234    pub children: Vec<Block>,
235}
236
237/// The kind of a GFM [`Alert`] (the `[!KIND]` marker).
238#[derive(Clone, Copy, Debug, Eq, PartialEq)]
239pub enum AlertKind {
240    /// `> [!NOTE]`.
241    Note,
242    /// `> [!TIP]`.
243    Tip,
244    /// `> [!IMPORTANT]`.
245    Important,
246    /// `> [!WARNING]`.
247    Warning,
248    /// `> [!CAUTION]`.
249    Caution,
250}
251
252/// A bullet or ordered list. Source: `- a` / `1. a` lines.
253#[derive(Clone, Debug, Eq, PartialEq)]
254pub struct List {
255    /// Node metadata (source span).
256    pub meta: NodeMeta,
257    /// `true` for an ordered list, `false` for a bullet list.
258    pub ordered: bool,
259    /// The marker delimiter used by the list items.
260    pub delimiter: ListDelimiter,
261    /// `true` if the list is tight (no blank lines between items / no `<p>`).
262    pub tight: bool,
263    /// The list's items.
264    pub children: Vec<ListItem>,
265}
266
267/// The marker character that delimits a list's items.
268#[derive(Clone, Copy, Debug, Eq, PartialEq)]
269pub enum ListDelimiter {
270    /// Bullet `-`.
271    Dash,
272    /// Bullet `*`.
273    Asterisk,
274    /// Bullet `+`.
275    Plus,
276    /// Ordered `1.`.
277    Period,
278    /// Ordered `1)`.
279    Paren,
280}
281
282/// A single list item, optionally a GFM task-list checkbox.
283#[derive(Clone, Debug, Eq, PartialEq)]
284pub struct ListItem {
285    /// Node metadata (source span).
286    pub meta: NodeMeta,
287    /// Task-list state: `Some(true)` for `[x]`, `Some(false)` for `[ ]`, `None` otherwise.
288    pub checked: Option<bool>,
289    /// The number an ordered item is written with (`3.` => `Some(3)`), which
290    /// for the first item is the list's start. `None` is written as the
291    /// previous item's number plus one, or 1 for the first item; a bullet
292    /// item holds `None`.
293    pub number: Option<u64>,
294    /// The item's block content.
295    pub children: Vec<Block>,
296}
297
298/// A code block. Source: ```` ```lang … ``` ```` (fenced) or 4-space-indented lines.
299#[derive(Clone, Debug, Eq, PartialEq)]
300pub struct CodeBlock {
301    /// Node metadata (source span).
302    pub meta: NodeMeta,
303    /// Whether the block is fenced (and with what fence) or indented.
304    pub kind: CodeBlockKind,
305    /// The info string after a fence (e.g. the `rust` in ```` ```rust ````).
306    pub info: Option<String>,
307    /// The literal code contents.
308    pub value: String,
309}
310
311/// Whether a [`CodeBlock`] is fenced or indented.
312#[derive(Clone, Copy, Debug, Eq, PartialEq)]
313pub enum CodeBlockKind {
314    /// A fenced code block; records the fence char and its run length.
315    Fenced {
316        /// Which character formed the fence (backtick or tilde).
317        marker: FenceMarker,
318        /// The number of fence characters in the opening fence (>=3).
319        length: usize,
320    },
321    /// A 4-space-indented code block.
322    Indented,
323}
324
325/// The character used to fence a [`CodeBlock`].
326#[derive(Clone, Copy, Debug, Eq, PartialEq)]
327pub enum FenceMarker {
328    /// Backtick fence: ```` ``` ````.
329    Backtick,
330    /// Tilde fence: `~~~`.
331    Tilde,
332}
333
334/// A raw HTML block: HTML emitted verbatim.
335#[derive(Clone, Debug, Eq, PartialEq)]
336pub struct HtmlBlock {
337    /// Node metadata (source span).
338    pub meta: NodeMeta,
339    /// The literal HTML source.
340    pub value: String,
341}
342
343/// A structured raw HTML container, such as `<details> ... </details>`.
344#[derive(Clone, Debug, Eq, PartialEq)]
345pub struct HtmlContainer {
346    /// Node metadata (source span).
347    pub meta: NodeMeta,
348    /// The opening HTML tag.
349    pub opening: HtmlTag,
350    /// The parsed container content.
351    pub content: HtmlContainerContent,
352    /// The closing HTML tag.
353    pub closing: HtmlTag,
354}
355
356/// A raw HTML tag that bounds an [`HtmlContainer`].
357#[derive(Clone, Debug, Eq, PartialEq)]
358pub struct HtmlTag {
359    /// Node metadata (source span).
360    pub meta: NodeMeta,
361    /// The normalized lowercase tag name.
362    pub name: String,
363    /// The literal tag source, including angle brackets and attributes.
364    pub raw: String,
365}
366
367/// The parsed content inside an [`HtmlContainer`].
368#[derive(Clone, Debug, Eq, PartialEq)]
369pub enum HtmlContainerContent {
370    /// Block-level Markdown content.
371    Blocks(Vec<Block>),
372    /// Inline-level Markdown content.
373    Inlines(Vec<Inline>),
374}
375
376/// A link reference definition. Source: `[label]: destination "title"`.
377#[derive(Clone, Debug, Eq, PartialEq)]
378pub struct Definition {
379    /// Node metadata (source span).
380    pub meta: NodeMeta,
381    /// The label as written in the source (e.g. `Foo Bar`).
382    pub label: String,
383    /// The normalized lookup key (case-folded, whitespace-collapsed) for matching references.
384    pub identifier: String,
385    /// The link target URL.
386    pub destination: String,
387    /// How the destination was delimited (bare or `<…>`).
388    pub destination_kind: LinkDestinationKind,
389    /// The optional link title, with the quotes it is written in.
390    pub title: Option<Title>,
391}
392
393/// A footnote definition. Source: `[^id]: footnote text`.
394#[derive(Clone, Debug, Eq, PartialEq)]
395pub struct FootnoteDefinition {
396    /// Node metadata (source span).
397    pub meta: NodeMeta,
398    /// The label as written in the source (the text after `^`).
399    pub label: String,
400    /// The normalized lookup key matching [`FootnoteReference`]s.
401    pub identifier: String,
402    /// The footnote's block content.
403    pub children: Vec<Block>,
404}
405
406/// A GFM pipe table: a header row, an alignment row, then body rows. Source:
407/// `| a | b |` / `|---|---|` / `| 1 | 2 |`.
408#[derive(Clone, Debug, Eq, PartialEq)]
409pub struct Table {
410    /// Node metadata (source span).
411    pub meta: NodeMeta,
412    /// Per-column alignment from the delimiter row.
413    pub alignments: Vec<TableAlignment>,
414    /// All rows; the first is the header row.
415    pub rows: Vec<TableRow>,
416}
417
418/// The alignment of a [`Table`] column, from the `:---:` delimiter row.
419#[derive(Clone, Copy, Debug, Eq, PartialEq)]
420pub enum TableAlignment {
421    /// No explicit alignment: `---`.
422    None,
423    /// Left-aligned: `:---`.
424    Left,
425    /// Center-aligned: `:---:`.
426    Center,
427    /// Right-aligned: `---:`.
428    Right,
429}
430
431/// A single row of a [`Table`].
432#[derive(Clone, Debug, Eq, PartialEq)]
433pub struct TableRow {
434    /// Node metadata (source span).
435    pub meta: NodeMeta,
436    /// The row's cells.
437    pub cells: Vec<TableCell>,
438}
439
440/// A single cell of a [`TableRow`].
441#[derive(Clone, Debug, Eq, PartialEq)]
442pub struct TableCell {
443    /// Node metadata (source span).
444    pub meta: NodeMeta,
445    /// The cell's inline content.
446    pub children: Vec<Inline>,
447    /// The text the content was built from, by [`TableCell::with_source`].
448    pub(crate) source: Option<String>,
449}
450
451/// A display math block. Source: `$$ … $$`.
452#[derive(Clone, Debug, Eq, PartialEq)]
453pub struct MathBlock {
454    /// Node metadata (source span).
455    pub meta: NodeMeta,
456    /// The literal math contents (between the `$$` fences).
457    pub value: String,
458}
459
460/// A leading frontmatter block. Source: `---` YAML or `+++` TOML at the top of
461/// the document.
462#[derive(Clone, Debug, Eq, PartialEq)]
463pub struct Frontmatter {
464    /// Node metadata (source span).
465    pub meta: NodeMeta,
466    /// Whether the frontmatter is YAML or TOML.
467    pub kind: FrontmatterKind,
468    /// The literal frontmatter contents (between the fences).
469    pub value: String,
470}
471
472/// The format of a [`Frontmatter`] block.
473#[derive(Clone, Copy, Debug, Eq, PartialEq)]
474pub enum FrontmatterKind {
475    /// YAML frontmatter, fenced by `---`.
476    Yaml,
477    /// TOML frontmatter, fenced by `+++`.
478    Toml,
479}
480
481/// A leaf directive. Source: `::name[label]{attrs}`.
482#[derive(Clone, Debug, Eq, PartialEq)]
483pub struct LeafDirective {
484    /// Node metadata (source span).
485    pub meta: NodeMeta,
486    /// The directive name following the `::`.
487    pub name: String,
488    /// The optional `[label]` inline content.
489    pub label: Vec<Inline>,
490    /// The optional `{attrs}` attributes.
491    pub attributes: Vec<DirectiveAttribute>,
492    /// The text the label was built from, by [`LeafDirective::with_source`].
493    pub(crate) label_source: Option<String>,
494}
495
496/// A container directive. Source: `:::name[label]{attrs}` … `:::`.
497#[derive(Clone, Debug, Eq, PartialEq)]
498pub struct ContainerDirective {
499    /// Node metadata (source span).
500    pub meta: NodeMeta,
501    /// The directive name following the `:::`.
502    pub name: String,
503    /// The optional `[label]` inline content.
504    pub label: Vec<Inline>,
505    /// The optional `{attrs}` attributes.
506    pub attributes: Vec<DirectiveAttribute>,
507    /// The directive's enclosed block content.
508    pub children: Vec<Block>,
509    /// The text the label was built from, by
510    /// [`ContainerDirective::with_source`].
511    pub(crate) label_source: Option<String>,
512}
513
514/// An inline-level node: the leaf and span content inside blocks.
515#[derive(Clone, Debug, Eq, PartialEq)]
516pub enum Inline {
517    /// Literal text.
518    Text(Text),
519    /// A backslash escape such as `\*`.
520    Escape(Escape),
521    /// A character reference such as `&amp;` or `&#247;`.
522    CharacterReference(CharacterReference),
523    /// Emphasis: `*text*` or `_text_`.
524    Emphasis(Emphasis),
525    /// Strong emphasis: `**text**` or `__text__`.
526    Strong(Strong),
527    /// Strikethrough: `~~text~~`.
528    Delete(Delete),
529    /// A highlight / "mark" span: `==text==`.
530    Mark(Mark),
531    /// An emoji-style shortcode: `:name:`.
532    Shortcode(Shortcode),
533    /// An inline code span: `` `code` ``.
534    Code(CodeInline),
535    /// An inline link: `[text](url)`.
536    Link(Link),
537    /// An autolink: `<https://example.com>` or a bare literal URL or email.
538    Autolink(Autolink),
539    /// An inline image: `![alt](url)`.
540    Image(Image),
541    /// A reference link: `[text][label]`.
542    LinkReference(LinkReference),
543    /// A reference image: `![alt][label]`.
544    ImageReference(ImageReference),
545    /// Raw inline HTML such as `<span>`.
546    Html(HtmlInline),
547    /// A soft line break (a plain newline within a paragraph).
548    SoftBreak(SoftBreak),
549    /// A hard line break (`\` or two trailing spaces).
550    LineBreak(LineBreak),
551    /// Inline math: `$x$`.
552    Math(MathInline),
553    /// A footnote reference: `[^id]`.
554    FootnoteReference(FootnoteReference),
555    /// An inline footnote: `^[inline note]`.
556    InlineFootnote(InlineFootnote),
557    /// A wiki link: `[[target|label]]`.
558    WikiLink(WikiLink),
559    /// A text directive: `:name[label]{attrs}`.
560    TextDirective(TextDirective),
561    /// A tag: `#name`.
562    Tag(Tag),
563}
564
565/// Literal text content.
566#[derive(Clone, Debug, Eq, PartialEq)]
567pub struct Text {
568    /// Node metadata (source span).
569    pub meta: NodeMeta,
570    /// The text value.
571    pub value: String,
572}
573
574/// A backslash escape such as `\*` or `\\`.
575#[derive(Clone, Debug, Eq, PartialEq)]
576pub struct Escape {
577    /// Node metadata (source span).
578    pub meta: NodeMeta,
579    /// The escaped (literal) character.
580    pub value: char,
581}
582
583/// A character reference such as `&amp;` or `&#247;`. The reference as
584/// written is the node's one fact; [`CharacterReference::value`] decodes it.
585#[derive(Clone, Debug, Eq, PartialEq)]
586pub struct CharacterReference {
587    /// Node metadata (source span).
588    pub meta: NodeMeta,
589    /// The reference as written, including `&` and `;` (e.g. `&amp;`).
590    pub reference: String,
591}
592
593/// Emphasis (typically italic): `*text*` or `_text_`.
594#[derive(Clone, Debug, Eq, PartialEq)]
595pub struct Emphasis {
596    /// Node metadata (source span).
597    pub meta: NodeMeta,
598    /// The delimiter the span was written with.
599    pub delimiter: EmphasisDelimiter,
600    /// The emphasized inline content.
601    pub children: Vec<Inline>,
602}
603
604/// Strong emphasis (typically bold): `**text**` or `__text__`.
605#[derive(Clone, Debug, Eq, PartialEq)]
606pub struct Strong {
607    /// Node metadata (source span).
608    pub meta: NodeMeta,
609    /// The delimiter the span was written with.
610    pub delimiter: EmphasisDelimiter,
611    /// The strongly-emphasized inline content.
612    pub children: Vec<Inline>,
613}
614
615/// The delimiter character of an [`Emphasis`] or [`Strong`] span.
616#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
617pub enum EmphasisDelimiter {
618    /// `*text*` / `**text**`.
619    #[default]
620    Asterisk,
621    /// `_text_` / `__text__`.
622    Underscore,
623}
624
625/// Strikethrough: `~~text~~`.
626#[derive(Clone, Debug, Eq, PartialEq)]
627pub struct Delete {
628    /// Node metadata (source span).
629    pub meta: NodeMeta,
630    /// The struck-through inline content.
631    pub children: Vec<Inline>,
632}
633
634/// A highlight / "mark" span: `==text==`.
635#[derive(Clone, Debug, Eq, PartialEq)]
636pub struct Mark {
637    /// Node metadata (source span).
638    pub meta: NodeMeta,
639    /// The highlighted inline content.
640    pub children: Vec<Inline>,
641}
642
643/// An emoji shortcode: `:name:`, where `name` is a gemoji name or alias.
644#[derive(Clone, Debug, Eq, PartialEq)]
645pub struct Shortcode {
646    /// Node metadata (source span).
647    pub meta: NodeMeta,
648    /// The shortcode name between the colons (e.g. `smile` for `:smile:`).
649    pub name: String,
650}
651
652/// A tag: `#` and a name, such as `#rust` or `#ai/agents`.
653#[derive(Clone, Debug, Eq, PartialEq)]
654pub struct Tag {
655    /// Node metadata (source span).
656    pub meta: NodeMeta,
657    /// The name as written, without the `#`. A `/` separates levels, which
658    /// the caller splits.
659    pub name: String,
660}
661
662/// An inline code span: `` `code` ``. The fence and the padding spaces are
663/// not recorded: the serializer chooses them from the value.
664#[derive(Clone, Debug, Eq, PartialEq)]
665pub struct CodeInline {
666    /// Node metadata (source span).
667    pub meta: NodeMeta,
668    /// The code text, normalized as CommonMark reads it: line endings as
669    /// spaces, and one space stripped from each end when both ends hold one
670    /// and the text is not all spaces.
671    pub value: String,
672}
673
674/// An inline link: `[text](destination "title")`.
675#[derive(Clone, Debug, Eq, PartialEq)]
676pub struct Link {
677    /// Node metadata (source span).
678    pub meta: NodeMeta,
679    /// The link target URL.
680    pub destination: String,
681    /// How the destination was delimited (bare or `<…>`).
682    pub destination_kind: LinkDestinationKind,
683    /// The optional link title, with the quotes it is written in.
684    pub title: Option<Title>,
685    /// The link's inline content (the visible text).
686    pub children: Vec<Inline>,
687}
688
689/// An autolink: `<https://example.com>`, `<a@b.c>`, or a GFM literal
690/// autolink, a bare `https://…`, `www.…`, or email address. The text as
691/// written is the node's one fact; [`Autolink::destination`] derives the link
692/// target from it.
693#[derive(Clone, Debug, Eq, PartialEq)]
694pub struct Autolink {
695    /// Node metadata (source span). An angle-bracket autolink's span covers
696    /// its brackets.
697    pub meta: NodeMeta,
698    /// Which autolink syntax the text is written in.
699    pub form: AutolinkForm,
700    /// The URL or address as written, without an angle-bracket autolink's
701    /// brackets.
702    pub text: String,
703}
704
705/// How an [`Autolink`] is written.
706#[derive(Clone, Copy, Debug, Eq, PartialEq)]
707pub enum AutolinkForm {
708    /// An angle-bracket autolink: `<https://example.com>` or `<a@b.c>`.
709    Angle,
710    /// A GFM literal autolink: a bare `https://…`, `www.…`, or email address.
711    Literal,
712}
713
714/// An inline image: `![alt](destination "title")`.
715#[derive(Clone, Debug, Eq, PartialEq)]
716pub struct Image {
717    /// Node metadata (source span).
718    pub meta: NodeMeta,
719    /// The image source URL.
720    pub destination: String,
721    /// How the destination was delimited (bare or `<…>`).
722    pub destination_kind: LinkDestinationKind,
723    /// The optional image title, with the quotes it is written in.
724    pub title: Option<Title>,
725    /// The image's alt-text inline content.
726    pub alt: Vec<Inline>,
727}
728
729/// How a link/image destination was delimited in the source.
730#[derive(Clone, Copy, Debug, Eq, PartialEq)]
731pub enum LinkDestinationKind {
732    /// A bare destination: `(url)`.
733    Bare,
734    /// An angle-bracketed destination: `(<url>)`.
735    Angle,
736    /// No destination present: `()`.
737    Omitted,
738}
739
740/// How a link/image title was quoted in the source.
741#[derive(Clone, Copy, Debug, Eq, PartialEq)]
742pub enum LinkTitleKind {
743    /// Double-quoted: `"title"`.
744    DoubleQuote,
745    /// Single-quoted: `'title'`.
746    SingleQuote,
747    /// Parenthesized: `(title)`.
748    Paren,
749}
750
751/// The title of a [`Link`], [`Image`], or [`Definition`]: its text and the
752/// quotes it is written in.
753#[derive(Clone, Debug, Eq, PartialEq)]
754pub struct Title {
755    /// The title text, with its escapes and character references decoded.
756    pub value: String,
757    /// How the title is quoted.
758    pub kind: LinkTitleKind,
759}
760
761/// A reference link: `[text][label]`, `[text][]`, or `[text]`.
762#[derive(Clone, Debug, Eq, PartialEq)]
763pub struct LinkReference {
764    /// Node metadata (source span).
765    pub meta: NodeMeta,
766    /// The normalized lookup key matching a [`Definition`].
767    pub identifier: String,
768    /// The label as written in the source.
769    pub label: String,
770    /// Whether the reference is full, collapsed, or shortcut form.
771    pub kind: ReferenceKind,
772    /// The link's inline content (the visible text).
773    pub children: Vec<Inline>,
774}
775
776/// A reference image: `![alt][label]`, `![alt][]`, or `![alt]`.
777#[derive(Clone, Debug, Eq, PartialEq)]
778pub struct ImageReference {
779    /// Node metadata (source span).
780    pub meta: NodeMeta,
781    /// The normalized lookup key matching a [`Definition`].
782    pub identifier: String,
783    /// The label as written in the source.
784    pub label: String,
785    /// Whether the reference is full, collapsed, or shortcut form.
786    pub kind: ReferenceKind,
787    /// The image's alt-text inline content.
788    pub alt: Vec<Inline>,
789}
790
791/// The form of a reference link/image.
792#[derive(Clone, Copy, Debug, Eq, PartialEq)]
793pub enum ReferenceKind {
794    /// Full reference: `[text][label]`.
795    Full,
796    /// Collapsed reference: `[label][]`.
797    Collapsed,
798    /// Shortcut reference: `[label]`.
799    Shortcut,
800}
801
802/// Raw inline HTML such as `<span>` or `</em>`.
803#[derive(Clone, Debug, Eq, PartialEq)]
804pub struct HtmlInline {
805    /// Node metadata (source span).
806    pub meta: NodeMeta,
807    /// The literal HTML source.
808    pub value: String,
809}
810
811/// A soft line break: a plain newline within a paragraph.
812#[derive(Clone, Debug, Eq, PartialEq)]
813pub struct SoftBreak {
814    /// Node metadata (source span).
815    pub meta: NodeMeta,
816}
817
818/// A hard line break: a trailing `\` or two trailing spaces.
819#[derive(Clone, Debug, Eq, PartialEq)]
820pub struct LineBreak {
821    /// Node metadata (source span).
822    pub meta: NodeMeta,
823    /// Which syntax produced the break.
824    pub kind: LineBreakKind,
825}
826
827/// Which syntax produced a hard [`LineBreak`].
828#[derive(Clone, Copy, Debug, Eq, PartialEq)]
829pub enum LineBreakKind {
830    /// A trailing backslash: `\`.
831    Backslash,
832    /// Two or more trailing spaces.
833    Spaces,
834}
835
836/// Which syntax delimited an inline [`MathInline`] span.
837#[derive(Clone, Copy, Debug, Eq, PartialEq)]
838pub enum MathInlineKind {
839    /// Dollar-fenced inline math (`$…$`, `$$…$$`, …); `dollars` is the fence
840    /// length (>=1). Dollar math always renders inline, while a 2-dollar fence
841    /// is conventionally treated as display elsewhere in the ecosystem.
842    Dollar {
843        /// The number of `$` characters in the fence (>=1).
844        dollars: u8,
845    },
846    /// Math-code span: `$`…`$`.
847    Code,
848}
849
850/// Inline math: `$x$`.
851#[derive(Clone, Debug, Eq, PartialEq)]
852pub struct MathInline {
853    /// Node metadata (source span).
854    pub meta: NodeMeta,
855    /// The literal math contents (between the fences).
856    pub value: String,
857    /// Which delimiter syntax was used.
858    pub kind: MathInlineKind,
859}
860
861/// A footnote reference: `[^id]`.
862#[derive(Clone, Debug, Eq, PartialEq)]
863pub struct FootnoteReference {
864    /// Node metadata (source span).
865    pub meta: NodeMeta,
866    /// The label as written in the source (the text after `^`).
867    pub label: String,
868    /// The normalized lookup key matching a [`FootnoteDefinition`].
869    pub identifier: String,
870}
871
872/// An inline footnote: `^[inline note]`.
873#[derive(Clone, Debug, Eq, PartialEq)]
874pub struct InlineFootnote {
875    /// Node metadata (source span).
876    pub meta: NodeMeta,
877    /// The footnote's inline content.
878    pub children: Vec<Inline>,
879}
880
881/// A wiki link: `[[target|label]]`, or an embed: `![[target|label]]`.
882#[derive(Clone, Debug, Eq, PartialEq)]
883pub struct WikiLink {
884    /// Node metadata (source span). An embed's span starts at its `!`.
885    pub meta: NodeMeta,
886    /// The link target (page name) as written in the source, its backslash
887    /// escapes and character references kept; [`WikiLink::decoded_target`]
888    /// gives the text they stand for.
889    pub target: String,
890    /// The visible label as written in the source, its backslash escapes and
891    /// character references kept; [`WikiLink::decoded_label`] gives the text
892    /// they stand for. A label equal to `target` means the link is written
893    /// without a `|label` part.
894    pub label: String,
895    /// Whether a `!` directly before the `[[` marks the link as an embed.
896    pub embed: bool,
897}
898
899/// A text directive. Source: `:name[label]{attrs}`.
900#[derive(Clone, Debug, Eq, PartialEq)]
901pub struct TextDirective {
902    /// Node metadata (source span).
903    pub meta: NodeMeta,
904    /// The directive name following the `:`.
905    pub name: String,
906    /// The optional `[label]` inline content.
907    pub label: Vec<Inline>,
908    /// The optional `{attrs}` attributes.
909    pub attributes: Vec<DirectiveAttribute>,
910}
911
912/// One attribute of a directive's `{name=value}` block.
913#[derive(Clone, Debug, Eq, PartialEq)]
914pub struct DirectiveAttribute {
915    /// The attribute name.
916    pub name: String,
917    /// The attribute value, or `None` for a valueless attribute.
918    pub value: Option<String>,
919}
920
921// ---------------------------------------------------------------------------
922// Ergonomic accessors and a minimal construction layer.
923//
924// Every node carries a `meta: NodeMeta`, so `meta()`/`span()` are uniform across
925// the enums and free callers from writing an exhaustive match just to read a
926// span. `From`/`new` collapse the `Variant(Struct { meta, .. })` boilerplate for
927// hand-built ASTs; the raw struct literals remain available for full control.
928// ---------------------------------------------------------------------------
929
930macro_rules! impl_meta_accessors {
931    ($enum:ident { $($variant:ident),+ $(,)? }) => {
932        impl $enum {
933            /// Borrow this node's [`NodeMeta`].
934            pub fn meta(&self) -> &NodeMeta {
935                match self { $( $enum::$variant(node) => &node.meta, )+ }
936            }
937
938            /// This node's source span, if it carries one.
939            pub fn span(&self) -> Option<Span> {
940                self.meta().span
941            }
942        }
943    };
944}
945
946macro_rules! impl_from_variants {
947    ($enum:ident { $($variant:ident($ty:ty)),+ $(,)? }) => {
948        $(
949            impl From<$ty> for $enum {
950                fn from(node: $ty) -> Self {
951                    $enum::$variant(node)
952                }
953            }
954        )+
955    };
956}
957
958impl_meta_accessors!(Block {
959    Paragraph,
960    Heading,
961    ThematicBreak,
962    BlockQuote,
963    Alert,
964    List,
965    CodeBlock,
966    HtmlBlock,
967    HtmlContainer,
968    Definition,
969    FootnoteDefinition,
970    Table,
971    MathBlock,
972    Frontmatter,
973    LeafDirective,
974    ContainerDirective,
975    BlankLine,
976});
977
978impl_from_variants!(Block {
979    Paragraph(Paragraph), Heading(Heading), ThematicBreak(ThematicBreak),
980    BlockQuote(BlockQuote), Alert(Alert), List(List),
981    CodeBlock(CodeBlock), HtmlBlock(HtmlBlock), Definition(Definition),
982    HtmlContainer(HtmlContainer),
983    FootnoteDefinition(FootnoteDefinition), Table(Table), MathBlock(MathBlock),
984    Frontmatter(Frontmatter), LeafDirective(LeafDirective), ContainerDirective(ContainerDirective),
985    BlankLine(BlankLine),
986});
987
988impl_meta_accessors!(Inline {
989    Text,
990    Escape,
991    CharacterReference,
992    Emphasis,
993    Strong,
994    Delete,
995    Mark,
996    Shortcode,
997    Code,
998    Link,
999    Autolink,
1000    Image,
1001    LinkReference,
1002    ImageReference,
1003    Html,
1004    SoftBreak,
1005    LineBreak,
1006    Math,
1007    FootnoteReference,
1008    InlineFootnote,
1009    WikiLink,
1010    TextDirective,
1011    Tag,
1012});
1013
1014impl_from_variants!(Inline {
1015    Text(Text), Escape(Escape), CharacterReference(CharacterReference),
1016    Emphasis(Emphasis), Strong(Strong), Delete(Delete), Mark(Mark),
1017    Shortcode(Shortcode), Code(CodeInline), Link(Link), Autolink(Autolink), Image(Image),
1018    LinkReference(LinkReference), ImageReference(ImageReference), Html(HtmlInline),
1019    SoftBreak(SoftBreak), LineBreak(LineBreak), Math(MathInline),
1020    FootnoteReference(FootnoteReference), InlineFootnote(InlineFootnote), WikiLink(WikiLink),
1021    TextDirective(TextDirective), Tag(Tag),
1022});
1023
1024impl Block {
1025    /// The blocks directly inside this block: a block quote's, an alert's, a
1026    /// footnote definition's, a container directive's, or an HTML container's
1027    /// that holds blocks; an empty slice for every other block. A list's
1028    /// blocks are in its items ([`ListItem::children`]), as [`NodePath`]
1029    /// counts them, so a walker over blocks steps into a list's items itself.
1030    pub fn children(&self) -> &[Block] {
1031        match self {
1032            Block::BlockQuote(n) => &n.children,
1033            Block::Alert(n) => &n.children,
1034            Block::FootnoteDefinition(n) => &n.children,
1035            Block::ContainerDirective(n) => &n.children,
1036            Block::HtmlContainer(n) => match &n.content {
1037                HtmlContainerContent::Blocks(blocks) => blocks,
1038                HtmlContainerContent::Inlines(_) => &[],
1039            },
1040            Block::Paragraph(_)
1041            | Block::Heading(_)
1042            | Block::ThematicBreak(_)
1043            | Block::List(_)
1044            | Block::CodeBlock(_)
1045            | Block::HtmlBlock(_)
1046            | Block::Definition(_)
1047            | Block::Table(_)
1048            | Block::MathBlock(_)
1049            | Block::Frontmatter(_)
1050            | Block::LeafDirective(_)
1051            | Block::BlankLine(_) => &[],
1052        }
1053    }
1054}
1055
1056impl Inline {
1057    /// The inline subtree of this node, or an empty slice for a leaf. Covers the
1058    /// `alt`/`label` fields uniformly, so a generic walker never silently skips
1059    /// an image's alt text or a directive's label.
1060    pub fn children(&self) -> &[Inline] {
1061        match self {
1062            Inline::Emphasis(n) => &n.children,
1063            Inline::Strong(n) => &n.children,
1064            Inline::Delete(n) => &n.children,
1065            Inline::Mark(n) => &n.children,
1066            Inline::Link(n) => &n.children,
1067            Inline::Image(n) => &n.alt,
1068            Inline::LinkReference(n) => &n.children,
1069            Inline::ImageReference(n) => &n.alt,
1070            Inline::InlineFootnote(n) => &n.children,
1071            Inline::TextDirective(n) => &n.label,
1072            Inline::Text(_)
1073            | Inline::Escape(_)
1074            | Inline::CharacterReference(_)
1075            | Inline::Shortcode(_)
1076            | Inline::Code(_)
1077            | Inline::Autolink(_)
1078            | Inline::Html(_)
1079            | Inline::SoftBreak(_)
1080            | Inline::LineBreak(_)
1081            | Inline::Math(_)
1082            | Inline::FootnoteReference(_)
1083            | Inline::WikiLink(_)
1084            | Inline::Tag(_) => &[],
1085        }
1086    }
1087}
1088
1089impl CharacterReference {
1090    /// A character reference node for `reference`, written with its `&` and
1091    /// `;` (e.g. `&amp;`).
1092    pub fn new(reference: impl Into<String>) -> Self {
1093        Self {
1094            meta: NodeMeta::default(),
1095            reference: reference.into(),
1096        }
1097    }
1098
1099    /// The character the reference decodes to, or `None` when
1100    /// [`reference`](Self::reference) is not exactly one character reference.
1101    /// A valid document holds only references that decode.
1102    pub fn value(&self) -> Option<String> {
1103        crate::decode::decode_character_reference(&self.reference)
1104    }
1105}
1106
1107impl Autolink {
1108    /// An autolink of `form` whose text is `text`.
1109    pub fn new(form: AutolinkForm, text: impl Into<String>) -> Self {
1110        Self {
1111            meta: NodeMeta::default(),
1112            form,
1113            text: text.into(),
1114        }
1115    }
1116
1117    /// The link target the text writes, or `None` when the text is not
1118    /// exactly one autolink of its form. An angle-bracket URI links to itself
1119    /// and an email address to it after `mailto:`; a literal `www.` domain
1120    /// links to it after `http://`, a literal email address to it after
1121    /// `mailto:`, and any other literal autolink to itself. A valid document
1122    /// holds only autolinks with a destination.
1123    pub fn destination(&self) -> Option<String> {
1124        match self.form {
1125            AutolinkForm::Angle => crate::parse::angle_autolink_destination(&self.text),
1126            AutolinkForm::Literal => crate::parse::literal_autolink_destination(&self.text),
1127        }
1128    }
1129}
1130
1131impl Title {
1132    /// A title holding `value`, quoted as `kind`.
1133    pub fn new(value: impl Into<String>, kind: LinkTitleKind) -> Self {
1134        Self {
1135            value: value.into(),
1136            kind,
1137        }
1138    }
1139}
1140
1141impl Shortcode {
1142    /// The emoji the crate's pinned gemoji table gives this shortcode's
1143    /// name, or `None` for a name the table does not hold.
1144    pub fn glyph(&self) -> Option<&'static str> {
1145        crate::gemoji::glyph(&self.name)
1146    }
1147}
1148
1149impl Tag {
1150    /// A tag named `name`, written without its `#`.
1151    pub fn new(name: impl Into<String>) -> Self {
1152        Self {
1153            meta: NodeMeta::default(),
1154            name: name.into(),
1155        }
1156    }
1157}
1158
1159impl WikiLink {
1160    /// The target with its backslash escapes and character references
1161    /// decoded, as CommonMark decodes a link destination: `a \| b &amp; c`
1162    /// decodes to `a | b & c`.
1163    pub fn decoded_target(&self) -> String {
1164        crate::decode::decode_escapes_and_references(&self.target)
1165    }
1166
1167    /// The label with its backslash escapes and character references
1168    /// decoded, as [`WikiLink::decoded_target`] decodes the target.
1169    pub fn decoded_label(&self) -> String {
1170        crate::decode::decode_escapes_and_references(&self.label)
1171    }
1172}
1173
1174impl Text {
1175    /// A text node with the given string value.
1176    pub fn new(value: impl Into<String>) -> Self {
1177        Self {
1178            meta: NodeMeta::default(),
1179            value: value.into(),
1180        }
1181    }
1182}
1183
1184impl From<&str> for Text {
1185    fn from(value: &str) -> Self {
1186        Text::new(value)
1187    }
1188}
1189
1190impl From<String> for Text {
1191    fn from(value: String) -> Self {
1192        Text::new(value)
1193    }
1194}
1195
1196impl Paragraph {
1197    /// A paragraph from any iterator of inline-convertible children.
1198    pub fn new<I, T>(children: I) -> Self
1199    where
1200        I: IntoIterator<Item = T>,
1201        T: Into<Inline>,
1202    {
1203        Self {
1204            meta: NodeMeta::default(),
1205            children: children.into_iter().map(Into::into).collect(),
1206            source: None,
1207        }
1208    }
1209
1210    /// A paragraph whose content is `text`, read as
1211    /// [`parse_inline`](crate::parse_inline) reads it with `definitions`, and
1212    /// the diagnostics of that read. The paragraph keeps `text`, which the
1213    /// writer writes in place of its inlines while they are the ones `text`
1214    /// reads as; the spans of its inlines are offsets in `text`. Pass the
1215    /// definitions of the document the paragraph will be written in
1216    /// ([`Definitions::of`]); after they change, or for a leaf built with
1217    /// others, [`Document::reread_kept_sources`] reads its text again.
1218    pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1219        let (children, diagnostics) = read_source(text, definitions);
1220        let paragraph = Self {
1221            source: Some(text.into()),
1222            ..Self::new(children)
1223        };
1224        (paragraph, diagnostics)
1225    }
1226
1227    /// The text [`Paragraph::with_source`] built the paragraph from.
1228    pub fn source(&self) -> Option<&str> {
1229        self.source.as_deref()
1230    }
1231}
1232
1233impl Heading {
1234    /// An ATX heading of the given depth.
1235    pub fn new<I, T>(depth: u8, children: I) -> Self
1236    where
1237        I: IntoIterator<Item = T>,
1238        T: Into<Inline>,
1239    {
1240        Self {
1241            meta: NodeMeta::default(),
1242            depth,
1243            kind: HeadingKind::Atx,
1244            children: children.into_iter().map(Into::into).collect(),
1245            source: None,
1246        }
1247    }
1248
1249    /// A heading of the given depth and kind whose content is `text`, built
1250    /// as [`Paragraph::with_source`] builds a paragraph.
1251    pub fn with_source(
1252        depth: u8,
1253        kind: HeadingKind,
1254        text: &str,
1255        definitions: &Definitions,
1256    ) -> (Self, Vec<Diagnostic>) {
1257        let (children, diagnostics) = read_source(text, definitions);
1258        let heading = Self {
1259            kind,
1260            source: Some(text.into()),
1261            ..Self::new(depth, children)
1262        };
1263        (heading, diagnostics)
1264    }
1265
1266    /// The text [`Heading::with_source`] built the heading from.
1267    pub fn source(&self) -> Option<&str> {
1268        self.source.as_deref()
1269    }
1270}
1271
1272impl TableCell {
1273    /// A cell from any iterator of inline-convertible children.
1274    pub fn new<I, T>(children: I) -> Self
1275    where
1276        I: IntoIterator<Item = T>,
1277        T: Into<Inline>,
1278    {
1279        Self {
1280            meta: NodeMeta::default(),
1281            children: children.into_iter().map(Into::into).collect(),
1282            source: None,
1283        }
1284    }
1285
1286    /// A cell whose content is `text`, built as [`Paragraph::with_source`]
1287    /// builds a paragraph. A `|` in `text` is content: the writer escapes it.
1288    pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1289        let (children, diagnostics) = read_source(text, definitions);
1290        let cell = Self {
1291            source: Some(text.into()),
1292            ..Self::new(children)
1293        };
1294        (cell, diagnostics)
1295    }
1296
1297    /// The text [`TableCell::with_source`] built the cell from.
1298    pub fn source(&self) -> Option<&str> {
1299        self.source.as_deref()
1300    }
1301}
1302
1303impl LeafDirective {
1304    /// A leaf directive named `name`, with no label and no attributes.
1305    pub fn new(name: impl Into<String>) -> Self {
1306        Self {
1307            meta: NodeMeta::default(),
1308            name: name.into(),
1309            label: Vec::new(),
1310            attributes: Vec::new(),
1311            label_source: None,
1312        }
1313    }
1314
1315    /// A leaf directive named `name` whose label is `text`, built as
1316    /// [`Paragraph::with_source`] builds a paragraph's content.
1317    pub fn with_source(
1318        name: impl Into<String>,
1319        text: &str,
1320        definitions: &Definitions,
1321    ) -> (Self, Vec<Diagnostic>) {
1322        let (label, diagnostics) = read_source(text, definitions);
1323        let directive = Self {
1324            label,
1325            label_source: Some(text.into()),
1326            ..Self::new(name)
1327        };
1328        (directive, diagnostics)
1329    }
1330
1331    /// The text [`LeafDirective::with_source`] built the label from.
1332    pub fn label_source(&self) -> Option<&str> {
1333        self.label_source.as_deref()
1334    }
1335}
1336
1337impl ContainerDirective {
1338    /// A container directive named `name` holding `children`, with no label
1339    /// and no attributes.
1340    pub fn new(name: impl Into<String>, children: Vec<Block>) -> Self {
1341        Self {
1342            meta: NodeMeta::default(),
1343            name: name.into(),
1344            label: Vec::new(),
1345            attributes: Vec::new(),
1346            children,
1347            label_source: None,
1348        }
1349    }
1350
1351    /// A container directive named `name` holding `children`, whose label
1352    /// is `text`, built as [`Paragraph::with_source`] builds a paragraph's
1353    /// content.
1354    pub fn with_source(
1355        name: impl Into<String>,
1356        text: &str,
1357        children: Vec<Block>,
1358        definitions: &Definitions,
1359    ) -> (Self, Vec<Diagnostic>) {
1360        let (label, diagnostics) = read_source(text, definitions);
1361        let directive = Self {
1362            label,
1363            label_source: Some(text.into()),
1364            ..Self::new(name, children)
1365        };
1366        (directive, diagnostics)
1367    }
1368
1369    /// The text [`ContainerDirective::with_source`] built the label from.
1370    pub fn label_source(&self) -> Option<&str> {
1371        self.label_source.as_deref()
1372    }
1373}
1374
1375/// The inlines `text` reads as, with the diagnostics of the read.
1376fn read_source(text: &str, definitions: &Definitions) -> (Vec<Inline>, Vec<Diagnostic>) {
1377    let output = crate::parse::parse_inline_tree(text, definitions);
1378    (output.inlines, output.diagnostics)
1379}
1380
1381impl Link {
1382    /// An inline, bare-destination link with no title.
1383    pub fn new<I, T>(destination: impl Into<String>, children: I) -> Self
1384    where
1385        I: IntoIterator<Item = T>,
1386        T: Into<Inline>,
1387    {
1388        Self {
1389            meta: NodeMeta::default(),
1390            destination: destination.into(),
1391            destination_kind: LinkDestinationKind::Bare,
1392            title: None,
1393            children: children.into_iter().map(Into::into).collect(),
1394        }
1395    }
1396}
1397
1398impl CodeInline {
1399    /// An inline code span holding `value`.
1400    pub fn new(value: impl Into<String>) -> Self {
1401        Self {
1402            meta: NodeMeta::default(),
1403            value: value.into(),
1404        }
1405    }
1406}
1407
1408impl List {
1409    /// A tight, dash-delimited bullet list.
1410    pub fn new<I>(children: I) -> Self
1411    where
1412        I: IntoIterator<Item = ListItem>,
1413    {
1414        Self {
1415            meta: NodeMeta::default(),
1416            ordered: false,
1417            delimiter: ListDelimiter::Dash,
1418            tight: true,
1419            children: children.into_iter().collect(),
1420        }
1421    }
1422}
1423
1424impl ListItem {
1425    /// An item holding `children`, with no task marker and no number.
1426    pub fn new<I, T>(children: I) -> Self
1427    where
1428        I: IntoIterator<Item = T>,
1429        T: Into<Block>,
1430    {
1431        Self {
1432            meta: NodeMeta::default(),
1433            checked: None,
1434            number: None,
1435            children: children.into_iter().map(Into::into).collect(),
1436        }
1437    }
1438}
1439
1440impl BlankLine {
1441    /// A blank line.
1442    pub fn new() -> Self {
1443        Self::default()
1444    }
1445}