1use cstree::util::NodeOrToken;
10
11use crate::SyntaxKind;
12use crate::syntax_kind::{GdNode, GdToken};
13
14pub trait AstNode: Sized {
16 fn can_cast(kind: SyntaxKind) -> bool;
18 fn cast(node: GdNode) -> Option<Self>;
20 fn syntax(&self) -> &GdNode;
22}
23
24macro_rules! ast_node {
26 ($(#[$meta:meta])* $name:ident) => {
27 $(#[$meta])*
28 #[derive(Debug, Clone)]
29 pub struct $name(GdNode);
30
31 impl AstNode for $name {
32 fn can_cast(kind: SyntaxKind) -> bool {
33 kind == SyntaxKind::$name
34 }
35 fn cast(node: GdNode) -> Option<Self> {
36 if node.kind() == SyntaxKind::$name {
37 Some(Self(node))
38 } else {
39 None
40 }
41 }
42 fn syntax(&self) -> &GdNode {
43 &self.0
44 }
45 }
46 };
47}
48
49ast_node!(
50 SourceFile
52);
53ast_node!(ClassNameDecl);
54ast_node!(ExtendsClause);
55ast_node!(Annotation);
56ast_node!(FuncDecl);
57ast_node!(VarDecl);
58ast_node!(ConstDecl);
59ast_node!(EnumDecl);
60ast_node!(EnumVariant);
61ast_node!(SignalDecl);
62ast_node!(InnerClassDecl);
63ast_node!(ClassBody);
64ast_node!(ParamList);
65ast_node!(Param);
66ast_node!(Block);
67ast_node!(TypeRef);
68ast_node!(
69 Name
71);
72
73fn child<N: AstNode>(node: &GdNode) -> Option<N> {
77 node.children().find_map(|c| N::cast(c.clone()))
78}
79
80fn children<N: AstNode>(node: &GdNode) -> impl Iterator<Item = N> + '_ {
82 node.children().filter_map(|c| N::cast(c.clone()))
83}
84
85fn has_token(node: &GdNode, kind: SyntaxKind) -> bool {
87 node.children_with_tokens()
88 .filter_map(NodeOrToken::into_token)
89 .any(|t| t.kind() == kind)
90}
91
92fn token_text(node: &GdNode, kind: SyntaxKind) -> Option<String> {
94 node.children_with_tokens()
95 .filter_map(NodeOrToken::into_token)
96 .find(|t| t.kind() == kind)
97 .map(|t| t.text().to_owned())
98}
99
100fn name_token_text(node: &GdNode) -> Option<String> {
105 node.children_with_tokens()
106 .filter_map(NodeOrToken::into_token)
107 .find(|t| {
108 matches!(
109 t.kind(),
110 SyntaxKind::Ident | SyntaxKind::MatchKw | SyntaxKind::WhenKw
111 )
112 })
113 .map(|t| t.text().to_owned())
114}
115
116impl Name {
119 #[must_use]
121 pub fn text(&self) -> Option<String> {
122 name_token_text(&self.0)
123 }
124}
125
126impl SourceFile {
127 pub fn decls(&self) -> impl Iterator<Item = Decl> + '_ {
129 self.0.children().filter_map(|c| Decl::cast(c.clone()))
130 }
131}
132
133impl FuncDecl {
134 #[must_use]
136 pub fn name(&self) -> Option<Name> {
137 child(&self.0)
138 }
139 #[must_use]
141 pub fn param_list(&self) -> Option<ParamList> {
142 child(&self.0)
143 }
144 #[must_use]
146 pub fn body(&self) -> Option<Block> {
147 child(&self.0)
148 }
149 #[must_use]
151 pub fn return_type(&self) -> Option<TypeRef> {
152 child(&self.0)
153 }
154 #[must_use]
156 pub fn is_static(&self) -> bool {
157 has_token(&self.0, SyntaxKind::StaticKw)
158 }
159}
160
161impl ParamList {
162 pub fn params(&self) -> impl Iterator<Item = Param> + '_ {
164 children(&self.0)
165 }
166}
167
168impl Param {
169 #[must_use]
171 pub fn name(&self) -> Option<Name> {
172 child(&self.0)
173 }
174 #[must_use]
176 pub fn type_ref(&self) -> Option<TypeRef> {
177 child(&self.0)
178 }
179}
180
181impl VarDecl {
182 #[must_use]
184 pub fn name(&self) -> Option<Name> {
185 child(&self.0)
186 }
187 #[must_use]
189 pub fn type_ref(&self) -> Option<TypeRef> {
190 child(&self.0)
191 }
192 #[must_use]
194 pub fn is_static(&self) -> bool {
195 has_token(&self.0, SyntaxKind::StaticKw)
196 }
197}
198
199impl ConstDecl {
200 #[must_use]
202 pub fn name(&self) -> Option<Name> {
203 child(&self.0)
204 }
205}
206
207impl EnumDecl {
208 #[must_use]
210 pub fn name(&self) -> Option<Name> {
211 child(&self.0)
212 }
213 pub fn variants(&self) -> impl Iterator<Item = EnumVariant> + '_ {
215 children(&self.0)
216 }
217}
218
219impl EnumVariant {
220 #[must_use]
222 pub fn text(&self) -> Option<String> {
223 name_token_text(&self.0)
224 }
225}
226
227impl SignalDecl {
228 #[must_use]
230 pub fn name(&self) -> Option<Name> {
231 child(&self.0)
232 }
233 #[must_use]
235 pub fn param_list(&self) -> Option<ParamList> {
236 child(&self.0)
237 }
238}
239
240impl ClassNameDecl {
241 #[must_use]
243 pub fn name(&self) -> Option<Name> {
244 child(&self.0)
245 }
246}
247
248impl InnerClassDecl {
249 #[must_use]
251 pub fn name(&self) -> Option<Name> {
252 child(&self.0)
253 }
254 #[must_use]
256 pub fn body(&self) -> Option<ClassBody> {
257 child(&self.0)
258 }
259}
260
261impl ClassBody {
262 pub fn decls(&self) -> impl Iterator<Item = Decl> + '_ {
264 self.0.children().filter_map(|c| Decl::cast(c.clone()))
265 }
266}
267
268impl Annotation {
269 #[must_use]
271 pub fn name(&self) -> Option<String> {
272 token_text(&self.0, SyntaxKind::Ident)
273 }
274}
275
276impl TypeRef {
277 #[must_use]
279 pub fn text(&self) -> Option<String> {
280 self.0
281 .children_with_tokens()
282 .filter_map(NodeOrToken::into_token)
283 .find(|t| matches!(t.kind(), SyntaxKind::Ident | SyntaxKind::VoidKw))
284 .map(|t| t.text().to_owned())
285 }
286}
287
288#[derive(Debug, Clone)]
290pub enum Decl {
291 ClassName(ClassNameDecl),
293 Func(FuncDecl),
295 Var(VarDecl),
297 Const(ConstDecl),
299 Enum(EnumDecl),
301 Signal(SignalDecl),
303 Class(InnerClassDecl),
305}
306
307impl Decl {
308 #[must_use]
310 pub fn cast(node: GdNode) -> Option<Self> {
311 match node.kind() {
312 SyntaxKind::ClassNameDecl => ClassNameDecl::cast(node).map(Self::ClassName),
313 SyntaxKind::FuncDecl => FuncDecl::cast(node).map(Self::Func),
314 SyntaxKind::VarDecl => VarDecl::cast(node).map(Self::Var),
315 SyntaxKind::ConstDecl => ConstDecl::cast(node).map(Self::Const),
316 SyntaxKind::EnumDecl => EnumDecl::cast(node).map(Self::Enum),
317 SyntaxKind::SignalDecl => SignalDecl::cast(node).map(Self::Signal),
318 SyntaxKind::InnerClassDecl => InnerClassDecl::cast(node).map(Self::Class),
319 _ => None,
320 }
321 }
322
323 #[must_use]
325 pub fn syntax(&self) -> &GdNode {
326 match self {
327 Self::ClassName(d) => d.syntax(),
328 Self::Func(d) => d.syntax(),
329 Self::Var(d) => d.syntax(),
330 Self::Const(d) => d.syntax(),
331 Self::Enum(d) => d.syntax(),
332 Self::Signal(d) => d.syntax(),
333 Self::Class(d) => d.syntax(),
334 }
335 }
336
337 #[must_use]
339 pub fn name(&self) -> Option<String> {
340 let name = match self {
341 Self::ClassName(d) => d.name(),
342 Self::Func(d) => d.name(),
343 Self::Var(d) => d.name(),
344 Self::Const(d) => d.name(),
345 Self::Enum(d) => d.name(),
346 Self::Signal(d) => d.name(),
347 Self::Class(d) => d.name(),
348 };
349 name.and_then(|n| n.text())
350 }
351}
352
353#[must_use]
356pub fn descendants(root: &GdNode) -> Vec<GdNode> {
357 let mut out = vec![root.clone()];
358 for child in root.children() {
359 out.extend(descendants(child));
360 }
361 out
362}
363
364#[must_use]
366pub fn token_at(root: &GdNode, offset: text_size::TextSize) -> Option<GdToken> {
367 root.token_at_offset(offset).right_biased()
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373 use crate::parse;
374
375 #[test]
376 fn func_accessors() {
377 let parse = parse("static func add(a: int, b := 1) -> int:\n\treturn a + b\n");
378 let file = SourceFile::cast(parse.syntax_node()).unwrap();
379 let func = file
380 .decls()
381 .find_map(|d| match d {
382 Decl::Func(f) => Some(f),
383 _ => None,
384 })
385 .unwrap();
386 assert!(func.is_static());
387 assert_eq!(func.name().and_then(|n| n.text()).as_deref(), Some("add"));
388 assert_eq!(
389 func.return_type().and_then(|t| t.text()).as_deref(),
390 Some("int")
391 );
392 let params: Vec<_> = func
393 .param_list()
394 .unwrap()
395 .params()
396 .filter_map(|p| p.name().and_then(|n| n.text()))
397 .collect();
398 assert_eq!(params, vec!["a", "b"]);
399 assert!(func.body().is_some());
400 }
401
402 #[test]
403 fn declarations_are_enumerated() {
404 let parse = parse(
405 "class_name Foo\nconst K = 1\nvar x: int\nsignal s\nenum E { A, B }\nfunc f():\n\tpass\nclass Inner:\n\tvar y = 2\n",
406 );
407 let file = SourceFile::cast(parse.syntax_node()).unwrap();
408 let names: Vec<_> = file.decls().map(|d| d.name().unwrap_or_default()).collect();
409 assert_eq!(names, vec!["Foo", "K", "x", "s", "E", "f", "Inner"]);
410 }
411
412 #[test]
413 fn enum_variants_and_inner_class_members() {
414 let parse =
415 parse("enum E { A, B = 5, C }\nclass Inner:\n\tvar a = 1\n\tfunc m():\n\t\tpass\n");
416 let file = SourceFile::cast(parse.syntax_node()).unwrap();
417
418 let en = file
419 .decls()
420 .find_map(|d| match d {
421 Decl::Enum(e) => Some(e),
422 _ => None,
423 })
424 .unwrap();
425 let variants: Vec<_> = en.variants().filter_map(|v| v.text()).collect();
426 assert_eq!(variants, vec!["A", "B", "C"]);
427
428 let inner = file
429 .decls()
430 .find_map(|d| match d {
431 Decl::Class(c) => Some(c),
432 _ => None,
433 })
434 .unwrap();
435 let member_names: Vec<_> = inner
436 .body()
437 .unwrap()
438 .decls()
439 .map(|d| d.name().unwrap_or_default())
440 .collect();
441 assert_eq!(member_names, vec!["a", "m"]);
442 }
443
444 #[test]
445 fn token_at_offset_finds_identifier() {
446 let src = "var hello = 1\n";
447 let parse = parse(src);
448 let node = parse.syntax_node();
449 let tok = node
451 .token_at_offset(text_size::TextSize::new(5))
452 .right_biased()
453 .unwrap();
454 assert_eq!(tok.kind(), SyntaxKind::Ident);
455 assert_eq!(tok.text(), "hello");
456 }
457}