Skip to main content

panache_parser/syntax/
blocks.rs

1use super::ast::support;
2use super::{AstNode, ImageLink, PanacheLanguage, SyntaxKind, SyntaxNode};
3
4#[derive(Debug, Clone, PartialEq, Eq, Hash)]
5pub struct Document(SyntaxNode);
6
7impl AstNode for Document {
8    type Language = PanacheLanguage;
9
10    fn can_cast(kind: SyntaxKind) -> bool {
11        kind == SyntaxKind::DOCUMENT
12    }
13
14    fn cast(syntax: SyntaxNode) -> Option<Self> {
15        Self::can_cast(syntax.kind()).then(|| Self(syntax))
16    }
17
18    fn syntax(&self) -> &SyntaxNode {
19        &self.0
20    }
21}
22
23impl Document {
24    pub fn blocks(&self) -> impl Iterator<Item = SyntaxNode> {
25        self.0.children()
26    }
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Hash)]
30pub struct Paragraph(SyntaxNode);
31
32impl AstNode for Paragraph {
33    type Language = PanacheLanguage;
34
35    fn can_cast(kind: SyntaxKind) -> bool {
36        kind == SyntaxKind::PARAGRAPH
37    }
38
39    fn cast(syntax: SyntaxNode) -> Option<Self> {
40        Self::can_cast(syntax.kind()).then(|| Self(syntax))
41    }
42
43    fn syntax(&self) -> &SyntaxNode {
44        &self.0
45    }
46}
47
48impl Paragraph {
49    pub fn image_links(&self) -> impl Iterator<Item = ImageLink> {
50        support::children(&self.0)
51    }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Hash)]
55pub struct Plain(SyntaxNode);
56
57impl AstNode for Plain {
58    type Language = PanacheLanguage;
59
60    fn can_cast(kind: SyntaxKind) -> bool {
61        kind == SyntaxKind::PLAIN
62    }
63
64    fn cast(syntax: SyntaxNode) -> Option<Self> {
65        Self::can_cast(syntax.kind()).then(|| Self(syntax))
66    }
67
68    fn syntax(&self) -> &SyntaxNode {
69        &self.0
70    }
71}
72
73#[derive(Debug, Clone, PartialEq, Eq, Hash)]
74pub struct LineBlock(SyntaxNode);
75
76impl AstNode for LineBlock {
77    type Language = PanacheLanguage;
78
79    fn can_cast(kind: SyntaxKind) -> bool {
80        kind == SyntaxKind::LINE_BLOCK
81    }
82
83    fn cast(syntax: SyntaxNode) -> Option<Self> {
84        Self::can_cast(syntax.kind()).then(|| Self(syntax))
85    }
86
87    fn syntax(&self) -> &SyntaxNode {
88        &self.0
89    }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq, Hash)]
93pub struct LineBlockLine(SyntaxNode);
94
95impl AstNode for LineBlockLine {
96    type Language = PanacheLanguage;
97
98    fn can_cast(kind: SyntaxKind) -> bool {
99        kind == SyntaxKind::LINE_BLOCK_LINE
100    }
101
102    fn cast(syntax: SyntaxNode) -> Option<Self> {
103        Self::can_cast(syntax.kind()).then(|| Self(syntax))
104    }
105
106    fn syntax(&self) -> &SyntaxNode {
107        &self.0
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112pub struct PandocTitleBlock(SyntaxNode);
113
114impl AstNode for PandocTitleBlock {
115    type Language = PanacheLanguage;
116
117    fn can_cast(kind: SyntaxKind) -> bool {
118        kind == SyntaxKind::PANDOC_TITLE_BLOCK
119    }
120
121    fn cast(syntax: SyntaxNode) -> Option<Self> {
122        Self::can_cast(syntax.kind()).then(|| Self(syntax))
123    }
124
125    fn syntax(&self) -> &SyntaxNode {
126        &self.0
127    }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
131pub struct MmdTitleBlock(SyntaxNode);
132
133impl AstNode for MmdTitleBlock {
134    type Language = PanacheLanguage;
135
136    fn can_cast(kind: SyntaxKind) -> bool {
137        kind == SyntaxKind::MMD_TITLE_BLOCK
138    }
139
140    fn cast(syntax: SyntaxNode) -> Option<Self> {
141        Self::can_cast(syntax.kind()).then(|| Self(syntax))
142    }
143
144    fn syntax(&self) -> &SyntaxNode {
145        &self.0
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn document_blocks_iterates_top_level_nodes() {
155        let input = "# H1\n\nParagraph\n";
156        let tree = crate::parse(input, None);
157        let document = Document::cast(tree).expect("document");
158        let kinds = document
159            .blocks()
160            .map(|node| node.kind())
161            .collect::<Vec<_>>();
162        assert_eq!(
163            kinds,
164            vec![
165                SyntaxKind::HEADING,
166                SyntaxKind::BLANK_LINE,
167                SyntaxKind::PARAGRAPH
168            ]
169        );
170    }
171
172    #[test]
173    fn paragraph_image_links_extracts_inline_image_nodes() {
174        let input = "See ![Alt](img.png) here.\n";
175        let tree = crate::parse(input, None);
176        let paragraph = tree
177            .descendants()
178            .find_map(Paragraph::cast)
179            .expect("paragraph");
180        let images = paragraph.image_links().collect::<Vec<_>>();
181        assert_eq!(images.len(), 1);
182        assert_eq!(
183            images[0].alt().map(|alt| alt.text()),
184            Some("Alt".to_string())
185        );
186    }
187}