1use std::cell::RefCell;
2use std::collections::{HashMap, HashSet};
3use std::path::{Path, PathBuf};
4use std::sync::{Arc, LazyLock, RwLock};
5use std::time::SystemTime;
6
7use streaming_iterator::StreamingIterator;
8use tree_sitter::{Language, Node, Parser, Query, QueryCursor, Tree};
9
10use crate::cache_freshness::{self, FileFreshness, FreshnessVerdict};
11use crate::callgraph::resolve_module_path;
12use crate::error::AftError;
13use crate::symbol_cache_disk;
14use crate::symbols::{Range, Symbol, SymbolKind, SymbolMatch};
15
16const MAX_REEXPORT_DEPTH: usize = 10;
17
18const TS_QUERY: &str = r#"
21;; function declarations
22(function_declaration
23 name: (identifier) @fn.name) @fn.def
24
25;; function-like values assigned to const/let/var
26(lexical_declaration
27 (variable_declarator
28 name: (identifier) @arrow.name
29 value: (arrow_function) @arrow.body)) @arrow.def
30(lexical_declaration
31 (variable_declarator
32 name: (identifier) @arrow.name
33 value: (function_expression) @arrow.body)) @arrow.def
34(lexical_declaration
35 (variable_declarator
36 name: (identifier) @arrow.name
37 value: (generator_function) @arrow.body)) @arrow.def
38
39;; anonymous default exports
40(export_statement
41 value: (function_expression) @default.body) @default.def
42(export_statement
43 value: (generator_function) @default.body) @default.def
44(export_statement
45 value: (class) @default.body) @default.def
46
47;; class declarations
48(class_declaration
49 name: (type_identifier) @class.name) @class.def
50
51;; method definitions inside classes
52(class_declaration
53 name: (type_identifier) @method.class_name
54 body: (class_body
55 (method_definition
56 name: (property_identifier) @method.name) @method.def))
57
58;; interface declarations
59(interface_declaration
60 name: (type_identifier) @interface.name) @interface.def
61
62;; enum declarations
63(enum_declaration
64 name: (identifier) @enum.name) @enum.def
65
66;; type alias declarations
67(type_alias_declaration
68 name: (type_identifier) @type_alias.name) @type_alias.def
69
70;; top-level const/let variable declarations
71(lexical_declaration
72 (variable_declarator
73 name: (identifier) @var.name)) @var.def
74
75;; export statement wrappers (top-level only)
76(export_statement) @export.stmt
77"#;
78
79const JS_QUERY: &str = r#"
80;; function declarations
81(function_declaration
82 name: (identifier) @fn.name) @fn.def
83
84;; function-like values assigned to const/let/var
85(lexical_declaration
86 (variable_declarator
87 name: (identifier) @arrow.name
88 value: (arrow_function) @arrow.body)) @arrow.def
89(lexical_declaration
90 (variable_declarator
91 name: (identifier) @arrow.name
92 value: (function_expression) @arrow.body)) @arrow.def
93(lexical_declaration
94 (variable_declarator
95 name: (identifier) @arrow.name
96 value: (generator_function) @arrow.body)) @arrow.def
97
98;; anonymous default exports
99(export_statement
100 value: (function_expression) @default.body) @default.def
101(export_statement
102 value: (generator_function) @default.body) @default.def
103(export_statement
104 value: (class) @default.body) @default.def
105
106;; class declarations
107(class_declaration
108 name: (identifier) @class.name) @class.def
109
110;; method definitions inside classes
111(class_declaration
112 name: (identifier) @method.class_name
113 body: (class_body
114 (method_definition
115 name: (property_identifier) @method.name) @method.def))
116
117;; top-level const/let variable declarations
118(lexical_declaration
119 (variable_declarator
120 name: (identifier) @var.name)) @var.def
121
122;; export statement wrappers (top-level only)
123(export_statement) @export.stmt
124"#;
125
126const PY_QUERY: &str = r#"
127;; function definitions (top-level and nested)
128(function_definition
129 name: (identifier) @fn.name) @fn.def
130
131;; class definitions
132(class_definition
133 name: (identifier) @class.name) @class.def
134
135;; decorated definitions (wraps function_definition or class_definition)
136(decorated_definition
137 (decorator) @dec.decorator) @dec.def
138"#;
139
140const RS_QUERY: &str = r#"
141;; free functions (with optional visibility)
142(function_item
143 name: (identifier) @fn.name) @fn.def
144
145;; struct items
146(struct_item
147 name: (type_identifier) @struct.name) @struct.def
148
149;; enum items
150(enum_item
151 name: (type_identifier) @enum.name) @enum.def
152
153;; trait items
154(trait_item
155 name: (type_identifier) @trait.name) @trait.def
156
157;; impl blocks — capture the whole block to find methods
158(impl_item) @impl.def
159
160;; visibility modifiers on any item
161(visibility_modifier) @vis.mod
162"#;
163
164const GO_QUERY: &str = r#"
165;; function declarations
166(function_declaration
167 name: (identifier) @fn.name) @fn.def
168
169;; method declarations (with receiver)
170(method_declaration
171 name: (field_identifier) @method.name) @method.def
172
173;; type declarations (struct and interface)
174(type_declaration
175 (type_spec
176 name: (type_identifier) @type.name
177 type: (_) @type.body)) @type.def
178"#;
179
180const C_QUERY: &str = r#"
181;; function definitions
182(function_definition
183 declarator: (function_declarator
184 declarator: (identifier) @fn.name)) @fn.def
185
186;; function declarations / prototypes
187(declaration
188 declarator: (function_declarator
189 declarator: (identifier) @fn.name)) @fn.def
190
191;; struct declarations
192(struct_specifier
193 name: (type_identifier) @struct.name
194 body: (field_declaration_list)) @struct.def
195
196;; enum declarations
197(enum_specifier
198 name: (type_identifier) @enum.name
199 body: (enumerator_list)) @enum.def
200
201;; typedef aliases
202(type_definition
203 declarator: (type_identifier) @type.name) @type.def
204
205;; macros
206(preproc_def
207 name: (identifier) @macro.name) @macro.def
208
209(preproc_function_def
210 name: (identifier) @macro.name) @macro.def
211"#;
212
213const CPP_QUERY: &str = r#"
214;; free function definitions
215(function_definition
216 declarator: (function_declarator
217 declarator: (identifier) @fn.name)) @fn.def
218
219;; free function declarations
220(declaration
221 declarator: (function_declarator
222 declarator: (identifier) @fn.name)) @fn.def
223
224;; inline method definitions / declarations inside class bodies
225(function_definition
226 declarator: (function_declarator
227 declarator: (field_identifier) @method.name)) @method.def
228
229(field_declaration
230 declarator: (function_declarator
231 declarator: (field_identifier) @method.name)) @method.def
232
233;; qualified functions / methods
234(function_definition
235 declarator: (function_declarator
236 declarator: (qualified_identifier
237 scope: (_) @qual.scope
238 name: (identifier) @qual.name))) @qual.def
239
240(declaration
241 declarator: (function_declarator
242 declarator: (qualified_identifier
243 scope: (_) @qual.scope
244 name: (identifier) @qual.name))) @qual.def
245
246;; class / struct / enum / namespace declarations
247(class_specifier
248 name: (_) @class.name) @class.def
249
250(struct_specifier
251 name: (_) @struct.name) @struct.def
252
253(enum_specifier
254 name: (_) @enum.name) @enum.def
255
256(namespace_definition
257 name: (_) @namespace.name) @namespace.def
258
259;; template declarations
260(template_declaration
261 (class_specifier
262 name: (_) @template.class.name) @template.class.item) @template.class.def
263
264(template_declaration
265 (struct_specifier
266 name: (_) @template.struct.name) @template.struct.item) @template.struct.def
267
268(template_declaration
269 (function_definition
270 declarator: (function_declarator
271 declarator: (identifier) @template.fn.name)) @template.fn.item) @template.fn.def
272
273(template_declaration
274 (function_definition
275 declarator: (function_declarator
276 declarator: (qualified_identifier
277 scope: (_) @template.qual.scope
278 name: (identifier) @template.qual.name))) @template.qual.item) @template.qual.def
279"#;
280
281const ZIG_QUERY: &str = r#"
282;; functions
283(function_declaration
284 name: (identifier) @fn.name) @fn.def
285
286;; container declarations bound to const names
287(variable_declaration
288 (identifier) @struct.name
289 "="
290 (struct_declaration) @struct.body) @struct.def
291
292(variable_declaration
293 (identifier) @enum.name
294 "="
295 (enum_declaration) @enum.body) @enum.def
296
297(variable_declaration
298 (identifier) @union.name
299 "="
300 (union_declaration) @union.body) @union.def
301
302;; const declarations
303(variable_declaration
304 (identifier) @const.name) @const.def
305
306;; tests
307(test_declaration
308 (string) @test.name) @test.def
309
310(test_declaration
311 (identifier) @test.name) @test.def
312"#;
313
314const CSHARP_QUERY: &str = r#"
315;; types
316(class_declaration
317 name: (identifier) @class.name) @class.def
318
319(interface_declaration
320 name: (identifier) @interface.name) @interface.def
321
322(struct_declaration
323 name: (identifier) @struct.name) @struct.def
324
325(enum_declaration
326 name: (identifier) @enum.name) @enum.def
327
328;; members
329(method_declaration
330 name: (identifier) @method.name) @method.def
331
332(property_declaration
333 name: (identifier) @property.name) @property.def
334
335;; namespaces
336(namespace_declaration
337 name: (_) @namespace.name) @namespace.def
338
339(file_scoped_namespace_declaration
340 name: (_) @namespace.name) @namespace.def
341"#;
342
343const BASH_QUERY: &str = r#"
346;; function definitions (both `function foo()` and `foo()` styles)
347(function_definition
348 name: (word) @fn.name) @fn.def
349"#;
350
351const SOL_QUERY: &str = r#"
354;; contracts / libraries / interfaces
355(contract_declaration
356 name: (identifier) @contract.name) @contract.def
357
358(library_declaration
359 name: (identifier) @library.name) @library.def
360
361(interface_declaration
362 name: (identifier) @interface.name) @interface.def
363
364;; functions, modifiers, constructors
365(function_definition
366 name: (identifier) @fn.name) @fn.def
367
368(modifier_definition
369 name: (identifier) @modifier.name) @modifier.def
370
371(constructor_definition) @constructor.def
372
373(fallback_receive_definition) @fallback_receive.def
374
375;; events / errors
376(event_definition
377 name: (identifier) @event.name) @event.def
378
379(error_declaration
380 name: (identifier) @error.name) @error.def
381
382;; data types
383(struct_declaration
384 name: (identifier) @struct.name) @struct.def
385
386(enum_declaration
387 name: (identifier) @enum.name) @enum.def
388
389;; state variables (top-level inside a contract)
390(state_variable_declaration
391 name: (identifier) @var.name) @var.def
392"#;
393
394const SCALA_QUERY: &str = r#"
395;; classes / objects / traits
396(class_definition
397 name: (identifier) @class.name) @class.def
398(object_definition
399 name: (identifier) @object.name) @object.def
400(enum_definition
401 name: (_) @enum.name) @enum.def
402(trait_definition
403 name: (identifier) @trait.name) @trait.def
404;; methods (def)
405(function_definition
406 name: (identifier) @fn.name) @fn.def
407(function_declaration
408 name: (identifier) @fn.name) @fn.def
409;; vals / vars / type aliases
410(val_definition
411 pattern: (identifier) @val.name) @val.def
412(var_definition
413 pattern: (identifier) @var.name) @var.def
414(given_definition
415 name: (_) @given.name) @given.def
416(type_definition
417 name: (type_identifier) @type.name) @type.def
418"#;
419
420const JAVA_QUERY: &str = r#"
421;; types
422(class_declaration
423 name: (identifier) @class.name) @class.def
424(interface_declaration
425 name: (identifier) @interface.name) @interface.def
426(annotation_type_declaration
427 name: (identifier) @interface.name) @interface.def
428(enum_declaration
429 name: (identifier) @enum.name) @enum.def
430(record_declaration
431 name: (identifier) @struct.name) @struct.def
432
433;; members
434(method_declaration
435 name: (identifier) @fn.name) @fn.def
436(constructor_declaration
437 name: (identifier) @fn.name) @fn.def
438(field_declaration
439 declarator: (variable_declarator
440 name: (identifier) @var.name)) @var.def
441"#;
442
443const RUBY_QUERY: &str = r#"
444;; modules / classes
445(module
446 name: (constant) @module.name) @module.def
447(class
448 name: (constant) @class.name) @class.def
449
450;; methods
451(method
452 name: (_) @fn.name) @fn.def
453(singleton_method
454 name: (_) @fn.name) @fn.def
455
456;; constants
457(assignment
458 left: (constant) @var.name) @var.def
459"#;
460
461const KOTLIN_QUERY: &str = r#"
462;; declarations
463(class_declaration
464 (type_identifier) @class.name) @class.def
465(object_declaration
466 (type_identifier) @object.name) @object.def
467(function_declaration
468 (simple_identifier) @fn.name) @fn.def
469(property_declaration
470 (variable_declaration
471 (simple_identifier) @var.name)) @var.def
472(type_alias
473 (type_identifier) @type.name) @type.def
474"#;
475
476const SWIFT_QUERY: &str = r#"
477;; types
478(class_declaration
479 name: (type_identifier) @class.name) @class.def
480(protocol_declaration
481 name: (type_identifier) @interface.name) @interface.def
482
483;; functions and members
484(function_declaration
485 name: (simple_identifier) @fn.name) @fn.def
486(protocol_function_declaration
487 name: (simple_identifier) @fn.name) @fn.def
488(property_declaration
489 name: (pattern
490 bound_identifier: (simple_identifier) @var.name)) @var.def
491(typealias_declaration
492 name: (type_identifier) @type.name) @type.def
493"#;
494
495const PHP_QUERY: &str = r#"
496;; namespaces and types
497(namespace_definition
498 name: (namespace_name) @namespace.name) @namespace.def
499(class_declaration
500 name: (name) @class.name) @class.def
501(interface_declaration
502 name: (name) @interface.name) @interface.def
503(trait_declaration
504 name: (name) @trait.name) @trait.def
505(enum_declaration
506 name: (name) @enum.name) @enum.def
507
508;; functions and members
509(function_definition
510 name: (name) @fn.name) @fn.def
511(method_declaration
512 name: (name) @fn.name) @fn.def
513(property_declaration
514 (property_element
515 name: (variable_name (name) @var.name))) @var.def
516"#;
517
518const LUA_QUERY: &str = r#"
519;; functions
520(function_declaration
521 name: (identifier) @fn.name) @fn.def
522(function_declaration
523 name: (dot_index_expression
524 field: (identifier) @fn.name)) @fn.def
525(function_declaration
526 name: (method_index_expression
527 method: (identifier) @fn.name)) @fn.def
528
529;; locals / module tables
530(variable_declaration
531 (assignment_statement
532 (variable_list
533 name: (identifier) @var.name))) @var.def
534(variable_declaration
535 (assignment_statement
536 (variable_list
537 name: (variable) @var.name))) @var.def
538(variable_declaration
539 (variable_list
540 name: (identifier) @var.name)) @var.def
541(variable_declaration
542 (variable_list
543 name: (variable) @var.name)) @var.def
544"#;
545
546const PERL_QUERY: &str = r#"
547;; packages and subroutines
548(package_statement
549 (package_name) @package.name) @package.def
550(function_definition
551 name: (identifier) @fn.name) @fn.def
552(function_definition_without_sub
553 name: (identifier) @fn.name) @fn.def
554
555;; constants / lexical variables
556(use_constant_statement
557 constant: (identifier) @var.name) @var.def
558(variable_declaration
559 variable_name: (_) @var.name) @var.def
560"#;
561
562#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
564pub enum LangId {
565 TypeScript,
566 Tsx,
567 JavaScript,
568 Python,
569 Rust,
570 Go,
571 C,
572 Cpp,
573 Zig,
574 CSharp,
575 Bash,
576 Html,
577 Markdown,
578 Solidity,
579 Vue,
580 Json,
581 Scala,
582 Java,
583 Ruby,
584 Kotlin,
585 Swift,
586 Php,
587 Lua,
588 Perl,
589}
590
591pub fn detect_language(path: &Path) -> Option<LangId> {
593 let ext = path.extension()?.to_str()?;
594 match ext {
595 "ts" | "mts" | "cts" => Some(LangId::TypeScript),
596 "tsx" => Some(LangId::Tsx),
597 "js" | "jsx" | "mjs" | "cjs" => Some(LangId::JavaScript),
598 "py" | "pyi" => Some(LangId::Python),
599 "rs" => Some(LangId::Rust),
600 "go" => Some(LangId::Go),
601 "c" | "h" => Some(LangId::C),
602 "cc" | "cpp" | "cxx" | "hpp" | "hh" => Some(LangId::Cpp),
603 "zig" => Some(LangId::Zig),
604 "cs" => Some(LangId::CSharp),
605 "sh" | "bash" | "zsh" => Some(LangId::Bash),
606 "html" | "htm" => Some(LangId::Html),
607 "md" | "markdown" | "mdx" => Some(LangId::Markdown),
608 "sol" => Some(LangId::Solidity),
609 "vue" => Some(LangId::Vue),
610 "json" | "jsonc" => Some(LangId::Json),
611 "scala" | "sc" => Some(LangId::Scala),
612 "java" => Some(LangId::Java),
613 "rb" => Some(LangId::Ruby),
614 "kt" | "kts" => Some(LangId::Kotlin),
615 "swift" => Some(LangId::Swift),
616 "php" => Some(LangId::Php),
617 "lua" => Some(LangId::Lua),
618 "pl" | "pm" | "t" => Some(LangId::Perl),
619 _ => None,
620 }
621}
622
623pub fn grammar_for(lang: LangId) -> Language {
625 match lang {
626 LangId::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
627 LangId::Tsx => tree_sitter_typescript::LANGUAGE_TSX.into(),
628 LangId::JavaScript => tree_sitter_javascript::LANGUAGE.into(),
629 LangId::Python => tree_sitter_python::LANGUAGE.into(),
630 LangId::Rust => tree_sitter_rust::LANGUAGE.into(),
631 LangId::Go => tree_sitter_go::LANGUAGE.into(),
632 LangId::C => tree_sitter_c::LANGUAGE.into(),
633 LangId::Cpp => tree_sitter_cpp::LANGUAGE.into(),
634 LangId::Zig => tree_sitter_zig::LANGUAGE.into(),
635 LangId::CSharp => tree_sitter_c_sharp::LANGUAGE.into(),
636 LangId::Bash => tree_sitter_bash::LANGUAGE.into(),
637 LangId::Html => tree_sitter_html::LANGUAGE.into(),
638 LangId::Markdown => tree_sitter_md::LANGUAGE.into(),
639 LangId::Solidity => tree_sitter_solidity::LANGUAGE.into(),
640 LangId::Vue => tree_sitter_vue::LANGUAGE.into(),
641 LangId::Json => tree_sitter_json::LANGUAGE.into(),
642 LangId::Scala => tree_sitter_scala::LANGUAGE.into(),
643 LangId::Java => tree_sitter_java::LANGUAGE.into(),
644 LangId::Ruby => tree_sitter_ruby::LANGUAGE.into(),
645 LangId::Kotlin => tree_sitter_kotlin_sg::LANGUAGE.into(),
646 LangId::Swift => tree_sitter_swift::LANGUAGE.into(),
647 LangId::Php => tree_sitter_php::LANGUAGE_PHP.into(),
648 LangId::Lua => tree_sitter_lua::LANGUAGE.into(),
649 LangId::Perl => tree_sitter_perl::LANGUAGE.into(),
650 }
651}
652
653fn query_for(lang: LangId) -> Option<&'static str> {
655 match lang {
656 LangId::TypeScript | LangId::Tsx => Some(TS_QUERY),
657 LangId::JavaScript => Some(JS_QUERY),
658 LangId::Python => Some(PY_QUERY),
659 LangId::Rust => Some(RS_QUERY),
660 LangId::Go => Some(GO_QUERY),
661 LangId::C => Some(C_QUERY),
662 LangId::Cpp => Some(CPP_QUERY),
663 LangId::Zig => Some(ZIG_QUERY),
664 LangId::CSharp => Some(CSHARP_QUERY),
665 LangId::Bash => Some(BASH_QUERY),
666 LangId::Html => None, LangId::Markdown => None,
668 LangId::Solidity => Some(SOL_QUERY),
669 LangId::Vue => None,
670 LangId::Json => None,
671 LangId::Scala => Some(SCALA_QUERY),
672 LangId::Java => Some(JAVA_QUERY),
673 LangId::Ruby => Some(RUBY_QUERY),
674 LangId::Kotlin => Some(KOTLIN_QUERY),
675 LangId::Swift => Some(SWIFT_QUERY),
676 LangId::Php => Some(PHP_QUERY),
677 LangId::Lua => Some(LUA_QUERY),
678 LangId::Perl => Some(PERL_QUERY),
679 }
680}
681
682static TS_QUERY_CACHE: LazyLock<Result<Query, String>> =
683 LazyLock::new(|| compile_query(LangId::TypeScript));
684static TSX_QUERY_CACHE: LazyLock<Result<Query, String>> =
685 LazyLock::new(|| compile_query(LangId::Tsx));
686static JS_QUERY_CACHE: LazyLock<Result<Query, String>> =
687 LazyLock::new(|| compile_query(LangId::JavaScript));
688static PY_QUERY_CACHE: LazyLock<Result<Query, String>> =
689 LazyLock::new(|| compile_query(LangId::Python));
690static RS_QUERY_CACHE: LazyLock<Result<Query, String>> =
691 LazyLock::new(|| compile_query(LangId::Rust));
692static GO_QUERY_CACHE: LazyLock<Result<Query, String>> =
693 LazyLock::new(|| compile_query(LangId::Go));
694static C_QUERY_CACHE: LazyLock<Result<Query, String>> = LazyLock::new(|| compile_query(LangId::C));
695static CPP_QUERY_CACHE: LazyLock<Result<Query, String>> =
696 LazyLock::new(|| compile_query(LangId::Cpp));
697static ZIG_QUERY_CACHE: LazyLock<Result<Query, String>> =
698 LazyLock::new(|| compile_query(LangId::Zig));
699static CSHARP_QUERY_CACHE: LazyLock<Result<Query, String>> =
700 LazyLock::new(|| compile_query(LangId::CSharp));
701static BASH_QUERY_CACHE: LazyLock<Result<Query, String>> =
702 LazyLock::new(|| compile_query(LangId::Bash));
703static SOL_QUERY_CACHE: LazyLock<Result<Query, String>> =
704 LazyLock::new(|| compile_query(LangId::Solidity));
705static SCALA_QUERY_CACHE: LazyLock<Result<Query, String>> =
706 LazyLock::new(|| compile_query(LangId::Scala));
707static JAVA_QUERY_CACHE: LazyLock<Result<Query, String>> =
708 LazyLock::new(|| compile_query(LangId::Java));
709static RUBY_QUERY_CACHE: LazyLock<Result<Query, String>> =
710 LazyLock::new(|| compile_query(LangId::Ruby));
711static KOTLIN_QUERY_CACHE: LazyLock<Result<Query, String>> =
712 LazyLock::new(|| compile_query(LangId::Kotlin));
713static SWIFT_QUERY_CACHE: LazyLock<Result<Query, String>> =
714 LazyLock::new(|| compile_query(LangId::Swift));
715static PHP_QUERY_CACHE: LazyLock<Result<Query, String>> =
716 LazyLock::new(|| compile_query(LangId::Php));
717static LUA_QUERY_CACHE: LazyLock<Result<Query, String>> =
718 LazyLock::new(|| compile_query(LangId::Lua));
719static PERL_QUERY_CACHE: LazyLock<Result<Query, String>> =
720 LazyLock::new(|| compile_query(LangId::Perl));
721
722fn compile_query(lang: LangId) -> Result<Query, String> {
723 let query_src = query_for(lang).ok_or_else(|| format!("missing query for {lang:?}"))?;
724 let grammar = grammar_for(lang);
725 Query::new(&grammar, query_src)
726 .map_err(|error| format!("query compile error for {lang:?}: {error}"))
727}
728
729fn cached_query_for(lang: LangId) -> Result<Option<&'static Query>, AftError> {
730 let query = match lang {
731 LangId::TypeScript => Some(&*TS_QUERY_CACHE),
732 LangId::Tsx => Some(&*TSX_QUERY_CACHE),
733 LangId::JavaScript => Some(&*JS_QUERY_CACHE),
734 LangId::Python => Some(&*PY_QUERY_CACHE),
735 LangId::Rust => Some(&*RS_QUERY_CACHE),
736 LangId::Go => Some(&*GO_QUERY_CACHE),
737 LangId::C => Some(&*C_QUERY_CACHE),
738 LangId::Cpp => Some(&*CPP_QUERY_CACHE),
739 LangId::Zig => Some(&*ZIG_QUERY_CACHE),
740 LangId::CSharp => Some(&*CSHARP_QUERY_CACHE),
741 LangId::Bash => Some(&*BASH_QUERY_CACHE),
742 LangId::Solidity => Some(&*SOL_QUERY_CACHE),
743 LangId::Scala => Some(&*SCALA_QUERY_CACHE),
744 LangId::Java => Some(&*JAVA_QUERY_CACHE),
745 LangId::Ruby => Some(&*RUBY_QUERY_CACHE),
746 LangId::Kotlin => Some(&*KOTLIN_QUERY_CACHE),
747 LangId::Swift => Some(&*SWIFT_QUERY_CACHE),
748 LangId::Php => Some(&*PHP_QUERY_CACHE),
749 LangId::Lua => Some(&*LUA_QUERY_CACHE),
750 LangId::Perl => Some(&*PERL_QUERY_CACHE),
751 LangId::Html | LangId::Markdown | LangId::Vue | LangId::Json => None,
752 };
753
754 query
755 .map(|result| {
756 result.as_ref().map_err(|message| AftError::ParseError {
757 message: message.clone(),
758 })
759 })
760 .transpose()
761}
762
763struct CachedTree {
765 mtime: SystemTime,
766 size: u64,
767 content_hash: blake3::Hash,
768 tree: Tree,
769}
770
771#[derive(Clone)]
773struct CachedSymbols {
774 mtime: SystemTime,
775 size: u64,
776 content_hash: blake3::Hash,
777 symbols: Vec<Symbol>,
778}
779
780fn content_hash_for_source(source: &str) -> blake3::Hash {
781 if source.len() as u64 > cache_freshness::CONTENT_HASH_SIZE_CAP {
782 cache_freshness::zero_hash()
783 } else {
784 cache_freshness::hash_bytes(source.as_bytes())
785 }
786}
787
788fn cached_file_is_fresh(
789 path: &Path,
790 cached_mtime: SystemTime,
791 cached_size: u64,
792 cached_content_hash: blake3::Hash,
793 fallback_mtime: SystemTime,
794) -> bool {
795 let Ok(metadata) = std::fs::metadata(path) else {
796 return false;
797 };
798 let current_size = metadata.len();
799 if current_size != cached_size {
800 return false;
801 }
802
803 let current_mtime = metadata.modified().unwrap_or(fallback_mtime);
804 if current_size > cache_freshness::CONTENT_HASH_SIZE_CAP {
805 return current_mtime == cached_mtime;
806 }
807
808 matches!(
809 cache_freshness::hash_file_if_small(path, current_size),
810 Ok(Some(hash)) if hash == cached_content_hash
811 )
812}
813
814#[derive(Clone, Default)]
817pub struct SymbolCache {
818 entries: HashMap<PathBuf, CachedSymbols>,
819 generation: u64,
820 project_root: Option<PathBuf>,
821}
822
823pub type SharedSymbolCache = Arc<RwLock<SymbolCache>>;
824
825impl SymbolCache {
826 pub fn new() -> Self {
827 Self {
828 entries: HashMap::new(),
829 generation: 0,
830 project_root: None,
831 }
832 }
833
834 pub fn set_project_root(&mut self, project_root: PathBuf) {
836 debug_assert!(project_root.is_absolute());
837 self.project_root = Some(project_root);
838 }
839
840 pub fn set_project_root_for_generation(
843 &mut self,
844 generation: u64,
845 project_root: PathBuf,
846 ) -> bool {
847 if self.generation != generation {
848 return false;
849 }
850 self.set_project_root(project_root);
851 true
852 }
853
854 pub fn insert(
856 &mut self,
857 path: PathBuf,
858 mtime: SystemTime,
859 size: u64,
860 content_hash: blake3::Hash,
861 symbols: Vec<Symbol>,
862 ) {
863 self.entries.insert(
864 path,
865 CachedSymbols {
866 mtime,
867 size,
868 content_hash,
869 symbols,
870 },
871 );
872 }
873
874 pub fn insert_for_generation(
876 &mut self,
877 generation: u64,
878 path: PathBuf,
879 mtime: SystemTime,
880 size: u64,
881 content_hash: blake3::Hash,
882 symbols: Vec<Symbol>,
883 ) -> bool {
884 if self.generation != generation {
885 return false;
886 }
887 self.insert(path, mtime, size, content_hash, symbols);
888 true
889 }
890
891 pub fn get(&self, path: &Path, mtime: SystemTime) -> Option<Vec<Symbol>> {
893 self.entries.get(path).and_then(|cached| {
894 cached_file_is_fresh(path, cached.mtime, cached.size, cached.content_hash, mtime)
895 .then(|| cached.symbols.clone())
896 })
897 }
898
899 pub fn contains_path_with_mtime(&self, path: &Path, mtime: SystemTime) -> bool {
901 self.entries
902 .get(path)
903 .is_some_and(|cached| cached.mtime == mtime)
904 }
905
906 pub fn load_from_disk(
908 &mut self,
909 storage_dir: &Path,
910 project_key: &str,
911 current_root: &Path,
912 ) -> usize {
913 debug_assert!(current_root.is_absolute());
914 let Some(cache) = symbol_cache_disk::read_from_disk(storage_dir, project_key) else {
915 return 0;
916 };
917
918 self.project_root = Some(current_root.to_path_buf());
919 self.entries.clear();
920 let mut loaded = 0usize;
921
922 for entry in cache.entries {
923 let Some(path) =
924 crate::search_index::cached_path_under_root(current_root, &entry.relative_path)
925 else {
926 continue;
927 };
928 let cached_freshness = FileFreshness {
929 mtime: entry.mtime,
930 size: entry.size,
931 content_hash: entry.content_hash,
932 };
933 let mtime = match cache_freshness::verify_file(&path, &cached_freshness) {
934 FreshnessVerdict::HotFresh => entry.mtime,
935 FreshnessVerdict::ContentFresh { new_mtime, .. } => new_mtime,
936 FreshnessVerdict::Stale | FreshnessVerdict::Deleted => continue,
937 };
938
939 self.entries.insert(
940 path,
941 CachedSymbols {
942 mtime,
943 size: entry.size,
944 content_hash: entry.content_hash,
945 symbols: entry.symbols,
946 },
947 );
948 loaded += 1;
949 }
950
951 loaded
952 }
953
954 pub fn load_from_disk_for_generation(
957 &mut self,
958 generation: u64,
959 storage_dir: &Path,
960 project_key: &str,
961 current_root: &Path,
962 ) -> usize {
963 if self.generation != generation {
964 return 0;
965 }
966 self.load_from_disk(storage_dir, project_key, current_root)
967 }
968
969 pub fn invalidate(&mut self, path: &Path) {
971 self.entries.remove(path);
972 }
973
974 pub fn reset(&mut self) -> u64 {
976 self.entries.clear();
977 self.project_root = None;
978 self.generation = self.generation.wrapping_add(1);
979 self.generation
980 }
981
982 pub fn generation(&self) -> u64 {
984 self.generation
985 }
986
987 pub fn contains_key(&self, path: &Path) -> bool {
989 self.entries.contains_key(path)
990 }
991
992 pub fn len(&self) -> usize {
994 self.entries.len()
995 }
996
997 pub(crate) fn project_root(&self) -> Option<PathBuf> {
998 self.project_root.clone()
999 }
1000
1001 pub(crate) fn disk_entries(
1002 &self,
1003 ) -> Vec<(&PathBuf, SystemTime, u64, blake3::Hash, &Vec<Symbol>)> {
1004 self.entries
1005 .iter()
1006 .filter_map(|(path, cached)| {
1007 if cached.symbols.is_empty() {
1008 return None;
1009 }
1010 Some((
1011 path,
1012 cached.mtime,
1013 cached.size,
1014 cached.content_hash,
1015 &cached.symbols,
1016 ))
1017 })
1018 .collect()
1019 }
1020}
1021
1022pub struct FileParser {
1025 cache: HashMap<PathBuf, CachedTree>,
1026 parsers: HashMap<LangId, Parser>,
1027 symbol_cache: SharedSymbolCache,
1028 symbol_cache_generation: Option<u64>,
1029}
1030
1031impl FileParser {
1032 pub fn new() -> Self {
1034 Self::with_symbol_cache(Arc::new(RwLock::new(SymbolCache::new())))
1035 }
1036
1037 pub fn with_symbol_cache(symbol_cache: SharedSymbolCache) -> Self {
1039 Self::with_symbol_cache_generation(symbol_cache, None)
1040 }
1041
1042 pub fn with_symbol_cache_generation(
1044 symbol_cache: SharedSymbolCache,
1045 symbol_cache_generation: Option<u64>,
1046 ) -> Self {
1047 Self {
1048 cache: HashMap::new(),
1049 parsers: HashMap::new(),
1050 symbol_cache,
1051 symbol_cache_generation,
1052 }
1053 }
1054
1055 fn parser_for(&mut self, lang: LangId) -> Result<&mut Parser, AftError> {
1056 use std::collections::hash_map::Entry;
1057
1058 match self.parsers.entry(lang) {
1059 Entry::Occupied(entry) => Ok(entry.into_mut()),
1060 Entry::Vacant(entry) => {
1061 let grammar = grammar_for(lang);
1062 let mut parser = Parser::new();
1063 parser.set_language(&grammar).map_err(|e| {
1064 crate::slog_error!("grammar init failed for {:?}: {}", lang, e);
1065 AftError::ParseError {
1066 message: format!("grammar init failed for {:?}: {}", lang, e),
1067 }
1068 })?;
1069 Ok(entry.insert(parser))
1070 }
1071 }
1072 }
1073
1074 pub fn symbol_cache_len(&self) -> usize {
1076 self.symbol_cache
1077 .read()
1078 .map(|cache| cache.len())
1079 .unwrap_or(0)
1080 }
1081
1082 pub fn symbol_cache(&self) -> SharedSymbolCache {
1084 Arc::clone(&self.symbol_cache)
1085 }
1086
1087 pub fn parse(&mut self, path: &Path) -> Result<(&Tree, LangId), AftError> {
1090 let lang = detect_language(path).ok_or_else(|| AftError::InvalidRequest {
1091 message: format!(
1092 "unsupported file extension: {}",
1093 path.extension()
1094 .and_then(|e| e.to_str())
1095 .unwrap_or("<none>")
1096 ),
1097 })?;
1098
1099 let canon = path.to_path_buf();
1100 let current_mtime = std::fs::metadata(path)
1101 .and_then(|m| m.modified())
1102 .map_err(|e| AftError::FileNotFound {
1103 path: format!("{}: {}", path.display(), e),
1104 })?;
1105
1106 let needs_reparse = match self.cache.get(&canon) {
1110 Some(cached) => !cached_file_is_fresh(
1111 path,
1112 cached.mtime,
1113 cached.size,
1114 cached.content_hash,
1115 current_mtime,
1116 ),
1117 None => true,
1118 };
1119
1120 if needs_reparse {
1121 let source = std::fs::read_to_string(path).map_err(|e| AftError::FileNotFound {
1122 path: format!("{}: {}", path.display(), e),
1123 })?;
1124
1125 let tree = self.parser_for(lang)?.parse(&source, None).ok_or_else(|| {
1126 crate::slog_error!("parse failed for {}", path.display());
1127 AftError::ParseError {
1128 message: format!("tree-sitter parse returned None for {}", path.display()),
1129 }
1130 })?;
1131
1132 self.cache.insert(
1133 canon.clone(),
1134 CachedTree {
1135 mtime: current_mtime,
1136 size: source.len() as u64,
1137 content_hash: content_hash_for_source(&source),
1138 tree,
1139 },
1140 );
1141 }
1142
1143 let cached = self.cache.get(&canon).ok_or_else(|| AftError::ParseError {
1144 message: format!("parser cache missing entry for {}", path.display()),
1145 })?;
1146 Ok((&cached.tree, lang))
1147 }
1148
1149 pub fn parse_cloned(&mut self, path: &Path) -> Result<(Tree, LangId), AftError> {
1154 let (tree, lang) = self.parse(path)?;
1155 Ok((tree.clone(), lang))
1156 }
1157
1158 pub fn extract_symbols(&mut self, path: &Path) -> Result<Vec<Symbol>, AftError> {
1162 let canon = path.to_path_buf();
1163 let current_mtime = std::fs::metadata(path)
1164 .and_then(|m| m.modified())
1165 .map_err(|e| AftError::FileNotFound {
1166 path: format!("{}: {}", path.display(), e),
1167 })?;
1168
1169 if let Some(symbols) = self
1171 .symbol_cache
1172 .read()
1173 .map_err(|_| AftError::ParseError {
1174 message: "symbol cache lock poisoned".to_string(),
1175 })?
1176 .get(&canon, current_mtime)
1177 {
1178 return Ok(symbols);
1179 }
1180
1181 let source = std::fs::read_to_string(path).map_err(|e| AftError::FileNotFound {
1182 path: format!("{}: {}", path.display(), e),
1183 })?;
1184 let size = source.len() as u64;
1185 let content_hash = content_hash_for_source(&source);
1186
1187 let symbols = {
1188 let (tree, lang) = self.parse(path)?;
1189 extract_symbols_from_tree(&source, tree, lang)?
1190 };
1191
1192 let mut symbol_cache = self
1194 .symbol_cache
1195 .write()
1196 .map_err(|_| AftError::ParseError {
1197 message: "symbol cache lock poisoned".to_string(),
1198 })?;
1199 if let Some(generation) = self.symbol_cache_generation {
1200 symbol_cache.insert_for_generation(
1201 generation,
1202 canon,
1203 current_mtime,
1204 size,
1205 content_hash,
1206 symbols.clone(),
1207 );
1208 } else {
1209 symbol_cache.insert(canon, current_mtime, size, content_hash, symbols.clone());
1210 }
1211
1212 Ok(symbols)
1213 }
1214
1215 pub fn invalidate_symbols(&mut self, path: &Path) {
1217 if let Ok(mut symbol_cache) = self.symbol_cache.write() {
1218 symbol_cache.invalidate(path);
1219 }
1220 self.cache.remove(path);
1221 }
1222}
1223
1224pub fn extract_symbols_from_tree(
1229 source: &str,
1230 tree: &Tree,
1231 lang: LangId,
1232) -> Result<Vec<Symbol>, AftError> {
1233 let root = tree.root_node();
1234
1235 if lang == LangId::Html {
1236 return extract_html_symbols(source, &root);
1237 }
1238 if lang == LangId::Markdown {
1239 return extract_md_symbols(source, &root);
1240 }
1241 if lang == LangId::Vue {
1242 return extract_vue_symbols(source, &root);
1243 }
1244 if lang == LangId::Json {
1245 return extract_json_symbols(source, &root);
1246 }
1247
1248 let query = cached_query_for(lang)?.ok_or_else(|| AftError::InvalidRequest {
1249 message: format!("no query patterns implemented for {:?} yet", lang),
1250 })?;
1251
1252 match lang {
1253 LangId::TypeScript | LangId::Tsx => extract_ts_symbols(source, &root, query),
1254 LangId::JavaScript => extract_js_symbols(source, &root, query),
1255 LangId::Python => extract_py_symbols(source, &root, query),
1256 LangId::Rust => extract_rs_symbols(source, &root, query),
1257 LangId::Go => extract_go_symbols(source, &root, query),
1258 LangId::C => extract_c_symbols(source, &root, query),
1259 LangId::Cpp => extract_cpp_symbols(source, &root, query),
1260 LangId::Zig => extract_zig_symbols(source, &root, query),
1261 LangId::CSharp => extract_csharp_symbols(source, &root, query),
1262 LangId::Bash => extract_bash_symbols(source, &root, query),
1263 LangId::Solidity => extract_solidity_symbols(source, &root, query),
1264 LangId::Scala => extract_scala_symbols(source, &root, query),
1265 LangId::Java => extract_java_symbols(source, &root, query),
1266 LangId::Ruby => extract_ruby_symbols(source, &root, query),
1267 LangId::Kotlin => extract_kotlin_symbols(source, &root, query),
1268 LangId::Swift => extract_swift_symbols(source, &root, query),
1269 LangId::Php => extract_php_symbols(source, &root, query),
1270 LangId::Lua => extract_lua_symbols(source, &root, query),
1271 LangId::Perl => extract_perl_symbols(source, &root, query),
1272 LangId::Html | LangId::Markdown | LangId::Vue | LangId::Json => {
1273 unreachable!("handled before query lookup")
1274 }
1275 }
1276}
1277
1278pub(crate) fn node_range(node: &Node) -> Range {
1280 let start = node.start_position();
1281 let end = node.end_position();
1282 Range {
1283 start_line: start.row as u32,
1284 start_col: start.column as u32,
1285 end_line: end.row as u32,
1286 end_col: end.column as u32,
1287 }
1288}
1289
1290pub(crate) fn node_range_with_decorators(node: &Node, source: &str, lang: LangId) -> Range {
1296 if matches!(lang, LangId::Python) {
1297 if let Some(parent) = node.parent() {
1298 if parent.kind() == "decorated_definition" {
1299 return node_range(&parent);
1300 }
1301 }
1302 }
1303
1304 if matches!(lang, LangId::TypeScript | LangId::Tsx | LangId::JavaScript) {
1312 if let Some(parent) = node.parent() {
1313 if parent.kind() == "export_statement" {
1314 return node_range_with_decorators_inner(&parent, source, lang);
1315 }
1316 }
1317 }
1318
1319 node_range_with_decorators_inner(node, source, lang)
1320}
1321
1322fn node_range_with_decorators_inner(node: &Node, source: &str, lang: LangId) -> Range {
1326 let mut range = node_range(node);
1327
1328 let mut current = *node;
1329 while let Some(prev) = current.prev_sibling() {
1330 let kind = prev.kind();
1331 let should_include = match lang {
1332 LangId::Rust => {
1333 kind == "attribute_item"
1335 || (kind == "line_comment"
1337 && node_text(source, &prev).starts_with("///"))
1338 || (kind == "block_comment"
1340 && node_text(source, &prev).starts_with("/**"))
1341 }
1342 LangId::TypeScript | LangId::Tsx | LangId::JavaScript => {
1343 kind == "decorator"
1345 || (kind == "comment"
1347 && node_text(source, &prev).starts_with("/**"))
1348 }
1349 LangId::Go | LangId::C | LangId::Cpp | LangId::Zig | LangId::CSharp | LangId::Bash => {
1350 kind == "comment" && is_adjacent_line(&prev, ¤t, source)
1352 }
1353 LangId::Solidity
1354 | LangId::Scala
1355 | LangId::Java
1356 | LangId::Kotlin
1357 | LangId::Swift
1358 | LangId::Php => {
1359 let text = node_text(source, &prev);
1361 (kind == "comment" || kind == "line_comment" || kind == "block_comment")
1362 && (text.starts_with("///") || text.starts_with("/**"))
1363 && is_adjacent_line(&prev, ¤t, source)
1364 }
1365 LangId::Ruby | LangId::Lua => {
1366 let text = node_text(source, &prev);
1368 kind == "comment"
1369 && (text.starts_with('#') || text.starts_with("---"))
1370 && is_adjacent_line(&prev, ¤t, source)
1371 }
1372 LangId::Perl => false,
1373 LangId::Python => {
1374 false
1376 }
1377 LangId::Html | LangId::Markdown | LangId::Vue | LangId::Json => false,
1378 };
1379
1380 if should_include {
1381 range.start_line = prev.start_position().row as u32;
1382 range.start_col = prev.start_position().column as u32;
1383 current = prev;
1384 } else {
1385 break;
1386 }
1387 }
1388
1389 range
1390}
1391
1392fn is_adjacent_line(upper: &Node, lower: &Node, source: &str) -> bool {
1394 let upper_end = upper.end_position().row;
1395 let lower_start = lower.start_position().row;
1396
1397 if lower_start == 0 || lower_start <= upper_end {
1398 return true;
1399 }
1400
1401 let lines: Vec<&str> = source.lines().collect();
1403 for row in (upper_end + 1)..lower_start {
1404 if row < lines.len() && lines[row].trim().is_empty() {
1405 return false;
1406 }
1407 }
1408 true
1409}
1410
1411pub(crate) fn node_text<'a>(source: &'a str, node: &Node) -> &'a str {
1413 &source[node.byte_range()]
1414}
1415
1416fn lexical_declaration_has_function_value(node: &Node) -> bool {
1417 let mut cursor = node.walk();
1418 if !cursor.goto_first_child() {
1419 return false;
1420 }
1421
1422 loop {
1423 let child = cursor.node();
1424 if matches!(
1425 child.kind(),
1426 "arrow_function" | "function_expression" | "generator_function"
1427 ) {
1428 return true;
1429 }
1430
1431 if lexical_declaration_has_function_value(&child) {
1432 return true;
1433 }
1434
1435 if !cursor.goto_next_sibling() {
1436 break;
1437 }
1438 }
1439
1440 false
1441}
1442
1443fn collect_export_ranges(source: &str, root: &Node, query: &Query) -> Vec<std::ops::Range<usize>> {
1445 let export_idx = query
1446 .capture_names()
1447 .iter()
1448 .position(|n| *n == "export.stmt");
1449 let export_idx = match export_idx {
1450 Some(i) => i as u32,
1451 None => return vec![],
1452 };
1453
1454 let mut cursor = QueryCursor::new();
1455 let mut ranges = Vec::new();
1456 let mut matches = cursor.matches(query, *root, source.as_bytes());
1457
1458 while let Some(m) = {
1459 matches.advance();
1460 matches.get()
1461 } {
1462 for cap in m.captures {
1463 if cap.index == export_idx {
1464 ranges.push(cap.node.byte_range());
1465 }
1466 }
1467 }
1468 ranges
1469}
1470
1471fn is_exported(node: &Node, export_ranges: &[std::ops::Range<usize>]) -> bool {
1473 let r = node.byte_range();
1474 export_ranges
1475 .iter()
1476 .any(|er| er.start <= r.start && r.end <= er.end)
1477}
1478
1479fn collect_exported_symbol_names(source: &str, root: &Node) -> HashSet<String> {
1480 let mut exported = HashSet::new();
1481 collect_exported_symbol_names_inner(source, root, &mut exported);
1482 exported
1483}
1484
1485fn collect_exported_symbol_names_inner(source: &str, node: &Node, exported: &mut HashSet<String>) {
1486 if node.kind() == "export_statement" {
1487 collect_names_from_export_statement(source, node, exported);
1488 }
1489
1490 let mut cursor = node.walk();
1491 if !cursor.goto_first_child() {
1492 return;
1493 }
1494
1495 loop {
1496 let child = cursor.node();
1497 collect_exported_symbol_names_inner(source, &child, exported);
1498 if !cursor.goto_next_sibling() {
1499 break;
1500 }
1501 }
1502}
1503
1504fn collect_names_from_export_statement(source: &str, node: &Node, exported: &mut HashSet<String>) {
1505 let mut cursor = node.walk();
1506 if !cursor.goto_first_child() {
1507 return;
1508 }
1509
1510 let mut saw_default = false;
1511 loop {
1512 let child = cursor.node();
1513 match child.kind() {
1514 "default" => saw_default = true,
1515 "export_clause" => collect_names_from_export_clause(source, &child, exported),
1516 "identifier" | "type_identifier" | "property_identifier" if saw_default => {
1517 exported.insert(node_text(source, &child).to_string());
1518 return;
1519 }
1520 _ => {}
1521 }
1522 if !cursor.goto_next_sibling() {
1523 break;
1524 }
1525 }
1526}
1527
1528fn collect_names_from_export_clause(source: &str, node: &Node, exported: &mut HashSet<String>) {
1529 let mut cursor = node.walk();
1530 if !cursor.goto_first_child() {
1531 return;
1532 }
1533
1534 loop {
1535 let child = cursor.node();
1536 if child.kind() == "export_specifier" {
1537 if let Some(exported_name) = last_identifier_text(source, &child) {
1538 exported.insert(exported_name);
1539 }
1540 }
1541 if !cursor.goto_next_sibling() {
1542 break;
1543 }
1544 }
1545}
1546
1547fn last_identifier_text(source: &str, node: &Node) -> Option<String> {
1548 let mut cursor = node.walk();
1549 if !cursor.goto_first_child() {
1550 return None;
1551 }
1552
1553 let mut last = None;
1554 loop {
1555 let child = cursor.node();
1556 if matches!(
1557 child.kind(),
1558 "identifier"
1559 | "type_identifier"
1560 | "property_identifier"
1561 | "shorthand_property_identifier"
1562 ) {
1563 last = Some(node_text(source, &child).to_string());
1564 }
1565 if !cursor.goto_next_sibling() {
1566 break;
1567 }
1568 }
1569 last
1570}
1571
1572fn mark_named_exports(symbols: &mut [Symbol], exported_names: &HashSet<String>) {
1573 for symbol in symbols {
1574 if symbol.scope_chain.is_empty()
1575 && symbol.parent.is_none()
1576 && exported_names.contains(&symbol.name)
1577 {
1578 symbol.exported = true;
1579 }
1580 }
1581}
1582
1583fn extract_signature(source: &str, node: &Node) -> String {
1585 let text = node_text(source, node);
1586 let first_line = text.lines().next().unwrap_or(text);
1587 let trimmed = first_line.trim_end();
1589 let trimmed = trimmed.strip_suffix('{').unwrap_or(trimmed).trim_end();
1590 trimmed.to_string()
1591}
1592
1593fn push_default_export_symbol(
1594 symbols: &mut Vec<Symbol>,
1595 source: &str,
1596 lang: LangId,
1597 body_node: Node,
1598 def_node: Node,
1599) {
1600 let kind = if body_node.kind() == "class" {
1601 SymbolKind::Class
1602 } else {
1603 SymbolKind::Function
1604 };
1605
1606 symbols.push(Symbol {
1607 name: "default".to_string(),
1608 kind,
1609 range: node_range_with_decorators(&def_node, source, lang),
1610 signature: Some(extract_signature(source, &def_node)),
1611 scope_chain: vec![],
1612 exported: true,
1613 parent: None,
1614 });
1615}
1616
1617fn extract_ts_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
1619 let lang = LangId::TypeScript;
1620 let capture_names = query.capture_names();
1621
1622 let export_ranges = collect_export_ranges(source, root, query);
1623 let exported_names = collect_exported_symbol_names(source, root);
1624
1625 let mut symbols = Vec::new();
1626 let mut cursor = QueryCursor::new();
1627 let mut matches = cursor.matches(query, *root, source.as_bytes());
1628
1629 while let Some(m) = {
1630 matches.advance();
1631 matches.get()
1632 } {
1633 let mut fn_name_node = None;
1635 let mut fn_def_node = None;
1636 let mut arrow_name_node = None;
1637 let mut arrow_def_node = None;
1638 let mut class_name_node = None;
1639 let mut class_def_node = None;
1640 let mut method_class_name_node = None;
1641 let mut method_name_node = None;
1642 let mut method_def_node = None;
1643 let mut interface_name_node = None;
1644 let mut interface_def_node = None;
1645 let mut enum_name_node = None;
1646 let mut enum_def_node = None;
1647 let mut type_alias_name_node = None;
1648 let mut type_alias_def_node = None;
1649 let mut var_name_node = None;
1650 let mut var_def_node = None;
1651 let mut default_body_node = None;
1652 let mut default_def_node = None;
1653
1654 for cap in m.captures {
1655 let Some(&name) = capture_names.get(cap.index as usize) else {
1656 continue;
1657 };
1658 match name {
1659 "fn.name" => fn_name_node = Some(cap.node),
1660 "fn.def" => fn_def_node = Some(cap.node),
1661 "arrow.name" => arrow_name_node = Some(cap.node),
1662 "arrow.def" => arrow_def_node = Some(cap.node),
1663 "class.name" => class_name_node = Some(cap.node),
1664 "class.def" => class_def_node = Some(cap.node),
1665 "method.class_name" => method_class_name_node = Some(cap.node),
1666 "method.name" => method_name_node = Some(cap.node),
1667 "method.def" => method_def_node = Some(cap.node),
1668 "interface.name" => interface_name_node = Some(cap.node),
1669 "interface.def" => interface_def_node = Some(cap.node),
1670 "enum.name" => enum_name_node = Some(cap.node),
1671 "enum.def" => enum_def_node = Some(cap.node),
1672 "type_alias.name" => type_alias_name_node = Some(cap.node),
1673 "type_alias.def" => type_alias_def_node = Some(cap.node),
1674 "var.name" => var_name_node = Some(cap.node),
1675 "var.def" => var_def_node = Some(cap.node),
1676 "default.body" => default_body_node = Some(cap.node),
1677 "default.def" => default_def_node = Some(cap.node),
1678 _ => {}
1680 }
1681 }
1682
1683 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
1685 symbols.push(Symbol {
1686 name: node_text(source, &name_node).to_string(),
1687 kind: SymbolKind::Function,
1688 range: node_range_with_decorators(&def_node, source, lang),
1689 signature: Some(extract_signature(source, &def_node)),
1690 scope_chain: vec![],
1691 exported: is_exported(&def_node, &export_ranges),
1692 parent: None,
1693 });
1694 }
1695
1696 if let (Some(name_node), Some(def_node)) = (arrow_name_node, arrow_def_node) {
1698 symbols.push(Symbol {
1699 name: node_text(source, &name_node).to_string(),
1700 kind: SymbolKind::Function,
1701 range: node_range_with_decorators(&def_node, source, lang),
1702 signature: Some(extract_signature(source, &def_node)),
1703 scope_chain: vec![],
1704 exported: is_exported(&def_node, &export_ranges),
1705 parent: None,
1706 });
1707 }
1708
1709 if let (Some(body_node), Some(def_node)) = (default_body_node, default_def_node) {
1711 push_default_export_symbol(&mut symbols, source, lang, body_node, def_node);
1712 }
1713
1714 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
1716 symbols.push(Symbol {
1717 name: node_text(source, &name_node).to_string(),
1718 kind: SymbolKind::Class,
1719 range: node_range_with_decorators(&def_node, source, lang),
1720 signature: Some(extract_signature(source, &def_node)),
1721 scope_chain: vec![],
1722 exported: is_exported(&def_node, &export_ranges),
1723 parent: None,
1724 });
1725 }
1726
1727 if let (Some(class_name_node), Some(name_node), Some(def_node)) =
1729 (method_class_name_node, method_name_node, method_def_node)
1730 {
1731 let class_name = node_text(source, &class_name_node).to_string();
1732 symbols.push(Symbol {
1733 name: node_text(source, &name_node).to_string(),
1734 kind: SymbolKind::Method,
1735 range: node_range_with_decorators(&def_node, source, lang),
1736 signature: Some(extract_signature(source, &def_node)),
1737 scope_chain: vec![class_name.clone()],
1738 exported: false, parent: Some(class_name),
1740 });
1741 }
1742
1743 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
1745 symbols.push(Symbol {
1746 name: node_text(source, &name_node).to_string(),
1747 kind: SymbolKind::Interface,
1748 range: node_range_with_decorators(&def_node, source, lang),
1749 signature: Some(extract_signature(source, &def_node)),
1750 scope_chain: vec![],
1751 exported: is_exported(&def_node, &export_ranges),
1752 parent: None,
1753 });
1754 }
1755
1756 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
1758 symbols.push(Symbol {
1759 name: node_text(source, &name_node).to_string(),
1760 kind: SymbolKind::Enum,
1761 range: node_range_with_decorators(&def_node, source, lang),
1762 signature: Some(extract_signature(source, &def_node)),
1763 scope_chain: vec![],
1764 exported: is_exported(&def_node, &export_ranges),
1765 parent: None,
1766 });
1767 }
1768
1769 if let (Some(name_node), Some(def_node)) = (type_alias_name_node, type_alias_def_node) {
1771 symbols.push(Symbol {
1772 name: node_text(source, &name_node).to_string(),
1773 kind: SymbolKind::TypeAlias,
1774 range: node_range_with_decorators(&def_node, source, lang),
1775 signature: Some(extract_signature(source, &def_node)),
1776 scope_chain: vec![],
1777 exported: is_exported(&def_node, &export_ranges),
1778 parent: None,
1779 });
1780 }
1781
1782 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
1784 let is_top_level = def_node
1786 .parent()
1787 .map(|p| p.kind() == "program" || p.kind() == "export_statement")
1788 .unwrap_or(false);
1789 let is_function_like = lexical_declaration_has_function_value(&def_node);
1790 let name = node_text(source, &name_node).to_string();
1791 let already_captured = symbols.iter().any(|s| s.name == name);
1792 if is_top_level && !is_function_like && !already_captured {
1793 symbols.push(Symbol {
1794 name,
1795 kind: SymbolKind::Variable,
1796 range: node_range_with_decorators(&def_node, source, lang),
1797 signature: Some(extract_signature(source, &def_node)),
1798 scope_chain: vec![],
1799 exported: is_exported(&def_node, &export_ranges),
1800 parent: None,
1801 });
1802 }
1803 }
1804 }
1805
1806 mark_named_exports(&mut symbols, &exported_names);
1807
1808 dedup_symbols(&mut symbols);
1810 Ok(symbols)
1811}
1812
1813fn extract_js_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
1815 let lang = LangId::JavaScript;
1816 let capture_names = query.capture_names();
1817
1818 let export_ranges = collect_export_ranges(source, root, query);
1819 let exported_names = collect_exported_symbol_names(source, root);
1820
1821 let mut symbols = Vec::new();
1822 let mut cursor = QueryCursor::new();
1823 let mut matches = cursor.matches(query, *root, source.as_bytes());
1824
1825 while let Some(m) = {
1826 matches.advance();
1827 matches.get()
1828 } {
1829 let mut fn_name_node = None;
1830 let mut fn_def_node = None;
1831 let mut arrow_name_node = None;
1832 let mut arrow_def_node = None;
1833 let mut class_name_node = None;
1834 let mut class_def_node = None;
1835 let mut method_class_name_node = None;
1836 let mut method_name_node = None;
1837 let mut method_def_node = None;
1838 let mut default_body_node = None;
1839 let mut default_def_node = None;
1840
1841 for cap in m.captures {
1842 let Some(&name) = capture_names.get(cap.index as usize) else {
1843 continue;
1844 };
1845 match name {
1846 "fn.name" => fn_name_node = Some(cap.node),
1847 "fn.def" => fn_def_node = Some(cap.node),
1848 "arrow.name" => arrow_name_node = Some(cap.node),
1849 "arrow.def" => arrow_def_node = Some(cap.node),
1850 "class.name" => class_name_node = Some(cap.node),
1851 "class.def" => class_def_node = Some(cap.node),
1852 "method.class_name" => method_class_name_node = Some(cap.node),
1853 "method.name" => method_name_node = Some(cap.node),
1854 "method.def" => method_def_node = Some(cap.node),
1855 "default.body" => default_body_node = Some(cap.node),
1856 "default.def" => default_def_node = Some(cap.node),
1857 _ => {}
1858 }
1859 }
1860
1861 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
1862 symbols.push(Symbol {
1863 name: node_text(source, &name_node).to_string(),
1864 kind: SymbolKind::Function,
1865 range: node_range_with_decorators(&def_node, source, lang),
1866 signature: Some(extract_signature(source, &def_node)),
1867 scope_chain: vec![],
1868 exported: is_exported(&def_node, &export_ranges),
1869 parent: None,
1870 });
1871 }
1872
1873 if let (Some(name_node), Some(def_node)) = (arrow_name_node, arrow_def_node) {
1874 symbols.push(Symbol {
1875 name: node_text(source, &name_node).to_string(),
1876 kind: SymbolKind::Function,
1877 range: node_range_with_decorators(&def_node, source, lang),
1878 signature: Some(extract_signature(source, &def_node)),
1879 scope_chain: vec![],
1880 exported: is_exported(&def_node, &export_ranges),
1881 parent: None,
1882 });
1883 }
1884
1885 if let (Some(body_node), Some(def_node)) = (default_body_node, default_def_node) {
1886 push_default_export_symbol(&mut symbols, source, lang, body_node, def_node);
1887 }
1888
1889 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
1890 symbols.push(Symbol {
1891 name: node_text(source, &name_node).to_string(),
1892 kind: SymbolKind::Class,
1893 range: node_range_with_decorators(&def_node, source, lang),
1894 signature: Some(extract_signature(source, &def_node)),
1895 scope_chain: vec![],
1896 exported: is_exported(&def_node, &export_ranges),
1897 parent: None,
1898 });
1899 }
1900
1901 if let (Some(class_name_node), Some(name_node), Some(def_node)) =
1902 (method_class_name_node, method_name_node, method_def_node)
1903 {
1904 let class_name = node_text(source, &class_name_node).to_string();
1905 symbols.push(Symbol {
1906 name: node_text(source, &name_node).to_string(),
1907 kind: SymbolKind::Method,
1908 range: node_range_with_decorators(&def_node, source, lang),
1909 signature: Some(extract_signature(source, &def_node)),
1910 scope_chain: vec![class_name.clone()],
1911 exported: false,
1912 parent: Some(class_name),
1913 });
1914 }
1915 }
1916
1917 mark_named_exports(&mut symbols, &exported_names);
1918 dedup_symbols(&mut symbols);
1919 Ok(symbols)
1920}
1921
1922fn py_scope_chain(node: &Node, source: &str) -> Vec<String> {
1925 let mut chain = Vec::new();
1926 let mut current = node.parent();
1927 while let Some(parent) = current {
1928 if parent.kind() == "class_definition" {
1929 if let Some(name_node) = parent.child_by_field_name("name") {
1930 chain.push(node_text(source, &name_node).to_string());
1931 }
1932 }
1933 current = parent.parent();
1934 }
1935 chain.reverse();
1936 chain
1937}
1938
1939fn extract_py_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
1941 let lang = LangId::Python;
1942 let capture_names = query.capture_names();
1943
1944 let mut symbols = Vec::new();
1945 let mut cursor = QueryCursor::new();
1946 let mut matches = cursor.matches(query, *root, source.as_bytes());
1947
1948 let mut decorated_fn_lines = std::collections::HashSet::new();
1950
1951 {
1953 let mut cursor2 = QueryCursor::new();
1954 let mut matches2 = cursor2.matches(query, *root, source.as_bytes());
1955 while let Some(m) = {
1956 matches2.advance();
1957 matches2.get()
1958 } {
1959 let mut dec_def_node = None;
1960 let mut dec_decorator_node = None;
1961
1962 for cap in m.captures {
1963 let Some(&name) = capture_names.get(cap.index as usize) else {
1964 continue;
1965 };
1966 match name {
1967 "dec.def" => dec_def_node = Some(cap.node),
1968 "dec.decorator" => dec_decorator_node = Some(cap.node),
1969 _ => {}
1970 }
1971 }
1972
1973 if let (Some(def_node), Some(_dec_node)) = (dec_def_node, dec_decorator_node) {
1974 let mut child_cursor = def_node.walk();
1976 if child_cursor.goto_first_child() {
1977 loop {
1978 let child = child_cursor.node();
1979 if child.kind() == "function_definition"
1980 || child.kind() == "class_definition"
1981 {
1982 decorated_fn_lines.insert(child.start_position().row);
1983 }
1984 if !child_cursor.goto_next_sibling() {
1985 break;
1986 }
1987 }
1988 }
1989 }
1990 }
1991 }
1992
1993 while let Some(m) = {
1994 matches.advance();
1995 matches.get()
1996 } {
1997 let mut fn_name_node = None;
1998 let mut fn_def_node = None;
1999 let mut class_name_node = None;
2000 let mut class_def_node = None;
2001
2002 for cap in m.captures {
2003 let Some(&name) = capture_names.get(cap.index as usize) else {
2004 continue;
2005 };
2006 match name {
2007 "fn.name" => fn_name_node = Some(cap.node),
2008 "fn.def" => fn_def_node = Some(cap.node),
2009 "class.name" => class_name_node = Some(cap.node),
2010 "class.def" => class_def_node = Some(cap.node),
2011 _ => {}
2012 }
2013 }
2014
2015 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
2017 let scope = py_scope_chain(&def_node, source);
2018 let is_method = !scope.is_empty();
2019 let name = node_text(source, &name_node).to_string();
2020 let kind = if is_method {
2022 SymbolKind::Method
2023 } else {
2024 SymbolKind::Function
2025 };
2026
2027 let sig = if decorated_fn_lines.contains(&def_node.start_position().row) {
2029 let mut sig_parts = Vec::new();
2031 let mut parent = def_node.parent();
2032 while let Some(p) = parent {
2033 if p.kind() == "decorated_definition" {
2034 let mut dc = p.walk();
2036 if dc.goto_first_child() {
2037 loop {
2038 if dc.node().kind() == "decorator" {
2039 sig_parts.push(node_text(source, &dc.node()).to_string());
2040 }
2041 if !dc.goto_next_sibling() {
2042 break;
2043 }
2044 }
2045 }
2046 break;
2047 }
2048 parent = p.parent();
2049 }
2050 sig_parts.push(extract_signature(source, &def_node));
2051 Some(sig_parts.join("\n"))
2052 } else {
2053 Some(extract_signature(source, &def_node))
2054 };
2055
2056 symbols.push(Symbol {
2057 name,
2058 kind,
2059 range: node_range_with_decorators(&def_node, source, lang),
2060 signature: sig,
2061 scope_chain: scope.clone(),
2062 exported: false, parent: scope.last().cloned(),
2064 });
2065 }
2066
2067 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
2069 let scope = py_scope_chain(&def_node, source);
2070
2071 let sig = if decorated_fn_lines.contains(&def_node.start_position().row) {
2073 let mut sig_parts = Vec::new();
2074 let mut parent = def_node.parent();
2075 while let Some(p) = parent {
2076 if p.kind() == "decorated_definition" {
2077 let mut dc = p.walk();
2078 if dc.goto_first_child() {
2079 loop {
2080 if dc.node().kind() == "decorator" {
2081 sig_parts.push(node_text(source, &dc.node()).to_string());
2082 }
2083 if !dc.goto_next_sibling() {
2084 break;
2085 }
2086 }
2087 }
2088 break;
2089 }
2090 parent = p.parent();
2091 }
2092 sig_parts.push(extract_signature(source, &def_node));
2093 Some(sig_parts.join("\n"))
2094 } else {
2095 Some(extract_signature(source, &def_node))
2096 };
2097
2098 symbols.push(Symbol {
2099 name: node_text(source, &name_node).to_string(),
2100 kind: SymbolKind::Class,
2101 range: node_range_with_decorators(&def_node, source, lang),
2102 signature: sig,
2103 scope_chain: scope.clone(),
2104 exported: false,
2105 parent: scope.last().cloned(),
2106 });
2107 }
2108 }
2109
2110 dedup_symbols(&mut symbols);
2111 Ok(symbols)
2112}
2113
2114fn extract_rs_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
2117 let lang = LangId::Rust;
2118 let capture_names = query.capture_names();
2119
2120 let mut vis_ranges: Vec<std::ops::Range<usize>> = Vec::new();
2122 {
2123 let vis_idx = capture_names.iter().position(|n| *n == "vis.mod");
2124 if let Some(idx) = vis_idx {
2125 let idx = idx as u32;
2126 let mut cursor = QueryCursor::new();
2127 let mut matches = cursor.matches(query, *root, source.as_bytes());
2128 while let Some(m) = {
2129 matches.advance();
2130 matches.get()
2131 } {
2132 for cap in m.captures {
2133 if cap.index == idx {
2134 vis_ranges.push(cap.node.byte_range());
2135 }
2136 }
2137 }
2138 }
2139 }
2140
2141 let is_pub = |node: &Node| -> bool {
2142 let mut child_cursor = node.walk();
2144 if child_cursor.goto_first_child() {
2145 loop {
2146 if child_cursor.node().kind() == "visibility_modifier" {
2147 return true;
2148 }
2149 if !child_cursor.goto_next_sibling() {
2150 break;
2151 }
2152 }
2153 }
2154 false
2155 };
2156
2157 let mut symbols = Vec::new();
2158 let mut cursor = QueryCursor::new();
2159 let mut matches = cursor.matches(query, *root, source.as_bytes());
2160
2161 while let Some(m) = {
2162 matches.advance();
2163 matches.get()
2164 } {
2165 let mut fn_name_node = None;
2166 let mut fn_def_node = None;
2167 let mut struct_name_node = None;
2168 let mut struct_def_node = None;
2169 let mut enum_name_node = None;
2170 let mut enum_def_node = None;
2171 let mut trait_name_node = None;
2172 let mut trait_def_node = None;
2173 let mut impl_def_node = None;
2174
2175 for cap in m.captures {
2176 let Some(&name) = capture_names.get(cap.index as usize) else {
2177 continue;
2178 };
2179 match name {
2180 "fn.name" => fn_name_node = Some(cap.node),
2181 "fn.def" => fn_def_node = Some(cap.node),
2182 "struct.name" => struct_name_node = Some(cap.node),
2183 "struct.def" => struct_def_node = Some(cap.node),
2184 "enum.name" => enum_name_node = Some(cap.node),
2185 "enum.def" => enum_def_node = Some(cap.node),
2186 "trait.name" => trait_name_node = Some(cap.node),
2187 "trait.def" => trait_def_node = Some(cap.node),
2188 "impl.def" => impl_def_node = Some(cap.node),
2189 _ => {}
2190 }
2191 }
2192
2193 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
2195 let parent = def_node.parent();
2196 let in_impl = parent
2197 .map(|p| p.kind() == "declaration_list")
2198 .unwrap_or(false);
2199 if !in_impl {
2200 symbols.push(Symbol {
2201 name: node_text(source, &name_node).to_string(),
2202 kind: SymbolKind::Function,
2203 range: node_range_with_decorators(&def_node, source, lang),
2204 signature: Some(extract_signature(source, &def_node)),
2205 scope_chain: vec![],
2206 exported: is_pub(&def_node),
2207 parent: None,
2208 });
2209 }
2210 }
2211
2212 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
2214 symbols.push(Symbol {
2215 name: node_text(source, &name_node).to_string(),
2216 kind: SymbolKind::Struct,
2217 range: node_range_with_decorators(&def_node, source, lang),
2218 signature: Some(extract_signature(source, &def_node)),
2219 scope_chain: vec![],
2220 exported: is_pub(&def_node),
2221 parent: None,
2222 });
2223 }
2224
2225 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
2227 symbols.push(Symbol {
2228 name: node_text(source, &name_node).to_string(),
2229 kind: SymbolKind::Enum,
2230 range: node_range_with_decorators(&def_node, source, lang),
2231 signature: Some(extract_signature(source, &def_node)),
2232 scope_chain: vec![],
2233 exported: is_pub(&def_node),
2234 parent: None,
2235 });
2236 }
2237
2238 if let (Some(name_node), Some(def_node)) = (trait_name_node, trait_def_node) {
2240 symbols.push(Symbol {
2241 name: node_text(source, &name_node).to_string(),
2242 kind: SymbolKind::Interface,
2243 range: node_range_with_decorators(&def_node, source, lang),
2244 signature: Some(extract_signature(source, &def_node)),
2245 scope_chain: vec![],
2246 exported: is_pub(&def_node),
2247 parent: None,
2248 });
2249 }
2250
2251 if let Some(impl_node) = impl_def_node {
2253 let mut type_names: Vec<String> = Vec::new();
2257 let mut child_cursor = impl_node.walk();
2258 if child_cursor.goto_first_child() {
2259 loop {
2260 let child = child_cursor.node();
2261 if child.kind() == "type_identifier" || child.kind() == "generic_type" {
2262 type_names.push(node_text(source, &child).to_string());
2263 }
2264 if !child_cursor.goto_next_sibling() {
2265 break;
2266 }
2267 }
2268 }
2269
2270 let scope_name = if type_names.len() >= 2 {
2271 format!("{} for {}", type_names[0], type_names[1])
2273 } else if type_names.len() == 1 {
2274 type_names[0].clone()
2275 } else {
2276 String::new()
2277 };
2278
2279 let parent_name = type_names.last().cloned().unwrap_or_default();
2280
2281 let mut child_cursor = impl_node.walk();
2283 if child_cursor.goto_first_child() {
2284 loop {
2285 let child = child_cursor.node();
2286 if child.kind() == "declaration_list" {
2287 let mut fn_cursor = child.walk();
2288 if fn_cursor.goto_first_child() {
2289 loop {
2290 let fn_node = fn_cursor.node();
2291 if fn_node.kind() == "function_item" {
2292 if let Some(name_node) = fn_node.child_by_field_name("name") {
2293 symbols.push(Symbol {
2294 name: node_text(source, &name_node).to_string(),
2295 kind: SymbolKind::Method,
2296 range: node_range_with_decorators(
2297 &fn_node, source, lang,
2298 ),
2299 signature: Some(extract_signature(source, &fn_node)),
2300 scope_chain: if scope_name.is_empty() {
2301 vec![]
2302 } else {
2303 vec![scope_name.clone()]
2304 },
2305 exported: is_pub(&fn_node),
2306 parent: if parent_name.is_empty() {
2307 None
2308 } else {
2309 Some(parent_name.clone())
2310 },
2311 });
2312 }
2313 }
2314 if !fn_cursor.goto_next_sibling() {
2315 break;
2316 }
2317 }
2318 }
2319 }
2320 if !child_cursor.goto_next_sibling() {
2321 break;
2322 }
2323 }
2324 }
2325 }
2326 }
2327
2328 dedup_symbols(&mut symbols);
2329 Ok(symbols)
2330}
2331
2332fn extract_go_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
2336 let lang = LangId::Go;
2337 let capture_names = query.capture_names();
2338
2339 let is_go_exported = |name: &str| -> bool {
2340 name.chars()
2341 .next()
2342 .map(|c| c.is_uppercase())
2343 .unwrap_or(false)
2344 };
2345
2346 let mut symbols = Vec::new();
2347 let mut cursor = QueryCursor::new();
2348 let mut matches = cursor.matches(query, *root, source.as_bytes());
2349
2350 while let Some(m) = {
2351 matches.advance();
2352 matches.get()
2353 } {
2354 let mut fn_name_node = None;
2355 let mut fn_def_node = None;
2356 let mut method_name_node = None;
2357 let mut method_def_node = None;
2358 let mut type_name_node = None;
2359 let mut type_body_node = None;
2360 let mut type_def_node = None;
2361
2362 for cap in m.captures {
2363 let Some(&name) = capture_names.get(cap.index as usize) else {
2364 continue;
2365 };
2366 match name {
2367 "fn.name" => fn_name_node = Some(cap.node),
2368 "fn.def" => fn_def_node = Some(cap.node),
2369 "method.name" => method_name_node = Some(cap.node),
2370 "method.def" => method_def_node = Some(cap.node),
2371 "type.name" => type_name_node = Some(cap.node),
2372 "type.body" => type_body_node = Some(cap.node),
2373 "type.def" => type_def_node = Some(cap.node),
2374 _ => {}
2375 }
2376 }
2377
2378 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
2380 let name = node_text(source, &name_node).to_string();
2381 symbols.push(Symbol {
2382 exported: is_go_exported(&name),
2383 name,
2384 kind: SymbolKind::Function,
2385 range: node_range_with_decorators(&def_node, source, lang),
2386 signature: Some(extract_signature(source, &def_node)),
2387 scope_chain: vec![],
2388 parent: None,
2389 });
2390 }
2391
2392 if let (Some(name_node), Some(def_node)) = (method_name_node, method_def_node) {
2394 let name = node_text(source, &name_node).to_string();
2395
2396 let receiver_type = extract_go_receiver_type(&def_node, source);
2398 let scope_chain = if let Some(ref rt) = receiver_type {
2399 vec![rt.clone()]
2400 } else {
2401 vec![]
2402 };
2403
2404 symbols.push(Symbol {
2405 exported: is_go_exported(&name),
2406 name,
2407 kind: SymbolKind::Method,
2408 range: node_range_with_decorators(&def_node, source, lang),
2409 signature: Some(extract_signature(source, &def_node)),
2410 scope_chain,
2411 parent: receiver_type,
2412 });
2413 }
2414
2415 if let (Some(name_node), Some(body_node), Some(def_node)) =
2417 (type_name_node, type_body_node, type_def_node)
2418 {
2419 let name = node_text(source, &name_node).to_string();
2420 let kind = match body_node.kind() {
2421 "struct_type" => SymbolKind::Struct,
2422 "interface_type" => SymbolKind::Interface,
2423 _ => SymbolKind::TypeAlias,
2424 };
2425
2426 symbols.push(Symbol {
2427 exported: is_go_exported(&name),
2428 name,
2429 kind,
2430 range: node_range_with_decorators(&def_node, source, lang),
2431 signature: Some(extract_signature(source, &def_node)),
2432 scope_chain: vec![],
2433 parent: None,
2434 });
2435 }
2436 }
2437
2438 dedup_symbols(&mut symbols);
2439 Ok(symbols)
2440}
2441
2442fn extract_go_receiver_type(method_node: &Node, source: &str) -> Option<String> {
2445 let mut child_cursor = method_node.walk();
2447 if child_cursor.goto_first_child() {
2448 loop {
2449 let child = child_cursor.node();
2450 if child.kind() == "parameter_list" {
2451 return find_type_identifier_recursive(&child, source);
2453 }
2454 if !child_cursor.goto_next_sibling() {
2455 break;
2456 }
2457 }
2458 }
2459 None
2460}
2461
2462fn split_scope_text(text: &str, separator: &str) -> Vec<String> {
2463 text.split(separator)
2464 .map(str::trim)
2465 .filter(|segment| !segment.is_empty())
2466 .map(ToString::to_string)
2467 .collect()
2468}
2469
2470fn last_scope_segment(text: &str, separator: &str) -> String {
2471 split_scope_text(text, separator)
2472 .pop()
2473 .unwrap_or_else(|| text.trim().to_string())
2474}
2475
2476fn zig_container_scope_chain(node: &Node, source: &str) -> Vec<String> {
2477 let mut chain = Vec::new();
2478 let mut current = node.parent();
2479
2480 while let Some(parent) = current {
2481 if matches!(
2482 parent.kind(),
2483 "struct_declaration" | "enum_declaration" | "union_declaration" | "opaque_declaration"
2484 ) {
2485 if let Some(container) = parent.parent() {
2486 if container.kind() == "variable_declaration" {
2487 let mut cursor = container.walk();
2488 if cursor.goto_first_child() {
2489 loop {
2490 let child = cursor.node();
2491 if child.kind() == "identifier" {
2492 chain.push(node_text(source, &child).to_string());
2493 break;
2494 }
2495 if !cursor.goto_next_sibling() {
2496 break;
2497 }
2498 }
2499 }
2500 }
2501 }
2502 }
2503 current = parent.parent();
2504 }
2505
2506 chain.reverse();
2507 chain
2508}
2509
2510fn csharp_scope_chain(node: &Node, source: &str) -> Vec<String> {
2511 let mut chain = Vec::new();
2512 let mut current = node.parent();
2513
2514 while let Some(parent) = current {
2515 match parent.kind() {
2516 "namespace_declaration" | "file_scoped_namespace_declaration" => {
2517 if let Some(name_node) = parent.child_by_field_name("name") {
2518 chain.push(node_text(source, &name_node).to_string());
2519 }
2520 }
2521 "class_declaration"
2522 | "interface_declaration"
2523 | "struct_declaration"
2524 | "record_declaration" => {
2525 if let Some(name_node) = parent.child_by_field_name("name") {
2526 chain.push(node_text(source, &name_node).to_string());
2527 }
2528 }
2529 _ => {}
2530 }
2531 current = parent.parent();
2532 }
2533
2534 chain.reverse();
2535 chain
2536}
2537
2538fn cpp_parent_scope_chain(node: &Node, source: &str) -> Vec<String> {
2539 let mut chain = Vec::new();
2540 let mut current = node.parent();
2541
2542 while let Some(parent) = current {
2543 match parent.kind() {
2544 "namespace_definition" => {
2545 if let Some(name_node) = parent.child_by_field_name("name") {
2546 chain.push(node_text(source, &name_node).to_string());
2547 }
2548 }
2549 "class_specifier" | "struct_specifier" => {
2550 if let Some(name_node) = parent.child_by_field_name("name") {
2551 chain.push(last_scope_segment(node_text(source, &name_node), "::"));
2552 }
2553 }
2554 _ => {}
2555 }
2556 current = parent.parent();
2557 }
2558
2559 chain.reverse();
2560 chain
2561}
2562
2563fn template_signature(source: &str, template_node: &Node, item_node: &Node) -> String {
2564 format!(
2565 "{}\n{}",
2566 extract_signature(source, template_node),
2567 extract_signature(source, item_node)
2568 )
2569}
2570
2571fn extract_c_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
2573 let lang = LangId::C;
2574 let capture_names = query.capture_names();
2575
2576 let mut symbols = Vec::new();
2577 let mut cursor = QueryCursor::new();
2578 let mut matches = cursor.matches(query, *root, source.as_bytes());
2579
2580 while let Some(m) = {
2581 matches.advance();
2582 matches.get()
2583 } {
2584 let mut fn_name_node = None;
2585 let mut fn_def_node = None;
2586 let mut struct_name_node = None;
2587 let mut struct_def_node = None;
2588 let mut enum_name_node = None;
2589 let mut enum_def_node = None;
2590 let mut type_name_node = None;
2591 let mut type_def_node = None;
2592 let mut macro_name_node = None;
2593 let mut macro_def_node = None;
2594
2595 for cap in m.captures {
2596 let Some(&name) = capture_names.get(cap.index as usize) else {
2597 continue;
2598 };
2599 match name {
2600 "fn.name" => fn_name_node = Some(cap.node),
2601 "fn.def" => fn_def_node = Some(cap.node),
2602 "struct.name" => struct_name_node = Some(cap.node),
2603 "struct.def" => struct_def_node = Some(cap.node),
2604 "enum.name" => enum_name_node = Some(cap.node),
2605 "enum.def" => enum_def_node = Some(cap.node),
2606 "type.name" => type_name_node = Some(cap.node),
2607 "type.def" => type_def_node = Some(cap.node),
2608 "macro.name" => macro_name_node = Some(cap.node),
2609 "macro.def" => macro_def_node = Some(cap.node),
2610 _ => {}
2611 }
2612 }
2613
2614 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
2615 symbols.push(Symbol {
2616 name: node_text(source, &name_node).to_string(),
2617 kind: SymbolKind::Function,
2618 range: node_range_with_decorators(&def_node, source, lang),
2619 signature: Some(extract_signature(source, &def_node)),
2620 scope_chain: vec![],
2621 exported: false,
2622 parent: None,
2623 });
2624 }
2625
2626 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
2627 symbols.push(Symbol {
2628 name: node_text(source, &name_node).to_string(),
2629 kind: SymbolKind::Struct,
2630 range: node_range_with_decorators(&def_node, source, lang),
2631 signature: Some(extract_signature(source, &def_node)),
2632 scope_chain: vec![],
2633 exported: false,
2634 parent: None,
2635 });
2636 }
2637
2638 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
2639 symbols.push(Symbol {
2640 name: node_text(source, &name_node).to_string(),
2641 kind: SymbolKind::Enum,
2642 range: node_range_with_decorators(&def_node, source, lang),
2643 signature: Some(extract_signature(source, &def_node)),
2644 scope_chain: vec![],
2645 exported: false,
2646 parent: None,
2647 });
2648 }
2649
2650 if let (Some(name_node), Some(def_node)) = (type_name_node, type_def_node) {
2651 symbols.push(Symbol {
2652 name: node_text(source, &name_node).to_string(),
2653 kind: SymbolKind::TypeAlias,
2654 range: node_range_with_decorators(&def_node, source, lang),
2655 signature: Some(extract_signature(source, &def_node)),
2656 scope_chain: vec![],
2657 exported: false,
2658 parent: None,
2659 });
2660 }
2661
2662 if let (Some(name_node), Some(def_node)) = (macro_name_node, macro_def_node) {
2663 symbols.push(Symbol {
2664 name: node_text(source, &name_node).to_string(),
2665 kind: SymbolKind::Variable,
2666 range: node_range(&def_node),
2667 signature: Some(extract_signature(source, &def_node)),
2668 scope_chain: vec![],
2669 exported: false,
2670 parent: None,
2671 });
2672 }
2673 }
2674
2675 dedup_symbols(&mut symbols);
2676 Ok(symbols)
2677}
2678
2679fn extract_cpp_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
2681 let lang = LangId::Cpp;
2682 let capture_names = query.capture_names();
2683
2684 let mut type_names = HashSet::new();
2685 {
2686 let mut cursor = QueryCursor::new();
2687 let mut matches = cursor.matches(query, *root, source.as_bytes());
2688 while let Some(m) = {
2689 matches.advance();
2690 matches.get()
2691 } {
2692 for cap in m.captures {
2693 let Some(&name) = capture_names.get(cap.index as usize) else {
2694 continue;
2695 };
2696 match name {
2697 "class.name"
2698 | "struct.name"
2699 | "template.class.name"
2700 | "template.struct.name" => {
2701 type_names.insert(last_scope_segment(node_text(source, &cap.node), "::"));
2702 }
2703 _ => {}
2704 }
2705 }
2706 }
2707 }
2708
2709 let mut symbols = Vec::new();
2710 let mut cursor = QueryCursor::new();
2711 let mut matches = cursor.matches(query, *root, source.as_bytes());
2712
2713 while let Some(m) = {
2714 matches.advance();
2715 matches.get()
2716 } {
2717 let mut fn_name_node = None;
2718 let mut fn_def_node = None;
2719 let mut method_name_node = None;
2720 let mut method_def_node = None;
2721 let mut qual_scope_node = None;
2722 let mut qual_name_node = None;
2723 let mut qual_def_node = None;
2724 let mut class_name_node = None;
2725 let mut class_def_node = None;
2726 let mut struct_name_node = None;
2727 let mut struct_def_node = None;
2728 let mut enum_name_node = None;
2729 let mut enum_def_node = None;
2730 let mut namespace_name_node = None;
2731 let mut namespace_def_node = None;
2732 let mut template_class_name_node = None;
2733 let mut template_class_def_node = None;
2734 let mut template_class_item_node = None;
2735 let mut template_struct_name_node = None;
2736 let mut template_struct_def_node = None;
2737 let mut template_struct_item_node = None;
2738 let mut template_fn_name_node = None;
2739 let mut template_fn_def_node = None;
2740 let mut template_fn_item_node = None;
2741 let mut template_qual_scope_node = None;
2742 let mut template_qual_name_node = None;
2743 let mut template_qual_def_node = None;
2744 let mut template_qual_item_node = None;
2745
2746 for cap in m.captures {
2747 let Some(&name) = capture_names.get(cap.index as usize) else {
2748 continue;
2749 };
2750 match name {
2751 "fn.name" => fn_name_node = Some(cap.node),
2752 "fn.def" => fn_def_node = Some(cap.node),
2753 "method.name" => method_name_node = Some(cap.node),
2754 "method.def" => method_def_node = Some(cap.node),
2755 "qual.scope" => qual_scope_node = Some(cap.node),
2756 "qual.name" => qual_name_node = Some(cap.node),
2757 "qual.def" => qual_def_node = Some(cap.node),
2758 "class.name" => class_name_node = Some(cap.node),
2759 "class.def" => class_def_node = Some(cap.node),
2760 "struct.name" => struct_name_node = Some(cap.node),
2761 "struct.def" => struct_def_node = Some(cap.node),
2762 "enum.name" => enum_name_node = Some(cap.node),
2763 "enum.def" => enum_def_node = Some(cap.node),
2764 "namespace.name" => namespace_name_node = Some(cap.node),
2765 "namespace.def" => namespace_def_node = Some(cap.node),
2766 "template.class.name" => template_class_name_node = Some(cap.node),
2767 "template.class.def" => template_class_def_node = Some(cap.node),
2768 "template.class.item" => template_class_item_node = Some(cap.node),
2769 "template.struct.name" => template_struct_name_node = Some(cap.node),
2770 "template.struct.def" => template_struct_def_node = Some(cap.node),
2771 "template.struct.item" => template_struct_item_node = Some(cap.node),
2772 "template.fn.name" => template_fn_name_node = Some(cap.node),
2773 "template.fn.def" => template_fn_def_node = Some(cap.node),
2774 "template.fn.item" => template_fn_item_node = Some(cap.node),
2775 "template.qual.scope" => template_qual_scope_node = Some(cap.node),
2776 "template.qual.name" => template_qual_name_node = Some(cap.node),
2777 "template.qual.def" => template_qual_def_node = Some(cap.node),
2778 "template.qual.item" => template_qual_item_node = Some(cap.node),
2779 _ => {}
2780 }
2781 }
2782
2783 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
2784 let in_template = def_node
2785 .parent()
2786 .map(|parent| parent.kind() == "template_declaration")
2787 .unwrap_or(false);
2788 if !in_template {
2789 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2790 symbols.push(Symbol {
2791 name: node_text(source, &name_node).to_string(),
2792 kind: SymbolKind::Function,
2793 range: node_range_with_decorators(&def_node, source, lang),
2794 signature: Some(extract_signature(source, &def_node)),
2795 scope_chain: scope_chain.clone(),
2796 exported: false,
2797 parent: scope_chain.last().cloned(),
2798 });
2799 }
2800 }
2801
2802 if let (Some(name_node), Some(def_node)) = (method_name_node, method_def_node) {
2803 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2804 symbols.push(Symbol {
2805 name: node_text(source, &name_node).to_string(),
2806 kind: SymbolKind::Method,
2807 range: node_range_with_decorators(&def_node, source, lang),
2808 signature: Some(extract_signature(source, &def_node)),
2809 scope_chain: scope_chain.clone(),
2810 exported: false,
2811 parent: scope_chain.last().cloned(),
2812 });
2813 }
2814
2815 if let (Some(scope_node), Some(name_node), Some(def_node)) =
2816 (qual_scope_node, qual_name_node, qual_def_node)
2817 {
2818 let in_template = def_node
2819 .parent()
2820 .map(|parent| parent.kind() == "template_declaration")
2821 .unwrap_or(false);
2822 if !in_template {
2823 let scope_text = node_text(source, &scope_node);
2824 let scope_chain = split_scope_text(scope_text, "::");
2825 let parent = scope_chain.last().cloned();
2826 let kind = if parent
2827 .as_ref()
2828 .map(|segment| type_names.contains(segment))
2829 .unwrap_or(false)
2830 {
2831 SymbolKind::Method
2832 } else {
2833 SymbolKind::Function
2834 };
2835
2836 symbols.push(Symbol {
2837 name: node_text(source, &name_node).to_string(),
2838 kind,
2839 range: node_range_with_decorators(&def_node, source, lang),
2840 signature: Some(extract_signature(source, &def_node)),
2841 scope_chain,
2842 exported: false,
2843 parent,
2844 });
2845 }
2846 }
2847
2848 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
2849 let in_template = def_node
2850 .parent()
2851 .map(|parent| parent.kind() == "template_declaration")
2852 .unwrap_or(false);
2853 if !in_template {
2854 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2855 let name = last_scope_segment(node_text(source, &name_node), "::");
2856 symbols.push(Symbol {
2857 name: name.clone(),
2858 kind: SymbolKind::Class,
2859 range: node_range_with_decorators(&def_node, source, lang),
2860 signature: Some(extract_signature(source, &def_node)),
2861 scope_chain: scope_chain.clone(),
2862 exported: false,
2863 parent: scope_chain.last().cloned(),
2864 });
2865 }
2866 }
2867
2868 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
2869 let in_template = def_node
2870 .parent()
2871 .map(|parent| parent.kind() == "template_declaration")
2872 .unwrap_or(false);
2873 if !in_template {
2874 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2875 let name = last_scope_segment(node_text(source, &name_node), "::");
2876 symbols.push(Symbol {
2877 name: name.clone(),
2878 kind: SymbolKind::Struct,
2879 range: node_range_with_decorators(&def_node, source, lang),
2880 signature: Some(extract_signature(source, &def_node)),
2881 scope_chain: scope_chain.clone(),
2882 exported: false,
2883 parent: scope_chain.last().cloned(),
2884 });
2885 }
2886 }
2887
2888 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
2889 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2890 let name = last_scope_segment(node_text(source, &name_node), "::");
2891 symbols.push(Symbol {
2892 name: name.clone(),
2893 kind: SymbolKind::Enum,
2894 range: node_range_with_decorators(&def_node, source, lang),
2895 signature: Some(extract_signature(source, &def_node)),
2896 scope_chain: scope_chain.clone(),
2897 exported: false,
2898 parent: scope_chain.last().cloned(),
2899 });
2900 }
2901
2902 if let (Some(name_node), Some(def_node)) = (namespace_name_node, namespace_def_node) {
2903 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2904 symbols.push(Symbol {
2905 name: node_text(source, &name_node).to_string(),
2906 kind: SymbolKind::TypeAlias,
2907 range: node_range_with_decorators(&def_node, source, lang),
2908 signature: Some(extract_signature(source, &def_node)),
2909 scope_chain: scope_chain.clone(),
2910 exported: false,
2911 parent: scope_chain.last().cloned(),
2912 });
2913 }
2914
2915 if let (Some(name_node), Some(def_node), Some(item_node)) = (
2916 template_class_name_node,
2917 template_class_def_node,
2918 template_class_item_node,
2919 ) {
2920 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2921 let name = last_scope_segment(node_text(source, &name_node), "::");
2922 symbols.push(Symbol {
2923 name: name.clone(),
2924 kind: SymbolKind::Class,
2925 range: node_range_with_decorators(&def_node, source, lang),
2926 signature: Some(template_signature(source, &def_node, &item_node)),
2927 scope_chain: scope_chain.clone(),
2928 exported: false,
2929 parent: scope_chain.last().cloned(),
2930 });
2931 }
2932
2933 if let (Some(name_node), Some(def_node), Some(item_node)) = (
2934 template_struct_name_node,
2935 template_struct_def_node,
2936 template_struct_item_node,
2937 ) {
2938 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2939 let name = last_scope_segment(node_text(source, &name_node), "::");
2940 symbols.push(Symbol {
2941 name: name.clone(),
2942 kind: SymbolKind::Struct,
2943 range: node_range_with_decorators(&def_node, source, lang),
2944 signature: Some(template_signature(source, &def_node, &item_node)),
2945 scope_chain: scope_chain.clone(),
2946 exported: false,
2947 parent: scope_chain.last().cloned(),
2948 });
2949 }
2950
2951 if let (Some(name_node), Some(def_node), Some(item_node)) = (
2952 template_fn_name_node,
2953 template_fn_def_node,
2954 template_fn_item_node,
2955 ) {
2956 let scope_chain = cpp_parent_scope_chain(&def_node, source);
2957 symbols.push(Symbol {
2958 name: node_text(source, &name_node).to_string(),
2959 kind: SymbolKind::Function,
2960 range: node_range_with_decorators(&def_node, source, lang),
2961 signature: Some(template_signature(source, &def_node, &item_node)),
2962 scope_chain: scope_chain.clone(),
2963 exported: false,
2964 parent: scope_chain.last().cloned(),
2965 });
2966 }
2967
2968 if let (Some(scope_node), Some(name_node), Some(def_node), Some(item_node)) = (
2969 template_qual_scope_node,
2970 template_qual_name_node,
2971 template_qual_def_node,
2972 template_qual_item_node,
2973 ) {
2974 let scope_chain = split_scope_text(node_text(source, &scope_node), "::");
2975 let parent = scope_chain.last().cloned();
2976 let kind = if parent
2977 .as_ref()
2978 .map(|segment| type_names.contains(segment))
2979 .unwrap_or(false)
2980 {
2981 SymbolKind::Method
2982 } else {
2983 SymbolKind::Function
2984 };
2985
2986 symbols.push(Symbol {
2987 name: node_text(source, &name_node).to_string(),
2988 kind,
2989 range: node_range_with_decorators(&def_node, source, lang),
2990 signature: Some(template_signature(source, &def_node, &item_node)),
2991 scope_chain,
2992 exported: false,
2993 parent,
2994 });
2995 }
2996 }
2997
2998 dedup_symbols(&mut symbols);
2999 Ok(symbols)
3000}
3001
3002fn extract_zig_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
3004 let lang = LangId::Zig;
3005 let capture_names = query.capture_names();
3006
3007 let mut symbols = Vec::new();
3008 let mut cursor = QueryCursor::new();
3009 let mut matches = cursor.matches(query, *root, source.as_bytes());
3010
3011 while let Some(m) = {
3012 matches.advance();
3013 matches.get()
3014 } {
3015 let mut fn_name_node = None;
3016 let mut fn_def_node = None;
3017 let mut struct_name_node = None;
3018 let mut struct_def_node = None;
3019 let mut enum_name_node = None;
3020 let mut enum_def_node = None;
3021 let mut union_name_node = None;
3022 let mut union_def_node = None;
3023 let mut const_name_node = None;
3024 let mut const_def_node = None;
3025 let mut test_name_node = None;
3026 let mut test_def_node = None;
3027
3028 for cap in m.captures {
3029 let Some(&name) = capture_names.get(cap.index as usize) else {
3030 continue;
3031 };
3032 match name {
3033 "fn.name" => fn_name_node = Some(cap.node),
3034 "fn.def" => fn_def_node = Some(cap.node),
3035 "struct.name" => struct_name_node = Some(cap.node),
3036 "struct.def" => struct_def_node = Some(cap.node),
3037 "enum.name" => enum_name_node = Some(cap.node),
3038 "enum.def" => enum_def_node = Some(cap.node),
3039 "union.name" => union_name_node = Some(cap.node),
3040 "union.def" => union_def_node = Some(cap.node),
3041 "const.name" => const_name_node = Some(cap.node),
3042 "const.def" => const_def_node = Some(cap.node),
3043 "test.name" => test_name_node = Some(cap.node),
3044 "test.def" => test_def_node = Some(cap.node),
3045 _ => {}
3046 }
3047 }
3048
3049 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
3050 let scope_chain = zig_container_scope_chain(&def_node, source);
3051 let kind = if scope_chain.is_empty() {
3052 SymbolKind::Function
3053 } else {
3054 SymbolKind::Method
3055 };
3056 symbols.push(Symbol {
3057 name: node_text(source, &name_node).to_string(),
3058 kind,
3059 range: node_range_with_decorators(&def_node, source, lang),
3060 signature: Some(extract_signature(source, &def_node)),
3061 scope_chain: scope_chain.clone(),
3062 exported: false,
3063 parent: scope_chain.last().cloned(),
3064 });
3065 }
3066
3067 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
3068 symbols.push(Symbol {
3069 name: node_text(source, &name_node).to_string(),
3070 kind: SymbolKind::Struct,
3071 range: node_range_with_decorators(&def_node, source, lang),
3072 signature: Some(extract_signature(source, &def_node)),
3073 scope_chain: vec![],
3074 exported: false,
3075 parent: None,
3076 });
3077 }
3078
3079 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
3080 symbols.push(Symbol {
3081 name: node_text(source, &name_node).to_string(),
3082 kind: SymbolKind::Enum,
3083 range: node_range_with_decorators(&def_node, source, lang),
3084 signature: Some(extract_signature(source, &def_node)),
3085 scope_chain: vec![],
3086 exported: false,
3087 parent: None,
3088 });
3089 }
3090
3091 if let (Some(name_node), Some(def_node)) = (union_name_node, union_def_node) {
3092 symbols.push(Symbol {
3093 name: node_text(source, &name_node).to_string(),
3094 kind: SymbolKind::TypeAlias,
3095 range: node_range_with_decorators(&def_node, source, lang),
3096 signature: Some(extract_signature(source, &def_node)),
3097 scope_chain: vec![],
3098 exported: false,
3099 parent: None,
3100 });
3101 }
3102
3103 if let (Some(name_node), Some(def_node)) = (const_name_node, const_def_node) {
3104 let signature = extract_signature(source, &def_node);
3105 let is_container = signature.contains("= struct")
3106 || signature.contains("= enum")
3107 || signature.contains("= union")
3108 || signature.contains("= opaque");
3109 let is_const = signature.trim_start().starts_with("const ");
3110 let name = node_text(source, &name_node).to_string();
3111 let already_captured = symbols.iter().any(|symbol| symbol.name == name);
3112 if is_const && !is_container && !already_captured {
3113 symbols.push(Symbol {
3114 name,
3115 kind: SymbolKind::Variable,
3116 range: node_range_with_decorators(&def_node, source, lang),
3117 signature: Some(signature),
3118 scope_chain: vec![],
3119 exported: false,
3120 parent: None,
3121 });
3122 }
3123 }
3124
3125 if let (Some(name_node), Some(def_node)) = (test_name_node, test_def_node) {
3126 let scope_chain = zig_container_scope_chain(&def_node, source);
3127 symbols.push(Symbol {
3128 name: node_text(source, &name_node).trim_matches('"').to_string(),
3129 kind: SymbolKind::Function,
3130 range: node_range_with_decorators(&def_node, source, lang),
3131 signature: Some(extract_signature(source, &def_node)),
3132 scope_chain: scope_chain.clone(),
3133 exported: false,
3134 parent: scope_chain.last().cloned(),
3135 });
3136 }
3137 }
3138
3139 dedup_symbols(&mut symbols);
3140 Ok(symbols)
3141}
3142
3143fn extract_csharp_symbols(
3145 source: &str,
3146 root: &Node,
3147 query: &Query,
3148) -> Result<Vec<Symbol>, AftError> {
3149 let lang = LangId::CSharp;
3150 let capture_names = query.capture_names();
3151
3152 let mut symbols = Vec::new();
3153 let mut cursor = QueryCursor::new();
3154 let mut matches = cursor.matches(query, *root, source.as_bytes());
3155
3156 while let Some(m) = {
3157 matches.advance();
3158 matches.get()
3159 } {
3160 let mut class_name_node = None;
3161 let mut class_def_node = None;
3162 let mut interface_name_node = None;
3163 let mut interface_def_node = None;
3164 let mut struct_name_node = None;
3165 let mut struct_def_node = None;
3166 let mut enum_name_node = None;
3167 let mut enum_def_node = None;
3168 let mut method_name_node = None;
3169 let mut method_def_node = None;
3170 let mut property_name_node = None;
3171 let mut property_def_node = None;
3172 let mut namespace_name_node = None;
3173 let mut namespace_def_node = None;
3174
3175 for cap in m.captures {
3176 let Some(&name) = capture_names.get(cap.index as usize) else {
3177 continue;
3178 };
3179 match name {
3180 "class.name" => class_name_node = Some(cap.node),
3181 "class.def" => class_def_node = Some(cap.node),
3182 "interface.name" => interface_name_node = Some(cap.node),
3183 "interface.def" => interface_def_node = Some(cap.node),
3184 "struct.name" => struct_name_node = Some(cap.node),
3185 "struct.def" => struct_def_node = Some(cap.node),
3186 "enum.name" => enum_name_node = Some(cap.node),
3187 "enum.def" => enum_def_node = Some(cap.node),
3188 "method.name" => method_name_node = Some(cap.node),
3189 "method.def" => method_def_node = Some(cap.node),
3190 "property.name" => property_name_node = Some(cap.node),
3191 "property.def" => property_def_node = Some(cap.node),
3192 "namespace.name" => namespace_name_node = Some(cap.node),
3193 "namespace.def" => namespace_def_node = Some(cap.node),
3194 _ => {}
3195 }
3196 }
3197
3198 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
3199 let scope_chain = csharp_scope_chain(&def_node, source);
3200 symbols.push(Symbol {
3201 name: node_text(source, &name_node).to_string(),
3202 kind: SymbolKind::Class,
3203 range: node_range_with_decorators(&def_node, source, lang),
3204 signature: Some(extract_signature(source, &def_node)),
3205 scope_chain: scope_chain.clone(),
3206 exported: false,
3207 parent: scope_chain.last().cloned(),
3208 });
3209 }
3210
3211 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
3212 let scope_chain = csharp_scope_chain(&def_node, source);
3213 symbols.push(Symbol {
3214 name: node_text(source, &name_node).to_string(),
3215 kind: SymbolKind::Interface,
3216 range: node_range_with_decorators(&def_node, source, lang),
3217 signature: Some(extract_signature(source, &def_node)),
3218 scope_chain: scope_chain.clone(),
3219 exported: false,
3220 parent: scope_chain.last().cloned(),
3221 });
3222 }
3223
3224 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
3225 let scope_chain = csharp_scope_chain(&def_node, source);
3226 symbols.push(Symbol {
3227 name: node_text(source, &name_node).to_string(),
3228 kind: SymbolKind::Struct,
3229 range: node_range_with_decorators(&def_node, source, lang),
3230 signature: Some(extract_signature(source, &def_node)),
3231 scope_chain: scope_chain.clone(),
3232 exported: false,
3233 parent: scope_chain.last().cloned(),
3234 });
3235 }
3236
3237 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
3238 let scope_chain = csharp_scope_chain(&def_node, source);
3239 symbols.push(Symbol {
3240 name: node_text(source, &name_node).to_string(),
3241 kind: SymbolKind::Enum,
3242 range: node_range_with_decorators(&def_node, source, lang),
3243 signature: Some(extract_signature(source, &def_node)),
3244 scope_chain: scope_chain.clone(),
3245 exported: false,
3246 parent: scope_chain.last().cloned(),
3247 });
3248 }
3249
3250 if let (Some(name_node), Some(def_node)) = (method_name_node, method_def_node) {
3251 let scope_chain = csharp_scope_chain(&def_node, source);
3252 symbols.push(Symbol {
3253 name: node_text(source, &name_node).to_string(),
3254 kind: SymbolKind::Method,
3255 range: node_range_with_decorators(&def_node, source, lang),
3256 signature: Some(extract_signature(source, &def_node)),
3257 scope_chain: scope_chain.clone(),
3258 exported: false,
3259 parent: scope_chain.last().cloned(),
3260 });
3261 }
3262
3263 if let (Some(name_node), Some(def_node)) = (property_name_node, property_def_node) {
3264 let scope_chain = csharp_scope_chain(&def_node, source);
3265 symbols.push(Symbol {
3266 name: node_text(source, &name_node).to_string(),
3267 kind: SymbolKind::Variable,
3268 range: node_range_with_decorators(&def_node, source, lang),
3269 signature: Some(extract_signature(source, &def_node)),
3270 scope_chain: scope_chain.clone(),
3271 exported: false,
3272 parent: scope_chain.last().cloned(),
3273 });
3274 }
3275
3276 if let (Some(name_node), Some(def_node)) = (namespace_name_node, namespace_def_node) {
3277 let scope_chain = csharp_scope_chain(&def_node, source);
3278 symbols.push(Symbol {
3279 name: node_text(source, &name_node).to_string(),
3280 kind: SymbolKind::TypeAlias,
3281 range: node_range_with_decorators(&def_node, source, lang),
3282 signature: Some(extract_signature(source, &def_node)),
3283 scope_chain: scope_chain.clone(),
3284 exported: false,
3285 parent: scope_chain.last().cloned(),
3286 });
3287 }
3288 }
3289
3290 dedup_symbols(&mut symbols);
3291 Ok(symbols)
3292}
3293
3294fn find_type_identifier_recursive(node: &Node, source: &str) -> Option<String> {
3296 if node.kind() == "type_identifier" {
3297 return Some(node_text(source, node).to_string());
3298 }
3299 let mut cursor = node.walk();
3300 if cursor.goto_first_child() {
3301 loop {
3302 if let Some(result) = find_type_identifier_recursive(&cursor.node(), source) {
3303 return Some(result);
3304 }
3305 if !cursor.goto_next_sibling() {
3306 break;
3307 }
3308 }
3309 }
3310 None
3311}
3312
3313fn extract_bash_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
3317 let lang = LangId::Bash;
3318 let capture_names = query.capture_names();
3319
3320 let mut symbols = Vec::new();
3321 let mut cursor = QueryCursor::new();
3322 let mut matches = cursor.matches(query, *root, source.as_bytes());
3323
3324 while let Some(m) = {
3325 matches.advance();
3326 matches.get()
3327 } {
3328 let mut fn_name_node = None;
3329 let mut fn_def_node = None;
3330
3331 for cap in m.captures {
3332 let Some(&name) = capture_names.get(cap.index as usize) else {
3333 continue;
3334 };
3335 match name {
3336 "fn.name" => fn_name_node = Some(cap.node),
3337 "fn.def" => fn_def_node = Some(cap.node),
3338 _ => {}
3339 }
3340 }
3341
3342 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
3343 symbols.push(Symbol {
3344 name: node_text(source, &name_node).to_string(),
3345 kind: SymbolKind::Function,
3346 range: node_range_with_decorators(&def_node, source, lang),
3347 signature: Some(extract_signature(source, &def_node)),
3348 scope_chain: vec![],
3349 exported: false,
3350 parent: None,
3351 });
3352 }
3353 }
3354
3355 Ok(symbols)
3356}
3357
3358fn solidity_scope_chain(node: &Node, source: &str) -> Vec<String> {
3361 let mut chain = Vec::new();
3362 let mut current = node.parent();
3363
3364 while let Some(parent) = current {
3365 match parent.kind() {
3366 "contract_declaration" | "library_declaration" | "interface_declaration" => {
3367 if let Some(name_node) = parent.child_by_field_name("name") {
3368 chain.push(node_text(source, &name_node).to_string());
3369 }
3370 }
3371 _ => {}
3372 }
3373 current = parent.parent();
3374 }
3375
3376 chain.reverse();
3377 chain
3378}
3379
3380fn extract_solidity_symbols(
3381 source: &str,
3382 root: &Node,
3383 query: &Query,
3384) -> Result<Vec<Symbol>, AftError> {
3385 let lang = LangId::Solidity;
3386 let capture_names = query.capture_names();
3387
3388 let mut symbols = Vec::new();
3389 let mut cursor = QueryCursor::new();
3390 let mut matches = cursor.matches(query, *root, source.as_bytes());
3391
3392 while let Some(m) = {
3393 matches.advance();
3394 matches.get()
3395 } {
3396 let mut contract_name_node = None;
3397 let mut contract_def_node = None;
3398 let mut library_name_node = None;
3399 let mut library_def_node = None;
3400 let mut interface_name_node = None;
3401 let mut interface_def_node = None;
3402 let mut fn_name_node = None;
3403 let mut fn_def_node = None;
3404 let mut modifier_name_node = None;
3405 let mut modifier_def_node = None;
3406 let mut constructor_def_node = None;
3407 let mut fallback_receive_def_node = None;
3408 let mut event_name_node = None;
3409 let mut event_def_node = None;
3410 let mut error_name_node = None;
3411 let mut error_def_node = None;
3412 let mut struct_name_node = None;
3413 let mut struct_def_node = None;
3414 let mut enum_name_node = None;
3415 let mut enum_def_node = None;
3416 let mut var_name_node = None;
3417 let mut var_def_node = None;
3418
3419 for cap in m.captures {
3420 let Some(&name) = capture_names.get(cap.index as usize) else {
3421 continue;
3422 };
3423 match name {
3424 "contract.name" => contract_name_node = Some(cap.node),
3425 "contract.def" => contract_def_node = Some(cap.node),
3426 "library.name" => library_name_node = Some(cap.node),
3427 "library.def" => library_def_node = Some(cap.node),
3428 "interface.name" => interface_name_node = Some(cap.node),
3429 "interface.def" => interface_def_node = Some(cap.node),
3430 "fn.name" => fn_name_node = Some(cap.node),
3431 "fn.def" => fn_def_node = Some(cap.node),
3432 "modifier.name" => modifier_name_node = Some(cap.node),
3433 "modifier.def" => modifier_def_node = Some(cap.node),
3434 "constructor.def" => constructor_def_node = Some(cap.node),
3435 "fallback_receive.def" => fallback_receive_def_node = Some(cap.node),
3436 "event.name" => event_name_node = Some(cap.node),
3437 "event.def" => event_def_node = Some(cap.node),
3438 "error.name" => error_name_node = Some(cap.node),
3439 "error.def" => error_def_node = Some(cap.node),
3440 "struct.name" => struct_name_node = Some(cap.node),
3441 "struct.def" => struct_def_node = Some(cap.node),
3442 "enum.name" => enum_name_node = Some(cap.node),
3443 "enum.def" => enum_def_node = Some(cap.node),
3444 "var.name" => var_name_node = Some(cap.node),
3445 "var.def" => var_def_node = Some(cap.node),
3446 _ => {}
3447 }
3448 }
3449
3450 if let (Some(name_node), Some(def_node)) = (contract_name_node, contract_def_node) {
3452 symbols.push(Symbol {
3453 name: node_text(source, &name_node).to_string(),
3454 kind: SymbolKind::Class,
3455 range: node_range_with_decorators(&def_node, source, lang),
3456 signature: Some(extract_signature(source, &def_node)),
3457 scope_chain: vec![],
3458 exported: true,
3459 parent: None,
3460 });
3461 }
3462
3463 if let (Some(name_node), Some(def_node)) = (library_name_node, library_def_node) {
3465 symbols.push(Symbol {
3466 name: node_text(source, &name_node).to_string(),
3467 kind: SymbolKind::Class,
3468 range: node_range_with_decorators(&def_node, source, lang),
3469 signature: Some(extract_signature(source, &def_node)),
3470 scope_chain: vec![],
3471 exported: true,
3472 parent: None,
3473 });
3474 }
3475
3476 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
3478 symbols.push(Symbol {
3479 name: node_text(source, &name_node).to_string(),
3480 kind: SymbolKind::Interface,
3481 range: node_range_with_decorators(&def_node, source, lang),
3482 signature: Some(extract_signature(source, &def_node)),
3483 scope_chain: vec![],
3484 exported: true,
3485 parent: None,
3486 });
3487 }
3488
3489 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
3491 let scope_chain = solidity_scope_chain(&def_node, source);
3492 let kind = if scope_chain.is_empty() {
3493 SymbolKind::Function
3494 } else {
3495 SymbolKind::Method
3496 };
3497 symbols.push(Symbol {
3498 name: node_text(source, &name_node).to_string(),
3499 kind,
3500 range: node_range_with_decorators(&def_node, source, lang),
3501 signature: Some(extract_signature(source, &def_node)),
3502 parent: scope_chain.last().cloned(),
3503 scope_chain,
3504 exported: true,
3505 });
3506 }
3507
3508 if let (Some(name_node), Some(def_node)) = (modifier_name_node, modifier_def_node) {
3510 let scope_chain = solidity_scope_chain(&def_node, source);
3511 symbols.push(Symbol {
3512 name: node_text(source, &name_node).to_string(),
3513 kind: SymbolKind::Method,
3514 range: node_range_with_decorators(&def_node, source, lang),
3515 signature: Some(extract_signature(source, &def_node)),
3516 parent: scope_chain.last().cloned(),
3517 scope_chain,
3518 exported: true,
3519 });
3520 }
3521
3522 if let Some(def_node) = constructor_def_node {
3524 let scope_chain = solidity_scope_chain(&def_node, source);
3525 symbols.push(Symbol {
3526 name: "constructor".to_string(),
3527 kind: SymbolKind::Method,
3528 range: node_range_with_decorators(&def_node, source, lang),
3529 signature: Some(extract_signature(source, &def_node)),
3530 parent: scope_chain.last().cloned(),
3531 scope_chain,
3532 exported: true,
3533 });
3534 }
3535
3536 if let Some(def_node) = fallback_receive_def_node {
3538 let scope_chain = solidity_scope_chain(&def_node, source);
3539 let signature = extract_signature(source, &def_node);
3540 let name = if signature.trim_start().starts_with("receive") {
3541 "receive"
3542 } else {
3543 "fallback"
3544 };
3545 symbols.push(Symbol {
3546 name: name.to_string(),
3547 kind: SymbolKind::Method,
3548 range: node_range_with_decorators(&def_node, source, lang),
3549 signature: Some(signature),
3550 parent: scope_chain.last().cloned(),
3551 scope_chain,
3552 exported: true,
3553 });
3554 }
3555
3556 if let (Some(name_node), Some(def_node)) = (event_name_node, event_def_node) {
3558 let scope_chain = solidity_scope_chain(&def_node, source);
3559 symbols.push(Symbol {
3560 name: node_text(source, &name_node).to_string(),
3561 kind: SymbolKind::Function,
3562 range: node_range_with_decorators(&def_node, source, lang),
3563 signature: Some(extract_signature(source, &def_node)),
3564 parent: scope_chain.last().cloned(),
3565 scope_chain,
3566 exported: true,
3567 });
3568 }
3569
3570 if let (Some(name_node), Some(def_node)) = (error_name_node, error_def_node) {
3572 let scope_chain = solidity_scope_chain(&def_node, source);
3573 symbols.push(Symbol {
3574 name: node_text(source, &name_node).to_string(),
3575 kind: SymbolKind::TypeAlias,
3576 range: node_range_with_decorators(&def_node, source, lang),
3577 signature: Some(extract_signature(source, &def_node)),
3578 parent: scope_chain.last().cloned(),
3579 scope_chain,
3580 exported: true,
3581 });
3582 }
3583
3584 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
3586 let scope_chain = solidity_scope_chain(&def_node, source);
3587 symbols.push(Symbol {
3588 name: node_text(source, &name_node).to_string(),
3589 kind: SymbolKind::Struct,
3590 range: node_range_with_decorators(&def_node, source, lang),
3591 signature: Some(extract_signature(source, &def_node)),
3592 parent: scope_chain.last().cloned(),
3593 scope_chain,
3594 exported: true,
3595 });
3596 }
3597
3598 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
3600 let scope_chain = solidity_scope_chain(&def_node, source);
3601 symbols.push(Symbol {
3602 name: node_text(source, &name_node).to_string(),
3603 kind: SymbolKind::Enum,
3604 range: node_range_with_decorators(&def_node, source, lang),
3605 signature: Some(extract_signature(source, &def_node)),
3606 parent: scope_chain.last().cloned(),
3607 scope_chain,
3608 exported: true,
3609 });
3610 }
3611
3612 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
3614 let scope_chain = solidity_scope_chain(&def_node, source);
3615 symbols.push(Symbol {
3616 name: node_text(source, &name_node).to_string(),
3617 kind: SymbolKind::Variable,
3618 range: node_range_with_decorators(&def_node, source, lang),
3619 signature: Some(extract_signature(source, &def_node)),
3620 parent: scope_chain.last().cloned(),
3621 scope_chain,
3622 exported: true,
3623 });
3624 }
3625 }
3626
3627 dedup_symbols(&mut symbols);
3628 Ok(symbols)
3629}
3630
3631fn extract_json_symbols(source: &str, root: &Node) -> Result<Vec<Symbol>, AftError> {
3632 let Some(value) = root.named_child(0) else {
3633 return Ok(Vec::new());
3634 };
3635
3636 if value.kind() != "object" {
3637 return Ok(Vec::new());
3638 }
3639
3640 let mut symbols = Vec::new();
3641 let mut cursor = value.walk();
3642 for child in value.named_children(&mut cursor) {
3643 if child.kind() != "pair" {
3644 continue;
3645 }
3646 let Some(key_node) = child.child_by_field_name("key") else {
3647 continue;
3648 };
3649 let name = node_text(source, &key_node).trim_matches('"').to_string();
3650 if name.is_empty() {
3651 continue;
3652 }
3653 symbols.push(Symbol {
3654 name,
3655 kind: SymbolKind::Variable,
3656 range: node_range_with_decorators(&child, source, LangId::Json),
3657 signature: None,
3658 scope_chain: vec![],
3659 exported: false,
3660 parent: None,
3661 });
3662 }
3663
3664 Ok(symbols)
3665}
3666
3667fn scala_scope_chain(node: &Node, source: &str) -> Vec<String> {
3668 let mut chain = Vec::new();
3669 let mut current = node.parent();
3670
3671 while let Some(parent) = current {
3672 match parent.kind() {
3673 "class_definition" | "object_definition" | "enum_definition" | "trait_definition" => {
3674 if let Some(name_node) = parent.child_by_field_name("name") {
3675 chain.push(node_text(source, &name_node).to_string());
3676 }
3677 }
3678 _ => {}
3679 }
3680 current = parent.parent();
3681 }
3682
3683 chain.reverse();
3684 chain
3685}
3686
3687fn extract_scala_symbols(
3688 source: &str,
3689 root: &Node,
3690 query: &Query,
3691) -> Result<Vec<Symbol>, AftError> {
3692 let lang = LangId::Scala;
3693 let capture_names = query.capture_names();
3694
3695 let mut symbols = Vec::new();
3696 let mut cursor = QueryCursor::new();
3697 let mut matches = cursor.matches(query, *root, source.as_bytes());
3698
3699 while let Some(m) = {
3700 matches.advance();
3701 matches.get()
3702 } {
3703 let mut class_name_node = None;
3704 let mut class_def_node = None;
3705 let mut object_name_node = None;
3706 let mut object_def_node = None;
3707 let mut enum_name_node = None;
3708 let mut enum_def_node = None;
3709 let mut trait_name_node = None;
3710 let mut trait_def_node = None;
3711 let mut fn_name_node = None;
3712 let mut fn_def_node = None;
3713 let mut val_name_node = None;
3714 let mut val_def_node = None;
3715 let mut var_name_node = None;
3716 let mut var_def_node = None;
3717 let mut type_name_node = None;
3718 let mut type_def_node = None;
3719
3720 for cap in m.captures {
3721 let Some(&name) = capture_names.get(cap.index as usize) else {
3722 continue;
3723 };
3724 match name {
3725 "class.name" => class_name_node = Some(cap.node),
3726 "class.def" => class_def_node = Some(cap.node),
3727 "object.name" => object_name_node = Some(cap.node),
3728 "object.def" => object_def_node = Some(cap.node),
3729 "enum.name" => enum_name_node = Some(cap.node),
3730 "enum.def" => enum_def_node = Some(cap.node),
3731 "trait.name" => trait_name_node = Some(cap.node),
3732 "trait.def" => trait_def_node = Some(cap.node),
3733 "fn.name" => fn_name_node = Some(cap.node),
3734 "fn.def" => fn_def_node = Some(cap.node),
3735 "val.name" => val_name_node = Some(cap.node),
3736 "val.def" => val_def_node = Some(cap.node),
3737 "var.name" => var_name_node = Some(cap.node),
3738 "var.def" => var_def_node = Some(cap.node),
3739 "given.name" => val_name_node = Some(cap.node),
3740 "given.def" => val_def_node = Some(cap.node),
3741 "type.name" => type_name_node = Some(cap.node),
3742 "type.def" => type_def_node = Some(cap.node),
3743 _ => {}
3744 }
3745 }
3746
3747 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
3748 symbols.push(Symbol {
3749 name: node_text(source, &name_node).to_string(),
3750 kind: SymbolKind::Class,
3751 range: node_range_with_decorators(&def_node, source, lang),
3752 signature: Some(extract_signature(source, &def_node)),
3753 scope_chain: scala_scope_chain(&def_node, source),
3754 exported: true,
3755 parent: scala_scope_chain(&def_node, source).last().cloned(),
3756 });
3757 }
3758
3759 if let (Some(name_node), Some(def_node)) = (object_name_node, object_def_node) {
3760 symbols.push(Symbol {
3761 name: node_text(source, &name_node).to_string(),
3762 kind: SymbolKind::Class,
3763 range: node_range_with_decorators(&def_node, source, lang),
3764 signature: Some(extract_signature(source, &def_node)),
3765 scope_chain: scala_scope_chain(&def_node, source),
3766 exported: true,
3767 parent: scala_scope_chain(&def_node, source).last().cloned(),
3768 });
3769 }
3770
3771 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
3772 symbols.push(Symbol {
3773 name: node_text(source, &name_node).to_string(),
3774 kind: SymbolKind::Enum,
3775 range: node_range_with_decorators(&def_node, source, lang),
3776 signature: Some(extract_signature(source, &def_node)),
3777 scope_chain: scala_scope_chain(&def_node, source),
3778 exported: true,
3779 parent: scala_scope_chain(&def_node, source).last().cloned(),
3780 });
3781 }
3782
3783 if let (Some(name_node), Some(def_node)) = (trait_name_node, trait_def_node) {
3784 symbols.push(Symbol {
3785 name: node_text(source, &name_node).to_string(),
3786 kind: SymbolKind::Interface,
3787 range: node_range_with_decorators(&def_node, source, lang),
3788 signature: Some(extract_signature(source, &def_node)),
3789 scope_chain: scala_scope_chain(&def_node, source),
3790 exported: true,
3791 parent: scala_scope_chain(&def_node, source).last().cloned(),
3792 });
3793 }
3794
3795 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
3796 let scope_chain = scala_scope_chain(&def_node, source);
3797 let kind = if scope_chain.is_empty() {
3798 SymbolKind::Function
3799 } else {
3800 SymbolKind::Method
3801 };
3802 symbols.push(Symbol {
3803 name: node_text(source, &name_node).to_string(),
3804 kind,
3805 range: node_range_with_decorators(&def_node, source, lang),
3806 signature: Some(extract_signature(source, &def_node)),
3807 parent: scope_chain.last().cloned(),
3808 scope_chain,
3809 exported: true,
3810 });
3811 }
3812
3813 if let (Some(name_node), Some(def_node)) = (val_name_node, val_def_node) {
3814 let scope_chain = scala_scope_chain(&def_node, source);
3815 symbols.push(Symbol {
3816 name: node_text(source, &name_node).to_string(),
3817 kind: SymbolKind::Variable,
3818 range: node_range_with_decorators(&def_node, source, lang),
3819 signature: Some(extract_signature(source, &def_node)),
3820 parent: scope_chain.last().cloned(),
3821 scope_chain,
3822 exported: true,
3823 });
3824 }
3825
3826 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
3827 let scope_chain = scala_scope_chain(&def_node, source);
3828 symbols.push(Symbol {
3829 name: node_text(source, &name_node).to_string(),
3830 kind: SymbolKind::Variable,
3831 range: node_range_with_decorators(&def_node, source, lang),
3832 signature: Some(extract_signature(source, &def_node)),
3833 parent: scope_chain.last().cloned(),
3834 scope_chain,
3835 exported: true,
3836 });
3837 }
3838
3839 if let (Some(name_node), Some(def_node)) = (type_name_node, type_def_node) {
3840 let scope_chain = scala_scope_chain(&def_node, source);
3841 symbols.push(Symbol {
3842 name: node_text(source, &name_node).to_string(),
3843 kind: SymbolKind::TypeAlias,
3844 range: node_range_with_decorators(&def_node, source, lang),
3845 signature: Some(extract_signature(source, &def_node)),
3846 parent: scope_chain.last().cloned(),
3847 scope_chain,
3848 exported: true,
3849 });
3850 }
3851 }
3852
3853 dedup_symbols(&mut symbols);
3854 Ok(symbols)
3855}
3856
3857fn child_text_by_field_or_kind(
3858 node: &Node,
3859 source: &str,
3860 field_name: &str,
3861 kinds: &[&str],
3862) -> Option<String> {
3863 if let Some(name_node) = node.child_by_field_name(field_name) {
3864 return Some(node_text(source, &name_node).to_string());
3865 }
3866
3867 let mut cursor = node.walk();
3868 if !cursor.goto_first_child() {
3869 return None;
3870 }
3871
3872 loop {
3873 let child = cursor.node();
3874 if kinds.contains(&child.kind()) {
3875 return Some(node_text(source, &child).to_string());
3876 }
3877 if !cursor.goto_next_sibling() {
3878 break;
3879 }
3880 }
3881
3882 None
3883}
3884
3885fn push_captured_symbol(
3886 symbols: &mut Vec<Symbol>,
3887 source: &str,
3888 lang: LangId,
3889 name_node: Node,
3890 def_node: Node,
3891 kind: SymbolKind,
3892 scope_chain: Vec<String>,
3893 exported: bool,
3894) {
3895 symbols.push(Symbol {
3896 name: node_text(source, &name_node).to_string(),
3897 kind,
3898 range: node_range_with_decorators(&def_node, source, lang),
3899 signature: Some(extract_signature(source, &def_node)),
3900 parent: scope_chain.last().cloned(),
3901 scope_chain,
3902 exported,
3903 });
3904}
3905
3906fn java_scope_chain(node: &Node, source: &str) -> Vec<String> {
3907 let mut chain = Vec::new();
3908 let mut current = node.parent();
3909
3910 while let Some(parent) = current {
3911 if matches!(
3912 parent.kind(),
3913 "class_declaration"
3914 | "interface_declaration"
3915 | "annotation_type_declaration"
3916 | "enum_declaration"
3917 | "record_declaration"
3918 ) {
3919 if let Some(name_node) = parent.child_by_field_name("name") {
3920 chain.push(node_text(source, &name_node).to_string());
3921 }
3922 }
3923 current = parent.parent();
3924 }
3925
3926 chain.reverse();
3927 chain
3928}
3929
3930fn extract_java_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
3931 let lang = LangId::Java;
3932 let capture_names = query.capture_names();
3933 let mut symbols = Vec::new();
3934 let mut cursor = QueryCursor::new();
3935 let mut matches = cursor.matches(query, *root, source.as_bytes());
3936
3937 while let Some(m) = {
3938 matches.advance();
3939 matches.get()
3940 } {
3941 let mut class_name_node = None;
3942 let mut class_def_node = None;
3943 let mut interface_name_node = None;
3944 let mut interface_def_node = None;
3945 let mut enum_name_node = None;
3946 let mut enum_def_node = None;
3947 let mut struct_name_node = None;
3948 let mut struct_def_node = None;
3949 let mut fn_name_node = None;
3950 let mut fn_def_node = None;
3951 let mut var_name_node = None;
3952 let mut var_def_node = None;
3953
3954 for cap in m.captures {
3955 let Some(&name) = capture_names.get(cap.index as usize) else {
3956 continue;
3957 };
3958 match name {
3959 "class.name" => class_name_node = Some(cap.node),
3960 "class.def" => class_def_node = Some(cap.node),
3961 "interface.name" => interface_name_node = Some(cap.node),
3962 "interface.def" => interface_def_node = Some(cap.node),
3963 "enum.name" => enum_name_node = Some(cap.node),
3964 "enum.def" => enum_def_node = Some(cap.node),
3965 "struct.name" => struct_name_node = Some(cap.node),
3966 "struct.def" => struct_def_node = Some(cap.node),
3967 "fn.name" => fn_name_node = Some(cap.node),
3968 "fn.def" => fn_def_node = Some(cap.node),
3969 "var.name" => var_name_node = Some(cap.node),
3970 "var.def" => var_def_node = Some(cap.node),
3971 _ => {}
3972 }
3973 }
3974
3975 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
3976 push_captured_symbol(
3977 &mut symbols,
3978 source,
3979 lang,
3980 name_node,
3981 def_node,
3982 SymbolKind::Class,
3983 java_scope_chain(&def_node, source),
3984 true,
3985 );
3986 }
3987 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
3988 push_captured_symbol(
3989 &mut symbols,
3990 source,
3991 lang,
3992 name_node,
3993 def_node,
3994 SymbolKind::Interface,
3995 java_scope_chain(&def_node, source),
3996 true,
3997 );
3998 }
3999 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
4000 push_captured_symbol(
4001 &mut symbols,
4002 source,
4003 lang,
4004 name_node,
4005 def_node,
4006 SymbolKind::Enum,
4007 java_scope_chain(&def_node, source),
4008 true,
4009 );
4010 }
4011 if let (Some(name_node), Some(def_node)) = (struct_name_node, struct_def_node) {
4012 push_captured_symbol(
4013 &mut symbols,
4014 source,
4015 lang,
4016 name_node,
4017 def_node,
4018 SymbolKind::Struct,
4019 java_scope_chain(&def_node, source),
4020 true,
4021 );
4022 }
4023 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4024 let scope_chain = java_scope_chain(&def_node, source);
4025 let kind = if scope_chain.is_empty() {
4026 SymbolKind::Function
4027 } else {
4028 SymbolKind::Method
4029 };
4030 push_captured_symbol(
4031 &mut symbols,
4032 source,
4033 lang,
4034 name_node,
4035 def_node,
4036 kind,
4037 scope_chain,
4038 true,
4039 );
4040 }
4041 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4042 push_captured_symbol(
4043 &mut symbols,
4044 source,
4045 lang,
4046 name_node,
4047 def_node,
4048 SymbolKind::Variable,
4049 java_scope_chain(&def_node, source),
4050 true,
4051 );
4052 }
4053 }
4054
4055 dedup_symbols(&mut symbols);
4056 Ok(symbols)
4057}
4058
4059fn ruby_scope_chain(node: &Node, source: &str) -> Vec<String> {
4060 let mut chain = Vec::new();
4061 let mut current = node.parent();
4062
4063 while let Some(parent) = current {
4064 if matches!(parent.kind(), "class" | "module") {
4065 if let Some(name_node) = parent.child_by_field_name("name") {
4066 chain.push(node_text(source, &name_node).to_string());
4067 }
4068 }
4069 current = parent.parent();
4070 }
4071
4072 chain.reverse();
4073 chain
4074}
4075
4076fn extract_ruby_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
4077 let lang = LangId::Ruby;
4078 let capture_names = query.capture_names();
4079 let mut symbols = Vec::new();
4080 let mut cursor = QueryCursor::new();
4081 let mut matches = cursor.matches(query, *root, source.as_bytes());
4082
4083 while let Some(m) = {
4084 matches.advance();
4085 matches.get()
4086 } {
4087 let mut module_name_node = None;
4088 let mut module_def_node = None;
4089 let mut class_name_node = None;
4090 let mut class_def_node = None;
4091 let mut fn_name_node = None;
4092 let mut fn_def_node = None;
4093 let mut var_name_node = None;
4094 let mut var_def_node = None;
4095
4096 for cap in m.captures {
4097 let Some(&name) = capture_names.get(cap.index as usize) else {
4098 continue;
4099 };
4100 match name {
4101 "module.name" => module_name_node = Some(cap.node),
4102 "module.def" => module_def_node = Some(cap.node),
4103 "class.name" => class_name_node = Some(cap.node),
4104 "class.def" => class_def_node = Some(cap.node),
4105 "fn.name" => fn_name_node = Some(cap.node),
4106 "fn.def" => fn_def_node = Some(cap.node),
4107 "var.name" => var_name_node = Some(cap.node),
4108 "var.def" => var_def_node = Some(cap.node),
4109 _ => {}
4110 }
4111 }
4112
4113 if let (Some(name_node), Some(def_node)) = (module_name_node, module_def_node) {
4114 push_captured_symbol(
4115 &mut symbols,
4116 source,
4117 lang,
4118 name_node,
4119 def_node,
4120 SymbolKind::Class,
4121 ruby_scope_chain(&def_node, source),
4122 true,
4123 );
4124 }
4125 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
4126 push_captured_symbol(
4127 &mut symbols,
4128 source,
4129 lang,
4130 name_node,
4131 def_node,
4132 SymbolKind::Class,
4133 ruby_scope_chain(&def_node, source),
4134 true,
4135 );
4136 }
4137 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4138 let scope_chain = ruby_scope_chain(&def_node, source);
4139 let kind = if scope_chain.is_empty() {
4140 SymbolKind::Function
4141 } else {
4142 SymbolKind::Method
4143 };
4144 push_captured_symbol(
4145 &mut symbols,
4146 source,
4147 lang,
4148 name_node,
4149 def_node,
4150 kind,
4151 scope_chain,
4152 true,
4153 );
4154 }
4155 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4156 push_captured_symbol(
4157 &mut symbols,
4158 source,
4159 lang,
4160 name_node,
4161 def_node,
4162 SymbolKind::Variable,
4163 ruby_scope_chain(&def_node, source),
4164 true,
4165 );
4166 }
4167 }
4168
4169 dedup_symbols(&mut symbols);
4170 Ok(symbols)
4171}
4172
4173fn kotlin_scope_chain(node: &Node, source: &str) -> Vec<String> {
4174 let mut chain = Vec::new();
4175 let mut current = node.parent();
4176
4177 while let Some(parent) = current {
4178 if matches!(parent.kind(), "class_declaration" | "object_declaration") {
4179 if let Some(name) =
4180 child_text_by_field_or_kind(&parent, source, "name", &["type_identifier"])
4181 {
4182 chain.push(name);
4183 }
4184 }
4185 current = parent.parent();
4186 }
4187
4188 chain.reverse();
4189 chain
4190}
4191
4192fn extract_kotlin_symbols(
4193 source: &str,
4194 root: &Node,
4195 query: &Query,
4196) -> Result<Vec<Symbol>, AftError> {
4197 let lang = LangId::Kotlin;
4198 let capture_names = query.capture_names();
4199 let mut symbols = Vec::new();
4200 let mut cursor = QueryCursor::new();
4201 let mut matches = cursor.matches(query, *root, source.as_bytes());
4202
4203 while let Some(m) = {
4204 matches.advance();
4205 matches.get()
4206 } {
4207 let mut class_name_node = None;
4208 let mut class_def_node = None;
4209 let mut object_name_node = None;
4210 let mut object_def_node = None;
4211 let mut fn_name_node = None;
4212 let mut fn_def_node = None;
4213 let mut var_name_node = None;
4214 let mut var_def_node = None;
4215 let mut type_name_node = None;
4216 let mut type_def_node = None;
4217
4218 for cap in m.captures {
4219 let Some(&name) = capture_names.get(cap.index as usize) else {
4220 continue;
4221 };
4222 match name {
4223 "class.name" => class_name_node = Some(cap.node),
4224 "class.def" => class_def_node = Some(cap.node),
4225 "object.name" => object_name_node = Some(cap.node),
4226 "object.def" => object_def_node = Some(cap.node),
4227 "fn.name" => fn_name_node = Some(cap.node),
4228 "fn.def" => fn_def_node = Some(cap.node),
4229 "var.name" => var_name_node = Some(cap.node),
4230 "var.def" => var_def_node = Some(cap.node),
4231 "type.name" => type_name_node = Some(cap.node),
4232 "type.def" => type_def_node = Some(cap.node),
4233 _ => {}
4234 }
4235 }
4236
4237 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
4238 push_captured_symbol(
4239 &mut symbols,
4240 source,
4241 lang,
4242 name_node,
4243 def_node,
4244 SymbolKind::Class,
4245 kotlin_scope_chain(&def_node, source),
4246 true,
4247 );
4248 }
4249 if let (Some(name_node), Some(def_node)) = (object_name_node, object_def_node) {
4250 push_captured_symbol(
4251 &mut symbols,
4252 source,
4253 lang,
4254 name_node,
4255 def_node,
4256 SymbolKind::Class,
4257 kotlin_scope_chain(&def_node, source),
4258 true,
4259 );
4260 }
4261 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4262 let scope_chain = kotlin_scope_chain(&def_node, source);
4263 let kind = if scope_chain.is_empty() {
4264 SymbolKind::Function
4265 } else {
4266 SymbolKind::Method
4267 };
4268 push_captured_symbol(
4269 &mut symbols,
4270 source,
4271 lang,
4272 name_node,
4273 def_node,
4274 kind,
4275 scope_chain,
4276 true,
4277 );
4278 }
4279 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4280 push_captured_symbol(
4281 &mut symbols,
4282 source,
4283 lang,
4284 name_node,
4285 def_node,
4286 SymbolKind::Variable,
4287 kotlin_scope_chain(&def_node, source),
4288 true,
4289 );
4290 }
4291 if let (Some(name_node), Some(def_node)) = (type_name_node, type_def_node) {
4292 push_captured_symbol(
4293 &mut symbols,
4294 source,
4295 lang,
4296 name_node,
4297 def_node,
4298 SymbolKind::TypeAlias,
4299 kotlin_scope_chain(&def_node, source),
4300 true,
4301 );
4302 }
4303 }
4304
4305 dedup_symbols(&mut symbols);
4306 Ok(symbols)
4307}
4308
4309fn swift_scope_chain(node: &Node, source: &str) -> Vec<String> {
4310 let mut chain = Vec::new();
4311 let mut current = node.parent();
4312
4313 while let Some(parent) = current {
4314 if matches!(parent.kind(), "class_declaration" | "protocol_declaration") {
4315 if let Some(name_node) = parent.child_by_field_name("name") {
4316 chain.push(node_text(source, &name_node).to_string());
4317 }
4318 }
4319 current = parent.parent();
4320 }
4321
4322 chain.reverse();
4323 chain
4324}
4325
4326fn swift_type_kind(source: &str, node: &Node) -> SymbolKind {
4327 match node
4328 .child_by_field_name("declaration_kind")
4329 .map(|kind_node| node_text(source, &kind_node))
4330 {
4331 Some("struct") => SymbolKind::Struct,
4332 Some("enum") => SymbolKind::Enum,
4333 _ => SymbolKind::Class,
4334 }
4335}
4336
4337fn extract_swift_symbols(
4338 source: &str,
4339 root: &Node,
4340 query: &Query,
4341) -> Result<Vec<Symbol>, AftError> {
4342 let lang = LangId::Swift;
4343 let capture_names = query.capture_names();
4344 let mut symbols = Vec::new();
4345 let mut cursor = QueryCursor::new();
4346 let mut matches = cursor.matches(query, *root, source.as_bytes());
4347
4348 while let Some(m) = {
4349 matches.advance();
4350 matches.get()
4351 } {
4352 let mut class_name_node = None;
4353 let mut class_def_node = None;
4354 let mut interface_name_node = None;
4355 let mut interface_def_node = None;
4356 let mut fn_name_node = None;
4357 let mut fn_def_node = None;
4358 let mut var_name_node = None;
4359 let mut var_def_node = None;
4360 let mut type_name_node = None;
4361 let mut type_def_node = None;
4362
4363 for cap in m.captures {
4364 let Some(&name) = capture_names.get(cap.index as usize) else {
4365 continue;
4366 };
4367 match name {
4368 "class.name" => class_name_node = Some(cap.node),
4369 "class.def" => class_def_node = Some(cap.node),
4370 "interface.name" => interface_name_node = Some(cap.node),
4371 "interface.def" => interface_def_node = Some(cap.node),
4372 "fn.name" => fn_name_node = Some(cap.node),
4373 "fn.def" => fn_def_node = Some(cap.node),
4374 "var.name" => var_name_node = Some(cap.node),
4375 "var.def" => var_def_node = Some(cap.node),
4376 "type.name" => type_name_node = Some(cap.node),
4377 "type.def" => type_def_node = Some(cap.node),
4378 _ => {}
4379 }
4380 }
4381
4382 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
4383 let kind = swift_type_kind(source, &def_node);
4384 push_captured_symbol(
4385 &mut symbols,
4386 source,
4387 lang,
4388 name_node,
4389 def_node,
4390 kind,
4391 swift_scope_chain(&def_node, source),
4392 true,
4393 );
4394 }
4395 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
4396 push_captured_symbol(
4397 &mut symbols,
4398 source,
4399 lang,
4400 name_node,
4401 def_node,
4402 SymbolKind::Interface,
4403 swift_scope_chain(&def_node, source),
4404 true,
4405 );
4406 }
4407 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4408 let scope_chain = swift_scope_chain(&def_node, source);
4409 let kind = if scope_chain.is_empty() {
4410 SymbolKind::Function
4411 } else {
4412 SymbolKind::Method
4413 };
4414 push_captured_symbol(
4415 &mut symbols,
4416 source,
4417 lang,
4418 name_node,
4419 def_node,
4420 kind,
4421 scope_chain,
4422 true,
4423 );
4424 }
4425 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4426 push_captured_symbol(
4427 &mut symbols,
4428 source,
4429 lang,
4430 name_node,
4431 def_node,
4432 SymbolKind::Variable,
4433 swift_scope_chain(&def_node, source),
4434 true,
4435 );
4436 }
4437 if let (Some(name_node), Some(def_node)) = (type_name_node, type_def_node) {
4438 push_captured_symbol(
4439 &mut symbols,
4440 source,
4441 lang,
4442 name_node,
4443 def_node,
4444 SymbolKind::TypeAlias,
4445 swift_scope_chain(&def_node, source),
4446 true,
4447 );
4448 }
4449 }
4450
4451 dedup_symbols(&mut symbols);
4452 Ok(symbols)
4453}
4454
4455fn php_scope_chain(node: &Node, source: &str) -> Vec<String> {
4456 let mut chain = Vec::new();
4457 let mut current = node.parent();
4458
4459 while let Some(parent) = current {
4460 match parent.kind() {
4461 "namespace_definition"
4462 | "class_declaration"
4463 | "interface_declaration"
4464 | "trait_declaration"
4465 | "enum_declaration" => {
4466 if let Some(name_node) = parent.child_by_field_name("name") {
4467 chain.push(node_text(source, &name_node).to_string());
4468 }
4469 }
4470 _ => {}
4471 }
4472 current = parent.parent();
4473 }
4474
4475 chain.reverse();
4476 chain
4477}
4478
4479fn extract_php_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
4480 let lang = LangId::Php;
4481 let capture_names = query.capture_names();
4482 let mut symbols = Vec::new();
4483 let mut cursor = QueryCursor::new();
4484 let mut matches = cursor.matches(query, *root, source.as_bytes());
4485
4486 while let Some(m) = {
4487 matches.advance();
4488 matches.get()
4489 } {
4490 let mut namespace_name_node = None;
4491 let mut namespace_def_node = None;
4492 let mut class_name_node = None;
4493 let mut class_def_node = None;
4494 let mut interface_name_node = None;
4495 let mut interface_def_node = None;
4496 let mut trait_name_node = None;
4497 let mut trait_def_node = None;
4498 let mut enum_name_node = None;
4499 let mut enum_def_node = None;
4500 let mut fn_name_node = None;
4501 let mut fn_def_node = None;
4502 let mut var_name_node = None;
4503 let mut var_def_node = None;
4504
4505 for cap in m.captures {
4506 let Some(&name) = capture_names.get(cap.index as usize) else {
4507 continue;
4508 };
4509 match name {
4510 "namespace.name" => namespace_name_node = Some(cap.node),
4511 "namespace.def" => namespace_def_node = Some(cap.node),
4512 "class.name" => class_name_node = Some(cap.node),
4513 "class.def" => class_def_node = Some(cap.node),
4514 "interface.name" => interface_name_node = Some(cap.node),
4515 "interface.def" => interface_def_node = Some(cap.node),
4516 "trait.name" => trait_name_node = Some(cap.node),
4517 "trait.def" => trait_def_node = Some(cap.node),
4518 "enum.name" => enum_name_node = Some(cap.node),
4519 "enum.def" => enum_def_node = Some(cap.node),
4520 "fn.name" => fn_name_node = Some(cap.node),
4521 "fn.def" => fn_def_node = Some(cap.node),
4522 "var.name" => var_name_node = Some(cap.node),
4523 "var.def" => var_def_node = Some(cap.node),
4524 _ => {}
4525 }
4526 }
4527
4528 if let (Some(name_node), Some(def_node)) = (namespace_name_node, namespace_def_node) {
4529 push_captured_symbol(
4530 &mut symbols,
4531 source,
4532 lang,
4533 name_node,
4534 def_node,
4535 SymbolKind::Class,
4536 php_scope_chain(&def_node, source),
4537 true,
4538 );
4539 }
4540 if let (Some(name_node), Some(def_node)) = (class_name_node, class_def_node) {
4541 push_captured_symbol(
4542 &mut symbols,
4543 source,
4544 lang,
4545 name_node,
4546 def_node,
4547 SymbolKind::Class,
4548 php_scope_chain(&def_node, source),
4549 true,
4550 );
4551 }
4552 if let (Some(name_node), Some(def_node)) = (interface_name_node, interface_def_node) {
4553 push_captured_symbol(
4554 &mut symbols,
4555 source,
4556 lang,
4557 name_node,
4558 def_node,
4559 SymbolKind::Interface,
4560 php_scope_chain(&def_node, source),
4561 true,
4562 );
4563 }
4564 if let (Some(name_node), Some(def_node)) = (trait_name_node, trait_def_node) {
4565 push_captured_symbol(
4566 &mut symbols,
4567 source,
4568 lang,
4569 name_node,
4570 def_node,
4571 SymbolKind::Interface,
4572 php_scope_chain(&def_node, source),
4573 true,
4574 );
4575 }
4576 if let (Some(name_node), Some(def_node)) = (enum_name_node, enum_def_node) {
4577 push_captured_symbol(
4578 &mut symbols,
4579 source,
4580 lang,
4581 name_node,
4582 def_node,
4583 SymbolKind::Enum,
4584 php_scope_chain(&def_node, source),
4585 true,
4586 );
4587 }
4588 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4589 let scope_chain = php_scope_chain(&def_node, source);
4590 let kind = if scope_chain.is_empty() || def_node.kind() == "function_definition" {
4591 SymbolKind::Function
4592 } else {
4593 SymbolKind::Method
4594 };
4595 push_captured_symbol(
4596 &mut symbols,
4597 source,
4598 lang,
4599 name_node,
4600 def_node,
4601 kind,
4602 scope_chain,
4603 true,
4604 );
4605 }
4606 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4607 push_captured_symbol(
4608 &mut symbols,
4609 source,
4610 lang,
4611 name_node,
4612 def_node,
4613 SymbolKind::Variable,
4614 php_scope_chain(&def_node, source),
4615 true,
4616 );
4617 }
4618 }
4619
4620 dedup_symbols(&mut symbols);
4621 Ok(symbols)
4622}
4623
4624fn lua_scope_chain(node: &Node, source: &str) -> Vec<String> {
4625 let mut chain = Vec::new();
4626
4627 if node.kind() == "function_declaration" {
4628 if let Some(name_node) = node.child_by_field_name("name") {
4629 match name_node.kind() {
4630 "dot_index_expression" | "method_index_expression" => {
4631 if let Some(table_node) = name_node.child_by_field_name("table") {
4632 chain.push(node_text(source, &table_node).to_string());
4633 }
4634 }
4635 _ => {}
4636 }
4637 }
4638 }
4639
4640 chain
4641}
4642
4643fn extract_lua_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
4644 let lang = LangId::Lua;
4645 let capture_names = query.capture_names();
4646 let mut symbols = Vec::new();
4647 let mut cursor = QueryCursor::new();
4648 let mut matches = cursor.matches(query, *root, source.as_bytes());
4649
4650 while let Some(m) = {
4651 matches.advance();
4652 matches.get()
4653 } {
4654 let mut fn_name_node = None;
4655 let mut fn_def_node = None;
4656 let mut var_name_node = None;
4657 let mut var_def_node = None;
4658
4659 for cap in m.captures {
4660 let Some(&name) = capture_names.get(cap.index as usize) else {
4661 continue;
4662 };
4663 match name {
4664 "fn.name" => fn_name_node = Some(cap.node),
4665 "fn.def" => fn_def_node = Some(cap.node),
4666 "var.name" => var_name_node = Some(cap.node),
4667 "var.def" => var_def_node = Some(cap.node),
4668 _ => {}
4669 }
4670 }
4671
4672 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4673 let scope_chain = lua_scope_chain(&def_node, source);
4674 let kind = if scope_chain.is_empty() {
4675 SymbolKind::Function
4676 } else {
4677 SymbolKind::Method
4678 };
4679 push_captured_symbol(
4680 &mut symbols,
4681 source,
4682 lang,
4683 name_node,
4684 def_node,
4685 kind,
4686 scope_chain,
4687 true,
4688 );
4689 }
4690 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4691 push_captured_symbol(
4692 &mut symbols,
4693 source,
4694 lang,
4695 name_node,
4696 def_node,
4697 SymbolKind::Variable,
4698 vec![],
4699 true,
4700 );
4701 }
4702 }
4703
4704 dedup_symbols(&mut symbols);
4705 Ok(symbols)
4706}
4707
4708fn perl_package_name(source: &str, node: &Node) -> Option<String> {
4709 let mut cursor = node.walk();
4710 if !cursor.goto_first_child() {
4711 return None;
4712 }
4713
4714 loop {
4715 let child = cursor.node();
4716 if child.kind() == "package_name" {
4717 return Some(node_text(source, &child).to_string());
4718 }
4719 if !cursor.goto_next_sibling() {
4720 break;
4721 }
4722 }
4723
4724 None
4725}
4726
4727fn perl_scope_chain(node: &Node, source: &str) -> Vec<String> {
4728 let mut chain = Vec::new();
4729 let mut current = node.parent();
4730
4731 while let Some(parent) = current {
4732 if parent.kind() == "package_statement" {
4733 if let Some(name) = perl_package_name(source, &parent) {
4734 chain.push(name);
4735 }
4736 }
4737 current = parent.parent();
4738 }
4739
4740 if chain.is_empty() && node.kind() != "package_statement" {
4741 let mut sibling = node.prev_sibling();
4742 while let Some(prev) = sibling {
4743 if prev.kind() == "package_statement" {
4744 if let Some(name) = perl_package_name(source, &prev) {
4745 chain.push(name);
4746 }
4747 break;
4748 }
4749 sibling = prev.prev_sibling();
4750 }
4751 }
4752
4753 chain.reverse();
4754 chain
4755}
4756
4757fn extract_perl_symbols(source: &str, root: &Node, query: &Query) -> Result<Vec<Symbol>, AftError> {
4758 let lang = LangId::Perl;
4759 let capture_names = query.capture_names();
4760 let mut symbols = Vec::new();
4761 let mut cursor = QueryCursor::new();
4762 let mut matches = cursor.matches(query, *root, source.as_bytes());
4763
4764 while let Some(m) = {
4765 matches.advance();
4766 matches.get()
4767 } {
4768 let mut package_name_node = None;
4769 let mut package_def_node = None;
4770 let mut fn_name_node = None;
4771 let mut fn_def_node = None;
4772 let mut var_name_node = None;
4773 let mut var_def_node = None;
4774
4775 for cap in m.captures {
4776 let Some(&name) = capture_names.get(cap.index as usize) else {
4777 continue;
4778 };
4779 match name {
4780 "package.name" => package_name_node = Some(cap.node),
4781 "package.def" => package_def_node = Some(cap.node),
4782 "fn.name" => fn_name_node = Some(cap.node),
4783 "fn.def" => fn_def_node = Some(cap.node),
4784 "var.name" => var_name_node = Some(cap.node),
4785 "var.def" => var_def_node = Some(cap.node),
4786 _ => {}
4787 }
4788 }
4789
4790 if let (Some(name_node), Some(def_node)) = (package_name_node, package_def_node) {
4791 push_captured_symbol(
4792 &mut symbols,
4793 source,
4794 lang,
4795 name_node,
4796 def_node,
4797 SymbolKind::Class,
4798 vec![],
4799 true,
4800 );
4801 }
4802 if let (Some(name_node), Some(def_node)) = (fn_name_node, fn_def_node) {
4803 let scope_chain = perl_scope_chain(&def_node, source);
4804 let kind = if scope_chain.is_empty() {
4805 SymbolKind::Function
4806 } else {
4807 SymbolKind::Method
4808 };
4809 push_captured_symbol(
4810 &mut symbols,
4811 source,
4812 lang,
4813 name_node,
4814 def_node,
4815 kind,
4816 scope_chain,
4817 true,
4818 );
4819 }
4820 if let (Some(name_node), Some(def_node)) = (var_name_node, var_def_node) {
4821 push_captured_symbol(
4822 &mut symbols,
4823 source,
4824 lang,
4825 name_node,
4826 def_node,
4827 SymbolKind::Variable,
4828 perl_scope_chain(&def_node, source),
4829 true,
4830 );
4831 }
4832 }
4833
4834 dedup_symbols(&mut symbols);
4835 Ok(symbols)
4836}
4837
4838fn extract_vue_symbols(source: &str, root: &Node) -> Result<Vec<Symbol>, AftError> {
4839 let mut symbols = Vec::new();
4840 collect_vue_sections(source, root, &mut symbols, true);
4841 dedup_symbols(&mut symbols);
4842 Ok(symbols)
4843}
4844
4845fn collect_vue_sections(
4846 source: &str,
4847 node: &Node,
4848 symbols: &mut Vec<Symbol>,
4849 allow_sections: bool,
4850) {
4851 let mut cursor = node.walk();
4852 if !cursor.goto_first_child() {
4853 return;
4854 }
4855
4856 loop {
4857 let child = cursor.node();
4858 if let Some(section_name) = vue_section_name(&child) {
4859 if allow_sections {
4860 symbols.push(Symbol {
4861 name: section_name.to_string(),
4862 kind: SymbolKind::Heading,
4863 range: node_range(&child),
4864 signature: vue_opening_tag_signature(source, &child),
4865 scope_chain: vec![],
4866 exported: false,
4867 parent: None,
4868 });
4869 }
4870 } else {
4871 collect_vue_sections(
4872 source,
4873 &child,
4874 symbols,
4875 allow_sections && child.kind() == "document",
4876 );
4877 }
4878
4879 if !cursor.goto_next_sibling() {
4880 break;
4881 }
4882 }
4883}
4884
4885fn vue_section_name(node: &Node) -> Option<&'static str> {
4886 match node.kind() {
4887 "template_element" => Some("template"),
4888 "script_element" => Some("script"),
4889 "style_element" => Some("style"),
4890 _ => None,
4891 }
4892}
4893
4894fn vue_opening_tag_signature(source: &str, node: &Node) -> Option<String> {
4895 find_child_by_kind(*node, "start_tag")
4896 .or_else(|| find_child_by_kind(*node, "script_start_tag"))
4897 .or_else(|| find_child_by_kind(*node, "style_start_tag"))
4898 .or_else(|| find_child_by_kind(*node, "template_start_tag"))
4899 .map(|tag| node_text(source, &tag).trim().to_string())
4900}
4901
4902fn extract_html_symbols(source: &str, root: &Node) -> Result<Vec<Symbol>, AftError> {
4903 let mut headings: Vec<(u8, Symbol)> = Vec::new();
4904 collect_html_headings(source, root, &mut headings);
4905
4906 let total_lines = source.lines().count() as u32;
4907
4908 for i in 0..headings.len() {
4912 let level = headings[i].0;
4913 let section_end = headings[i + 1..]
4914 .iter()
4915 .find(|(l, _)| *l <= level)
4916 .map(|(_, s)| s.range.start_line.saturating_sub(1))
4917 .unwrap_or_else(|| total_lines.saturating_sub(1));
4918 headings[i].1.range.end_line = section_end;
4919 if section_end != headings[i].1.range.start_line {
4920 headings[i].1.range.end_col = 0;
4921 }
4922 }
4923
4924 let mut scope_stack: Vec<(u8, String)> = Vec::new(); for (level, symbol) in headings.iter_mut() {
4927 while scope_stack.last().is_some_and(|(l, _)| *l >= *level) {
4929 scope_stack.pop();
4930 }
4931 symbol.scope_chain = scope_stack.iter().map(|(_, name)| name.clone()).collect();
4932 symbol.parent = scope_stack.last().map(|(_, name)| name.clone());
4933 scope_stack.push((*level, symbol.name.clone()));
4934 }
4935
4936 Ok(headings.into_iter().map(|(_, s)| s).collect())
4937}
4938
4939fn collect_html_headings(source: &str, node: &Node, headings: &mut Vec<(u8, Symbol)>) {
4941 let mut cursor = node.walk();
4942 if !cursor.goto_first_child() {
4943 return;
4944 }
4945
4946 loop {
4947 let child = cursor.node();
4948 if child.kind() == "element" {
4949 if let Some(start_tag) = child
4951 .child_by_field_name("start_tag")
4952 .or_else(|| child.child(0).filter(|c| c.kind() == "start_tag"))
4953 {
4954 if let Some(tag_name_node) = start_tag
4955 .child_by_field_name("tag_name")
4956 .or_else(|| start_tag.child(1).filter(|c| c.kind() == "tag_name"))
4957 {
4958 let tag_name = node_text(source, &tag_name_node).to_lowercase();
4959 if let Some(level) = match tag_name.as_str() {
4960 "h1" => Some(1u8),
4961 "h2" => Some(2),
4962 "h3" => Some(3),
4963 "h4" => Some(4),
4964 "h5" => Some(5),
4965 "h6" => Some(6),
4966 _ => None,
4967 } {
4968 let text = extract_element_text(source, &child).trim().to_string();
4970 if !text.is_empty() {
4971 let range = node_range(&child);
4972 let signature = format!("<h{}> {}", level, text);
4973 headings.push((
4974 level,
4975 Symbol {
4976 name: text,
4977 kind: SymbolKind::Heading,
4978 range,
4979 signature: Some(signature),
4980 scope_chain: vec![], exported: false,
4982 parent: None, },
4984 ));
4985 }
4986 }
4987 }
4988 }
4989 collect_html_headings(source, &child, headings);
4991 } else {
4992 collect_html_headings(source, &child, headings);
4994 }
4995
4996 if !cursor.goto_next_sibling() {
4997 break;
4998 }
4999 }
5000}
5001
5002fn extract_element_text(source: &str, node: &Node) -> String {
5004 let mut text = String::new();
5005 let mut cursor = node.walk();
5006 if !cursor.goto_first_child() {
5007 return text;
5008 }
5009 loop {
5010 let child = cursor.node();
5011 match child.kind() {
5012 "text" => {
5013 text.push_str(node_text(source, &child));
5014 }
5015 "element" => {
5016 text.push_str(&extract_element_text(source, &child));
5018 }
5019 _ => {}
5020 }
5021 if !cursor.goto_next_sibling() {
5022 break;
5023 }
5024 }
5025 text
5026}
5027
5028fn extract_md_symbols(source: &str, root: &Node) -> Result<Vec<Symbol>, AftError> {
5032 let mut symbols = Vec::new();
5033 extract_md_sections(source, root, &mut symbols, &[]);
5034 Ok(symbols)
5035}
5036
5037fn extract_md_sections(
5039 source: &str,
5040 node: &Node,
5041 symbols: &mut Vec<Symbol>,
5042 scope_chain: &[String],
5043) {
5044 let mut cursor = node.walk();
5045 if !cursor.goto_first_child() {
5046 return;
5047 }
5048
5049 loop {
5050 let child = cursor.node();
5051 match child.kind() {
5052 "section" => {
5053 let mut section_cursor = child.walk();
5056 let mut heading_name = String::new();
5057 let mut heading_level: u8 = 0;
5058
5059 if section_cursor.goto_first_child() {
5060 loop {
5061 let section_child = section_cursor.node();
5062 if section_child.kind() == "atx_heading" {
5063 let mut h_cursor = section_child.walk();
5065 if h_cursor.goto_first_child() {
5066 loop {
5067 let h_child = h_cursor.node();
5068 let kind = h_child.kind();
5069 if kind.starts_with("atx_h") && kind.ends_with("_marker") {
5070 heading_level = kind
5072 .strip_prefix("atx_h")
5073 .and_then(|s| s.strip_suffix("_marker"))
5074 .and_then(|s| s.parse::<u8>().ok())
5075 .unwrap_or(1);
5076 } else if h_child.kind() == "inline" {
5077 heading_name =
5078 node_text(source, &h_child).trim().to_string();
5079 }
5080 if !h_cursor.goto_next_sibling() {
5081 break;
5082 }
5083 }
5084 }
5085 }
5086 if !section_cursor.goto_next_sibling() {
5087 break;
5088 }
5089 }
5090 }
5091
5092 if !heading_name.is_empty() {
5093 let range = node_range(&child);
5094 let signature = format!(
5095 "{} {}",
5096 "#".repeat((heading_level as usize).min(6)),
5097 heading_name
5098 );
5099
5100 symbols.push(Symbol {
5101 name: heading_name.clone(),
5102 kind: SymbolKind::Heading,
5103 range,
5104 signature: Some(signature),
5105 scope_chain: scope_chain.to_vec(),
5106 exported: false,
5107 parent: scope_chain.last().cloned(),
5108 });
5109
5110 let mut new_scope = scope_chain.to_vec();
5112 new_scope.push(heading_name);
5113 extract_md_sections(source, &child, symbols, &new_scope);
5114 }
5115 }
5116 _ => {}
5117 }
5118
5119 if !cursor.goto_next_sibling() {
5120 break;
5121 }
5122 }
5123}
5124
5125fn dedup_symbols(symbols: &mut Vec<Symbol>) {
5129 let mut seen = std::collections::HashSet::new();
5130 symbols.retain(|s| {
5131 let key = (s.name.clone(), format!("{:?}", s.kind), s.range.start_line);
5132 seen.insert(key)
5133 });
5134}
5135
5136pub struct TreeSitterProvider {
5139 parser: RefCell<FileParser>,
5140}
5141
5142#[derive(Debug, Clone)]
5143struct ReExportTarget {
5144 file: PathBuf,
5145 symbol_name: String,
5146}
5147
5148impl TreeSitterProvider {
5149 pub fn new() -> Self {
5151 Self::with_symbol_cache(Arc::new(RwLock::new(SymbolCache::new())))
5152 }
5153
5154 pub fn with_symbol_cache(symbol_cache: SharedSymbolCache) -> Self {
5156 Self {
5157 parser: RefCell::new(FileParser::with_symbol_cache(symbol_cache)),
5158 }
5159 }
5160
5161 pub fn symbol_cache_len(&self) -> usize {
5163 let parser = self.parser.borrow();
5164 parser.symbol_cache_len()
5165 }
5166
5167 pub fn symbol_cache(&self) -> SharedSymbolCache {
5169 let parser = self.parser.borrow();
5170 parser.symbol_cache()
5171 }
5172
5173 fn resolve_symbol_inner(
5174 &self,
5175 file: &Path,
5176 name: &str,
5177 depth: usize,
5178 visited: &mut HashSet<(PathBuf, String)>,
5179 ) -> Result<Vec<SymbolMatch>, AftError> {
5180 if depth > MAX_REEXPORT_DEPTH {
5181 return Ok(Vec::new());
5182 }
5183
5184 let canonical_file = std::fs::canonicalize(file).unwrap_or_else(|_| file.to_path_buf());
5185 if !visited.insert((canonical_file, name.to_string())) {
5186 return Ok(Vec::new());
5187 }
5188
5189 let symbols = self.parser.borrow_mut().extract_symbols(file)?;
5190 let local_matches = symbol_matches_in_file(file, &symbols, name);
5191 if !local_matches.is_empty() {
5192 return Ok(local_matches);
5193 }
5194
5195 if name == "default" {
5196 let default_matches = self.resolve_local_default_export(file, &symbols)?;
5197 if !default_matches.is_empty() {
5198 return Ok(default_matches);
5199 }
5200 }
5201
5202 let reexport_targets = self.collect_reexport_targets(file, name)?;
5203 let mut matches = Vec::new();
5204 let mut seen = HashSet::new();
5205 for target in reexport_targets {
5206 for resolved in
5207 self.resolve_symbol_inner(&target.file, &target.symbol_name, depth + 1, visited)?
5208 {
5209 let key = format!(
5210 "{}:{}:{}:{}:{}:{}",
5211 resolved.file,
5212 resolved.symbol.name,
5213 resolved.symbol.range.start_line,
5214 resolved.symbol.range.start_col,
5215 resolved.symbol.range.end_line,
5216 resolved.symbol.range.end_col
5217 );
5218 if seen.insert(key) {
5219 matches.push(resolved);
5220 }
5221 }
5222 }
5223
5224 Ok(matches)
5225 }
5226
5227 fn collect_reexport_targets(
5228 &self,
5229 file: &Path,
5230 requested_name: &str,
5231 ) -> Result<Vec<ReExportTarget>, AftError> {
5232 let (source, tree, lang) = self.read_parsed_file(file)?;
5233 if !matches!(lang, LangId::TypeScript | LangId::Tsx | LangId::JavaScript) {
5234 return Ok(Vec::new());
5235 }
5236
5237 let mut targets = Vec::new();
5238 let root = tree.root_node();
5239 let from_dir = file.parent().unwrap_or_else(|| Path::new("."));
5240
5241 let mut cursor = root.walk();
5242 if !cursor.goto_first_child() {
5243 return Ok(targets);
5244 }
5245
5246 loop {
5247 let node = cursor.node();
5248 if node.kind() == "export_statement" {
5249 let Some(source_node) = node.child_by_field_name("source") else {
5250 if let Some(export_clause) = find_child_by_kind(node, "export_clause") {
5251 if let Some(symbol_name) =
5252 resolve_export_clause_name(&source, &export_clause, requested_name)
5253 {
5254 targets.push(ReExportTarget {
5255 file: file.to_path_buf(),
5256 symbol_name,
5257 });
5258 }
5259 }
5260 if !cursor.goto_next_sibling() {
5261 break;
5262 }
5263 continue;
5264 };
5265
5266 let Some(module_path) = string_content(&source, &source_node) else {
5267 if !cursor.goto_next_sibling() {
5268 break;
5269 }
5270 continue;
5271 };
5272
5273 let Some(target_file) = resolve_module_path(from_dir, &module_path) else {
5274 if !cursor.goto_next_sibling() {
5275 break;
5276 }
5277 continue;
5278 };
5279
5280 if let Some(export_clause) = find_child_by_kind(node, "export_clause") {
5281 if let Some(symbol_name) =
5282 resolve_export_clause_name(&source, &export_clause, requested_name)
5283 {
5284 targets.push(ReExportTarget {
5285 file: target_file,
5286 symbol_name,
5287 });
5288 }
5289 } else if export_statement_has_wildcard(&source, &node) {
5290 targets.push(ReExportTarget {
5291 file: target_file,
5292 symbol_name: requested_name.to_string(),
5293 });
5294 }
5295 }
5296
5297 if !cursor.goto_next_sibling() {
5298 break;
5299 }
5300 }
5301
5302 Ok(targets)
5303 }
5304
5305 fn resolve_local_default_export(
5306 &self,
5307 file: &Path,
5308 symbols: &[Symbol],
5309 ) -> Result<Vec<SymbolMatch>, AftError> {
5310 let (source, tree, lang) = self.read_parsed_file(file)?;
5311 if !matches!(lang, LangId::TypeScript | LangId::Tsx | LangId::JavaScript) {
5312 return Ok(Vec::new());
5313 }
5314
5315 let root = tree.root_node();
5316 let mut matches = Vec::new();
5317 let mut seen = HashSet::new();
5318
5319 let mut cursor = root.walk();
5320 if !cursor.goto_first_child() {
5321 return Ok(matches);
5322 }
5323
5324 loop {
5325 let node = cursor.node();
5326 if node.kind() == "export_statement"
5327 && node.child_by_field_name("source").is_none()
5328 && node_contains_token(&source, &node, "default")
5329 {
5330 if let Some(target_name) = default_export_target_name(&source, &node) {
5331 for symbol_match in symbol_matches_in_file(file, symbols, &target_name) {
5332 let key = format!(
5333 "{}:{}:{}:{}:{}:{}",
5334 symbol_match.file,
5335 symbol_match.symbol.name,
5336 symbol_match.symbol.range.start_line,
5337 symbol_match.symbol.range.start_col,
5338 symbol_match.symbol.range.end_line,
5339 symbol_match.symbol.range.end_col
5340 );
5341 if seen.insert(key) {
5342 matches.push(symbol_match);
5343 }
5344 }
5345 }
5346 }
5347
5348 if !cursor.goto_next_sibling() {
5349 break;
5350 }
5351 }
5352
5353 Ok(matches)
5354 }
5355
5356 fn read_parsed_file(&self, file: &Path) -> Result<(String, Tree, LangId), AftError> {
5357 let source = std::fs::read_to_string(file).map_err(|e| AftError::FileNotFound {
5358 path: format!("{}: {}", file.display(), e),
5359 })?;
5360 let (tree, lang) = {
5361 let mut parser = self.parser.borrow_mut();
5362 parser.parse_cloned(file)?
5363 };
5364 Ok((source, tree, lang))
5365 }
5366}
5367
5368fn symbol_matches_in_file(file: &Path, symbols: &[Symbol], name: &str) -> Vec<SymbolMatch> {
5369 symbols
5370 .iter()
5371 .filter(|symbol| symbol.name == name)
5372 .cloned()
5373 .map(|symbol| SymbolMatch {
5374 file: file.display().to_string(),
5375 symbol,
5376 })
5377 .collect()
5378}
5379
5380fn string_content(source: &str, node: &Node) -> Option<String> {
5381 let text = node_text(source, node);
5382 if text.len() < 2 {
5383 return None;
5384 }
5385
5386 Some(
5387 text.trim_start_matches(|c| c == '\'' || c == '"')
5388 .trim_end_matches(|c| c == '\'' || c == '"')
5389 .to_string(),
5390 )
5391}
5392
5393fn find_child_by_kind<'tree>(node: Node<'tree>, kind: &str) -> Option<Node<'tree>> {
5394 let mut cursor = node.walk();
5395 if !cursor.goto_first_child() {
5396 return None;
5397 }
5398
5399 loop {
5400 let child = cursor.node();
5401 if child.kind() == kind {
5402 return Some(child);
5403 }
5404 if !cursor.goto_next_sibling() {
5405 break;
5406 }
5407 }
5408
5409 None
5410}
5411
5412fn resolve_export_clause_name(
5413 source: &str,
5414 export_clause: &Node,
5415 requested_name: &str,
5416) -> Option<String> {
5417 let mut cursor = export_clause.walk();
5418 if !cursor.goto_first_child() {
5419 return None;
5420 }
5421
5422 loop {
5423 let child = cursor.node();
5424 if child.kind() == "export_specifier" {
5425 let (source_name, exported_name) = export_specifier_names(source, &child)?;
5426 if exported_name == requested_name {
5427 return Some(source_name);
5428 }
5429 }
5430
5431 if !cursor.goto_next_sibling() {
5432 break;
5433 }
5434 }
5435
5436 None
5437}
5438
5439fn export_specifier_names(source: &str, specifier: &Node) -> Option<(String, String)> {
5440 let source_name = specifier
5441 .child_by_field_name("name")
5442 .map(|node| node_text(source, &node).to_string());
5443 let alias_name = specifier
5444 .child_by_field_name("alias")
5445 .map(|node| node_text(source, &node).to_string());
5446
5447 if let Some(source_name) = source_name {
5448 let exported_name = alias_name.unwrap_or_else(|| source_name.clone());
5449 return Some((source_name, exported_name));
5450 }
5451
5452 let mut names = Vec::new();
5453 let mut cursor = specifier.walk();
5454 if cursor.goto_first_child() {
5455 loop {
5456 let child = cursor.node();
5457 let child_text = node_text(source, &child).trim();
5458 if matches!(
5459 child.kind(),
5460 "identifier" | "type_identifier" | "property_identifier"
5461 ) || child_text == "default"
5462 {
5463 names.push(child_text.to_string());
5464 }
5465 if !cursor.goto_next_sibling() {
5466 break;
5467 }
5468 }
5469 }
5470
5471 match names.as_slice() {
5472 [name] => Some((name.clone(), name.clone())),
5473 [source_name, exported_name, ..] => Some((source_name.clone(), exported_name.clone())),
5474 _ => None,
5475 }
5476}
5477
5478fn export_statement_has_wildcard(source: &str, node: &Node) -> bool {
5479 let mut cursor = node.walk();
5480 if !cursor.goto_first_child() {
5481 return false;
5482 }
5483
5484 loop {
5485 if node_text(source, &cursor.node()).trim() == "*" {
5486 return true;
5487 }
5488 if !cursor.goto_next_sibling() {
5489 break;
5490 }
5491 }
5492
5493 false
5494}
5495
5496fn node_contains_token(source: &str, node: &Node, token: &str) -> bool {
5497 let mut cursor = node.walk();
5498 if !cursor.goto_first_child() {
5499 return false;
5500 }
5501
5502 loop {
5503 if node_text(source, &cursor.node()).trim() == token {
5504 return true;
5505 }
5506 if !cursor.goto_next_sibling() {
5507 break;
5508 }
5509 }
5510
5511 false
5512}
5513
5514fn default_export_target_name(source: &str, export_stmt: &Node) -> Option<String> {
5515 if let Some(value_node) = export_stmt.child_by_field_name("value") {
5516 if let Some(name) = default_export_node_name(source, &value_node) {
5517 return Some(name);
5518 }
5519 }
5520
5521 if let Some(declaration_node) = export_stmt.child_by_field_name("declaration") {
5522 if let Some(name) = default_export_node_name(source, &declaration_node) {
5523 return Some(name);
5524 }
5525 }
5526
5527 let mut cursor = export_stmt.walk();
5528 if !cursor.goto_first_child() {
5529 return None;
5530 }
5531
5532 loop {
5533 let child = cursor.node();
5534 if let Some(name) = default_export_node_name(source, &child) {
5535 return Some(name);
5536 }
5537
5538 if !cursor.goto_next_sibling() {
5539 break;
5540 }
5541 }
5542
5543 None
5544}
5545
5546fn default_export_node_name(source: &str, node: &Node) -> Option<String> {
5547 match node.kind() {
5548 "function_declaration"
5549 | "generator_function_declaration"
5550 | "function_expression"
5551 | "generator_function"
5552 | "class_declaration"
5553 | "class" => node
5554 .child_by_field_name("name")
5555 .map(|name_node| node_text(source, &name_node).to_string())
5556 .or_else(|| Some("default".to_string())),
5557 "interface_declaration" | "enum_declaration" | "type_alias_declaration" => node
5558 .child_by_field_name("name")
5559 .map(|name_node| node_text(source, &name_node).to_string()),
5560 "lexical_declaration" => lexical_declaration_name(source, node),
5561 "identifier" | "type_identifier" => {
5562 let text = node_text(source, node);
5563 (text != "export" && text != "default").then(|| text.to_string())
5564 }
5565 _ => None,
5566 }
5567}
5568
5569fn lexical_declaration_name(source: &str, node: &Node) -> Option<String> {
5570 let mut cursor = node.walk();
5571 if !cursor.goto_first_child() {
5572 return None;
5573 }
5574
5575 loop {
5576 let child = cursor.node();
5577 if child.kind() == "variable_declarator" {
5578 if let Some(name_node) = child.child_by_field_name("name") {
5579 return Some(node_text(source, &name_node).to_string());
5580 }
5581 }
5582 if !cursor.goto_next_sibling() {
5583 break;
5584 }
5585 }
5586
5587 None
5588}
5589
5590impl crate::language::LanguageProvider for TreeSitterProvider {
5591 fn resolve_symbol(&self, file: &Path, name: &str) -> Result<Vec<SymbolMatch>, AftError> {
5592 let matches = self.resolve_symbol_inner(file, name, 0, &mut HashSet::new())?;
5593
5594 if matches.is_empty() {
5595 Err(AftError::SymbolNotFound {
5596 name: name.to_string(),
5597 file: file.display().to_string(),
5598 })
5599 } else {
5600 Ok(matches)
5601 }
5602 }
5603
5604 fn list_symbols(&self, file: &Path) -> Result<Vec<Symbol>, AftError> {
5605 self.parser.borrow_mut().extract_symbols(file)
5606 }
5607
5608 fn as_any(&self) -> &dyn std::any::Any {
5609 self
5610 }
5611}
5612
5613#[cfg(test)]
5614mod tests {
5615 use super::*;
5616 use crate::language::LanguageProvider;
5617 use crate::symbol_cache_disk;
5618 use std::path::PathBuf;
5619
5620 fn fixture_path(name: &str) -> PathBuf {
5621 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
5622 .join("tests")
5623 .join("fixtures")
5624 .join(name)
5625 }
5626
5627 fn test_symbol(name: &str) -> Symbol {
5628 Symbol {
5629 name: name.to_string(),
5630 kind: SymbolKind::Function,
5631 range: Range {
5632 start_line: 0,
5633 start_col: 0,
5634 end_line: 0,
5635 end_col: 10,
5636 },
5637 signature: Some(format!("fn {name}()")),
5638 scope_chain: Vec::new(),
5639 exported: true,
5640 parent: None,
5641 }
5642 }
5643
5644 #[test]
5645 fn symbol_cache_load_from_disk_round_trips_synthetic_entry() {
5646 let project = tempfile::tempdir().expect("create project dir");
5647 let storage = tempfile::tempdir().expect("create storage dir");
5648 let source = project.path().join("src/lib.rs");
5649 std::fs::create_dir_all(source.parent().expect("source parent"))
5650 .expect("create source dir");
5651 std::fs::write(&source, "pub fn cached() {}\n").expect("write source");
5652 let mtime = std::fs::metadata(&source)
5653 .expect("stat source")
5654 .modified()
5655 .expect("source mtime");
5656 let content = std::fs::read(&source).expect("read source");
5657 let size = content.len() as u64;
5658 let hash = crate::cache_freshness::hash_bytes(&content);
5659
5660 let mut cache = SymbolCache::new();
5661 cache.set_project_root(project.path().to_path_buf());
5662 cache.insert(
5663 source.clone(),
5664 mtime,
5665 size,
5666 hash,
5667 vec![test_symbol("cached")],
5668 );
5669 symbol_cache_disk::write_to_disk(&cache, storage.path(), "unit-project")
5670 .expect("write symbol cache");
5671
5672 let mut restored = SymbolCache::new();
5673 let loaded = restored.load_from_disk(storage.path(), "unit-project", project.path());
5674 let symbols = restored.get(&source, mtime).expect("restored symbols");
5675
5676 assert_eq!(loaded, 1);
5677 assert_eq!(symbols.len(), 1);
5678 assert_eq!(symbols[0].name, "cached");
5679 }
5680
5681 #[test]
5682 fn symbol_cache_load_from_disk_drops_stale_synthetic_entry() {
5683 let project = tempfile::tempdir().expect("create project dir");
5684 let storage = tempfile::tempdir().expect("create storage dir");
5685 let source = project.path().join("src/lib.rs");
5686 std::fs::create_dir_all(source.parent().expect("source parent"))
5687 .expect("create source dir");
5688 std::fs::write(&source, "pub fn cached() {}\n").expect("write source");
5689 let mtime = std::fs::metadata(&source)
5690 .expect("stat source")
5691 .modified()
5692 .expect("source mtime");
5693 let content = std::fs::read(&source).expect("read source");
5694 let size = content.len() as u64;
5695 let hash = crate::cache_freshness::hash_bytes(&content);
5696
5697 let mut cache = SymbolCache::new();
5698 cache.set_project_root(project.path().to_path_buf());
5699 cache.insert(
5700 source.clone(),
5701 mtime,
5702 size,
5703 hash,
5704 vec![test_symbol("cached")],
5705 );
5706 symbol_cache_disk::write_to_disk(&cache, storage.path(), "stale-unit-project")
5707 .expect("write symbol cache");
5708
5709 std::fs::write(&source, "pub fn cached() {}\npub fn fresh() {}\n").expect("change source");
5710
5711 let mut restored = SymbolCache::new();
5712 let loaded = restored.load_from_disk(storage.path(), "stale-unit-project", project.path());
5713
5714 assert_eq!(loaded, 0);
5715 assert_eq!(restored.len(), 0);
5716 }
5717
5718 #[test]
5719 fn stale_prewarm_generation_cannot_repopulate_symbol_cache_after_reset() {
5720 let project = tempfile::tempdir().expect("create project dir");
5721 let storage = tempfile::tempdir().expect("create storage dir");
5722 let source = project.path().join("src/lib.rs");
5723 std::fs::create_dir_all(source.parent().expect("source parent"))
5724 .expect("create source dir");
5725 std::fs::write(&source, "pub fn cached() {}\n").expect("write source");
5726 let mtime = std::fs::metadata(&source)
5727 .expect("stat source")
5728 .modified()
5729 .expect("source mtime");
5730 let content = std::fs::read(&source).expect("read source");
5731 let size = content.len() as u64;
5732 let hash = crate::cache_freshness::hash_bytes(&content);
5733
5734 let mut disk_cache = SymbolCache::new();
5735 disk_cache.set_project_root(project.path().to_path_buf());
5736 disk_cache.insert(
5737 source.clone(),
5738 mtime,
5739 size,
5740 hash,
5741 vec![test_symbol("cached")],
5742 );
5743 symbol_cache_disk::write_to_disk(&disk_cache, storage.path(), "prewarm-reset")
5744 .expect("write symbol cache");
5745
5746 let shared = Arc::new(RwLock::new(SymbolCache::new()));
5747 let stale_generation = shared.write().unwrap().reset();
5748 let active_generation = shared.write().unwrap().reset();
5749 assert_ne!(stale_generation, active_generation);
5750
5751 {
5752 let mut cache = shared.write().unwrap();
5753 assert!(!cache
5754 .set_project_root_for_generation(stale_generation, project.path().to_path_buf()));
5755 assert_eq!(
5756 cache.load_from_disk_for_generation(
5757 stale_generation,
5758 storage.path(),
5759 "prewarm-reset",
5760 project.path()
5761 ),
5762 0
5763 );
5764 }
5765
5766 let mut stale_parser =
5767 FileParser::with_symbol_cache_generation(Arc::clone(&shared), Some(stale_generation));
5768 stale_parser
5769 .extract_symbols(&source)
5770 .expect("stale prewarm parses source but must not write cache");
5771
5772 let cache = shared.read().unwrap();
5773 assert_eq!(cache.generation(), active_generation);
5774 assert_eq!(cache.len(), 0);
5775 assert!(cache.project_root().is_none());
5776 assert!(!cache.contains_key(&source));
5777 }
5778
5779 #[test]
5782 fn detect_ts() {
5783 assert_eq!(
5784 detect_language(Path::new("foo.ts")),
5785 Some(LangId::TypeScript)
5786 );
5787 }
5788
5789 #[test]
5790 fn detect_tsx() {
5791 assert_eq!(detect_language(Path::new("foo.tsx")), Some(LangId::Tsx));
5792 }
5793
5794 #[test]
5795 fn detect_js() {
5796 assert_eq!(
5797 detect_language(Path::new("foo.js")),
5798 Some(LangId::JavaScript)
5799 );
5800 }
5801
5802 #[test]
5803 fn detect_jsx() {
5804 assert_eq!(
5805 detect_language(Path::new("foo.jsx")),
5806 Some(LangId::JavaScript)
5807 );
5808 }
5809
5810 #[test]
5811 fn detect_py() {
5812 assert_eq!(detect_language(Path::new("foo.py")), Some(LangId::Python));
5813 }
5814
5815 #[test]
5816 fn detect_rs() {
5817 assert_eq!(detect_language(Path::new("foo.rs")), Some(LangId::Rust));
5818 }
5819
5820 #[test]
5821 fn detect_go() {
5822 assert_eq!(detect_language(Path::new("foo.go")), Some(LangId::Go));
5823 }
5824
5825 #[test]
5826 fn detect_c() {
5827 assert_eq!(detect_language(Path::new("foo.c")), Some(LangId::C));
5828 }
5829
5830 #[test]
5831 fn detect_h() {
5832 assert_eq!(detect_language(Path::new("foo.h")), Some(LangId::C));
5833 }
5834
5835 #[test]
5836 fn detect_cc() {
5837 assert_eq!(detect_language(Path::new("foo.cc")), Some(LangId::Cpp));
5838 }
5839
5840 #[test]
5841 fn detect_cpp() {
5842 assert_eq!(detect_language(Path::new("foo.cpp")), Some(LangId::Cpp));
5843 }
5844
5845 #[test]
5846 fn detect_cxx() {
5847 assert_eq!(detect_language(Path::new("foo.cxx")), Some(LangId::Cpp));
5848 }
5849
5850 #[test]
5851 fn detect_hpp() {
5852 assert_eq!(detect_language(Path::new("foo.hpp")), Some(LangId::Cpp));
5853 }
5854
5855 #[test]
5856 fn detect_hh() {
5857 assert_eq!(detect_language(Path::new("foo.hh")), Some(LangId::Cpp));
5858 }
5859
5860 #[test]
5861 fn detect_zig() {
5862 assert_eq!(detect_language(Path::new("foo.zig")), Some(LangId::Zig));
5863 }
5864
5865 #[test]
5866 fn detect_cs() {
5867 assert_eq!(detect_language(Path::new("foo.cs")), Some(LangId::CSharp));
5868 }
5869
5870 #[test]
5871 fn detect_unknown_returns_none() {
5872 assert_eq!(detect_language(Path::new("foo.txt")), None);
5873 }
5874
5875 #[test]
5878 fn unsupported_extension_returns_invalid_request() {
5879 let path = fixture_path("sample.ts");
5881 let bad_path = path.with_extension("txt");
5882 std::fs::write(&bad_path, "hello").unwrap();
5884 let provider = TreeSitterProvider::new();
5885 let result = provider.list_symbols(&bad_path);
5886 std::fs::remove_file(&bad_path).ok();
5887 match result {
5888 Err(AftError::InvalidRequest { message }) => {
5889 assert!(
5890 message.contains("unsupported file extension"),
5891 "msg: {}",
5892 message
5893 );
5894 assert!(message.contains("txt"), "msg: {}", message);
5895 }
5896 other => panic!("expected InvalidRequest, got {:?}", other),
5897 }
5898 }
5899
5900 #[test]
5903 fn ts_extracts_all_symbol_kinds() {
5904 let provider = TreeSitterProvider::new();
5905 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
5906
5907 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
5908 assert!(
5909 names.contains(&"greet"),
5910 "missing function greet: {:?}",
5911 names
5912 );
5913 assert!(names.contains(&"add"), "missing arrow fn add: {:?}", names);
5914 assert!(
5915 names.contains(&"UserService"),
5916 "missing class UserService: {:?}",
5917 names
5918 );
5919 assert!(
5920 names.contains(&"Config"),
5921 "missing interface Config: {:?}",
5922 names
5923 );
5924 assert!(
5925 names.contains(&"Status"),
5926 "missing enum Status: {:?}",
5927 names
5928 );
5929 assert!(
5930 names.contains(&"UserId"),
5931 "missing type alias UserId: {:?}",
5932 names
5933 );
5934 assert!(
5935 names.contains(&"internalHelper"),
5936 "missing non-exported fn: {:?}",
5937 names
5938 );
5939
5940 assert!(
5942 symbols.len() >= 6,
5943 "expected ≥6 symbols, got {}: {:?}",
5944 symbols.len(),
5945 names
5946 );
5947 }
5948
5949 #[test]
5950 fn ts_symbol_kinds_correct() {
5951 let provider = TreeSitterProvider::new();
5952 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
5953
5954 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
5955
5956 assert_eq!(find("greet").kind, SymbolKind::Function);
5957 assert_eq!(find("add").kind, SymbolKind::Function); assert_eq!(find("UserService").kind, SymbolKind::Class);
5959 assert_eq!(find("Config").kind, SymbolKind::Interface);
5960 assert_eq!(find("Status").kind, SymbolKind::Enum);
5961 assert_eq!(find("UserId").kind, SymbolKind::TypeAlias);
5962 }
5963
5964 #[test]
5965 fn ts_export_detection() {
5966 let provider = TreeSitterProvider::new();
5967 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
5968
5969 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
5970
5971 assert!(find("greet").exported, "greet should be exported");
5972 assert!(find("add").exported, "add should be exported");
5973 assert!(
5974 find("UserService").exported,
5975 "UserService should be exported"
5976 );
5977 assert!(find("Config").exported, "Config should be exported");
5978 assert!(find("Status").exported, "Status should be exported");
5979 assert!(
5980 !find("internalHelper").exported,
5981 "internalHelper should not be exported"
5982 );
5983 }
5984
5985 #[test]
5986 fn ts_method_scope_chain() {
5987 let provider = TreeSitterProvider::new();
5988 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
5989
5990 let methods: Vec<&Symbol> = symbols
5991 .iter()
5992 .filter(|s| s.kind == SymbolKind::Method)
5993 .collect();
5994 assert!(!methods.is_empty(), "should have at least one method");
5995
5996 for method in &methods {
5997 assert_eq!(
5998 method.scope_chain,
5999 vec!["UserService"],
6000 "method {} should have UserService in scope chain",
6001 method.name
6002 );
6003 assert_eq!(method.parent.as_deref(), Some("UserService"));
6004 }
6005 }
6006
6007 #[test]
6008 fn ts_signatures_present() {
6009 let provider = TreeSitterProvider::new();
6010 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
6011
6012 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6013
6014 let greet_sig = find("greet").signature.as_ref().unwrap();
6015 assert!(
6016 greet_sig.contains("greet"),
6017 "signature should contain function name: {}",
6018 greet_sig
6019 );
6020 }
6021
6022 #[test]
6023 fn ts_ranges_valid() {
6024 let provider = TreeSitterProvider::new();
6025 let symbols = provider.list_symbols(&fixture_path("sample.ts")).unwrap();
6026
6027 for s in &symbols {
6028 assert!(
6029 s.range.end_line >= s.range.start_line,
6030 "symbol {} has invalid range: {:?}",
6031 s.name,
6032 s.range
6033 );
6034 }
6035 }
6036
6037 #[test]
6040 fn js_extracts_core_symbols() {
6041 let provider = TreeSitterProvider::new();
6042 let symbols = provider.list_symbols(&fixture_path("sample.js")).unwrap();
6043
6044 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
6045 assert!(
6046 names.contains(&"multiply"),
6047 "missing function multiply: {:?}",
6048 names
6049 );
6050 assert!(
6051 names.contains(&"divide"),
6052 "missing arrow fn divide: {:?}",
6053 names
6054 );
6055 assert!(
6056 names.contains(&"EventEmitter"),
6057 "missing class EventEmitter: {:?}",
6058 names
6059 );
6060 assert!(
6061 names.contains(&"main"),
6062 "missing default export fn main: {:?}",
6063 names
6064 );
6065
6066 assert!(
6067 symbols.len() >= 4,
6068 "expected ≥4 symbols, got {}: {:?}",
6069 symbols.len(),
6070 names
6071 );
6072 }
6073
6074 #[test]
6075 fn js_arrow_fn_correctly_named() {
6076 let provider = TreeSitterProvider::new();
6077 let symbols = provider.list_symbols(&fixture_path("sample.js")).unwrap();
6078
6079 let divide = symbols.iter().find(|s| s.name == "divide").unwrap();
6080 assert_eq!(divide.kind, SymbolKind::Function);
6081 assert!(divide.exported, "divide should be exported");
6082
6083 let internal = symbols.iter().find(|s| s.name == "internalUtil").unwrap();
6084 assert_eq!(internal.kind, SymbolKind::Function);
6085 assert!(!internal.exported, "internalUtil should not be exported");
6086 }
6087
6088 #[test]
6089 fn js_method_scope_chain() {
6090 let provider = TreeSitterProvider::new();
6091 let symbols = provider.list_symbols(&fixture_path("sample.js")).unwrap();
6092
6093 let methods: Vec<&Symbol> = symbols
6094 .iter()
6095 .filter(|s| s.kind == SymbolKind::Method)
6096 .collect();
6097
6098 for method in &methods {
6099 assert_eq!(
6100 method.scope_chain,
6101 vec!["EventEmitter"],
6102 "method {} should have EventEmitter in scope chain",
6103 method.name
6104 );
6105 }
6106 }
6107
6108 #[test]
6111 fn tsx_extracts_react_component() {
6112 let provider = TreeSitterProvider::new();
6113 let symbols = provider.list_symbols(&fixture_path("sample.tsx")).unwrap();
6114
6115 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
6116 assert!(
6117 names.contains(&"Button"),
6118 "missing React component Button: {:?}",
6119 names
6120 );
6121 assert!(
6122 names.contains(&"Counter"),
6123 "missing class Counter: {:?}",
6124 names
6125 );
6126 assert!(
6127 names.contains(&"formatLabel"),
6128 "missing function formatLabel: {:?}",
6129 names
6130 );
6131
6132 assert!(
6133 symbols.len() >= 2,
6134 "expected ≥2 symbols, got {}: {:?}",
6135 symbols.len(),
6136 names
6137 );
6138 }
6139
6140 #[test]
6141 fn tsx_jsx_doesnt_break_parser() {
6142 let provider = TreeSitterProvider::new();
6144 let result = provider.list_symbols(&fixture_path("sample.tsx"));
6145 assert!(
6146 result.is_ok(),
6147 "TSX parsing should succeed: {:?}",
6148 result.err()
6149 );
6150 }
6151
6152 #[test]
6155 fn resolve_symbol_finds_match() {
6156 let provider = TreeSitterProvider::new();
6157 let matches = provider
6158 .resolve_symbol(&fixture_path("sample.ts"), "greet")
6159 .unwrap();
6160 assert_eq!(matches.len(), 1);
6161 assert_eq!(matches[0].symbol.name, "greet");
6162 assert_eq!(matches[0].symbol.kind, SymbolKind::Function);
6163 }
6164
6165 #[test]
6166 fn resolve_symbol_not_found() {
6167 let provider = TreeSitterProvider::new();
6168 let result = provider.resolve_symbol(&fixture_path("sample.ts"), "nonexistent");
6169 assert!(matches!(result, Err(AftError::SymbolNotFound { .. })));
6170 }
6171
6172 #[test]
6173 fn resolve_symbol_follows_reexport_chains() {
6174 let dir = tempfile::tempdir().unwrap();
6175 let config = dir.path().join("config.ts");
6176 let barrel1 = dir.path().join("barrel1.ts");
6177 let barrel2 = dir.path().join("barrel2.ts");
6178 let barrel3 = dir.path().join("barrel3.ts");
6179 let index = dir.path().join("index.ts");
6180
6181 std::fs::write(
6182 &config,
6183 "export class Config {}\nexport default class DefaultConfig {}\n",
6184 )
6185 .unwrap();
6186 std::fs::write(
6187 &barrel1,
6188 "export { Config } from './config';\nexport { default as NamedDefault } from './config';\n",
6189 )
6190 .unwrap();
6191 std::fs::write(
6192 &barrel2,
6193 "export { Config as RenamedConfig } from './barrel1';\n",
6194 )
6195 .unwrap();
6196 std::fs::write(
6197 &barrel3,
6198 "export * from './barrel2';\nexport * from './barrel1';\n",
6199 )
6200 .unwrap();
6201 std::fs::write(
6202 &index,
6203 "export class Config {}\nexport { RenamedConfig as FinalConfig } from './barrel3';\nexport * from './barrel3';\n",
6204 )
6205 .unwrap();
6206
6207 let provider = TreeSitterProvider::new();
6208 let config_canon = std::fs::canonicalize(&config).unwrap();
6209
6210 let direct = provider.resolve_symbol(&barrel1, "Config").unwrap();
6211 assert_eq!(direct.len(), 1);
6212 assert_eq!(direct[0].symbol.name, "Config");
6213 assert_eq!(direct[0].file, config_canon.display().to_string());
6214
6215 let renamed = provider.resolve_symbol(&barrel2, "RenamedConfig").unwrap();
6216 assert_eq!(renamed.len(), 1);
6217 assert_eq!(renamed[0].symbol.name, "Config");
6218 assert_eq!(renamed[0].file, config_canon.display().to_string());
6219
6220 let wildcard_chain = provider.resolve_symbol(&index, "FinalConfig").unwrap();
6221 assert_eq!(wildcard_chain.len(), 1);
6222 assert_eq!(wildcard_chain[0].symbol.name, "Config");
6223 assert_eq!(wildcard_chain[0].file, config_canon.display().to_string());
6224
6225 let wildcard_default = provider.resolve_symbol(&index, "NamedDefault").unwrap();
6226 assert_eq!(wildcard_default.len(), 1);
6227 assert_eq!(wildcard_default[0].symbol.name, "DefaultConfig");
6228 assert_eq!(wildcard_default[0].file, config_canon.display().to_string());
6229
6230 let local = provider.resolve_symbol(&index, "Config").unwrap();
6231 assert_eq!(local.len(), 1);
6232 assert_eq!(local[0].symbol.name, "Config");
6233 assert_eq!(local[0].file, index.display().to_string());
6234 }
6235
6236 #[test]
6239 fn symbol_range_includes_rust_attributes() {
6240 let dir = tempfile::tempdir().unwrap();
6241 let path = dir.path().join("test_attrs.rs");
6242 std::fs::write(
6243 &path,
6244 "/// This is a doc comment\n#[test]\n#[cfg(test)]\nfn my_test_fn() {\n assert!(true);\n}\n",
6245 )
6246 .unwrap();
6247
6248 let provider = TreeSitterProvider::new();
6249 let matches = provider.resolve_symbol(&path, "my_test_fn").unwrap();
6250 assert_eq!(matches.len(), 1);
6251 assert_eq!(
6252 matches[0].symbol.range.start_line, 0,
6253 "symbol range should include preceding /// doc comment, got start_line={}",
6254 matches[0].symbol.range.start_line
6255 );
6256 }
6257
6258 #[test]
6259 fn symbol_range_includes_go_doc_comment() {
6260 let dir = tempfile::tempdir().unwrap();
6261 let path = dir.path().join("test_doc.go");
6262 std::fs::write(
6263 &path,
6264 "package main\n\n// MyFunc does something useful.\n// It has a multi-line doc.\nfunc MyFunc() {\n}\n",
6265 )
6266 .unwrap();
6267
6268 let provider = TreeSitterProvider::new();
6269 let matches = provider.resolve_symbol(&path, "MyFunc").unwrap();
6270 assert_eq!(matches.len(), 1);
6271 assert_eq!(
6272 matches[0].symbol.range.start_line, 2,
6273 "symbol range should include preceding doc comments, got start_line={}",
6274 matches[0].symbol.range.start_line
6275 );
6276 }
6277
6278 #[test]
6279 fn symbol_range_skips_unrelated_comments() {
6280 let dir = tempfile::tempdir().unwrap();
6281 let path = dir.path().join("test_gap.go");
6282 std::fs::write(
6283 &path,
6284 "package main\n\n// This is a standalone comment\n\nfunc Standalone() {\n}\n",
6285 )
6286 .unwrap();
6287
6288 let provider = TreeSitterProvider::new();
6289 let matches = provider.resolve_symbol(&path, "Standalone").unwrap();
6290 assert_eq!(matches.len(), 1);
6291 assert_eq!(
6292 matches[0].symbol.range.start_line, 4,
6293 "symbol range should NOT include comment separated by blank line, got start_line={}",
6294 matches[0].symbol.range.start_line
6295 );
6296 }
6297
6298 #[test]
6299 fn parse_cache_returns_same_tree() {
6300 let mut parser = FileParser::new();
6301 let path = fixture_path("sample.ts");
6302
6303 let (tree1, _) = parser.parse(&path).unwrap();
6304 let tree1_root = tree1.root_node().byte_range();
6305
6306 let (tree2, _) = parser.parse(&path).unwrap();
6307 let tree2_root = tree2.root_node().byte_range();
6308
6309 assert_eq!(tree1_root, tree2_root);
6311 }
6312
6313 #[test]
6314 fn extract_symbols_from_tree_matches_list_symbols() {
6315 let path = fixture_path("sample.rs");
6316 let source = std::fs::read_to_string(&path).unwrap();
6317
6318 let provider = TreeSitterProvider::new();
6319 let listed = provider.list_symbols(&path).unwrap();
6320
6321 let mut parser = FileParser::new();
6322 let (tree, lang) = parser.parse(&path).unwrap();
6323 let extracted = extract_symbols_from_tree(&source, tree, lang).unwrap();
6324
6325 assert_eq!(symbols_as_debug(&extracted), symbols_as_debug(&listed));
6326 }
6327
6328 fn symbols_as_debug(symbols: &[Symbol]) -> Vec<String> {
6329 symbols
6330 .iter()
6331 .map(|symbol| {
6332 format!(
6333 "{}|{:?}|{}:{}-{}:{}|{:?}|{:?}|{}|{:?}",
6334 symbol.name,
6335 symbol.kind,
6336 symbol.range.start_line,
6337 symbol.range.start_col,
6338 symbol.range.end_line,
6339 symbol.range.end_col,
6340 symbol.signature,
6341 symbol.scope_chain,
6342 symbol.exported,
6343 symbol.parent,
6344 )
6345 })
6346 .collect()
6347 }
6348
6349 #[test]
6352 fn py_extracts_all_symbols() {
6353 let provider = TreeSitterProvider::new();
6354 let symbols = provider.list_symbols(&fixture_path("sample.py")).unwrap();
6355
6356 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
6357 assert!(
6358 names.contains(&"top_level_function"),
6359 "missing top_level_function: {:?}",
6360 names
6361 );
6362 assert!(names.contains(&"MyClass"), "missing MyClass: {:?}", names);
6363 assert!(
6364 names.contains(&"instance_method"),
6365 "missing method instance_method: {:?}",
6366 names
6367 );
6368 assert!(
6369 names.contains(&"decorated_function"),
6370 "missing decorated_function: {:?}",
6371 names
6372 );
6373
6374 assert!(
6376 symbols.len() >= 4,
6377 "expected ≥4 symbols, got {}: {:?}",
6378 symbols.len(),
6379 names
6380 );
6381 }
6382
6383 #[test]
6384 fn py_symbol_kinds_correct() {
6385 let provider = TreeSitterProvider::new();
6386 let symbols = provider.list_symbols(&fixture_path("sample.py")).unwrap();
6387
6388 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6389
6390 assert_eq!(find("top_level_function").kind, SymbolKind::Function);
6391 assert_eq!(find("MyClass").kind, SymbolKind::Class);
6392 assert_eq!(find("instance_method").kind, SymbolKind::Method);
6393 assert_eq!(find("decorated_function").kind, SymbolKind::Function);
6394 assert_eq!(find("OuterClass").kind, SymbolKind::Class);
6395 assert_eq!(find("InnerClass").kind, SymbolKind::Class);
6396 assert_eq!(find("inner_method").kind, SymbolKind::Method);
6397 assert_eq!(find("outer_method").kind, SymbolKind::Method);
6398 }
6399
6400 #[test]
6401 fn py_method_scope_chain() {
6402 let provider = TreeSitterProvider::new();
6403 let symbols = provider.list_symbols(&fixture_path("sample.py")).unwrap();
6404
6405 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6406
6407 assert_eq!(
6409 find("instance_method").scope_chain,
6410 vec!["MyClass"],
6411 "instance_method should have MyClass in scope chain"
6412 );
6413 assert_eq!(find("instance_method").parent.as_deref(), Some("MyClass"));
6414
6415 assert_eq!(
6417 find("inner_method").scope_chain,
6418 vec!["OuterClass", "InnerClass"],
6419 "inner_method should have nested scope chain"
6420 );
6421
6422 assert_eq!(
6424 find("InnerClass").scope_chain,
6425 vec!["OuterClass"],
6426 "InnerClass should have OuterClass in scope"
6427 );
6428
6429 assert!(
6431 find("top_level_function").scope_chain.is_empty(),
6432 "top-level function should have empty scope chain"
6433 );
6434 }
6435
6436 #[test]
6437 fn py_decorated_function_signature() {
6438 let provider = TreeSitterProvider::new();
6439 let symbols = provider.list_symbols(&fixture_path("sample.py")).unwrap();
6440
6441 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6442
6443 let sig = find("decorated_function").signature.as_ref().unwrap();
6444 assert!(
6445 sig.contains("@staticmethod"),
6446 "decorated function signature should include decorator: {}",
6447 sig
6448 );
6449 assert!(
6450 sig.contains("def decorated_function"),
6451 "signature should include function def: {}",
6452 sig
6453 );
6454 }
6455
6456 #[test]
6457 fn py_ranges_valid() {
6458 let provider = TreeSitterProvider::new();
6459 let symbols = provider.list_symbols(&fixture_path("sample.py")).unwrap();
6460
6461 for s in &symbols {
6462 assert!(
6463 s.range.end_line >= s.range.start_line,
6464 "symbol {} has invalid range: {:?}",
6465 s.name,
6466 s.range
6467 );
6468 }
6469 }
6470
6471 #[test]
6474 fn rs_extracts_all_symbols() {
6475 let provider = TreeSitterProvider::new();
6476 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6477
6478 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
6479 assert!(
6480 names.contains(&"public_function"),
6481 "missing public_function: {:?}",
6482 names
6483 );
6484 assert!(
6485 names.contains(&"private_function"),
6486 "missing private_function: {:?}",
6487 names
6488 );
6489 assert!(names.contains(&"MyStruct"), "missing MyStruct: {:?}", names);
6490 assert!(names.contains(&"Color"), "missing enum Color: {:?}", names);
6491 assert!(
6492 names.contains(&"Drawable"),
6493 "missing trait Drawable: {:?}",
6494 names
6495 );
6496 assert!(
6498 names.contains(&"new"),
6499 "missing impl method new: {:?}",
6500 names
6501 );
6502
6503 assert!(
6505 symbols.len() >= 6,
6506 "expected ≥6 symbols, got {}: {:?}",
6507 symbols.len(),
6508 names
6509 );
6510 }
6511
6512 #[test]
6513 fn rs_symbol_kinds_correct() {
6514 let provider = TreeSitterProvider::new();
6515 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6516
6517 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6518
6519 assert_eq!(find("public_function").kind, SymbolKind::Function);
6520 assert_eq!(find("private_function").kind, SymbolKind::Function);
6521 assert_eq!(find("MyStruct").kind, SymbolKind::Struct);
6522 assert_eq!(find("Color").kind, SymbolKind::Enum);
6523 assert_eq!(find("Drawable").kind, SymbolKind::Interface); assert_eq!(find("new").kind, SymbolKind::Method);
6525 }
6526
6527 #[test]
6528 fn rs_pub_export_detection() {
6529 let provider = TreeSitterProvider::new();
6530 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6531
6532 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6533
6534 assert!(
6535 find("public_function").exported,
6536 "pub fn should be exported"
6537 );
6538 assert!(
6539 !find("private_function").exported,
6540 "non-pub fn should not be exported"
6541 );
6542 assert!(find("MyStruct").exported, "pub struct should be exported");
6543 assert!(find("Color").exported, "pub enum should be exported");
6544 assert!(find("Drawable").exported, "pub trait should be exported");
6545 assert!(
6546 find("new").exported,
6547 "pub fn inside impl should be exported"
6548 );
6549 assert!(
6550 !find("helper").exported,
6551 "non-pub fn inside impl should not be exported"
6552 );
6553 }
6554
6555 #[test]
6556 fn rs_impl_method_scope_chain() {
6557 let provider = TreeSitterProvider::new();
6558 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6559
6560 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6561
6562 assert_eq!(
6564 find("new").scope_chain,
6565 vec!["MyStruct"],
6566 "impl method should have type in scope chain"
6567 );
6568 assert_eq!(find("new").parent.as_deref(), Some("MyStruct"));
6569
6570 assert!(
6572 find("public_function").scope_chain.is_empty(),
6573 "free function should have empty scope chain"
6574 );
6575 }
6576
6577 #[test]
6578 fn rs_trait_impl_scope_chain() {
6579 let provider = TreeSitterProvider::new();
6580 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6581
6582 let draw = symbols.iter().find(|s| s.name == "draw").unwrap();
6584 assert_eq!(
6585 draw.scope_chain,
6586 vec!["Drawable for MyStruct"],
6587 "trait impl method should have 'Trait for Type' scope"
6588 );
6589 assert_eq!(draw.parent.as_deref(), Some("MyStruct"));
6590 }
6591
6592 #[test]
6593 fn rs_ranges_valid() {
6594 let provider = TreeSitterProvider::new();
6595 let symbols = provider.list_symbols(&fixture_path("sample.rs")).unwrap();
6596
6597 for s in &symbols {
6598 assert!(
6599 s.range.end_line >= s.range.start_line,
6600 "symbol {} has invalid range: {:?}",
6601 s.name,
6602 s.range
6603 );
6604 }
6605 }
6606
6607 #[test]
6610 fn go_extracts_all_symbols() {
6611 let provider = TreeSitterProvider::new();
6612 let symbols = provider.list_symbols(&fixture_path("sample.go")).unwrap();
6613
6614 let names: Vec<&str> = symbols.iter().map(|s| s.name.as_str()).collect();
6615 assert!(
6616 names.contains(&"ExportedFunction"),
6617 "missing ExportedFunction: {:?}",
6618 names
6619 );
6620 assert!(
6621 names.contains(&"unexportedFunction"),
6622 "missing unexportedFunction: {:?}",
6623 names
6624 );
6625 assert!(
6626 names.contains(&"MyStruct"),
6627 "missing struct MyStruct: {:?}",
6628 names
6629 );
6630 assert!(
6631 names.contains(&"Reader"),
6632 "missing interface Reader: {:?}",
6633 names
6634 );
6635 assert!(
6637 names.contains(&"String"),
6638 "missing receiver method String: {:?}",
6639 names
6640 );
6641
6642 assert!(
6644 symbols.len() >= 4,
6645 "expected ≥4 symbols, got {}: {:?}",
6646 symbols.len(),
6647 names
6648 );
6649 }
6650
6651 #[test]
6652 fn go_symbol_kinds_correct() {
6653 let provider = TreeSitterProvider::new();
6654 let symbols = provider.list_symbols(&fixture_path("sample.go")).unwrap();
6655
6656 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6657
6658 assert_eq!(find("ExportedFunction").kind, SymbolKind::Function);
6659 assert_eq!(find("unexportedFunction").kind, SymbolKind::Function);
6660 assert_eq!(find("MyStruct").kind, SymbolKind::Struct);
6661 assert_eq!(find("Reader").kind, SymbolKind::Interface);
6662 assert_eq!(find("String").kind, SymbolKind::Method);
6663 assert_eq!(find("helper").kind, SymbolKind::Method);
6664 }
6665
6666 #[test]
6667 fn go_uppercase_export_detection() {
6668 let provider = TreeSitterProvider::new();
6669 let symbols = provider.list_symbols(&fixture_path("sample.go")).unwrap();
6670
6671 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6672
6673 assert!(
6674 find("ExportedFunction").exported,
6675 "ExportedFunction (uppercase) should be exported"
6676 );
6677 assert!(
6678 !find("unexportedFunction").exported,
6679 "unexportedFunction (lowercase) should not be exported"
6680 );
6681 assert!(
6682 find("MyStruct").exported,
6683 "MyStruct (uppercase) should be exported"
6684 );
6685 assert!(
6686 find("Reader").exported,
6687 "Reader (uppercase) should be exported"
6688 );
6689 assert!(
6690 find("String").exported,
6691 "String method (uppercase) should be exported"
6692 );
6693 assert!(
6694 !find("helper").exported,
6695 "helper method (lowercase) should not be exported"
6696 );
6697 }
6698
6699 #[test]
6700 fn go_receiver_method_scope_chain() {
6701 let provider = TreeSitterProvider::new();
6702 let symbols = provider.list_symbols(&fixture_path("sample.go")).unwrap();
6703
6704 let find = |name: &str| symbols.iter().find(|s| s.name == name).unwrap();
6705
6706 assert_eq!(
6708 find("String").scope_chain,
6709 vec!["MyStruct"],
6710 "receiver method should have type in scope chain"
6711 );
6712 assert_eq!(find("String").parent.as_deref(), Some("MyStruct"));
6713
6714 assert!(
6716 find("ExportedFunction").scope_chain.is_empty(),
6717 "regular function should have empty scope chain"
6718 );
6719 }
6720
6721 #[test]
6722 fn go_ranges_valid() {
6723 let provider = TreeSitterProvider::new();
6724 let symbols = provider.list_symbols(&fixture_path("sample.go")).unwrap();
6725
6726 for s in &symbols {
6727 assert!(
6728 s.range.end_line >= s.range.start_line,
6729 "symbol {} has invalid range: {:?}",
6730 s.name,
6731 s.range
6732 );
6733 }
6734 }
6735
6736 #[test]
6739 fn cross_language_all_six_produce_symbols() {
6740 let provider = TreeSitterProvider::new();
6741
6742 let fixtures = [
6743 ("sample.ts", "TypeScript"),
6744 ("sample.tsx", "TSX"),
6745 ("sample.js", "JavaScript"),
6746 ("sample.py", "Python"),
6747 ("sample.rs", "Rust"),
6748 ("sample.go", "Go"),
6749 ];
6750
6751 for (fixture, lang) in &fixtures {
6752 let symbols = provider
6753 .list_symbols(&fixture_path(fixture))
6754 .unwrap_or_else(|e| panic!("{} ({}) failed: {:?}", lang, fixture, e));
6755 assert!(
6756 symbols.len() >= 2,
6757 "{} should produce ≥2 symbols, got {}: {:?}",
6758 lang,
6759 symbols.len(),
6760 symbols.iter().map(|s| &s.name).collect::<Vec<_>>()
6761 );
6762 }
6763 }
6764
6765 #[test]
6768 fn symbol_cache_returns_cached_results_on_second_call() {
6769 let dir = tempfile::tempdir().unwrap();
6770 let file = dir.path().join("test.rs");
6771 std::fs::write(&file, "pub fn hello() {}\npub fn world() {}").unwrap();
6772
6773 let mut parser = FileParser::new();
6774
6775 let symbols1 = parser.extract_symbols(&file).unwrap();
6776 assert_eq!(symbols1.len(), 2);
6777
6778 let symbols2 = parser.extract_symbols(&file).unwrap();
6780 assert_eq!(symbols2.len(), 2);
6781 assert_eq!(symbols1[0].name, symbols2[0].name);
6782
6783 assert!(parser.symbol_cache.read().unwrap().contains_key(&file));
6785 }
6786
6787 #[test]
6788 fn symbol_cache_invalidates_on_file_change() {
6789 let dir = tempfile::tempdir().unwrap();
6790 let file = dir.path().join("test.rs");
6791 std::fs::write(&file, "pub fn hello() {}").unwrap();
6792
6793 let mut parser = FileParser::new();
6794
6795 let symbols1 = parser.extract_symbols(&file).unwrap();
6796 assert_eq!(symbols1.len(), 1);
6797 assert_eq!(symbols1[0].name, "hello");
6798
6799 std::thread::sleep(std::time::Duration::from_millis(50));
6801
6802 std::fs::write(&file, "pub fn hello() {}\npub fn goodbye() {}").unwrap();
6804
6805 let symbols2 = parser.extract_symbols(&file).unwrap();
6807 assert_eq!(symbols2.len(), 2);
6808 assert!(symbols2.iter().any(|s| s.name == "goodbye"));
6809 }
6810
6811 #[test]
6812 fn symbol_cache_invalidate_method_clears_entry() {
6813 let dir = tempfile::tempdir().unwrap();
6814 let file = dir.path().join("test.rs");
6815 std::fs::write(&file, "pub fn hello() {}").unwrap();
6816
6817 let mut parser = FileParser::new();
6818 parser.extract_symbols(&file).unwrap();
6819 assert!(parser.symbol_cache.read().unwrap().contains_key(&file));
6820
6821 parser.invalidate_symbols(&file);
6822 assert!(!parser.symbol_cache.read().unwrap().contains_key(&file));
6823 assert!(!parser.cache.contains_key(&file));
6825 }
6826
6827 #[test]
6828 fn symbol_cache_works_for_multiple_languages() {
6829 let dir = tempfile::tempdir().unwrap();
6830 let rs_file = dir.path().join("lib.rs");
6831 let ts_file = dir.path().join("app.ts");
6832 let py_file = dir.path().join("main.py");
6833
6834 std::fs::write(&rs_file, "pub fn rust_fn() {}").unwrap();
6835 std::fs::write(&ts_file, "export function tsFn() {}").unwrap();
6836 std::fs::write(&py_file, "def py_fn():\n pass").unwrap();
6837
6838 let mut parser = FileParser::new();
6839
6840 let rs_syms = parser.extract_symbols(&rs_file).unwrap();
6841 let ts_syms = parser.extract_symbols(&ts_file).unwrap();
6842 let py_syms = parser.extract_symbols(&py_file).unwrap();
6843
6844 assert!(rs_syms.iter().any(|s| s.name == "rust_fn"));
6845 assert!(ts_syms.iter().any(|s| s.name == "tsFn"));
6846 assert!(py_syms.iter().any(|s| s.name == "py_fn"));
6847
6848 assert_eq!(parser.symbol_cache.read().unwrap().len(), 3);
6850
6851 let rs_syms2 = parser.extract_symbols(&rs_file).unwrap();
6853 assert_eq!(rs_syms.len(), rs_syms2.len());
6854 }
6855
6856 #[test]
6857 fn extract_json_symbols_top_level_keys() {
6858 let dir = tempfile::tempdir().unwrap();
6859 let file = dir.path().join("package.json");
6860 std::fs::write(&file, r#"{"name": "x", "version": "1"}"#).unwrap();
6861
6862 let mut parser = FileParser::new();
6863 let symbols = parser.extract_symbols(&file).unwrap();
6864
6865 assert_eq!(symbols.len(), 2);
6866 assert!(symbols
6867 .iter()
6868 .any(|s| s.name == "name" && s.kind == SymbolKind::Variable));
6869 assert!(symbols
6870 .iter()
6871 .any(|s| s.name == "version" && s.kind == SymbolKind::Variable));
6872 }
6873
6874 #[test]
6875 fn extract_json_symbols_root_array() {
6876 let dir = tempfile::tempdir().unwrap();
6877 let file = dir.path().join("array.json");
6878 std::fs::write(&file, "[1,2,3]").unwrap();
6879
6880 let mut parser = FileParser::new();
6881 let symbols = parser.extract_symbols(&file).unwrap();
6882
6883 assert_eq!(symbols.len(), 0);
6884 }
6885
6886 #[test]
6887 fn extract_json_symbols_no_recursion_into_nested() {
6888 let dir = tempfile::tempdir().unwrap();
6889 let file = dir.path().join("nested.json");
6890 std::fs::write(&file, r#"{"scripts": {"build": "tsc"}}"#).unwrap();
6891
6892 let mut parser = FileParser::new();
6893 let symbols = parser.extract_symbols(&file).unwrap();
6894
6895 assert_eq!(symbols.len(), 1);
6896 assert_eq!(symbols[0].name, "scripts");
6897 assert!(!symbols.iter().any(|s| s.name == "build"));
6898 }
6899
6900 #[test]
6901 fn extract_scala_symbols_object_and_method() {
6902 let dir = tempfile::tempdir().unwrap();
6903 let file = dir.path().join("Greeter.scala");
6904 std::fs::write(
6905 &file,
6906 "object Greeter {\n def hello(name: String): String = s\"hi $name\"\n}",
6907 )
6908 .unwrap();
6909
6910 let mut parser = FileParser::new();
6911 let symbols = parser.extract_symbols(&file).unwrap();
6912
6913 assert!(symbols
6914 .iter()
6915 .any(|s| s.name == "Greeter" && s.kind == SymbolKind::Class));
6916 assert!(symbols.iter().any(|s| s.name == "hello"
6917 && s.kind == SymbolKind::Method
6918 && s.scope_chain == vec!["Greeter".to_string()]));
6919 }
6920
6921 #[test]
6922 fn extract_scala_symbols_class_and_trait() {
6923 let dir = tempfile::tempdir().unwrap();
6924 let file = dir.path().join("Types.scala");
6925 std::fs::write(&file, "class Foo\ntrait Bar").unwrap();
6926
6927 let mut parser = FileParser::new();
6928 let symbols = parser.extract_symbols(&file).unwrap();
6929
6930 assert!(symbols
6931 .iter()
6932 .any(|s| s.name == "Foo" && s.kind == SymbolKind::Class));
6933 assert!(symbols
6934 .iter()
6935 .any(|s| s.name == "Bar" && s.kind == SymbolKind::Interface));
6936 }
6937}