Skip to main content

wat_syntax/amber/
node.rs

1use super::traversal::{DescendantToken, DescendantTokens};
2use crate::{
3    AmberToken, GreenNode, NodeOrToken, SyntaxKind, SyntaxKindMatch, SyntaxNode, SyntaxNodePtr, green::GreenChild,
4};
5use text_size::{TextRange, TextSize};
6
7#[derive(Clone, Copy, PartialEq, Eq, Hash)]
8/// Node in the amber syntax tree.
9///
10/// It's a lightweight version of [`SyntaxNode`](crate::SyntaxNode) without access to parent and siblings.
11/// It's much cheaper than [`SyntaxNode`](crate::SyntaxNode) to create and use.
12/// This is preferred to use for better performance if you don't need to visit parent and siblings.
13pub struct AmberNode<'a> {
14    green: &'a GreenNode,
15    range: TextRange,
16}
17
18impl<'a> AmberNode<'a> {
19    #[inline]
20    /// Build a new syntax tree on top of a green tree.
21    pub fn new_root(green: &'a GreenNode) -> Self {
22        Self {
23            green,
24            range: TextRange::new(0.into(), green.text_len()),
25        }
26    }
27
28    #[inline]
29    /// Create a new amber node with the given green node based on the offset.
30    ///
31    /// Note that passing wrong offset can cause unexpectedly incorrect syntax tree. Be careful.
32    pub fn new(green: &'a GreenNode, start: TextSize) -> Self {
33        Self {
34            green,
35            range: TextRange::new(start, start + green.text_len()),
36        }
37    }
38
39    #[inline]
40    /// Kind of this node.
41    pub fn kind(&self) -> SyntaxKind {
42        self.green.kind()
43    }
44
45    #[inline]
46    /// The range that this node covers in the original text.
47    pub fn text_range(&self) -> TextRange {
48        self.range
49    }
50
51    #[inline]
52    /// The underlying green node of this amber node.
53    pub fn green(&self) -> &'a GreenNode {
54        self.green
55    }
56
57    #[inline]
58    /// The corresponding [`SyntaxNodePtr`](crate::SyntaxNodePtr) of this amber node.
59    pub fn to_ptr(&self) -> SyntaxNodePtr {
60        SyntaxNodePtr {
61            kind: self.green.kind(),
62            range: self.range,
63        }
64    }
65
66    #[inline]
67    /// Iterator over the child nodes of this node.
68    ///
69    /// If you want to iterate over both nodes and tokens, use [`children_with_tokens`](Self::children_with_tokens) instead.
70    pub fn children(&self) -> impl DoubleEndedIterator<Item = AmberNode<'a>> + Clone {
71        self.green.slice().iter().filter_map(|child| match child {
72            GreenChild::Node { offset, node } => Some(AmberNode::new(node, self.range.start() + offset)),
73            GreenChild::Token { .. } => None,
74        })
75    }
76
77    #[inline]
78    /// Iterator over specific kinds of child nodes of this node.
79    pub fn children_by_kind<M>(&self, matcher: M) -> impl DoubleEndedIterator<Item = AmberNode<'a>> + use<'_, 'a, M>
80    where
81        M: SyntaxKindMatch,
82    {
83        self.green().slice().iter().filter_map(move |child| match child {
84            GreenChild::Node { offset, node } if matcher.matches(node.kind()) => {
85                Some(AmberNode::new(node, self.range.start() + offset))
86            }
87            _ => None,
88        })
89    }
90
91    #[inline]
92    /// Iterator over specific kinds of child tokens of this node.
93    pub fn tokens_by_kind<M>(&self, matcher: M) -> impl DoubleEndedIterator<Item = AmberToken<'a>> + use<'_, 'a, M>
94    where
95        M: SyntaxKindMatch,
96    {
97        self.green().slice().iter().filter_map(move |child| match child {
98            GreenChild::Token { offset, token } if matcher.matches(token.kind()) => {
99                Some(AmberToken::new(token, self.range.start() + offset))
100            }
101            _ => None,
102        })
103    }
104
105    #[inline]
106    /// Iterator over the child nodes and tokens of this node.
107    pub fn children_with_tokens(&self) -> impl DoubleEndedIterator<Item = NodeOrToken<AmberNode<'a>, AmberToken<'a>>> {
108        self.green.slice().iter().map(|child| match child {
109            GreenChild::Node { offset, node } => AmberNode::new(node, self.range.start() + offset).into(),
110            GreenChild::Token { offset, token } => AmberToken::new(token, self.range.start() + offset).into(),
111        })
112    }
113
114    #[inline]
115    /// Iterator over all tokens in the subtree.
116    ///
117    /// The iterator yields a three-component tuple:
118    /// 1. current token
119    /// 2. the parent of current token
120    /// 3. the grandparent of current token
121    pub fn descendant_tokens(&self) -> impl Iterator<Item = DescendantToken<'a>> + 'a {
122        DescendantTokens::new(*self)
123    }
124
125    #[inline]
126    pub(crate) fn child_or_token_at(&self, index: usize) -> Option<NodeOrToken<AmberNode<'a>, AmberToken<'a>>> {
127        self.green().slice().get(index).map(|child| match child {
128            GreenChild::Node { offset, node } => AmberNode::new(node, self.range.start() + offset).into(),
129            GreenChild::Token { offset, token } => AmberToken::new(token, self.range.start() + offset).into(),
130        })
131    }
132}
133
134impl<'a> From<&'a SyntaxNode> for AmberNode<'a> {
135    #[inline]
136    fn from(node: &'a SyntaxNode) -> Self {
137        Self {
138            green: node.green(),
139            range: node.text_range(),
140        }
141    }
142}