1use std::ops::Range;
5
6use tree_sitter::Node;
7
8use super::{
9 parser_language::Language,
10 parser_types::{FunctionDef, Import, ImportKind},
11};
12
13#[derive(Debug)]
15pub struct SyntaxIndex {
16 functions: Vec<IndexedFunction>,
17 imports: Vec<IndexedImport>,
18 line_offsets: Vec<usize>,
19}
20
21#[derive(Clone, Copy, Debug)]
23pub struct FunctionRef<'a> {
24 inner: &'a IndexedFunction,
25 source: &'a str,
26}
27
28#[derive(Clone, Copy, Debug)]
30pub struct ImportRef<'a> {
31 inner: &'a IndexedImport,
32 source: &'a str,
33}
34
35#[derive(Debug)]
36struct IndexedFunction {
37 name: String,
38 signature: String,
39 start_line: usize,
40 end_line: usize,
41 content: Range<usize>,
42}
43
44#[derive(Debug)]
45struct IndexedImport {
46 raw: Range<usize>,
47 kind: ImportKind,
48}
49
50impl SyntaxIndex {
51 pub(super) fn build(language: Language, source: &str, root: Node<'_>) -> Self {
52 let mut index = Self {
53 functions: Vec::new(),
54 imports: Vec::new(),
55 line_offsets: line_offsets(source),
56 };
57
58 let mut stack = vec![root];
59 while let Some(node) = stack.pop() {
60 if is_function_node(&node, language)
61 && let Some(name) = function_name(&node, source)
62 {
63 index.functions.push(IndexedFunction {
64 name: name.to_string(),
65 signature: function_signature(&node, source),
66 start_line: node.start_position().row,
67 end_line: node.end_position().row,
68 content: node.byte_range(),
69 });
70 }
71
72 push_children_reverse(node, &mut stack);
73 }
74
75 let mut cursor = root.walk();
76 for child in root.children(&mut cursor) {
77 match language {
78 Language::Rust => match child.kind() {
79 "use_declaration" => index.imports.push(IndexedImport {
80 raw: child.byte_range(),
81 kind: ImportKind::Use,
82 }),
83 "extern_crate_declaration" => index.imports.push(IndexedImport {
84 raw: child.byte_range(),
85 kind: ImportKind::ExternCrate,
86 }),
87 _ => {}
88 },
89 Language::Python => {
90 if matches!(child.kind(), "import_statement" | "import_from_statement") {
91 index.imports.push(IndexedImport {
92 raw: child.byte_range(),
93 kind: ImportKind::Import,
94 });
95 }
96 }
97 Language::JavaScript | Language::TypeScript => {
98 if child.kind() == "import_statement" {
99 index.imports.push(IndexedImport {
100 raw: child.byte_range(),
101 kind: ImportKind::Import,
102 });
103 }
104 }
105 Language::Go | Language::Java => {
106 if child.kind() == "import_declaration" {
107 index.imports.push(IndexedImport {
108 raw: child.byte_range(),
109 kind: ImportKind::Import,
110 });
111 }
112 }
113 Language::C | Language::Cpp | Language::Zig | Language::Unknown => {}
117 }
118 }
119
120 index
121 }
122
123 pub fn functions<'a>(&'a self, source: &'a str) -> impl Iterator<Item = FunctionRef<'a>> + 'a {
124 self.functions
125 .iter()
126 .map(move |inner| FunctionRef { inner, source })
127 }
128
129 pub fn imports<'a>(&'a self, source: &'a str) -> impl Iterator<Item = ImportRef<'a>> + 'a {
130 self.imports
131 .iter()
132 .map(move |inner| ImportRef { inner, source })
133 }
134
135 pub fn line_offsets(&self) -> &[usize] {
137 &self.line_offsets
138 }
139}
140
141impl FunctionRef<'_> {
142 pub fn name(&self) -> &str {
143 &self.inner.name
144 }
145
146 pub fn signature(&self) -> &str {
147 &self.inner.signature
148 }
149
150 pub fn start_line(&self) -> usize {
151 self.inner.start_line
152 }
153
154 pub fn end_line(&self) -> usize {
155 self.inner.end_line
156 }
157
158 pub fn content(&self) -> &str {
159 &self.source[self.inner.content.clone()]
160 }
161
162 pub fn to_owned(self) -> FunctionDef {
163 FunctionDef {
164 name: self.name().to_string(),
165 signature: self.signature().to_string(),
166 start_line: self.start_line(),
167 end_line: self.end_line(),
168 content: self.content().to_string(),
169 }
170 }
171}
172
173impl ImportRef<'_> {
174 pub fn raw(&self) -> &str {
175 &self.source[self.inner.raw.clone()]
176 }
177
178 pub fn kind(&self) -> ImportKind {
179 self.inner.kind
180 }
181
182 pub fn to_owned(self) -> Import {
183 Import {
184 raw: self.raw().to_string(),
185 kind: self.kind(),
186 }
187 }
188}
189
190pub(super) fn is_function_kind(kind: &str, language: Language) -> bool {
191 match language {
192 Language::Rust => {
193 kind == "function_item" || kind == "method_declaration" || kind == "closure_expression"
194 }
195 Language::Python => kind == "function_definition",
196 Language::JavaScript | Language::TypeScript => {
197 kind == "function_declaration"
198 || kind == "method_definition"
199 || kind == "generator_function_declaration"
200 || kind == "variable_declarator"
201 }
202 Language::Go => kind == "function_declaration" || kind == "method_declaration",
203 Language::C | Language::Cpp => kind == "function_definition",
204 Language::Java => kind == "method_declaration" || kind == "constructor_declaration",
205 Language::Zig => kind == "function_declaration",
206 Language::Unknown => false,
207 }
208}
209
210fn is_function_node(node: &Node<'_>, language: Language) -> bool {
211 match language {
212 Language::JavaScript | Language::TypeScript => {
213 matches!(
214 node.kind(),
215 "function_declaration" | "method_definition" | "generator_function_declaration"
216 ) || (node.kind() == "variable_declarator"
217 && node
218 .child_by_field_name("value")
219 .is_some_and(|value| is_javascript_function_value(value.kind())))
220 }
221 _ => is_function_kind(node.kind(), language),
222 }
223}
224
225fn function_name<'a>(node: &Node<'_>, source: &'a str) -> Option<&'a str> {
226 if let Some(name) = node.child_by_field_name("name") {
227 return Some(&source[name.byte_range()]);
228 }
229 if let Some(declarator) = node.child_by_field_name("declarator") {
230 if let Some(name) = c_function_name(declarator, source) {
231 return Some(name);
232 }
233 if let Some(name) = first_identifier_in_subtree(declarator, source) {
234 return Some(name);
235 }
236 }
237
238 let mut cursor = node.walk();
239 for child in node.children(&mut cursor) {
240 if matches!(
241 child.kind(),
242 "identifier" | "field_identifier" | "type_identifier" | "property_identifier"
243 ) {
244 return Some(&source[child.byte_range()]);
245 }
246 }
247 None
248}
249
250fn c_function_name<'a>(function_declarator: Node<'_>, source: &'a str) -> Option<&'a str> {
251 let mut current = function_declarator.child_by_field_name("declarator")?;
252 for _ in 0..32 {
253 match current.kind() {
254 "identifier"
255 | "field_identifier"
256 | "type_identifier"
257 | "property_identifier"
258 | "operator_name"
259 | "destructor_name" => return Some(&source[current.byte_range()]),
260 "qualified_identifier" | "template_function" => {
261 current = current.child_by_field_name("name")?;
262 }
263 "pointer_declarator"
264 | "reference_declarator"
265 | "function_declarator"
266 | "parenthesized_declarator" => {
267 current = current.child_by_field_name("declarator")?;
268 }
269 _ => return None,
270 }
271 }
272 None
273}
274
275fn first_identifier_in_subtree<'a>(node: Node<'_>, source: &'a str) -> Option<&'a str> {
276 let mut stack = vec![node];
277 while let Some(current) = stack.pop() {
278 if matches!(
279 current.kind(),
280 "identifier" | "field_identifier" | "type_identifier" | "property_identifier"
281 ) {
282 return Some(&source[current.byte_range()]);
283 }
284 push_children_reverse(current, &mut stack);
285 }
286 None
287}
288
289fn function_signature(node: &Node<'_>, source: &str) -> String {
290 if node.kind() == "variable_declarator" {
291 return variable_function_signature(node, source);
292 }
293
294 let mut signature_parts = Vec::new();
295 let mut cursor = node.walk();
296 for child in node.children(&mut cursor) {
297 let kind = child.kind();
298 if matches!(
299 kind,
300 "identifier"
301 | "field_identifier"
302 | "type_identifier"
303 | "property_identifier"
304 | "parameters"
305 | "formal_parameters"
306 | "parameter_list"
307 | "function_declarator"
308 | "type_parameters"
309 | "type_arguments"
310 | "return_type"
311 | "type_annotation"
312 | "result"
313 ) {
314 signature_parts.push(&source[child.byte_range()]);
315 }
316 if matches!(
317 kind,
318 "block" | "compound_statement" | "statement_block" | "suite"
319 ) {
320 break;
321 }
322 }
323
324 signature_parts.join(" ")
325}
326
327fn variable_function_signature(node: &Node<'_>, source: &str) -> String {
328 let Some(name) = node.child_by_field_name("name") else {
329 return String::new();
330 };
331 let Some(value) = node.child_by_field_name("value") else {
332 return source[name.byte_range()].to_string();
333 };
334
335 let mut signature_parts = vec![&source[name.byte_range()]];
336 let mut cursor = value.walk();
337 for child in value.children(&mut cursor) {
338 if matches!(child.kind(), "formal_parameters" | "parameters") {
339 signature_parts.push(&source[child.byte_range()]);
340 }
341 if matches!(child.kind(), "statement_block" | "body") {
342 break;
343 }
344 }
345 signature_parts.join(" ")
346}
347
348fn line_offsets(source: &str) -> Vec<usize> {
349 let mut offsets =
350 Vec::with_capacity(source.as_bytes().iter().filter(|&&b| b == b'\n').count() + 1);
351 offsets.push(0);
352 for (index, byte) in source.bytes().enumerate() {
353 if byte == b'\n' && index + 1 < source.len() {
354 offsets.push(index + 1);
355 }
356 }
357 offsets
358}
359
360fn is_javascript_function_value(kind: &str) -> bool {
361 matches!(
362 kind,
363 "arrow_function" | "function_expression" | "generator_function"
364 )
365}
366
367fn push_children_reverse<'tree>(node: Node<'tree>, stack: &mut Vec<Node<'tree>>) {
368 let child_count = node.child_count();
369 for index in (0..child_count).rev() {
370 if let Some(child) = node.child(index as u32) {
371 stack.push(child);
372 }
373 }
374}