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 `&` or `÷`.
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: ``.
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 `&` or `÷`. 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. `&`).
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: ``.
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 Inline {
1025 /// The inline subtree of this node, or an empty slice for a leaf. Covers the
1026 /// `alt`/`label` fields uniformly, so a generic walker never silently skips
1027 /// an image's alt text or a directive's label.
1028 pub fn children(&self) -> &[Inline] {
1029 match self {
1030 Inline::Emphasis(n) => &n.children,
1031 Inline::Strong(n) => &n.children,
1032 Inline::Delete(n) => &n.children,
1033 Inline::Mark(n) => &n.children,
1034 Inline::Link(n) => &n.children,
1035 Inline::Image(n) => &n.alt,
1036 Inline::LinkReference(n) => &n.children,
1037 Inline::ImageReference(n) => &n.alt,
1038 Inline::InlineFootnote(n) => &n.children,
1039 Inline::TextDirective(n) => &n.label,
1040 Inline::Text(_)
1041 | Inline::Escape(_)
1042 | Inline::CharacterReference(_)
1043 | Inline::Shortcode(_)
1044 | Inline::Code(_)
1045 | Inline::Autolink(_)
1046 | Inline::Html(_)
1047 | Inline::SoftBreak(_)
1048 | Inline::LineBreak(_)
1049 | Inline::Math(_)
1050 | Inline::FootnoteReference(_)
1051 | Inline::WikiLink(_)
1052 | Inline::Tag(_) => &[],
1053 }
1054 }
1055}
1056
1057impl CharacterReference {
1058 /// A character reference node for `reference`, written with its `&` and
1059 /// `;` (e.g. `&`).
1060 pub fn new(reference: impl Into<String>) -> Self {
1061 Self {
1062 meta: NodeMeta::default(),
1063 reference: reference.into(),
1064 }
1065 }
1066
1067 /// The character the reference decodes to, or `None` when
1068 /// [`reference`](Self::reference) is not exactly one character reference.
1069 /// A valid document holds only references that decode.
1070 pub fn value(&self) -> Option<String> {
1071 crate::decode::decode_character_reference(&self.reference)
1072 }
1073}
1074
1075impl Autolink {
1076 /// An autolink of `form` whose text is `text`.
1077 pub fn new(form: AutolinkForm, text: impl Into<String>) -> Self {
1078 Self {
1079 meta: NodeMeta::default(),
1080 form,
1081 text: text.into(),
1082 }
1083 }
1084
1085 /// The link target the text writes, or `None` when the text is not
1086 /// exactly one autolink of its form. An angle-bracket URI links to itself
1087 /// and an email address to it after `mailto:`; a literal `www.` domain
1088 /// links to it after `http://`, a literal email address to it after
1089 /// `mailto:`, and any other literal autolink to itself. A valid document
1090 /// holds only autolinks with a destination.
1091 pub fn destination(&self) -> Option<String> {
1092 match self.form {
1093 AutolinkForm::Angle => crate::parse::angle_autolink_destination(&self.text),
1094 AutolinkForm::Literal => crate::parse::literal_autolink_destination(&self.text),
1095 }
1096 }
1097}
1098
1099impl Title {
1100 /// A title holding `value`, quoted as `kind`.
1101 pub fn new(value: impl Into<String>, kind: LinkTitleKind) -> Self {
1102 Self {
1103 value: value.into(),
1104 kind,
1105 }
1106 }
1107}
1108
1109impl Shortcode {
1110 /// The emoji the crate's pinned gemoji table gives this shortcode's
1111 /// name, or `None` for a name the table does not hold.
1112 pub fn glyph(&self) -> Option<&'static str> {
1113 crate::gemoji::glyph(&self.name)
1114 }
1115}
1116
1117impl Tag {
1118 /// A tag named `name`, written without its `#`.
1119 pub fn new(name: impl Into<String>) -> Self {
1120 Self {
1121 meta: NodeMeta::default(),
1122 name: name.into(),
1123 }
1124 }
1125}
1126
1127impl WikiLink {
1128 /// The target with its backslash escapes and character references
1129 /// decoded, as CommonMark decodes a link destination: `a \| b & c`
1130 /// decodes to `a | b & c`.
1131 pub fn decoded_target(&self) -> String {
1132 crate::decode::decode_escapes_and_references(&self.target)
1133 }
1134
1135 /// The label with its backslash escapes and character references
1136 /// decoded, as [`WikiLink::decoded_target`] decodes the target.
1137 pub fn decoded_label(&self) -> String {
1138 crate::decode::decode_escapes_and_references(&self.label)
1139 }
1140}
1141
1142impl Text {
1143 /// A text node with the given string value.
1144 pub fn new(value: impl Into<String>) -> Self {
1145 Self {
1146 meta: NodeMeta::default(),
1147 value: value.into(),
1148 }
1149 }
1150}
1151
1152impl From<&str> for Text {
1153 fn from(value: &str) -> Self {
1154 Text::new(value)
1155 }
1156}
1157
1158impl From<String> for Text {
1159 fn from(value: String) -> Self {
1160 Text::new(value)
1161 }
1162}
1163
1164impl Paragraph {
1165 /// A paragraph from any iterator of inline-convertible children.
1166 pub fn new<I, T>(children: I) -> Self
1167 where
1168 I: IntoIterator<Item = T>,
1169 T: Into<Inline>,
1170 {
1171 Self {
1172 meta: NodeMeta::default(),
1173 children: children.into_iter().map(Into::into).collect(),
1174 source: None,
1175 }
1176 }
1177
1178 /// A paragraph whose content is `text`, read as
1179 /// [`parse_inline`](crate::parse_inline) reads it with `definitions`, and
1180 /// the diagnostics of that read. The paragraph keeps `text`, which the
1181 /// writer writes in place of its inlines while they are the ones `text`
1182 /// reads as; the spans of its inlines are offsets in `text`. Pass the
1183 /// definitions of the document the paragraph will be written in
1184 /// ([`Definitions::of`]); after they change, or for a leaf built with
1185 /// others, [`Document::reread_kept_sources`] reads its text again.
1186 pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1187 let (children, diagnostics) = read_source(text, definitions);
1188 let paragraph = Self {
1189 source: Some(text.into()),
1190 ..Self::new(children)
1191 };
1192 (paragraph, diagnostics)
1193 }
1194
1195 /// The text [`Paragraph::with_source`] built the paragraph from.
1196 pub fn source(&self) -> Option<&str> {
1197 self.source.as_deref()
1198 }
1199}
1200
1201impl Heading {
1202 /// An ATX heading of the given depth.
1203 pub fn new<I, T>(depth: u8, children: I) -> Self
1204 where
1205 I: IntoIterator<Item = T>,
1206 T: Into<Inline>,
1207 {
1208 Self {
1209 meta: NodeMeta::default(),
1210 depth,
1211 kind: HeadingKind::Atx,
1212 children: children.into_iter().map(Into::into).collect(),
1213 source: None,
1214 }
1215 }
1216
1217 /// A heading of the given depth and kind whose content is `text`, built
1218 /// as [`Paragraph::with_source`] builds a paragraph.
1219 pub fn with_source(
1220 depth: u8,
1221 kind: HeadingKind,
1222 text: &str,
1223 definitions: &Definitions,
1224 ) -> (Self, Vec<Diagnostic>) {
1225 let (children, diagnostics) = read_source(text, definitions);
1226 let heading = Self {
1227 kind,
1228 source: Some(text.into()),
1229 ..Self::new(depth, children)
1230 };
1231 (heading, diagnostics)
1232 }
1233
1234 /// The text [`Heading::with_source`] built the heading from.
1235 pub fn source(&self) -> Option<&str> {
1236 self.source.as_deref()
1237 }
1238}
1239
1240impl TableCell {
1241 /// A cell from any iterator of inline-convertible children.
1242 pub fn new<I, T>(children: I) -> Self
1243 where
1244 I: IntoIterator<Item = T>,
1245 T: Into<Inline>,
1246 {
1247 Self {
1248 meta: NodeMeta::default(),
1249 children: children.into_iter().map(Into::into).collect(),
1250 source: None,
1251 }
1252 }
1253
1254 /// A cell whose content is `text`, built as [`Paragraph::with_source`]
1255 /// builds a paragraph. A `|` in `text` is content: the writer escapes it.
1256 pub fn with_source(text: &str, definitions: &Definitions) -> (Self, Vec<Diagnostic>) {
1257 let (children, diagnostics) = read_source(text, definitions);
1258 let cell = Self {
1259 source: Some(text.into()),
1260 ..Self::new(children)
1261 };
1262 (cell, diagnostics)
1263 }
1264
1265 /// The text [`TableCell::with_source`] built the cell from.
1266 pub fn source(&self) -> Option<&str> {
1267 self.source.as_deref()
1268 }
1269}
1270
1271impl LeafDirective {
1272 /// A leaf directive named `name`, with no label and no attributes.
1273 pub fn new(name: impl Into<String>) -> Self {
1274 Self {
1275 meta: NodeMeta::default(),
1276 name: name.into(),
1277 label: Vec::new(),
1278 attributes: Vec::new(),
1279 label_source: None,
1280 }
1281 }
1282
1283 /// A leaf directive named `name` whose label is `text`, built as
1284 /// [`Paragraph::with_source`] builds a paragraph's content.
1285 pub fn with_source(
1286 name: impl Into<String>,
1287 text: &str,
1288 definitions: &Definitions,
1289 ) -> (Self, Vec<Diagnostic>) {
1290 let (label, diagnostics) = read_source(text, definitions);
1291 let directive = Self {
1292 label,
1293 label_source: Some(text.into()),
1294 ..Self::new(name)
1295 };
1296 (directive, diagnostics)
1297 }
1298
1299 /// The text [`LeafDirective::with_source`] built the label from.
1300 pub fn label_source(&self) -> Option<&str> {
1301 self.label_source.as_deref()
1302 }
1303}
1304
1305impl ContainerDirective {
1306 /// A container directive named `name` holding `children`, with no label
1307 /// and no attributes.
1308 pub fn new(name: impl Into<String>, children: Vec<Block>) -> Self {
1309 Self {
1310 meta: NodeMeta::default(),
1311 name: name.into(),
1312 label: Vec::new(),
1313 attributes: Vec::new(),
1314 children,
1315 label_source: None,
1316 }
1317 }
1318
1319 /// A container directive named `name` holding `children`, whose label
1320 /// is `text`, built as [`Paragraph::with_source`] builds a paragraph's
1321 /// content.
1322 pub fn with_source(
1323 name: impl Into<String>,
1324 text: &str,
1325 children: Vec<Block>,
1326 definitions: &Definitions,
1327 ) -> (Self, Vec<Diagnostic>) {
1328 let (label, diagnostics) = read_source(text, definitions);
1329 let directive = Self {
1330 label,
1331 label_source: Some(text.into()),
1332 ..Self::new(name, children)
1333 };
1334 (directive, diagnostics)
1335 }
1336
1337 /// The text [`ContainerDirective::with_source`] built the label from.
1338 pub fn label_source(&self) -> Option<&str> {
1339 self.label_source.as_deref()
1340 }
1341}
1342
1343/// The inlines `text` reads as, with the diagnostics of the read.
1344fn read_source(text: &str, definitions: &Definitions) -> (Vec<Inline>, Vec<Diagnostic>) {
1345 let output = crate::parse::parse_inline_tree(text, definitions);
1346 (output.inlines, output.diagnostics)
1347}
1348
1349impl Link {
1350 /// An inline, bare-destination link with no title.
1351 pub fn new<I, T>(destination: impl Into<String>, children: I) -> Self
1352 where
1353 I: IntoIterator<Item = T>,
1354 T: Into<Inline>,
1355 {
1356 Self {
1357 meta: NodeMeta::default(),
1358 destination: destination.into(),
1359 destination_kind: LinkDestinationKind::Bare,
1360 title: None,
1361 children: children.into_iter().map(Into::into).collect(),
1362 }
1363 }
1364}
1365
1366impl CodeInline {
1367 /// An inline code span holding `value`.
1368 pub fn new(value: impl Into<String>) -> Self {
1369 Self {
1370 meta: NodeMeta::default(),
1371 value: value.into(),
1372 }
1373 }
1374}
1375
1376impl List {
1377 /// A tight, dash-delimited bullet list.
1378 pub fn new<I>(children: I) -> Self
1379 where
1380 I: IntoIterator<Item = ListItem>,
1381 {
1382 Self {
1383 meta: NodeMeta::default(),
1384 ordered: false,
1385 delimiter: ListDelimiter::Dash,
1386 tight: true,
1387 children: children.into_iter().collect(),
1388 }
1389 }
1390}
1391
1392impl ListItem {
1393 /// An item holding `children`, with no task marker and no number.
1394 pub fn new<I, T>(children: I) -> Self
1395 where
1396 I: IntoIterator<Item = T>,
1397 T: Into<Block>,
1398 {
1399 Self {
1400 meta: NodeMeta::default(),
1401 checked: None,
1402 number: None,
1403 children: children.into_iter().map(Into::into).collect(),
1404 }
1405 }
1406}
1407
1408impl BlankLine {
1409 /// A blank line.
1410 pub fn new() -> Self {
1411 Self::default()
1412 }
1413}