1use cstree::util::NodeOrToken;
9use gdscript_base::TextRange;
10use gdscript_syntax::{GdNode, GdToken, SyntaxKind};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct AstPtr {
19 pub kind: SyntaxKind,
21 pub range: TextRange,
23}
24
25impl AstPtr {
26 #[must_use]
28 pub fn of(node: &GdNode) -> Self {
29 Self {
30 kind: node.kind(),
31 range: text_range_of(node),
32 }
33 }
34
35 #[must_use]
44 pub fn to_node(self, root: &GdNode) -> Option<GdNode> {
45 find_node(root, self)
46 }
47}
48
49fn find_node(node: &GdNode, ptr: AstPtr) -> Option<GdNode> {
50 let range = text_range_of(node);
51 if node.kind() == ptr.kind && range == ptr.range {
52 return Some(node.clone());
53 }
54 if range.start <= ptr.range.start && range.end >= ptr.range.end {
56 for child in node.children() {
57 if let Some(found) = find_node(child, ptr) {
58 return Some(found);
59 }
60 }
61 }
62 None
63}
64
65#[must_use]
68pub fn text_range_of(node: &GdNode) -> TextRange {
69 let r = node.text_range();
70 TextRange::new(u32::from(r.start()), u32::from(r.end()))
71}
72
73#[must_use]
75pub fn has_token(node: &GdNode, kind: SyntaxKind) -> bool {
76 node.children_with_tokens()
77 .filter_map(NodeOrToken::into_token)
78 .any(|t| t.kind() == kind)
79}
80
81#[must_use]
83pub fn child_token_text(node: &GdNode, kind: SyntaxKind) -> Option<String> {
84 node.children_with_tokens()
85 .filter_map(NodeOrToken::into_token)
86 .find(|t| t.kind() == kind)
87 .map(|t| t.text().to_owned())
88}
89
90#[must_use]
93pub fn is_expr_kind(kind: SyntaxKind) -> bool {
94 matches!(
95 kind,
96 SyntaxKind::BinExpr
97 | SyntaxKind::UnaryExpr
98 | SyntaxKind::TernaryExpr
99 | SyntaxKind::CastExpr
100 | SyntaxKind::IsExpr
101 | SyntaxKind::InExpr
102 | SyntaxKind::CallExpr
103 | SyntaxKind::IndexExpr
104 | SyntaxKind::FieldExpr
105 | SyntaxKind::AwaitExpr
106 | SyntaxKind::Literal
107 | SyntaxKind::NameRef
108 | SyntaxKind::ArrayLit
109 | SyntaxKind::DictLit
110 | SyntaxKind::LambdaExpr
111 | SyntaxKind::ParenExpr
112 | SyntaxKind::PreloadExpr
113 | SyntaxKind::GetNodeExpr
114 | SyntaxKind::UniqueNodeExpr
115 )
116}
117
118pub fn first_child(node: &GdNode, pred: impl Fn(SyntaxKind) -> bool) -> Option<GdNode> {
120 node.children()
121 .find_map(|c| pred(c.kind()).then(|| c.clone()))
122}
123
124#[must_use]
126pub fn first_child_expr(node: &GdNode) -> Option<GdNode> {
127 first_child(node, is_expr_kind)
128}
129
130#[must_use]
132pub fn child_exprs(node: &GdNode) -> Vec<GdNode> {
133 node.children()
134 .filter(|c| is_expr_kind(c.kind()))
135 .cloned()
136 .collect()
137}
138
139#[must_use]
141pub fn children_of(node: &GdNode, kind: SyntaxKind) -> Vec<GdNode> {
142 node.children()
143 .filter(|c| c.kind() == kind)
144 .cloned()
145 .collect()
146}
147
148#[must_use]
154pub fn extends_head_token(node: &GdNode) -> Option<GdToken> {
155 let mut after_extends = false;
156 for t in node
157 .children_with_tokens()
158 .filter_map(NodeOrToken::into_token)
159 {
160 if t.kind() == SyntaxKind::ExtendsKw {
161 after_extends = true;
162 } else if after_extends && t.kind() == SyntaxKind::Ident {
163 return Some(t.clone());
164 }
165 }
166 None
167}
168
169#[must_use]
171pub fn first_token(node: &GdNode) -> Option<GdToken> {
172 node.children_with_tokens()
173 .filter_map(NodeOrToken::into_token)
174 .find(|t| !t.kind().is_trivia() && !t.kind().is_synthetic_layout())
175 .cloned()
176}
177
178#[must_use]
180pub fn token_range(token: &GdToken) -> TextRange {
181 let r = token.text_range();
182 TextRange::new(u32::from(r.start()), u32::from(r.end()))
183}