Skip to main content

panache_parser/syntax/
block_quotes.rs

1//! Block quote AST node wrappers.
2
3use super::ast::{AstChildren, support};
4use super::{AstNode, PanacheLanguage, SyntaxKind, SyntaxNode};
5
6pub struct BlockQuote(SyntaxNode);
7
8impl AstNode for BlockQuote {
9    type Language = PanacheLanguage;
10
11    fn can_cast(kind: SyntaxKind) -> bool {
12        kind == SyntaxKind::BLOCK_QUOTE
13    }
14
15    fn cast(syntax: SyntaxNode) -> Option<Self> {
16        if Self::can_cast(syntax.kind()) {
17            Some(Self(syntax))
18        } else {
19            None
20        }
21    }
22
23    fn syntax(&self) -> &SyntaxNode {
24        &self.0
25    }
26}
27
28impl BlockQuote {
29    /// Returns block-level children inside this block quote.
30    pub fn blocks(&self) -> impl Iterator<Item = SyntaxNode> {
31        self.0.children().filter(|child| {
32            !matches!(
33                child.kind(),
34                SyntaxKind::BLOCK_QUOTE_MARKER | SyntaxKind::WHITESPACE
35            )
36        })
37    }
38
39    /// Returns nested block quotes directly inside this block quote.
40    pub fn nested_blockquotes(&self) -> AstChildren<BlockQuote> {
41        support::children(&self.0)
42    }
43
44    /// Returns true if the given node is inside any block quote.
45    pub fn contains_node(node: &SyntaxNode) -> bool {
46        node.ancestors()
47            .any(|ancestor| Self::can_cast(ancestor.kind()))
48    }
49
50    /// Returns nesting depth of this block quote (including self).
51    pub fn depth(&self) -> usize {
52        self.0
53            .ancestors()
54            .filter(|ancestor| Self::can_cast(ancestor.kind()))
55            .count()
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use crate::parse;
63
64    #[test]
65    fn blockquote_cast_and_blocks() {
66        let tree = parse("> Intro\n>\n> - Item\n>\n> Outro\n", None);
67
68        let bq = tree
69            .descendants()
70            .find_map(BlockQuote::cast)
71            .expect("blockquote");
72
73        let kinds: Vec<_> = bq.blocks().map(|n| n.kind()).collect();
74        assert!(kinds.contains(&SyntaxKind::PARAGRAPH));
75        assert!(kinds.contains(&SyntaxKind::LIST));
76    }
77
78    #[test]
79    fn blockquote_nested_blockquotes_iterator() {
80        let tree = parse("> outer\n>\n> > inner\n", None);
81
82        let outer = tree
83            .descendants()
84            .find_map(BlockQuote::cast)
85            .expect("outer blockquote");
86
87        assert!(outer.nested_blockquotes().next().is_some());
88        assert_eq!(outer.depth(), 1);
89    }
90
91    #[test]
92    fn blockquote_contains_node_detects_membership() {
93        let tree = parse("> quote\n", None);
94        let blockquote = tree
95            .descendants()
96            .find_map(BlockQuote::cast)
97            .expect("blockquote");
98        assert!(BlockQuote::contains_node(blockquote.syntax()));
99    }
100}