Skip to main content

markdown_syntax/
leaves.rs

1//! The leaves of a tree: paragraphs, headings, table cells, and the labels of
2//! leaf and container directives, the blocks whose content is inline. Each
3//! may keep the text it was built from (`with_source`).
4
5use alloc::string::String;
6use alloc::vec::Vec;
7
8use crate::ast::*;
9use crate::parse::Definitions;
10use crate::span::Span;
11
12/// One leaf: its span (a label's is its directive's), its inlines, and the
13/// text it keeps.
14pub(crate) struct LeafMut<'a> {
15    pub(crate) span: Option<Span>,
16    pub(crate) inlines: &'a mut Vec<Inline>,
17    pub(crate) source: &'a mut Option<String>,
18}
19
20/// Calls `visit_leaf` with the path, span, and kept text of every leaf in
21/// `blocks`, in document order, where `path` is the path of their container.
22pub(crate) fn visit(
23    blocks: &[Block],
24    path: &mut Vec<usize>,
25    visit_leaf: &mut impl FnMut(&[usize], Option<Span>, Option<&str>),
26) {
27    for (index, block) in blocks.iter().enumerate() {
28        path.push(index);
29        match block {
30            Block::Paragraph(node) => visit_leaf(path, node.meta.span, node.source.as_deref()),
31            Block::Heading(node) => visit_leaf(path, node.meta.span, node.source.as_deref()),
32            Block::LeafDirective(node) => {
33                visit_leaf(path, node.meta.span, node.label_source.as_deref())
34            }
35            Block::ContainerDirective(node) => {
36                visit_leaf(path, node.meta.span, node.label_source.as_deref());
37                visit(&node.children, path, visit_leaf);
38            }
39            Block::Table(node) => {
40                for (row, cells) in node.rows.iter().enumerate() {
41                    for (cell, node) in cells.cells.iter().enumerate() {
42                        path.extend([row, cell]);
43                        visit_leaf(path, node.meta.span, node.source.as_deref());
44                        path.truncate(path.len() - 2);
45                    }
46                }
47            }
48            Block::BlockQuote(node) => visit(&node.children, path, visit_leaf),
49            Block::Alert(node) => visit(&node.children, path, visit_leaf),
50            Block::FootnoteDefinition(node) => visit(&node.children, path, visit_leaf),
51            Block::HtmlContainer(node) => match &node.content {
52                HtmlContainerContent::Blocks(children) => visit(children, path, visit_leaf),
53                HtmlContainerContent::Inlines(_) => {}
54            },
55            Block::List(node) => {
56                for (item, node) in node.children.iter().enumerate() {
57                    path.push(item);
58                    visit(&node.children, path, visit_leaf);
59                    path.pop();
60                }
61            }
62            Block::ThematicBreak(_)
63            | Block::CodeBlock(_)
64            | Block::HtmlBlock(_)
65            | Block::Definition(_)
66            | Block::MathBlock(_)
67            | Block::Frontmatter(_)
68            | Block::BlankLine(_) => {}
69        }
70        path.pop();
71    }
72}
73
74/// Calls `visit_leaf` with every leaf in `blocks`, in document order.
75pub(crate) fn visit_mut(blocks: &mut [Block], visit_leaf: &mut impl FnMut(LeafMut<'_>)) {
76    for block in blocks {
77        match block {
78            Block::Paragraph(node) => visit_leaf(LeafMut {
79                span: node.meta.span,
80                inlines: &mut node.children,
81                source: &mut node.source,
82            }),
83            Block::Heading(node) => visit_leaf(LeafMut {
84                span: node.meta.span,
85                inlines: &mut node.children,
86                source: &mut node.source,
87            }),
88            Block::LeafDirective(node) => visit_leaf(LeafMut {
89                span: node.meta.span,
90                inlines: &mut node.label,
91                source: &mut node.label_source,
92            }),
93            Block::ContainerDirective(node) => {
94                visit_leaf(LeafMut {
95                    span: node.meta.span,
96                    inlines: &mut node.label,
97                    source: &mut node.label_source,
98                });
99                visit_mut(&mut node.children, visit_leaf);
100            }
101            Block::Table(node) => {
102                for cell in node.rows.iter_mut().flat_map(|row| &mut row.cells) {
103                    visit_leaf(LeafMut {
104                        span: cell.meta.span,
105                        inlines: &mut cell.children,
106                        source: &mut cell.source,
107                    });
108                }
109            }
110            Block::BlockQuote(node) => visit_mut(&mut node.children, visit_leaf),
111            Block::Alert(node) => visit_mut(&mut node.children, visit_leaf),
112            Block::FootnoteDefinition(node) => visit_mut(&mut node.children, visit_leaf),
113            Block::HtmlContainer(node) => match &mut node.content {
114                HtmlContainerContent::Blocks(children) => visit_mut(children, visit_leaf),
115                HtmlContainerContent::Inlines(_) => {}
116            },
117            Block::List(node) => {
118                for item in &mut node.children {
119                    visit_mut(&mut item.children, visit_leaf);
120                }
121            }
122            Block::ThematicBreak(_)
123            | Block::CodeBlock(_)
124            | Block::HtmlBlock(_)
125            | Block::Definition(_)
126            | Block::MathBlock(_)
127            | Block::Frontmatter(_)
128            | Block::BlankLine(_) => {}
129        }
130    }
131}
132
133/// Whether a leaf in `blocks` keeps the text it was built from.
134pub(crate) fn any_kept(blocks: &[Block]) -> bool {
135    let mut kept = false;
136    visit(blocks, &mut Vec::new(), &mut |_, _, source| {
137        kept |= source.is_some()
138    });
139    kept
140}
141
142impl Document {
143    /// Builds again, with this document's definitions, every leaf that keeps
144    /// the text it was built from (`with_source`), as `with_source` builds it,
145    /// keeping the leaf's span and its other fields. A leaf built with other
146    /// definitions than the document's, or kept after the document's
147    /// definitions changed, holds inlines its text no longer reads as, and
148    /// the writer then writes it from its inlines; this makes it read as its
149    /// text again.
150    pub fn reread_kept_sources(&mut self) {
151        let definitions = Definitions::of(self);
152        visit_mut(&mut self.children, &mut |leaf| {
153            if let Some(text) = leaf.source.as_deref() {
154                *leaf.inlines = crate::parse::parse_inline_tree(text, &definitions).inlines;
155            }
156        });
157    }
158}