1use std::collections::HashMap;
2use tree_sitter::{Language, Parser};
3use anyhow::{Result, anyhow};
4
5pub struct AstQuery {
9 language: String,
10 parser: Parser,
11}
12
13#[derive(Debug, Clone)]
15pub struct QueryMatch {
16 pub captures: HashMap<String, String>,
18 pub capture_lines: HashMap<String, usize>,
20 pub start_line: usize,
22 pub end_line: usize,
24}
25
26impl AstQuery {
27 pub fn new(language: &str) -> Result<Self> {
29 let lang = get_language(language)?;
30 let mut parser = Parser::new();
31 parser.set_language(&lang).map_err(|e| anyhow!("设置语言失败: {}", e))?;
32 Ok(Self { language: language.to_string(), parser })
33 }
34
35 pub fn find_definition(&mut self, source: &str, symbol: &str) -> Result<Option<QueryMatch>> {
40 let tree = self.parser.parse(source, None)
41 .ok_or_else(|| anyhow!("解析源码失败"))?;
42 let bytes = source.as_bytes();
43 let _root = tree.root_node();
44 let mut result = None;
45
46 let def_types = match self.language.as_str() {
48 "rust" => &["function_item", "struct_item", "trait_item", "enum_item", "type_item", "const_item", "static_item", "impl_item", "mod_item"][..],
49 "python" => &["function_definition", "class_definition", "assignment"][..],
50 "javascript" | "typescript" => &["function_declaration", "class_declaration", "method_definition", "variable_declaration", "interface_declaration", "type_alias_declaration", "enum_declaration"][..],
51 "go" => &["function_declaration", "type_declaration", "type_spec", "const_declaration", "var_declaration"][..],
52 "csharp" => &["method_declaration", "class_declaration", "struct_declaration", "interface_declaration", "enum_declaration", "delegate_declaration", "property_declaration"][..],
53 _ => return Ok(None),
54 };
55
56 let mut cursor = tree.walk();
58 'outer: loop {
59 let node = cursor.node();
60 if def_types.contains(&node.kind()) && let Some(name_node) = node.child_by_field_name("name") && let Ok(name) = name_node.utf8_text(bytes) && name == symbol {
61 let mut captures = HashMap::new();
62 let mut capture_lines = HashMap::new();
63 if let Ok(text) = node.utf8_text(bytes) {
64 captures.insert("name".to_string(), text.to_string());
65 }
66 capture_lines.insert("name".to_string(), name_node.start_position().row + 1);
67 result = Some(QueryMatch {
68 captures,
69 capture_lines,
70 start_line: node.start_position().row + 1,
71 end_line: node.end_position().row + 1,
72 });
73 break 'outer;
74 }
75
76 if cursor.goto_first_child() { continue; }
77 loop {
78 if cursor.goto_next_sibling() { continue 'outer; }
79 if !cursor.goto_parent() { break 'outer; }
80 }
81 }
82
83 Ok(result)
84 }
85
86 pub fn list_definitions(&mut self, source: &str) -> Result<Vec<String>> {
88 let tree = self.parser.parse(source, None)
89 .ok_or_else(|| anyhow!("解析源码失败"))?;
90 let bytes = source.as_bytes();
91 let mut defs = Vec::new();
92
93 let def_types = match self.language.as_str() {
94 "rust" => &["function_item", "struct_item", "trait_item", "enum_item", "type_item", "const_item", "impl_item"][..],
95 "python" => &["function_definition", "class_definition"][..],
96 "javascript" | "typescript" => &["function_declaration", "class_declaration", "method_definition", "interface_declaration"][..],
97 "go" => &["function_declaration", "type_spec"][..],
98 "csharp" => &["method_declaration", "class_declaration", "struct_declaration", "interface_declaration"][..],
99 _ => return Ok(defs),
100 };
101
102 let mut cursor = tree.walk();
103 'walk: loop {
104 let node = cursor.node();
105 if def_types.contains(&node.kind()) && let Some(name_node) = node.child_by_field_name("name") && let Ok(name) = name_node.utf8_text(bytes) {
106 defs.push(name.to_string());
107 }
108
109 if cursor.goto_first_child() { continue; }
110 loop {
111 if cursor.goto_next_sibling() { continue 'walk; }
112 if !cursor.goto_parent() { break 'walk; }
113 }
114 }
115
116 defs.sort();
117 defs.dedup();
118 Ok(defs)
119 }
120}
121
122pub fn get_language(name: &str) -> Result<Language> {
124 match name {
125 "rust" => Ok(tree_sitter_rust::LANGUAGE.into()),
126 "python" => Ok(tree_sitter_python::LANGUAGE.into()),
127 "javascript" => Ok(tree_sitter_javascript::LANGUAGE.into()),
128 "typescript" | "tsx" => Ok(tree_sitter_typescript::LANGUAGE_TSX.into()),
129 "go" => Ok(tree_sitter_go::LANGUAGE.into()),
130 "csharp" => Ok(tree_sitter_c_sharp::LANGUAGE.into()),
131 _ => anyhow::bail!("不支持的语言: {}", name),
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_find_definition_rust() {
141 let mut q = AstQuery::new("rust").unwrap();
142 let source = "struct Point { x: i32, y: i32 }\nfn add(a: i32, b: i32) -> i32 { a + b }";
143 let found = q.find_definition(source, "add").unwrap();
144 assert!(found.is_some());
145 let m = found.unwrap();
146 assert!(m.captures.contains_key("name"));
147 }
148
149 #[test]
150 fn test_find_definition_not_found() {
151 let mut q = AstQuery::new("rust").unwrap();
152 let source = "fn foo() {}";
153 let found = q.find_definition(source, "bar").unwrap();
154 assert!(found.is_none());
155 }
156
157 #[test]
158 fn test_list_definitions() {
159 let mut q = AstQuery::new("rust").unwrap();
160 let source = "struct A;\nfn b() {}\ntrait C {}";
161 let defs = q.list_definitions(source).unwrap();
162 assert!(defs.contains(&"A".to_string()));
163 assert!(defs.contains(&"b".to_string()));
164 assert!(defs.contains(&"C".to_string()));
165 }
166
167 #[test]
168 fn test_python_definition() {
169 let mut q = AstQuery::new("python").unwrap();
170 let source = "def hello(): pass\nclass World: pass";
171 let defs = q.list_definitions(source).unwrap();
172 assert!(defs.contains(&"hello".to_string()));
173 assert!(defs.contains(&"World".to_string()));
174 }
175
176 #[test]
177 fn test_js_definition() {
178 let mut q = AstQuery::new("javascript").unwrap();
179 let source = "function add(a, b) { return a + b; }";
180 let found = q.find_definition(source, "add").unwrap();
181 assert!(found.is_some());
182 }
183
184 #[test]
185 fn test_list_definitions_empty() {
186 let mut q = AstQuery::new("rust").unwrap();
187 let defs = q.list_definitions("").unwrap();
188 assert!(defs.is_empty());
189 }
190}