vtcode_core/tools/tree_sitter/
analyzer.rs

1//! Core tree-sitter analyzer for code parsing and analysis
2
3use crate::tools::tree_sitter::analysis::{
4    CodeAnalysis, CodeMetrics, DependencyInfo, DependencyKind,
5};
6use crate::tools::tree_sitter::languages::*;
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::Path;
11use tree_sitter::{Language, Parser, Tree};
12
13/// Tree-sitter analysis error
14#[derive(Debug, thiserror::Error)]
15pub enum TreeSitterError {
16    #[error("Unsupported language: {0}")]
17    UnsupportedLanguage(String),
18
19    #[error("Parse error: {0}")]
20    ParseError(String),
21
22    #[error("File read error: {0}")]
23    FileReadError(String),
24
25    #[error("Language detection failed: {0}")]
26    LanguageDetectionError(String),
27
28    #[error("Query execution error: {0}")]
29    QueryError(String),
30}
31
32/// Language support enumeration
33#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, Hash, PartialEq)]
34pub enum LanguageSupport {
35    Rust,
36    Python,
37    JavaScript,
38    TypeScript,
39    Go,
40    Java,
41    Swift,
42}
43
44/// Syntax tree representation
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct SyntaxTree {
47    pub root: SyntaxNode,
48    pub source_code: String,
49    pub language: LanguageSupport,
50    pub diagnostics: Vec<Diagnostic>,
51}
52
53/// Syntax node in the tree
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct SyntaxNode {
56    pub kind: String,
57    pub start_position: Position,
58    pub end_position: Position,
59    pub text: String,
60    // Children within the AST subtree
61    pub children: Vec<SyntaxNode>,
62    pub named_children: HashMap<String, Vec<SyntaxNode>>,
63    // Collected comments that immediately precede this node as sibling comments
64    // (useful for documentation extraction like docstrings or /// comments)
65    pub leading_comments: Vec<String>,
66}
67
68/// Position in source code
69#[derive(Debug, Clone, Serialize, Deserialize, Eq, Hash, PartialEq)]
70pub struct Position {
71    pub row: usize,
72    pub column: usize,
73    pub byte_offset: usize,
74}
75
76/// Diagnostic information
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct Diagnostic {
79    pub level: DiagnosticLevel,
80    pub message: String,
81    pub position: Position,
82    pub node_kind: String,
83}
84
85#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
86pub enum DiagnosticLevel {
87    Error,
88    Warning,
89    Info,
90}
91
92/// Main tree-sitter analyzer
93pub struct TreeSitterAnalyzer {
94    parsers: HashMap<LanguageSupport, Parser>,
95    supported_languages: Vec<LanguageSupport>,
96    current_file: String,
97}
98
99impl TreeSitterAnalyzer {
100    /// Create a new tree-sitter analyzer
101    pub fn new() -> Result<Self> {
102        let mut parsers = HashMap::new();
103
104        // Initialize parsers for all supported languages
105        let mut languages = vec![
106            LanguageSupport::Rust,
107            LanguageSupport::Python,
108            LanguageSupport::JavaScript,
109            LanguageSupport::TypeScript,
110            LanguageSupport::Go,
111            LanguageSupport::Java,
112        ];
113
114        if cfg!(feature = "swift") {
115            // Swift grammar provided by https://github.com/tree-sitter/swift-tree-sitter via the tree-sitter-swift crate
116            languages.push(LanguageSupport::Swift);
117        }
118
119        for language in &languages {
120            let mut parser = Parser::new();
121            let ts_language = get_language(language.clone())?;
122            parser.set_language(&ts_language)?;
123            parsers.insert(language.clone(), parser);
124        }
125
126        Ok(Self {
127            parsers,
128            supported_languages: languages,
129            current_file: String::new(),
130        })
131    }
132
133    /// Get supported languages
134    pub fn supported_languages(&self) -> &[LanguageSupport] {
135        &self.supported_languages
136    }
137
138    /// Detect language from file extension
139    pub fn detect_language_from_path<P: AsRef<Path>>(&self, path: P) -> Result<LanguageSupport> {
140        let path = path.as_ref();
141        let extension = path
142            .extension()
143            .and_then(|ext| ext.to_str())
144            .ok_or_else(|| {
145                TreeSitterError::LanguageDetectionError("No file extension found".to_string())
146            })?;
147
148        let normalized_extension = extension.to_ascii_lowercase();
149
150        match normalized_extension.as_str() {
151            "rs" => Ok(LanguageSupport::Rust),
152            "py" => Ok(LanguageSupport::Python),
153            "js" => Ok(LanguageSupport::JavaScript),
154            "ts" => Ok(LanguageSupport::TypeScript),
155            "tsx" => Ok(LanguageSupport::TypeScript),
156            "jsx" => Ok(LanguageSupport::JavaScript),
157            "go" => Ok(LanguageSupport::Go),
158            "java" => Ok(LanguageSupport::Java),
159            "swift" => {
160                if cfg!(feature = "swift") {
161                    Ok(LanguageSupport::Swift)
162                } else {
163                    Err(TreeSitterError::UnsupportedLanguage("Swift".to_string()).into())
164                }
165            }
166            _ => Err(TreeSitterError::UnsupportedLanguage(extension.to_string()).into()),
167        }
168    }
169
170    /// Parse source code into a syntax tree
171    pub fn parse(&mut self, source_code: &str, language: LanguageSupport) -> Result<Tree> {
172        let parser = self
173            .parsers
174            .get_mut(&language)
175            .ok_or_else(|| TreeSitterError::UnsupportedLanguage(format!("{:?}", language)))?;
176
177        let tree = parser.parse(source_code, None).ok_or_else(|| {
178            TreeSitterError::ParseError("Failed to parse source code".to_string())
179        })?;
180
181        Ok(tree)
182    }
183
184    /// Extract symbols from a syntax tree
185    pub fn extract_symbols(
186        &mut self,
187        syntax_tree: &Tree,
188        source_code: &str,
189        language: LanguageSupport,
190    ) -> Result<Vec<SymbolInfo>> {
191        let mut symbols = Vec::new();
192        let root_node = syntax_tree.root_node();
193
194        // Walk the tree and extract symbols based on language
195        self.extract_symbols_recursive(root_node, source_code, language, &mut symbols, None)?;
196
197        Ok(symbols)
198    }
199
200    /// Recursively extract symbols from a node
201    fn extract_symbols_recursive(
202        &self,
203        node: tree_sitter::Node,
204        source_code: &str,
205        language: LanguageSupport,
206        symbols: &mut Vec<SymbolInfo>,
207        parent_scope: Option<String>,
208    ) -> Result<()> {
209        let _node_text = &source_code[node.start_byte()..node.end_byte()];
210        let kind = node.kind();
211
212        // Extract symbols based on node type and language
213        match language {
214            LanguageSupport::Rust => {
215                if kind == "function_item" || kind == "method_definition" {
216                    // Extract function name
217                    if let Some(name_node) = self.find_child_by_type(node, "identifier") {
218                        let name = &source_code[name_node.start_byte()..name_node.end_byte()];
219                        symbols.push(SymbolInfo {
220                            name: name.to_string(),
221                            kind: SymbolKind::Function,
222                            position: Position {
223                                row: node.start_position().row,
224                                column: node.start_position().column,
225                                byte_offset: node.start_byte(),
226                            },
227                            scope: parent_scope.clone(),
228                            signature: None,
229                            documentation: None,
230                        });
231                    }
232                } else if kind == "struct_item" || kind == "enum_item" {
233                    // Extract type name
234                    if let Some(name_node) = self.find_child_by_type(node, "type_identifier") {
235                        let name = &source_code[name_node.start_byte()..name_node.end_byte()];
236                        symbols.push(SymbolInfo {
237                            name: name.to_string(),
238                            kind: SymbolKind::Type,
239                            position: Position {
240                                row: node.start_position().row,
241                                column: node.start_position().column,
242                                byte_offset: node.start_byte(),
243                            },
244                            scope: parent_scope.clone(),
245                            signature: None,
246                            documentation: None,
247                        });
248                    }
249                }
250            }
251            LanguageSupport::Python => {
252                if kind == "function_definition" {
253                    // Extract function name
254                    if let Some(name_node) = self.find_child_by_type(node, "identifier") {
255                        let name = &source_code[name_node.start_byte()..name_node.end_byte()];
256                        symbols.push(SymbolInfo {
257                            name: name.to_string(),
258                            kind: SymbolKind::Function,
259                            position: Position {
260                                row: node.start_position().row,
261                                column: node.start_position().column,
262                                byte_offset: node.start_byte(),
263                            },
264                            scope: parent_scope.clone(),
265                            signature: None,
266                            documentation: None,
267                        });
268                    }
269                } else if kind == "class_definition" {
270                    // Extract class name
271                    if let Some(name_node) = self.find_child_by_type(node, "identifier") {
272                        let name = &source_code[name_node.start_byte()..name_node.end_byte()];
273                        symbols.push(SymbolInfo {
274                            name: name.to_string(),
275                            kind: SymbolKind::Type,
276                            position: Position {
277                                row: node.start_position().row,
278                                column: node.start_position().column,
279                                byte_offset: node.start_byte(),
280                            },
281                            scope: parent_scope.clone(),
282                            signature: None,
283                            documentation: None,
284                        });
285                    }
286                }
287            }
288            _ => {
289                // For other languages, do a basic extraction
290                if kind.contains("function") || kind.contains("method") {
291                    // Try to find a name
292                    if let Some(name_node) = self.find_child_by_type(node, "identifier") {
293                        let name = &source_code[name_node.start_byte()..name_node.end_byte()];
294                        symbols.push(SymbolInfo {
295                            name: name.to_string(),
296                            kind: SymbolKind::Function,
297                            position: Position {
298                                row: node.start_position().row,
299                                column: node.start_position().column,
300                                byte_offset: node.start_byte(),
301                            },
302                            scope: parent_scope.clone(),
303                            signature: None,
304                            documentation: None,
305                        });
306                    }
307                }
308            }
309        }
310
311        // Recursively process children
312        let mut cursor = node.walk();
313        for child in node.children(&mut cursor) {
314            self.extract_symbols_recursive(
315                child,
316                source_code,
317                language.clone(),
318                symbols,
319                parent_scope.clone(),
320            )?;
321        }
322
323        Ok(())
324    }
325
326    /// Find a child node of a specific type
327    fn find_child_by_type<'a>(
328        &self,
329        node: tree_sitter::Node<'a>,
330        type_name: &str,
331    ) -> Option<tree_sitter::Node<'a>> {
332        let mut cursor = node.walk();
333        for child in node.children(&mut cursor) {
334            if child.kind() == type_name {
335                return Some(child);
336            }
337        }
338        None
339    }
340
341    /// Extract dependencies from a syntax tree
342    pub fn extract_dependencies(
343        &self,
344        syntax_tree: &Tree,
345        language: LanguageSupport,
346    ) -> Result<Vec<DependencyInfo>> {
347        let mut dependencies = Vec::new();
348        let root_node = syntax_tree.root_node();
349
350        // Extract dependencies based on language
351        match language {
352            LanguageSupport::Rust => {
353                self.extract_rust_dependencies(root_node, &mut dependencies)?;
354            }
355            LanguageSupport::Python => {
356                self.extract_python_dependencies(root_node, &mut dependencies)?;
357            }
358            LanguageSupport::JavaScript | LanguageSupport::TypeScript => {
359                self.extract_js_dependencies(root_node, &mut dependencies)?;
360            }
361            _ => {
362                // For other languages, do a basic extraction
363                self.extract_basic_dependencies(root_node, &mut dependencies)?;
364            }
365        }
366
367        Ok(dependencies)
368    }
369
370    /// Extract Rust dependencies
371    fn extract_rust_dependencies(
372        &self,
373        node: tree_sitter::Node,
374        dependencies: &mut Vec<DependencyInfo>,
375    ) -> Result<()> {
376        let mut cursor = node.walk();
377
378        // Look for use statements and extern crate declarations
379        if node.kind() == "use_declaration" {
380            // Extract the path from the use statement
381            if let Some(_path_node) = self
382                .find_child_by_type(node, "use_list")
383                .or_else(|| self.find_child_by_type(node, "scoped_identifier"))
384                .or_else(|| self.find_child_by_type(node, "identifier"))
385            {
386                // This is a simplified extraction
387                dependencies.push(DependencyInfo {
388                    name: "unknown_rust_dep".to_string(), // Would need more parsing for actual name
389                    kind: DependencyKind::Import,
390                    source: "use_declaration".to_string(),
391                    position: Position {
392                        row: node.start_position().row,
393                        column: node.start_position().column,
394                        byte_offset: node.start_byte(),
395                    },
396                });
397            }
398        } else if node.kind() == "extern_crate_declaration" {
399            // Extract crate name from extern crate declaration
400            if let Some(_name_node) = self.find_child_by_type(node, "identifier") {
401                dependencies.push(DependencyInfo {
402                    name: "unknown_crate".to_string(), // Would need more parsing for actual name
403                    kind: DependencyKind::External,
404                    source: "extern_crate".to_string(),
405                    position: Position {
406                        row: node.start_position().row,
407                        column: node.start_position().column,
408                        byte_offset: node.start_byte(),
409                    },
410                });
411            }
412        }
413
414        // Recursively process children
415        for child in node.children(&mut cursor) {
416            self.extract_rust_dependencies(child, dependencies)?;
417        }
418
419        Ok(())
420    }
421
422    /// Extract Python dependencies
423    fn extract_python_dependencies(
424        &self,
425        node: tree_sitter::Node,
426        dependencies: &mut Vec<DependencyInfo>,
427    ) -> Result<()> {
428        let mut cursor = node.walk();
429
430        // Look for import statements
431        if node.kind() == "import_statement" || node.kind() == "import_from_statement" {
432            // Extract the module name
433            dependencies.push(DependencyInfo {
434                name: "unknown_python_module".to_string(), // Would need more parsing for actual name
435                kind: DependencyKind::Import,
436                source: node.kind().to_string(),
437                position: Position {
438                    row: node.start_position().row,
439                    column: node.start_position().column,
440                    byte_offset: node.start_byte(),
441                },
442            });
443        }
444
445        // Recursively process children
446        for child in node.children(&mut cursor) {
447            self.extract_python_dependencies(child, dependencies)?;
448        }
449
450        Ok(())
451    }
452
453    /// Extract JavaScript/TypeScript dependencies
454    fn extract_js_dependencies(
455        &self,
456        node: tree_sitter::Node,
457        dependencies: &mut Vec<DependencyInfo>,
458    ) -> Result<()> {
459        let mut cursor = node.walk();
460
461        // Look for import statements
462        if node.kind() == "import_statement" {
463            // Extract the module name
464            dependencies.push(DependencyInfo {
465                name: "unknown_js_module".to_string(), // Would need more parsing for actual name
466                kind: DependencyKind::Import,
467                source: node.kind().to_string(),
468                position: Position {
469                    row: node.start_position().row,
470                    column: node.start_position().column,
471                    byte_offset: node.start_byte(),
472                },
473            });
474        }
475
476        // Recursively process children
477        for child in node.children(&mut cursor) {
478            self.extract_js_dependencies(child, dependencies)?;
479        }
480
481        Ok(())
482    }
483
484    /// Extract basic dependencies (fallback)
485    fn extract_basic_dependencies(
486        &self,
487        node: tree_sitter::Node,
488        dependencies: &mut Vec<DependencyInfo>,
489    ) -> Result<()> {
490        let mut cursor = node.walk();
491
492        // Look for import/include statements
493        if node.kind().contains("import") || node.kind().contains("include") {
494            // Extract the dependency name
495            dependencies.push(DependencyInfo {
496                name: "unknown_dependency".to_string(),
497                kind: DependencyKind::Import,
498                source: node.kind().to_string(),
499                position: Position {
500                    row: node.start_position().row,
501                    column: node.start_position().column,
502                    byte_offset: node.start_byte(),
503                },
504            });
505        }
506
507        // Recursively process children
508        for child in node.children(&mut cursor) {
509            self.extract_basic_dependencies(child, dependencies)?;
510        }
511
512        Ok(())
513    }
514
515    /// Calculate code metrics from a syntax tree
516    pub fn calculate_metrics(&self, syntax_tree: &Tree, source_code: &str) -> Result<CodeMetrics> {
517        let root_node = syntax_tree.root_node();
518        let lines = source_code.lines().collect::<Vec<_>>();
519
520        // Count different types of nodes
521        let mut functions_count = 0;
522        let mut classes_count = 0;
523        let mut variables_count = 0;
524        let mut imports_count = 0;
525
526        self.count_nodes_recursive(
527            root_node,
528            &mut functions_count,
529            &mut classes_count,
530            &mut variables_count,
531            &mut imports_count,
532        );
533
534        // Count comments
535        let lines_of_comments = lines
536            .iter()
537            .filter(|l| {
538                l.trim().starts_with("//")
539                    || l.trim().starts_with("/*")
540                    || l.trim().starts_with("#")
541            })
542            .count();
543
544        let blank_lines = lines.iter().filter(|l| l.trim().is_empty()).count();
545        let lines_of_code = lines.len();
546
547        let comment_ratio = if lines_of_code > 0 {
548            lines_of_comments as f64 / lines_of_code as f64
549        } else {
550            0.0
551        };
552
553        Ok(CodeMetrics {
554            lines_of_code,
555            lines_of_comments,
556            blank_lines,
557            functions_count,
558            classes_count,
559            variables_count,
560            imports_count,
561            comment_ratio,
562        })
563    }
564
565    /// Recursively count different types of nodes
566    fn count_nodes_recursive(
567        &self,
568        node: tree_sitter::Node,
569        functions_count: &mut usize,
570        classes_count: &mut usize,
571        variables_count: &mut usize,
572        imports_count: &mut usize,
573    ) {
574        let kind = node.kind();
575
576        // Count based on node type
577        if kind.contains("function") || kind.contains("method") {
578            *functions_count += 1;
579        } else if kind.contains("class") || kind.contains("struct") || kind.contains("enum") {
580            *classes_count += 1;
581        } else if kind.contains("variable") || kind.contains("let") || kind.contains("const") {
582            *variables_count += 1;
583        } else if kind.contains("import") || kind.contains("include") || kind.contains("use") {
584            *imports_count += 1;
585        }
586
587        // Recursively process children
588        let mut cursor = node.walk();
589        for child in node.children(&mut cursor) {
590            self.count_nodes_recursive(
591                child,
592                functions_count,
593                classes_count,
594                variables_count,
595                imports_count,
596            );
597        }
598    }
599
600    /// Parse file into a syntax tree
601    pub fn parse_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<SyntaxTree> {
602        let file_path = file_path.as_ref();
603        let language = self.detect_language_from_path(file_path)?;
604
605        let source_code = std::fs::read_to_string(file_path)
606            .map_err(|e| TreeSitterError::FileReadError(e.to_string()))?;
607
608        let tree = self.parse(&source_code, language.clone())?;
609
610        // Convert tree-sitter tree to our SyntaxTree representation
611        let root = self.convert_tree_to_syntax_node(tree.root_node(), &source_code);
612        let diagnostics = self.collect_diagnostics(&tree, &source_code);
613
614        Ok(SyntaxTree {
615            root,
616            source_code,
617            language,
618            diagnostics,
619        })
620    }
621
622    /// Convert tree-sitter node to our SyntaxNode
623    pub fn convert_tree_to_syntax_node(
624        &self,
625        node: tree_sitter::Node,
626        source_code: &str,
627    ) -> SyntaxNode {
628        let start = node.start_position();
629        let end = node.end_position();
630
631        // First, convert all children sequentially so we can compute leading sibling comments
632        let mut converted_children: Vec<SyntaxNode> = Vec::new();
633        let mut cursor = node.walk();
634        for child in node.children(&mut cursor) {
635            // Gather trailing run of comment siblings immediately preceding this child
636            let mut leading_comments: Vec<String> = Vec::new();
637            for prev in converted_children.iter().rev() {
638                let k = prev.kind.to_lowercase();
639                if k.contains("comment") {
640                    leading_comments.push(prev.text.trim().to_string());
641                } else {
642                    break;
643                }
644            }
645            leading_comments.reverse();
646
647            // Convert current child
648            let mut converted = self.convert_tree_to_syntax_node(child, source_code);
649            converted.leading_comments = leading_comments;
650            converted_children.push(converted);
651        }
652
653        SyntaxNode {
654            kind: node.kind().to_string(),
655            start_position: Position {
656                row: start.row,
657                column: start.column,
658                byte_offset: node.start_byte(),
659            },
660            end_position: Position {
661                row: end.row,
662                column: end.column,
663                byte_offset: node.end_byte(),
664            },
665            text: source_code[node.start_byte()..node.end_byte()].to_string(),
666            children: converted_children,
667            named_children: self.collect_named_children(node, source_code),
668            leading_comments: Vec::new(),
669        }
670    }
671
672    /// Collect named children for easier access
673    fn collect_named_children(
674        &self,
675        node: tree_sitter::Node,
676        source_code: &str,
677    ) -> HashMap<String, Vec<SyntaxNode>> {
678        let mut named_children = HashMap::new();
679
680        for child in node.named_children(&mut node.walk()) {
681            let kind = child.kind().to_string();
682            let syntax_node = self.convert_tree_to_syntax_node(child, source_code);
683
684            named_children
685                .entry(kind)
686                .or_insert_with(Vec::new)
687                .push(syntax_node);
688        }
689
690        named_children
691    }
692
693    /// Collect diagnostics from the parsed tree
694    pub fn collect_diagnostics(&self, tree: &Tree, _source_code: &str) -> Vec<Diagnostic> {
695        let mut diagnostics = Vec::new();
696
697        // Basic diagnostics collection - can be extended with more sophisticated analysis
698        if tree.root_node().has_error() {
699            diagnostics.push(Diagnostic {
700                level: DiagnosticLevel::Error,
701                message: "Syntax error detected in code".to_string(),
702                position: Position {
703                    row: 0,
704                    column: 0,
705                    byte_offset: 0,
706                },
707                node_kind: "root".to_string(),
708            });
709        }
710
711        diagnostics
712    }
713
714    /// Get parser statistics
715    pub fn get_parser_stats(&self) -> HashMap<String, usize> {
716        let mut stats = HashMap::new();
717        stats.insert(
718            "supported_languages".to_string(),
719            self.supported_languages.len(),
720        );
721        stats
722    }
723
724    pub fn analyze_file_with_tree_sitter(
725        &mut self,
726        file_path: &std::path::Path,
727        source_code: &str,
728    ) -> Result<CodeAnalysis> {
729        let extension = file_path
730            .extension()
731            .and_then(|ext| ext.to_str())
732            .map(|ext| ext.to_ascii_lowercase());
733
734        let is_swift_path = extension
735            .as_deref()
736            .map(|ext| ext == "swift")
737            .unwrap_or(false);
738
739        if is_swift_path && !cfg!(feature = "swift") {
740            return Err(TreeSitterError::UnsupportedLanguage("Swift".to_string()).into());
741        }
742
743        let language = match self.detect_language_from_path(file_path) {
744            Ok(language) => language,
745            Err(err) => match self.detect_language_from_content(source_code) {
746                Some(language) => language,
747                None => return Err(err),
748            },
749        };
750
751        self.current_file = file_path.to_string_lossy().to_string();
752
753        let tree = self.parse(source_code, language.clone())?;
754
755        // Extract actual symbols and dependencies
756        let symbols = self.extract_symbols(&tree, source_code, language.clone())?;
757        let dependencies = self.extract_dependencies(&tree, language.clone())?;
758        let metrics = self.calculate_metrics(&tree, source_code)?;
759
760        Ok(CodeAnalysis {
761            file_path: self.current_file.clone(),
762            language,
763            symbols,
764            dependencies,
765            metrics,
766            issues: vec![], // Would need to implement actual issue detection
767            complexity: Default::default(), // Would need to implement actual complexity analysis
768            structure: Default::default(), // Would need to implement actual structure analysis
769        })
770    }
771}
772
773/// Helper function to get tree-sitter language
774fn get_language(language: LanguageSupport) -> Result<Language> {
775    let lang = match language {
776        LanguageSupport::Rust => tree_sitter_rust::LANGUAGE,
777        LanguageSupport::Python => tree_sitter_python::LANGUAGE,
778        LanguageSupport::JavaScript => tree_sitter_javascript::LANGUAGE,
779        LanguageSupport::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
780        LanguageSupport::Go => tree_sitter_go::LANGUAGE,
781        LanguageSupport::Java => tree_sitter_java::LANGUAGE,
782        LanguageSupport::Swift => {
783            #[cfg(feature = "swift")]
784            {
785                tree_sitter_swift::LANGUAGE
786            }
787            #[cfg(not(feature = "swift"))]
788            {
789                return Err(TreeSitterError::UnsupportedLanguage("Swift".to_string()).into());
790            }
791        }
792    };
793    Ok(lang.into())
794}
795
796impl std::fmt::Display for LanguageSupport {
797    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
798        let language_name = match self {
799            LanguageSupport::Rust => "Rust",
800            LanguageSupport::Python => "Python",
801            LanguageSupport::JavaScript => "JavaScript",
802            LanguageSupport::TypeScript => "TypeScript",
803            LanguageSupport::Go => "Go",
804            LanguageSupport::Java => "Java",
805            LanguageSupport::Swift => "Swift",
806        };
807        write!(f, "{}", language_name)
808    }
809}
810
811impl TreeSitterAnalyzer {
812    pub fn detect_language_from_content(&self, content: &str) -> Option<LanguageSupport> {
813        // Simple heuristic-based language detection
814        if content.contains("fn ") && content.contains("{") && content.contains("}") {
815            Some(LanguageSupport::Rust)
816        } else if content.contains("def ") && content.contains(":") && !content.contains("{") {
817            Some(LanguageSupport::Python)
818        } else if content.contains("function") && content.contains("{") && content.contains("}") {
819            Some(LanguageSupport::JavaScript)
820        } else {
821            None
822        }
823    }
824}
825
826#[cfg(test)]
827mod tests {
828    use super::*;
829    use std::path::Path;
830
831    fn create_test_analyzer() -> TreeSitterAnalyzer {
832        TreeSitterAnalyzer::new().expect("Failed to create analyzer")
833    }
834
835    #[test]
836    fn test_analyzer_creation() {
837        let analyzer = create_test_analyzer();
838        assert!(
839            analyzer
840                .supported_languages
841                .contains(&LanguageSupport::Rust)
842        );
843        assert!(
844            analyzer
845                .supported_languages
846                .contains(&LanguageSupport::Python)
847        );
848    }
849
850    #[test]
851    fn test_language_detection_from_path() {
852        let analyzer = create_test_analyzer();
853
854        // Test basic file extensions
855        match analyzer.detect_language_from_path(Path::new("main.rs")) {
856            Ok(lang) => assert_eq!(lang, LanguageSupport::Rust),
857            Err(e) => panic!("Expected Rust language, got error: {}", e),
858        }
859
860        match analyzer.detect_language_from_path(Path::new("script.py")) {
861            Ok(lang) => assert_eq!(lang, LanguageSupport::Python),
862            Err(e) => panic!("Expected Python language, got error: {}", e),
863        }
864
865        // Test unknown extension should return error
866        assert!(
867            analyzer
868                .detect_language_from_path(Path::new("file.unknown"))
869                .is_err()
870        );
871    }
872
873    #[test]
874    fn test_language_detection_from_content() {
875        let analyzer = create_test_analyzer();
876
877        // Test Rust content
878        let rust_code = r#"fn main() { println!("Hello, world!"); let x = 42; }"#;
879        assert_eq!(
880            analyzer.detect_language_from_content(rust_code),
881            Some(LanguageSupport::Rust)
882        );
883
884        // Test Python content
885        let python_code = r#"def main(): print("Hello, world!"); x = 42"#;
886        assert_eq!(
887            analyzer.detect_language_from_content(python_code),
888            Some(LanguageSupport::Python)
889        );
890
891        // Test unknown content
892        let unknown_code = "This is not code just plain text.";
893        assert_eq!(analyzer.detect_language_from_content(unknown_code), None);
894    }
895
896    #[test]
897    fn test_parse_rust_code() {
898        let mut analyzer = create_test_analyzer();
899
900        let rust_code = r#"fn main() { println!("Hello, world!"); let x = 42; }"#;
901
902        let result = analyzer.parse(rust_code, LanguageSupport::Rust);
903        assert!(result.is_ok());
904
905        let tree = result.unwrap();
906        assert!(!tree.root_node().has_error());
907    }
908
909    #[cfg(feature = "swift")]
910    #[test]
911    fn test_parse_swift_code() {
912        let mut analyzer = create_test_analyzer();
913        let swift_code = "print(\"Hello, World!\")\n";
914        let result = analyzer.parse(swift_code, LanguageSupport::Swift);
915        assert!(result.is_ok());
916        let tree = result.unwrap();
917        assert!(!tree.root_node().has_error());
918    }
919}