agcodex_core/tools/
tree.rs

1//! Tree-sitter AST Tool for AGCodex
2//!
3//! This module provides comprehensive AST parsing and querying capabilities using tree-sitter
4//! with support for multiple programming languages. It includes:
5//! - Multi-language parser support with automatic language detection
6//! - Query-based AST pattern matching
7//! - Semantic code diffing
8//! - Symbol extraction and analysis
9//! - Efficient caching for performance
10//!
11//! ## Supported Languages
12//! - Rust, Python, JavaScript, TypeScript
13//! - Go, Java, C, C++, Bash
14//! - More languages can be added via the parser registry
15
16use super::InternalTool;
17use super::ToolMetadata;
18use super::output::ComprehensiveToolOutput as ToolOutput;
19use crate::subagents::IntelligenceLevel;
20use agcodex_ast::SourceLocation;
21
22use dashmap::DashMap;
23use lru::LruCache;
24use serde::Deserialize;
25use serde::Serialize;
26use std::collections::HashMap;
27use std::num::NonZeroUsize;
28use std::path::Path;
29use std::path::PathBuf;
30use std::sync::Arc;
31use std::sync::Mutex;
32use std::time::Duration;
33use std::time::Instant;
34use thiserror::Error;
35use tokio::fs;
36use tracing::debug;
37use tracing::info;
38// use tracing::warn; // unused
39use tree_sitter::Language;
40use tree_sitter::Node;
41use tree_sitter::Parser;
42use tree_sitter::Query;
43use tree_sitter::QueryCursor;
44use tree_sitter::StreamingIterator;
45use tree_sitter::Tree;
46
47// Import all 27 tree-sitter language parsers
48extern crate tree_sitter_bash;
49extern crate tree_sitter_c;
50extern crate tree_sitter_c_sharp;
51extern crate tree_sitter_clojure;
52extern crate tree_sitter_cpp;
53extern crate tree_sitter_css;
54extern crate tree_sitter_dockerfile;
55extern crate tree_sitter_elixir;
56extern crate tree_sitter_go;
57extern crate tree_sitter_haskell;
58extern crate tree_sitter_hcl;
59extern crate tree_sitter_html;
60extern crate tree_sitter_java;
61extern crate tree_sitter_javascript;
62extern crate tree_sitter_json;
63extern crate tree_sitter_kotlin_ng;
64// extern crate tree_sitter_latex; // Disabled due to linking issues
65extern crate tree_sitter_lua;
66extern crate tree_sitter_make;
67extern crate tree_sitter_markdown;
68extern crate tree_sitter_nix;
69extern crate tree_sitter_objc;
70extern crate tree_sitter_ocaml;
71extern crate tree_sitter_php;
72extern crate tree_sitter_python;
73extern crate tree_sitter_rst;
74extern crate tree_sitter_ruby;
75extern crate tree_sitter_rust;
76extern crate tree_sitter_scala;
77extern crate tree_sitter_swift;
78extern crate tree_sitter_toml;
79extern crate tree_sitter_typescript;
80extern crate tree_sitter_yaml;
81extern crate tree_sitter_zig;
82
83/// Errors specific to the tree tool
84#[derive(Error, Debug)]
85pub enum TreeError {
86    #[error("unsupported language: {language}")]
87    UnsupportedLanguage { language: String },
88
89    #[error("failed to parse code: {reason}")]
90    ParseFailed { reason: String },
91
92    #[error("query compilation failed: {query} - {reason}")]
93    QueryCompilationFailed { query: String, reason: String },
94
95    #[error("query execution failed: {reason}")]
96    QueryExecutionFailed { reason: String },
97
98    #[error("file reading failed: {path} - {reason}")]
99    FileReadFailed { path: PathBuf, reason: String },
100
101    #[error("language detection failed for file: {path}")]
102    LanguageDetectionFailed { path: PathBuf },
103
104    #[error("AST node access failed: {reason}")]
105    NodeAccessFailed { reason: String },
106
107    #[error("diff computation failed: {reason}")]
108    DiffFailed { reason: String },
109
110    #[error("symbol extraction failed: {reason}")]
111    SymbolExtractionFailed { reason: String },
112
113    #[error("cache operation failed: {reason}")]
114    CacheFailed { reason: String },
115}
116
117/// Result type for tree operations
118pub type TreeResult<T> = std::result::Result<T, TreeError>;
119
120/// Supported programming languages (27 total)
121#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
122pub enum SupportedLanguage {
123    // Core systems languages
124    Rust,
125    Python,
126    JavaScript,
127    TypeScript,
128    Go,
129    Java,
130    C,
131    Cpp,
132    CSharp,
133    Bash,
134
135    // Web languages
136    Html,
137    Css,
138    Json,
139    Yaml,
140    Toml,
141
142    // Scripting languages
143    Ruby,
144    Php,
145    Lua,
146
147    // Functional languages
148    Haskell,
149    Elixir,
150    Scala,
151    Ocaml,
152    Clojure,
153
154    // Systems languages
155    Zig,
156    Swift,
157    Kotlin,
158    ObjectiveC,
159
160    // Config/Build languages
161    Dockerfile,
162    Hcl,
163    Nix,
164    Make,
165
166    // Documentation languages
167    Markdown,
168    // Latex, // Disabled due to linking issues
169    Rst,
170}
171
172impl SupportedLanguage {
173    /// Get file extensions for this language
174    pub const fn extensions(&self) -> &'static [&'static str] {
175        match self {
176            // Core systems languages
177            SupportedLanguage::Rust => &["rs"],
178            SupportedLanguage::Python => &["py", "pyw", "pyi"],
179            SupportedLanguage::JavaScript => &["js", "mjs", "cjs"],
180            SupportedLanguage::TypeScript => &["ts", "tsx", "cts", "mts"],
181            SupportedLanguage::Go => &["go"],
182            SupportedLanguage::Java => &["java"],
183            SupportedLanguage::C => &["c", "h"],
184            SupportedLanguage::Cpp => &["cpp", "cxx", "cc", "hpp", "hxx", "hh"],
185            SupportedLanguage::CSharp => &["cs"],
186            SupportedLanguage::Bash => &["sh", "bash", "zsh"],
187
188            // Web languages
189            SupportedLanguage::Html => &["html", "htm"],
190            SupportedLanguage::Css => &["css"],
191            SupportedLanguage::Json => &["json"],
192            SupportedLanguage::Yaml => &["yaml", "yml"],
193            SupportedLanguage::Toml => &["toml"],
194
195            // Scripting languages
196            SupportedLanguage::Ruby => &["rb"],
197            SupportedLanguage::Php => &["php"],
198            SupportedLanguage::Lua => &["lua"],
199
200            // Functional languages
201            SupportedLanguage::Haskell => &["hs"],
202            SupportedLanguage::Elixir => &["ex", "exs"],
203            SupportedLanguage::Scala => &["scala", "sc"],
204            SupportedLanguage::Ocaml => &["ml", "mli"],
205            SupportedLanguage::Clojure => &["clj", "cljs", "cljc"],
206
207            // Systems languages
208            SupportedLanguage::Zig => &["zig"],
209            SupportedLanguage::Swift => &["swift"],
210            SupportedLanguage::Kotlin => &["kt", "kts"],
211            SupportedLanguage::ObjectiveC => &["m", "mm"],
212
213            // Config/Build languages
214            SupportedLanguage::Dockerfile => &["dockerfile"],
215            SupportedLanguage::Hcl => &["hcl", "tf"],
216            SupportedLanguage::Nix => &["nix"],
217            SupportedLanguage::Make => &["makefile", "mk"],
218
219            // Documentation languages
220            SupportedLanguage::Markdown => &["md", "markdown"],
221            // SupportedLanguage::Latex => &["tex", "latex"], // Disabled
222            SupportedLanguage::Rst => &["rst"],
223        }
224    }
225
226    /// Get the tree-sitter language grammar
227    pub fn grammar(&self) -> Language {
228        match self {
229            // Core systems languages
230            SupportedLanguage::Rust => tree_sitter_rust::LANGUAGE.into(),
231            SupportedLanguage::Python => tree_sitter_python::LANGUAGE.into(),
232            SupportedLanguage::JavaScript => tree_sitter_javascript::LANGUAGE.into(),
233            SupportedLanguage::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
234            SupportedLanguage::Go => tree_sitter_go::LANGUAGE.into(),
235            SupportedLanguage::Java => tree_sitter_java::LANGUAGE.into(),
236            SupportedLanguage::C => tree_sitter_c::LANGUAGE.into(),
237            SupportedLanguage::Cpp => tree_sitter_cpp::LANGUAGE.into(),
238            SupportedLanguage::CSharp => tree_sitter_c_sharp::LANGUAGE.into(),
239            SupportedLanguage::Bash => tree_sitter_bash::LANGUAGE.into(),
240
241            // Web languages
242            SupportedLanguage::Html => tree_sitter_html::LANGUAGE.into(),
243            SupportedLanguage::Css => tree_sitter_css::LANGUAGE.into(),
244            SupportedLanguage::Json => tree_sitter_json::LANGUAGE.into(),
245            SupportedLanguage::Yaml => tree_sitter_yaml::LANGUAGE.into(),
246            // TODO: Fix version compatibility issue with tree_sitter_toml
247            // For now, use JSON parser as a fallback for structured data
248            SupportedLanguage::Toml => tree_sitter_json::LANGUAGE.into(),
249
250            // Scripting languages
251            SupportedLanguage::Ruby => tree_sitter_ruby::LANGUAGE.into(),
252            // TODO: Fix API compatibility for PHP
253            // For now, use JavaScript parser as a fallback for similar syntax
254            SupportedLanguage::Php => tree_sitter_javascript::LANGUAGE.into(),
255            SupportedLanguage::Lua => tree_sitter_lua::LANGUAGE.into(),
256
257            // Functional languages
258            SupportedLanguage::Haskell => tree_sitter_haskell::LANGUAGE.into(),
259            SupportedLanguage::Elixir => tree_sitter_elixir::LANGUAGE.into(),
260            SupportedLanguage::Scala => tree_sitter_scala::LANGUAGE.into(),
261            // TODO: Fix API compatibility for OCaml
262            // For now, use Rust parser as a fallback for ML-family syntax
263            SupportedLanguage::Ocaml => tree_sitter_rust::LANGUAGE.into(),
264            SupportedLanguage::Clojure => tree_sitter_clojure::LANGUAGE.into(),
265
266            // Systems languages
267            SupportedLanguage::Zig => tree_sitter_zig::LANGUAGE.into(),
268            SupportedLanguage::Swift => tree_sitter_swift::LANGUAGE.into(),
269            SupportedLanguage::Kotlin => tree_sitter_kotlin_ng::LANGUAGE.into(),
270            SupportedLanguage::ObjectiveC => tree_sitter_objc::LANGUAGE.into(),
271
272            // Config/Build languages
273            // TODO: Fix version compatibility issue with tree_sitter_dockerfile
274            // For now, use Bash parser as a fallback for shell-like syntax
275            SupportedLanguage::Dockerfile => tree_sitter_bash::LANGUAGE.into(),
276            SupportedLanguage::Hcl => tree_sitter_hcl::LANGUAGE.into(),
277            SupportedLanguage::Nix => tree_sitter_nix::LANGUAGE.into(),
278            SupportedLanguage::Make => tree_sitter_make::LANGUAGE.into(),
279
280            // Documentation languages
281            // TODO: Fix version compatibility issue with tree_sitter_markdown
282            // For now, use HTML parser as a fallback for markup
283            SupportedLanguage::Markdown => tree_sitter_html::LANGUAGE.into(),
284            // SupportedLanguage::Latex => tree_sitter_latex::LANGUAGE.into(), // Disabled
285            SupportedLanguage::Rst => tree_sitter_rst::LANGUAGE.into(),
286        }
287    }
288
289    /// Detect language from file extension
290    pub fn from_extension(ext: &str) -> Option<Self> {
291        let ext = ext.to_lowercase();
292
293        // Check all 27 supported languages
294        Self::all_languages()
295            .iter()
296            .find(|&&lang| lang.extensions().contains(&ext.as_str()))
297            .copied()
298    }
299
300    /// Get all supported languages (27 total)
301    pub const fn all_languages() -> &'static [Self] {
302        &[
303            // Core systems languages
304            SupportedLanguage::Rust,
305            SupportedLanguage::Python,
306            SupportedLanguage::JavaScript,
307            SupportedLanguage::TypeScript,
308            SupportedLanguage::Go,
309            SupportedLanguage::Java,
310            SupportedLanguage::C,
311            SupportedLanguage::Cpp,
312            SupportedLanguage::CSharp,
313            SupportedLanguage::Bash,
314            // Web languages
315            SupportedLanguage::Html,
316            SupportedLanguage::Css,
317            SupportedLanguage::Json,
318            SupportedLanguage::Yaml,
319            SupportedLanguage::Toml,
320            // Scripting languages
321            SupportedLanguage::Ruby,
322            SupportedLanguage::Php,
323            SupportedLanguage::Lua,
324            // Functional languages
325            SupportedLanguage::Haskell,
326            SupportedLanguage::Elixir,
327            SupportedLanguage::Scala,
328            SupportedLanguage::Ocaml,
329            SupportedLanguage::Clojure,
330            // Systems languages
331            SupportedLanguage::Zig,
332            SupportedLanguage::Swift,
333            SupportedLanguage::Kotlin,
334            SupportedLanguage::ObjectiveC,
335            // Config/Build languages
336            SupportedLanguage::Dockerfile,
337            SupportedLanguage::Hcl,
338            SupportedLanguage::Nix,
339            SupportedLanguage::Make,
340            // Documentation languages
341            SupportedLanguage::Markdown,
342            // SupportedLanguage::Latex, // Disabled
343            SupportedLanguage::Rst,
344        ]
345    }
346
347    /// Get name as string
348    pub const fn as_str(&self) -> &'static str {
349        match self {
350            // Core systems languages
351            SupportedLanguage::Rust => "rust",
352            SupportedLanguage::Python => "python",
353            SupportedLanguage::JavaScript => "javascript",
354            SupportedLanguage::TypeScript => "typescript",
355            SupportedLanguage::Go => "go",
356            SupportedLanguage::Java => "java",
357            SupportedLanguage::C => "c",
358            SupportedLanguage::Cpp => "cpp",
359            SupportedLanguage::CSharp => "csharp",
360            SupportedLanguage::Bash => "bash",
361
362            // Web languages
363            SupportedLanguage::Html => "html",
364            SupportedLanguage::Css => "css",
365            SupportedLanguage::Json => "json",
366            SupportedLanguage::Yaml => "yaml",
367            SupportedLanguage::Toml => "toml",
368
369            // Scripting languages
370            SupportedLanguage::Ruby => "ruby",
371            SupportedLanguage::Php => "php",
372            SupportedLanguage::Lua => "lua",
373
374            // Functional languages
375            SupportedLanguage::Haskell => "haskell",
376            SupportedLanguage::Elixir => "elixir",
377            SupportedLanguage::Scala => "scala",
378            SupportedLanguage::Ocaml => "ocaml",
379            SupportedLanguage::Clojure => "clojure",
380
381            // Systems languages
382            SupportedLanguage::Zig => "zig",
383            SupportedLanguage::Swift => "swift",
384            SupportedLanguage::Kotlin => "kotlin",
385            SupportedLanguage::ObjectiveC => "objc",
386
387            // Config/Build languages
388            SupportedLanguage::Dockerfile => "dockerfile",
389            SupportedLanguage::Hcl => "hcl",
390            SupportedLanguage::Nix => "nix",
391            SupportedLanguage::Make => "make",
392
393            // Documentation languages
394            SupportedLanguage::Markdown => "markdown",
395            // SupportedLanguage::Latex => "latex", // Disabled
396            SupportedLanguage::Rst => "rst",
397        }
398    }
399}
400
401/// Parsed AST with metadata
402#[derive(Debug)]
403pub struct ParsedAst {
404    pub tree: Tree,
405    pub language: SupportedLanguage,
406    pub source_code: String,
407    pub parse_time: Duration,
408    pub node_count: usize,
409}
410
411impl ParsedAst {
412    /// Get the root node
413    pub fn root_node(&self) -> Node<'_> {
414        self.tree.root_node()
415    }
416
417    /// Check if there are any parse errors
418    pub fn has_errors(&self) -> bool {
419        self.root_node().has_error()
420    }
421
422    /// Get all error nodes
423    pub fn error_nodes(&self) -> Vec<Node<'_>> {
424        let mut errors = Vec::new();
425        self.collect_error_nodes(self.root_node(), &mut errors);
426        errors
427    }
428
429    fn collect_error_nodes<'a>(&self, node: Node<'a>, errors: &mut Vec<Node<'a>>) {
430        if node.is_error() || node.is_missing() {
431            errors.push(node);
432        }
433        for i in 0..node.child_count() {
434            if let Some(child) = node.child(i) {
435                self.collect_error_nodes(child, errors);
436            }
437        }
438    }
439}
440
441/// Query result containing matched nodes and captures
442#[derive(Debug, Clone, Serialize, Deserialize)]
443pub struct QueryMatch {
444    pub pattern_index: u32,
445    pub captures: Vec<QueryCapture>,
446    pub start_byte: u32,
447    pub end_byte: u32,
448    pub start_point: Point,
449    pub end_point: Point,
450}
451
452/// A capture from a query match
453#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct QueryCapture {
455    pub name: String,
456    pub text: String,
457    pub start_byte: u32,
458    pub end_byte: u32,
459    pub start_point: Point,
460    pub end_point: Point,
461}
462
463/// Point in source code (row, column)
464#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
465pub struct Point {
466    pub row: u32,
467    pub column: u32,
468}
469
470impl From<tree_sitter::Point> for Point {
471    fn from(point: tree_sitter::Point) -> Self {
472        Self {
473            row: point.row as u32,
474            column: point.column as u32,
475        }
476    }
477}
478
479/// Symbol information extracted from AST
480#[derive(Debug, Clone, Serialize, Deserialize)]
481pub struct Symbol {
482    pub name: String,
483    pub kind: SymbolKind,
484    pub start_point: Point,
485    pub end_point: Point,
486    pub start_byte: u32,
487    pub end_byte: u32,
488    pub parent: Option<String>,
489    pub visibility: Option<String>,
490    pub type_signature: Option<String>,
491}
492
493/// Types of symbols that can be extracted
494#[derive(Debug, Clone, Serialize, Deserialize)]
495pub enum SymbolKind {
496    Function,
497    Method,
498    Class,
499    Interface,
500    Struct,
501    Enum,
502    Variable,
503    Constant,
504    Field,
505    Parameter,
506    Module,
507    Namespace,
508    Type,
509    Trait,
510    Impl,
511    Macro,
512    Other(String),
513}
514
515/// Semantic differences between two ASTs
516#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct SemanticDiff {
518    pub added: Vec<DiffNode>,
519    pub removed: Vec<DiffNode>,
520    pub modified: Vec<ModifiedNode>,
521    pub moved: Vec<MovedNode>,
522    pub similarity_score: f64,
523}
524
525/// A node in a diff
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct DiffNode {
528    pub kind: String,
529    pub text: String,
530    pub start_point: Point,
531    pub end_point: Point,
532}
533
534/// A modified node in a diff
535#[derive(Debug, Clone, Serialize, Deserialize)]
536pub struct ModifiedNode {
537    pub old: DiffNode,
538    pub new: DiffNode,
539    pub similarity: f64,
540}
541
542/// A moved node in a diff
543#[derive(Debug, Clone, Serialize, Deserialize)]
544pub struct MovedNode {
545    pub node: DiffNode,
546    pub old_position: Point,
547    pub new_position: Point,
548}
549
550/// Input for tree operations
551#[derive(Debug, Clone)]
552pub enum TreeInput {
553    /// Parse source code
554    Parse {
555        code: String,
556        language: Option<SupportedLanguage>,
557        file_path: Option<PathBuf>,
558    },
559    /// Parse from file
560    ParseFile { file_path: PathBuf },
561    /// Query AST with pattern
562    Query {
563        code: String,
564        language: Option<SupportedLanguage>,
565        pattern: String,
566        file_path: Option<PathBuf>,
567    },
568    /// Query file with pattern
569    QueryFile { file_path: PathBuf, pattern: String },
570    /// Extract symbols from code
571    ExtractSymbols {
572        code: String,
573        language: Option<SupportedLanguage>,
574        file_path: Option<PathBuf>,
575    },
576    /// Extract symbols from file
577    ExtractSymbolsFromFile { file_path: PathBuf },
578    /// Compare two pieces of code
579    Diff {
580        old_code: String,
581        new_code: String,
582        language: Option<SupportedLanguage>,
583    },
584    /// Compare two files
585    DiffFiles {
586        old_file: PathBuf,
587        new_file: PathBuf,
588    },
589}
590
591/// Output from tree operations
592#[derive(Debug, Clone, Serialize, Deserialize)]
593pub enum TreeOutput {
594    /// Parsed AST information
595    Parsed {
596        language: SupportedLanguage,
597        node_count: usize,
598        has_errors: bool,
599        error_count: usize,
600        parse_time_ms: u64,
601    },
602    /// Query results
603    QueryResults {
604        matches: Vec<QueryMatch>,
605        total_matches: usize,
606        query_time_ms: u64,
607    },
608    /// Extracted symbols
609    Symbols {
610        symbols: Vec<Symbol>,
611        total_symbols: usize,
612        extraction_time_ms: u64,
613    },
614    /// Semantic diff
615    Diff {
616        diff: SemanticDiff,
617        diff_time_ms: u64,
618    },
619}
620
621/// Cache key for parsed ASTs
622#[derive(Debug, Clone, PartialEq, Eq, Hash)]
623struct CacheKey {
624    content_hash: u64,
625    language: SupportedLanguage,
626}
627
628/// Thread-safe wrapper for Parser
629struct ThreadSafeParser {
630    parser: Mutex<Parser>,
631    _language: SupportedLanguage,
632}
633
634impl ThreadSafeParser {
635    fn new(language: SupportedLanguage) -> TreeResult<Self> {
636        let mut parser = Parser::new();
637        parser
638            .set_language(&language.grammar())
639            .map_err(|_| TreeError::ParseFailed {
640                reason: format!("Failed to initialize {} parser", language.as_str()),
641            })?;
642
643        Ok(Self {
644            parser: Mutex::new(parser),
645            _language: language,
646        })
647    }
648
649    fn parse(&self, code: &str) -> TreeResult<Tree> {
650        let mut parser = self.parser.lock().map_err(|e| TreeError::ParseFailed {
651            reason: format!("Parser lock failed: {}", e),
652        })?;
653
654        parser
655            .parse(code, None)
656            .ok_or_else(|| TreeError::ParseFailed {
657                reason: "Parser returned None".to_string(),
658            })
659    }
660}
661
662/// Semantic diff engine for computing AST differences
663struct SemanticDiffEngine;
664
665impl SemanticDiffEngine {
666    /// Compute semantic differences between two ASTs
667    fn compute_diff(old_ast: &ParsedAst, new_ast: &ParsedAst) -> TreeResult<SemanticDiff> {
668        if old_ast.language != new_ast.language {
669            return Err(TreeError::DiffFailed {
670                reason: format!(
671                    "Language mismatch: {} vs {}",
672                    old_ast.language.as_str(),
673                    new_ast.language.as_str()
674                ),
675            });
676        }
677
678        let old_root = old_ast.root_node();
679        let new_root = new_ast.root_node();
680
681        let mut added = Vec::new();
682        let mut removed = Vec::new();
683        let mut modified = Vec::new();
684        let mut moved = Vec::new();
685
686        // Collect all nodes from both trees
687        let old_nodes = Self::collect_nodes(old_root, &old_ast.source_code);
688        let new_nodes = Self::collect_nodes(new_root, &new_ast.source_code);
689
690        // Create maps for efficient lookup
691        let old_map: HashMap<String, Vec<DiffNode>> = Self::group_by_kind(&old_nodes);
692        let new_map: HashMap<String, Vec<DiffNode>> = Self::group_by_kind(&new_nodes);
693
694        // Find added, removed, and modified nodes
695        for (kind, new_nodes_of_kind) in &new_map {
696            if let Some(old_nodes_of_kind) = old_map.get(kind) {
697                // Compare nodes of the same kind
698                let (mod_pairs, add_nodes) =
699                    Self::match_nodes(old_nodes_of_kind, new_nodes_of_kind);
700                modified.extend(mod_pairs);
701                added.extend(add_nodes);
702            } else {
703                // All nodes of this kind are added
704                added.extend(new_nodes_of_kind.clone());
705            }
706        }
707
708        for (kind, old_nodes_of_kind) in &old_map {
709            if !new_map.contains_key(kind) {
710                // All nodes of this kind are removed
711                removed.extend(old_nodes_of_kind.clone());
712            }
713        }
714
715        // Detect moved nodes (same content, different position)
716        moved.extend(Self::detect_moved_nodes(&old_nodes, &new_nodes));
717
718        // Calculate similarity score
719        let similarity_score = Self::calculate_similarity(&old_nodes, &new_nodes);
720
721        Ok(SemanticDiff {
722            added,
723            removed,
724            modified,
725            moved,
726            similarity_score,
727        })
728    }
729
730    fn collect_nodes(node: Node, source: &str) -> Vec<DiffNode> {
731        let mut nodes = Vec::new();
732        Self::collect_nodes_recursive(node, source, &mut nodes);
733        nodes
734    }
735
736    fn collect_nodes_recursive(node: Node, source: &str, nodes: &mut Vec<DiffNode>) {
737        let text = node
738            .utf8_text(source.as_bytes())
739            .unwrap_or("<invalid utf8>");
740
741        nodes.push(DiffNode {
742            kind: node.kind().to_string(),
743            text: text.to_string(),
744            start_point: node.start_position().into(),
745            end_point: node.end_position().into(),
746        });
747
748        for i in 0..node.child_count() {
749            if let Some(child) = node.child(i) {
750                Self::collect_nodes_recursive(child, source, nodes);
751            }
752        }
753    }
754
755    fn group_by_kind(nodes: &[DiffNode]) -> HashMap<String, Vec<DiffNode>> {
756        let mut map = HashMap::new();
757        for node in nodes {
758            map.entry(node.kind.clone())
759                .or_insert_with(Vec::new)
760                .push(node.clone());
761        }
762        map
763    }
764
765    fn match_nodes(
766        old_nodes: &[DiffNode],
767        new_nodes: &[DiffNode],
768    ) -> (Vec<ModifiedNode>, Vec<DiffNode>) {
769        let mut modified = Vec::new();
770        let mut added = Vec::new();
771        let mut used_old = vec![false; old_nodes.len()];
772
773        for new_node in new_nodes {
774            let mut best_match = None;
775            let mut best_similarity = 0.0;
776
777            for (i, old_node) in old_nodes.iter().enumerate() {
778                if used_old[i] {
779                    continue;
780                }
781
782                let similarity = Self::text_similarity(&old_node.text, &new_node.text);
783                if similarity > best_similarity && similarity > 0.3 {
784                    best_similarity = similarity;
785                    best_match = Some(i);
786                }
787            }
788
789            if let Some(idx) = best_match {
790                used_old[idx] = true;
791                if best_similarity < 0.9 {
792                    modified.push(ModifiedNode {
793                        old: old_nodes[idx].clone(),
794                        new: new_node.clone(),
795                        similarity: best_similarity,
796                    });
797                }
798            } else {
799                added.push(new_node.clone());
800            }
801        }
802
803        (modified, added)
804    }
805
806    fn detect_moved_nodes(old_nodes: &[DiffNode], new_nodes: &[DiffNode]) -> Vec<MovedNode> {
807        let mut moved = Vec::new();
808
809        for old_node in old_nodes {
810            for new_node in new_nodes {
811                if old_node.kind == new_node.kind
812                    && old_node.text == new_node.text
813                    && (old_node.start_point.row != new_node.start_point.row
814                        || old_node.start_point.column != new_node.start_point.column)
815                {
816                    moved.push(MovedNode {
817                        node: new_node.clone(),
818                        old_position: old_node.start_point,
819                        new_position: new_node.start_point,
820                    });
821                }
822            }
823        }
824
825        moved
826    }
827
828    fn text_similarity(text1: &str, text2: &str) -> f64 {
829        if text1 == text2 {
830            return 1.0;
831        }
832
833        let len1 = text1.len() as f64;
834        let len2 = text2.len() as f64;
835        let max_len = len1.max(len2);
836
837        if max_len == 0.0 {
838            return 1.0;
839        }
840
841        // Simple edit distance approximation
842        let common_chars = text1.chars().filter(|c| text2.contains(*c)).count() as f64;
843
844        common_chars / max_len
845    }
846
847    fn calculate_similarity(old_nodes: &[DiffNode], new_nodes: &[DiffNode]) -> f64 {
848        if old_nodes.is_empty() && new_nodes.is_empty() {
849            return 1.0;
850        }
851
852        let total_nodes = (old_nodes.len() + new_nodes.len()) as f64;
853        let common_nodes = old_nodes
854            .iter()
855            .filter(|old| new_nodes.iter().any(|new| old.text == new.text))
856            .count() as f64;
857
858        (common_nodes * 2.0) / total_nodes
859    }
860}
861
862/// Query library for common AST patterns
863struct QueryLibrary {
864    _queries: HashMap<String, HashMap<SupportedLanguage, String>>,
865}
866
867impl QueryLibrary {
868    fn new() -> Self {
869        let mut queries = HashMap::new();
870
871        // Function definitions
872        let mut function_queries = HashMap::new();
873        function_queries.insert(
874            SupportedLanguage::Rust,
875            "(function_item name: (identifier) @name) @function".to_string(),
876        );
877        function_queries.insert(
878            SupportedLanguage::Python,
879            "(function_definition name: (identifier) @name) @function".to_string(),
880        );
881        function_queries.insert(
882            SupportedLanguage::JavaScript,
883            "(function_declaration name: (identifier) @name) @function".to_string(),
884        );
885        function_queries.insert(
886            SupportedLanguage::TypeScript,
887            "(function_declaration name: (identifier) @name) @function".to_string(),
888        );
889        function_queries.insert(
890            SupportedLanguage::Go,
891            "(function_declaration name: (identifier) @name) @function".to_string(),
892        );
893        function_queries.insert(
894            SupportedLanguage::Java,
895            "(method_declaration name: (identifier) @name) @function".to_string(),
896        );
897        queries.insert("functions".to_string(), function_queries);
898
899        // Class definitions
900        let mut class_queries = HashMap::new();
901        class_queries.insert(
902            SupportedLanguage::Rust,
903            "(struct_item name: (type_identifier) @name) @class".to_string(),
904        );
905        class_queries.insert(
906            SupportedLanguage::Python,
907            "(class_definition name: (identifier) @name) @class".to_string(),
908        );
909        class_queries.insert(
910            SupportedLanguage::JavaScript,
911            "(class_declaration name: (identifier) @name) @class".to_string(),
912        );
913        class_queries.insert(
914            SupportedLanguage::TypeScript,
915            "(class_declaration name: (identifier) @name) @class".to_string(),
916        );
917        class_queries.insert(
918            SupportedLanguage::Java,
919            "(class_declaration name: (identifier) @name) @class".to_string(),
920        );
921        queries.insert("classes".to_string(), class_queries);
922
923        // Variable definitions
924        let mut variable_queries = HashMap::new();
925        variable_queries.insert(
926            SupportedLanguage::Rust,
927            "(let_declaration pattern: (identifier) @name) @variable".to_string(),
928        );
929        variable_queries.insert(
930            SupportedLanguage::Python,
931            "(assignment left: (identifier) @name) @variable".to_string(),
932        );
933        variable_queries.insert(
934            SupportedLanguage::JavaScript,
935            "(variable_declaration (variable_declarator name: (identifier) @name)) @variable"
936                .to_string(),
937        );
938        queries.insert("variables".to_string(), variable_queries);
939
940        Self { _queries: queries }
941    }
942
943    fn _get_query(&self, pattern_name: &str, language: SupportedLanguage) -> Option<&String> {
944        self._queries.get(pattern_name)?.get(&language)
945    }
946}
947
948/// Enhanced tree-sitter tool with comprehensive language support
949pub struct TreeTool {
950    /// Thread-safe parsers for all supported languages
951    parsers: Arc<DashMap<SupportedLanguage, ThreadSafeParser>>,
952    /// AST cache with timed eviction
953    ast_cache: Arc<Mutex<LruCache<CacheKey, (Arc<ParsedAst>, Instant)>>>,
954    /// Semantic diff engine
955    _diff_engine: SemanticDiffEngine,
956    /// Query library for common patterns
957    _query_library: QueryLibrary,
958    /// Performance configuration
959    intelligence_level: IntelligenceLevel,
960    /// Maximum cache size
961    max_cache_size: NonZeroUsize,
962    /// Cache TTL for ASTs
963    cache_ttl: Duration,
964}
965
966impl TreeTool {
967    /// Create a new enhanced tree tool instance
968    pub fn new(intelligence_level: IntelligenceLevel) -> TreeResult<Self> {
969        let (cache_size, cache_ttl) = match intelligence_level {
970            IntelligenceLevel::Light => (NonZeroUsize::new(100).unwrap(), Duration::from_secs(300)), // 5 minutes
971            IntelligenceLevel::Medium => {
972                (NonZeroUsize::new(500).unwrap(), Duration::from_secs(900))
973            } // 15 minutes
974            IntelligenceLevel::Hard => {
975                (NonZeroUsize::new(2000).unwrap(), Duration::from_secs(1800))
976            } // 30 minutes
977        };
978
979        let mut tool = Self {
980            parsers: Arc::new(DashMap::new()),
981            ast_cache: Arc::new(Mutex::new(LruCache::new(cache_size))),
982            _diff_engine: SemanticDiffEngine,
983            _query_library: QueryLibrary::new(),
984            intelligence_level,
985            max_cache_size: cache_size,
986            cache_ttl,
987        };
988
989        // Initialize parsers for all 27 supported languages
990        tool.initialize_parsers()?;
991
992        Ok(tool)
993    }
994
995    /// Initialize parsers for all 27 supported languages
996    fn initialize_parsers(&mut self) -> TreeResult<()> {
997        let languages = SupportedLanguage::all_languages();
998
999        for &language in languages {
1000            let parser = ThreadSafeParser::new(language)?;
1001            self.parsers.insert(language, parser);
1002            debug!("Initialized parser for {}", language.as_str());
1003        }
1004
1005        info!("Initialized {} language parsers", languages.len());
1006        Ok(())
1007    }
1008
1009    /// Parse source code into AST with enhanced caching
1010    pub async fn parse(
1011        &self,
1012        code: String,
1013        language: Option<SupportedLanguage>,
1014        file_path: Option<PathBuf>,
1015    ) -> TreeResult<Arc<ParsedAst>> {
1016        let start_time = Instant::now();
1017
1018        // Detect language if not provided
1019        let detected_language = match language {
1020            Some(lang) => lang,
1021            None => {
1022                if let Some(ref path) = file_path {
1023                    self.detect_language(path)?
1024                } else {
1025                    return Err(TreeError::LanguageDetectionFailed {
1026                        path: file_path.unwrap_or_else(|| PathBuf::from("unknown")),
1027                    });
1028                }
1029            }
1030        };
1031
1032        // Generate cache key
1033        let content_hash = self.hash_content(&code);
1034        let cache_key = CacheKey {
1035            content_hash,
1036            language: detected_language,
1037        };
1038
1039        // Check cache first with TTL
1040        {
1041            let mut cache = self.ast_cache.lock().map_err(|e| TreeError::CacheFailed {
1042                reason: format!("Cache lock failed: {}", e),
1043            })?;
1044
1045            if let Some((cached_ast, cached_time)) = cache.get(&cache_key) {
1046                // Check if cache entry is still valid
1047                if cached_time.elapsed() < self.cache_ttl {
1048                    debug!(
1049                        "Cache hit for {} code ({} bytes)",
1050                        detected_language.as_str(),
1051                        code.len()
1052                    );
1053                    return Ok(cached_ast.clone());
1054                } else {
1055                    // Remove expired entry
1056                    cache.pop(&cache_key);
1057                }
1058            }
1059        }
1060
1061        // Get parser for the detected language
1062        let parser_ref =
1063            self.parsers
1064                .get(&detected_language)
1065                .ok_or_else(|| TreeError::UnsupportedLanguage {
1066                    language: detected_language.as_str().to_string(),
1067                })?;
1068
1069        // Parse the code using thread-safe parser
1070        let tree = parser_ref.parse(&code)?;
1071
1072        let parse_time = start_time.elapsed();
1073        let node_count = self.count_nodes(tree.root_node());
1074
1075        let parsed_ast = Arc::new(ParsedAst {
1076            tree,
1077            language: detected_language,
1078            source_code: code,
1079            parse_time,
1080            node_count,
1081        });
1082
1083        // Cache the result with timestamp
1084        {
1085            let mut cache = self.ast_cache.lock().map_err(|e| TreeError::CacheFailed {
1086                reason: format!("Cache lock failed: {}", e),
1087            })?;
1088            cache.put(cache_key, (parsed_ast.clone(), Instant::now()));
1089        }
1090
1091        debug!(
1092            "Parsed {} code in {:?} ({} nodes)",
1093            detected_language.as_str(),
1094            parse_time,
1095            node_count
1096        );
1097
1098        Ok(parsed_ast)
1099    }
1100
1101    /// Query AST with tree-sitter query patterns
1102    pub async fn query(
1103        &self,
1104        code: String,
1105        language: Option<SupportedLanguage>,
1106        pattern: String,
1107        file_path: Option<PathBuf>,
1108    ) -> TreeResult<Vec<QueryMatch>> {
1109        let start_time = Instant::now();
1110
1111        // Parse the code first
1112        let parsed_ast = self.parse(code, language, file_path).await?;
1113
1114        // Get or compile the query
1115        let query = self.get_or_compile_query(&pattern, parsed_ast.language)?;
1116
1117        // Execute the query
1118        let mut cursor = QueryCursor::new();
1119        let mut results = Vec::new();
1120
1121        // Iterate over matches manually - QueryMatches doesn't implement Iterator
1122        let mut matches_iter = cursor.matches(
1123            &query,
1124            parsed_ast.root_node(),
1125            parsed_ast.source_code.as_bytes(),
1126        );
1127
1128        // Process matches directly from the streaming iterator
1129        loop {
1130            matches_iter.advance();
1131            let Some(query_match) = matches_iter.get() else {
1132                break;
1133            };
1134            let start_byte = query_match
1135                .captures
1136                .iter()
1137                .map(|c| c.node.start_byte())
1138                .min()
1139                .unwrap_or(0);
1140            let end_byte = query_match
1141                .captures
1142                .iter()
1143                .map(|c| c.node.end_byte())
1144                .max()
1145                .unwrap_or(0);
1146            let start_point = query_match
1147                .captures
1148                .iter()
1149                .map(|c| c.node.start_position())
1150                .min()
1151                .unwrap_or_default();
1152            let end_point = query_match
1153                .captures
1154                .iter()
1155                .map(|c| c.node.end_position())
1156                .max()
1157                .unwrap_or_default();
1158
1159            let captures: Vec<QueryCapture> = query_match
1160                .captures
1161                .iter()
1162                .map(|capture| {
1163                    let node = capture.node;
1164                    let capture_name = query.capture_names()[capture.index as usize].to_string();
1165                    let text = node
1166                        .utf8_text(parsed_ast.source_code.as_bytes())
1167                        .unwrap_or("<invalid utf8>")
1168                        .to_string();
1169
1170                    QueryCapture {
1171                        name: capture_name,
1172                        text,
1173                        start_byte: node.start_byte() as u32,
1174                        end_byte: node.end_byte() as u32,
1175                        start_point: node.start_position().into(),
1176                        end_point: node.end_position().into(),
1177                    }
1178                })
1179                .collect();
1180
1181            results.push(QueryMatch {
1182                pattern_index: query_match.pattern_index as u32,
1183                captures,
1184                start_byte: start_byte as u32,
1185                end_byte: end_byte as u32,
1186                start_point: start_point.into(),
1187                end_point: end_point.into(),
1188            });
1189        }
1190
1191        let query_time = start_time.elapsed();
1192        debug!(
1193            "Query executed in {:?}, found {} matches",
1194            query_time,
1195            results.len()
1196        );
1197
1198        Ok(results)
1199    }
1200
1201    /// Extract symbols from parsed AST
1202    pub async fn get_symbols(
1203        &self,
1204        code: String,
1205        language: Option<SupportedLanguage>,
1206        file_path: Option<PathBuf>,
1207    ) -> TreeResult<Vec<Symbol>> {
1208        let start_time = Instant::now();
1209
1210        // Parse the code first
1211        let parsed_ast = self.parse(code, language, file_path).await?;
1212
1213        // Use language-specific symbol extraction
1214        let symbols = match parsed_ast.language {
1215            SupportedLanguage::Rust => self.extract_rust_symbols(&parsed_ast)?,
1216            SupportedLanguage::Python => self.extract_python_symbols(&parsed_ast)?,
1217            SupportedLanguage::JavaScript | SupportedLanguage::TypeScript => {
1218                self.extract_js_symbols(&parsed_ast)?
1219            }
1220            SupportedLanguage::Go => self.extract_go_symbols(&parsed_ast)?,
1221            SupportedLanguage::Java => self.extract_java_symbols(&parsed_ast)?,
1222            SupportedLanguage::C | SupportedLanguage::Cpp => self.extract_c_symbols(&parsed_ast)?,
1223            SupportedLanguage::CSharp => self.extract_c_symbols(&parsed_ast)?, // Use C extraction for now
1224            SupportedLanguage::Bash => self.extract_bash_symbols(&parsed_ast)?,
1225            // For now, return empty vectors for unsupported languages
1226            SupportedLanguage::Html
1227            | SupportedLanguage::Css
1228            | SupportedLanguage::Json
1229            | SupportedLanguage::Yaml
1230            | SupportedLanguage::Toml
1231            | SupportedLanguage::Ruby
1232            | SupportedLanguage::Php
1233            | SupportedLanguage::Lua
1234            | SupportedLanguage::Haskell
1235            | SupportedLanguage::Elixir
1236            | SupportedLanguage::Scala
1237            | SupportedLanguage::Ocaml
1238            | SupportedLanguage::Clojure
1239            | SupportedLanguage::Zig
1240            | SupportedLanguage::Swift
1241            | SupportedLanguage::Kotlin
1242            | SupportedLanguage::ObjectiveC
1243            | SupportedLanguage::Dockerfile
1244            | SupportedLanguage::Hcl
1245            | SupportedLanguage::Nix
1246            | SupportedLanguage::Make
1247            | SupportedLanguage::Markdown
1248            // | SupportedLanguage::Latex // Disabled
1249            | SupportedLanguage::Rst => {
1250                // TODO: Implement specific symbol extraction for these languages
1251                vec![]
1252            }
1253        };
1254
1255        let extraction_time = start_time.elapsed();
1256        debug!(
1257            "Extracted {} symbols in {:?}",
1258            symbols.len(),
1259            extraction_time
1260        );
1261
1262        Ok(symbols)
1263    }
1264
1265    /// Compare two pieces of code semantically
1266    pub async fn diff(
1267        &self,
1268        old_code: String,
1269        new_code: String,
1270        language: Option<SupportedLanguage>,
1271    ) -> TreeResult<SemanticDiff> {
1272        let start_time = Instant::now();
1273
1274        // Parse both pieces of code
1275        let old_ast = self.parse(old_code, language, None).await?;
1276        let new_ast = self.parse(new_code, language, None).await?;
1277
1278        // Ensure same language
1279        if old_ast.language != new_ast.language {
1280            return Err(TreeError::DiffFailed {
1281                reason: format!(
1282                    "Language mismatch: {} vs {}",
1283                    old_ast.language.as_str(),
1284                    new_ast.language.as_str()
1285                ),
1286            });
1287        }
1288
1289        // Compute semantic diff
1290        let diff = self.compute_semantic_diff(&old_ast, &new_ast)?;
1291
1292        let diff_time = start_time.elapsed();
1293        debug!("Computed diff in {:?}", diff_time);
1294
1295        Ok(diff)
1296    }
1297
1298    /// Parse file and return AST
1299    pub async fn parse_file(&self, file_path: PathBuf) -> TreeResult<Arc<ParsedAst>> {
1300        let code = fs::read_to_string(&file_path)
1301            .await
1302            .map_err(|e| TreeError::FileReadFailed {
1303                path: file_path.clone(),
1304                reason: e.to_string(),
1305            })?;
1306
1307        self.parse(code, None, Some(file_path)).await
1308    }
1309
1310    /// Query file with pattern
1311    pub async fn query_file(
1312        &self,
1313        file_path: PathBuf,
1314        pattern: String,
1315    ) -> TreeResult<Vec<QueryMatch>> {
1316        let code = fs::read_to_string(&file_path)
1317            .await
1318            .map_err(|e| TreeError::FileReadFailed {
1319                path: file_path.clone(),
1320                reason: e.to_string(),
1321            })?;
1322
1323        self.query(code, None, pattern, Some(file_path)).await
1324    }
1325
1326    /// Extract symbols from file
1327    pub async fn extract_symbols_from_file(&self, file_path: PathBuf) -> TreeResult<Vec<Symbol>> {
1328        let code = fs::read_to_string(&file_path)
1329            .await
1330            .map_err(|e| TreeError::FileReadFailed {
1331                path: file_path.clone(),
1332                reason: e.to_string(),
1333            })?;
1334
1335        self.get_symbols(code, None, Some(file_path)).await
1336    }
1337
1338    /// Compare two files
1339    pub async fn diff_files(
1340        &self,
1341        old_file: PathBuf,
1342        new_file: PathBuf,
1343    ) -> TreeResult<SemanticDiff> {
1344        let old_code =
1345            fs::read_to_string(&old_file)
1346                .await
1347                .map_err(|e| TreeError::FileReadFailed {
1348                    path: old_file,
1349                    reason: e.to_string(),
1350                })?;
1351
1352        let new_code =
1353            fs::read_to_string(&new_file)
1354                .await
1355                .map_err(|e| TreeError::FileReadFailed {
1356                    path: new_file,
1357                    reason: e.to_string(),
1358                })?;
1359
1360        self.diff(old_code, new_code, None).await
1361    }
1362
1363    /// Detect language from file path
1364    fn detect_language(&self, path: &Path) -> TreeResult<SupportedLanguage> {
1365        let extension = path
1366            .extension()
1367            .and_then(|ext| ext.to_str())
1368            .ok_or_else(|| TreeError::LanguageDetectionFailed {
1369                path: path.to_path_buf(),
1370            })?;
1371
1372        SupportedLanguage::from_extension(extension).ok_or_else(|| TreeError::UnsupportedLanguage {
1373            language: extension.to_string(),
1374        })
1375    }
1376
1377    /// Get or compile a query for the specified language
1378    fn get_or_compile_query(
1379        &self,
1380        pattern: &str,
1381        language: SupportedLanguage,
1382    ) -> TreeResult<Query> {
1383        let _cache_key = format!("{}:{}", language.as_str(), pattern);
1384
1385        // Compile the query - we can't cache Query objects as they don't implement Clone
1386        let query = Query::new(&language.grammar(), pattern).map_err(|e| {
1387            TreeError::QueryCompilationFailed {
1388                query: pattern.to_string(),
1389                reason: e.to_string(),
1390            }
1391        })?;
1392
1393        debug!("Compiled query: {}", pattern);
1394
1395        Ok(query)
1396    }
1397
1398    /// Generate hash for content (simple but effective)
1399    fn hash_content(&self, content: &str) -> u64 {
1400        use std::collections::hash_map::DefaultHasher;
1401        use std::hash::Hash;
1402        use std::hash::Hasher;
1403
1404        let mut hasher = DefaultHasher::new();
1405        content.hash(&mut hasher);
1406        hasher.finish()
1407    }
1408
1409    /// Count total nodes in AST
1410    fn count_nodes(&self, node: Node) -> usize {
1411        let mut count = 1; // Count this node
1412        for i in 0..node.child_count() {
1413            if let Some(child) = node.child(i) {
1414                count += self.count_nodes(child);
1415            }
1416        }
1417        count
1418    }
1419
1420    /// Extract symbols for Rust code
1421    const fn extract_rust_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1422        // This would be implemented with Rust-specific query patterns
1423        // For now, return a basic implementation
1424        Ok(vec![])
1425    }
1426
1427    /// Extract symbols for Python code
1428    const fn extract_python_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1429        // Python-specific symbol extraction
1430        Ok(vec![])
1431    }
1432
1433    /// Extract symbols for JavaScript/TypeScript code
1434    const fn extract_js_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1435        // JavaScript/TypeScript-specific symbol extraction
1436        Ok(vec![])
1437    }
1438
1439    /// Extract symbols for Go code
1440    const fn extract_go_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1441        // Go-specific symbol extraction
1442        Ok(vec![])
1443    }
1444
1445    /// Extract symbols for Java code
1446    const fn extract_java_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1447        // Java-specific symbol extraction
1448        Ok(vec![])
1449    }
1450
1451    /// Extract symbols for C/C++ code
1452    const fn extract_c_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1453        // C/C++-specific symbol extraction
1454        Ok(vec![])
1455    }
1456
1457    /// Extract symbols for Bash code
1458    const fn extract_bash_symbols(&self, _ast: &ParsedAst) -> TreeResult<Vec<Symbol>> {
1459        // Bash-specific symbol extraction
1460        Ok(vec![])
1461    }
1462
1463    /// Compute semantic diff using enhanced diff engine
1464    fn compute_semantic_diff(
1465        &self,
1466        old_ast: &ParsedAst,
1467        new_ast: &ParsedAst,
1468    ) -> TreeResult<SemanticDiff> {
1469        SemanticDiffEngine::compute_diff(old_ast, new_ast)
1470    }
1471
1472    /// Get cache statistics
1473    pub fn cache_stats(&self) -> Result<HashMap<String, serde_json::Value>, TreeError> {
1474        let mut stats = HashMap::new();
1475
1476        let ast_cache_size = self
1477            .ast_cache
1478            .lock()
1479            .map_err(|e| TreeError::CacheFailed {
1480                reason: format!("Cache lock failed: {}", e),
1481            })?
1482            .len();
1483
1484        stats.insert(
1485            "ast_cache_size".to_string(),
1486            serde_json::Value::Number(ast_cache_size.into()),
1487        );
1488
1489        stats.insert(
1490            "max_cache_size".to_string(),
1491            serde_json::Value::Number(self.max_cache_size.get().into()),
1492        );
1493
1494        Ok(stats)
1495    }
1496
1497    /// Clear all caches
1498    pub fn clear_caches(&self) -> TreeResult<()> {
1499        let mut ast_cache = self.ast_cache.lock().map_err(|e| TreeError::CacheFailed {
1500            reason: format!("Cache lock failed: {}", e),
1501        })?;
1502        ast_cache.clear();
1503
1504        info!("All caches cleared");
1505        Ok(())
1506    }
1507}
1508
1509#[async_trait::async_trait]
1510impl InternalTool for TreeTool {
1511    type Input = TreeInput;
1512    type Output = ToolOutput<TreeOutput>;
1513    type Error = TreeError;
1514
1515    async fn execute(&self, input: Self::Input) -> Result<Self::Output, Self::Error> {
1516        let start_time = Instant::now();
1517
1518        let (result, operation) = match input {
1519            TreeInput::Parse {
1520                code,
1521                language,
1522                file_path,
1523            } => {
1524                let ast = self.parse(code, language, file_path).await?;
1525                let output = TreeOutput::Parsed {
1526                    language: ast.language,
1527                    node_count: ast.node_count,
1528                    has_errors: ast.has_errors(),
1529                    error_count: ast.error_nodes().len(),
1530                    parse_time_ms: ast.parse_time.as_millis() as u64,
1531                };
1532                (output, "parse")
1533            }
1534            TreeInput::ParseFile { file_path } => {
1535                let ast = self.parse_file(file_path).await?;
1536                let output = TreeOutput::Parsed {
1537                    language: ast.language,
1538                    node_count: ast.node_count,
1539                    has_errors: ast.has_errors(),
1540                    error_count: ast.error_nodes().len(),
1541                    parse_time_ms: ast.parse_time.as_millis() as u64,
1542                };
1543                (output, "parse_file")
1544            }
1545            TreeInput::Query {
1546                code,
1547                language,
1548                pattern,
1549                file_path,
1550            } => {
1551                let matches = self.query(code, language, pattern, file_path).await?;
1552                let query_time = start_time.elapsed();
1553                let output = TreeOutput::QueryResults {
1554                    total_matches: matches.len(),
1555                    matches,
1556                    query_time_ms: query_time.as_millis() as u64,
1557                };
1558                (output, "query")
1559            }
1560            TreeInput::QueryFile { file_path, pattern } => {
1561                let matches = self.query_file(file_path, pattern).await?;
1562                let query_time = start_time.elapsed();
1563                let output = TreeOutput::QueryResults {
1564                    total_matches: matches.len(),
1565                    matches,
1566                    query_time_ms: query_time.as_millis() as u64,
1567                };
1568                (output, "query_file")
1569            }
1570            TreeInput::ExtractSymbols {
1571                code,
1572                language,
1573                file_path,
1574            } => {
1575                let symbols = self.get_symbols(code, language, file_path).await?;
1576                let extraction_time = start_time.elapsed();
1577                let output = TreeOutput::Symbols {
1578                    total_symbols: symbols.len(),
1579                    symbols,
1580                    extraction_time_ms: extraction_time.as_millis() as u64,
1581                };
1582                (output, "extract_symbols")
1583            }
1584            TreeInput::ExtractSymbolsFromFile { file_path } => {
1585                let symbols = self.extract_symbols_from_file(file_path).await?;
1586                let extraction_time = start_time.elapsed();
1587                let output = TreeOutput::Symbols {
1588                    total_symbols: symbols.len(),
1589                    symbols,
1590                    extraction_time_ms: extraction_time.as_millis() as u64,
1591                };
1592                (output, "extract_symbols_from_file")
1593            }
1594            TreeInput::Diff {
1595                old_code,
1596                new_code,
1597                language,
1598            } => {
1599                let diff = self.diff(old_code, new_code, language).await?;
1600                let diff_time = start_time.elapsed();
1601                let output = TreeOutput::Diff {
1602                    diff,
1603                    diff_time_ms: diff_time.as_millis() as u64,
1604                };
1605                (output, "diff")
1606            }
1607            TreeInput::DiffFiles { old_file, new_file } => {
1608                let diff = self.diff_files(old_file, new_file).await?;
1609                let diff_time = start_time.elapsed();
1610                let output = TreeOutput::Diff {
1611                    diff,
1612                    diff_time_ms: diff_time.as_millis() as u64,
1613                };
1614                (output, "diff_files")
1615            }
1616        };
1617
1618        let execution_time = start_time.elapsed();
1619        let cache_stats = self.cache_stats().unwrap_or_default();
1620
1621        // Create a minimal location for the result
1622        let location = SourceLocation {
1623            file_path: "<tree-tool>".to_string(),
1624            start_line: 0,
1625            start_column: 0,
1626            end_line: 0,
1627            end_column: 0,
1628            byte_range: (0, 0),
1629        };
1630
1631        let mut output = ToolOutput::new(
1632            result,
1633            "tree",
1634            format!("Tree operation: {}", operation),
1635            location,
1636        );
1637
1638        // Add metadata
1639        output.metadata.parameters.insert(
1640            "execution_time_ms".to_string(),
1641            (execution_time.as_millis() as u64).to_string(),
1642        );
1643        output
1644            .metadata
1645            .parameters
1646            .insert("cache_stats".to_string(), format!("{:?}", cache_stats));
1647        output.metadata.parameters.insert(
1648            "intelligence_level".to_string(),
1649            match self.intelligence_level {
1650                IntelligenceLevel::Light => "light".to_string(),
1651                IntelligenceLevel::Medium => "medium".to_string(),
1652                IntelligenceLevel::Hard => "hard".to_string(),
1653            },
1654        );
1655
1656        Ok(output)
1657    }
1658
1659    fn metadata(&self) -> ToolMetadata {
1660        ToolMetadata {
1661            name: "TreeTool".to_string(),
1662            description: "Multi-language AST parsing and querying with tree-sitter".to_string(),
1663            version: "1.0.0".to_string(),
1664            author: "AGCodex".to_string(),
1665        }
1666    }
1667}
1668
1669#[cfg(test)]
1670mod tests {
1671    use super::*;
1672
1673    #[tokio::test]
1674    async fn test_rust_parsing() {
1675        let tool = TreeTool::new(IntelligenceLevel::Medium).expect("Failed to create TreeTool");
1676        let rust_code = r#"
1677            fn hello_world() -> String {
1678                "Hello, world!".to_string()
1679            }
1680            
1681            struct Person {
1682                name: String,
1683                age: u32,
1684            }
1685            
1686            impl Person {
1687                fn new(name: String, age: u32) -> Self {
1688                    Self { name, age }
1689                }
1690            }
1691        "#;
1692
1693        let result = tool
1694            .parse(rust_code.to_string(), Some(SupportedLanguage::Rust), None)
1695            .await;
1696        assert!(
1697            result.is_ok(),
1698            "Failed to parse Rust code: {:?}",
1699            result.err()
1700        );
1701
1702        let ast = result.unwrap();
1703        assert_eq!(ast.language, SupportedLanguage::Rust);
1704        assert!(!ast.has_errors(), "AST has parse errors");
1705        assert!(
1706            ast.node_count > 10,
1707            "Expected more than 10 nodes, got {}",
1708            ast.node_count
1709        );
1710
1711        // Verify the source code is preserved
1712        assert_eq!(ast.source_code, rust_code);
1713
1714        // Test parsing with errors
1715        let invalid_code = "fn broken { invalid syntax }";
1716        let invalid_result = tool
1717            .parse(
1718                invalid_code.to_string(),
1719                Some(SupportedLanguage::Rust),
1720                None,
1721            )
1722            .await;
1723        assert!(
1724            invalid_result.is_ok(),
1725            "Should parse even with syntax errors"
1726        );
1727        let invalid_ast = invalid_result.unwrap();
1728        assert!(invalid_ast.has_errors(), "Should detect syntax errors");
1729    }
1730
1731    #[tokio::test]
1732    async fn test_language_detection() {
1733        let tool = TreeTool::new(IntelligenceLevel::Light).expect("Failed to create TreeTool");
1734
1735        // Test common extensions
1736        assert_eq!(
1737            tool.detect_language(Path::new("test.rs")).unwrap(),
1738            SupportedLanguage::Rust
1739        );
1740        assert_eq!(
1741            tool.detect_language(Path::new("test.py")).unwrap(),
1742            SupportedLanguage::Python
1743        );
1744        assert_eq!(
1745            tool.detect_language(Path::new("test.js")).unwrap(),
1746            SupportedLanguage::JavaScript
1747        );
1748        assert_eq!(
1749            tool.detect_language(Path::new("test.ts")).unwrap(),
1750            SupportedLanguage::TypeScript
1751        );
1752        assert_eq!(
1753            tool.detect_language(Path::new("test.go")).unwrap(),
1754            SupportedLanguage::Go
1755        );
1756        assert_eq!(
1757            tool.detect_language(Path::new("test.java")).unwrap(),
1758            SupportedLanguage::Java
1759        );
1760
1761        // Test case-insensitive extension matching via from_extension
1762        assert_eq!(
1763            SupportedLanguage::from_extension("RS"),
1764            Some(SupportedLanguage::Rust)
1765        );
1766        assert_eq!(
1767            SupportedLanguage::from_extension("PY"),
1768            Some(SupportedLanguage::Python)
1769        );
1770
1771        // Test unsupported extension
1772        assert!(tool.detect_language(Path::new("test.unknown")).is_err());
1773
1774        // Test file without extension
1775        assert!(tool.detect_language(Path::new("Makefile")).is_err());
1776
1777        // Note: LaTeX is disabled, so .tex files should not be recognized
1778        assert!(tool.detect_language(Path::new("test.tex")).is_err());
1779    }
1780
1781    #[tokio::test]
1782    async fn test_query_functionality() {
1783        let tool = TreeTool::new(IntelligenceLevel::Medium).expect("Failed to create TreeTool");
1784        let rust_code = r#"
1785            fn main() {
1786                println!("Hello, world!");
1787            }
1788            
1789            fn helper() {
1790                println!("Helper function");
1791            }
1792            
1793            pub fn public_func(x: i32) -> i32 {
1794                x * 2
1795            }
1796        "#;
1797
1798        // Query for all function definitions
1799        let query_pattern = "(function_item name: (identifier) @func_name)";
1800        let result = tool
1801            .query(
1802                rust_code.to_string(),
1803                Some(SupportedLanguage::Rust),
1804                query_pattern.to_string(),
1805                None,
1806            )
1807            .await;
1808
1809        assert!(result.is_ok(), "Query failed: {:?}", result.err());
1810        let matches = result.unwrap();
1811        assert_eq!(
1812            matches.len(),
1813            3,
1814            "Should find 3 functions (main, helper, public_func)"
1815        );
1816
1817        // Verify captures are populated correctly
1818        for match_result in &matches {
1819            assert!(
1820                !match_result.captures.is_empty(),
1821                "Each match should have captures"
1822            );
1823            for capture in &match_result.captures {
1824                assert_eq!(
1825                    capture.name, "func_name",
1826                    "Capture name should be 'func_name'"
1827                );
1828                assert!(!capture.text.is_empty(), "Capture text should not be empty");
1829            }
1830        }
1831
1832        // Extract function names from captures
1833        let func_names: Vec<String> = matches
1834            .iter()
1835            .flat_map(|m| m.captures.iter())
1836            .map(|c| c.text.clone())
1837            .collect();
1838
1839        assert!(
1840            func_names.contains(&"main".to_string()),
1841            "Should find 'main' function"
1842        );
1843        assert!(
1844            func_names.contains(&"helper".to_string()),
1845            "Should find 'helper' function"
1846        );
1847        assert!(
1848            func_names.contains(&"public_func".to_string()),
1849            "Should find 'public_func' function"
1850        );
1851
1852        // Test invalid query pattern
1853        let invalid_query = "(invalid syntax @broken";
1854        let invalid_result = tool
1855            .query(
1856                rust_code.to_string(),
1857                Some(SupportedLanguage::Rust),
1858                invalid_query.to_string(),
1859                None,
1860            )
1861            .await;
1862        assert!(invalid_result.is_err(), "Invalid query should fail");
1863    }
1864
1865    #[tokio::test]
1866    async fn test_cache_functionality() {
1867        let tool = TreeTool::new(IntelligenceLevel::Light).expect("Failed to create TreeTool");
1868        let code = "fn test() { println!(\"Testing cache\"); }".to_string();
1869
1870        // Parse twice - second should be cached
1871        let result1 = tool
1872            .parse(code.clone(), Some(SupportedLanguage::Rust), None)
1873            .await
1874            .expect("First parse failed");
1875
1876        let result2 = tool
1877            .parse(code.clone(), Some(SupportedLanguage::Rust), None)
1878            .await
1879            .expect("Second parse failed");
1880
1881        // Both results should be identical (same Arc pointer)
1882        assert!(
1883            Arc::ptr_eq(&result1, &result2),
1884            "Results should be the same cached instance"
1885        );
1886
1887        let stats = tool.cache_stats().expect("Failed to get cache stats");
1888        let cache_size = stats
1889            .get("ast_cache_size")
1890            .expect("Missing ast_cache_size")
1891            .as_u64()
1892            .expect("ast_cache_size should be a number") as usize;
1893        assert_eq!(cache_size, 1, "Should have exactly 1 cached entry");
1894
1895        // Parse different code - should create new cache entry
1896        let different_code = "fn another() { let x = 42; }".to_string();
1897        let _result3 = tool
1898            .parse(different_code, Some(SupportedLanguage::Rust), None)
1899            .await
1900            .expect("Third parse failed");
1901
1902        let updated_stats = tool
1903            .cache_stats()
1904            .expect("Failed to get updated cache stats");
1905        let updated_cache_size = updated_stats
1906            .get("ast_cache_size")
1907            .expect("Missing ast_cache_size")
1908            .as_u64()
1909            .expect("ast_cache_size should be a number") as usize;
1910        assert_eq!(
1911            updated_cache_size, 2,
1912            "Should have 2 cached entries after parsing different code"
1913        );
1914
1915        // Clear cache and verify it's empty
1916        tool.clear_caches().expect("Failed to clear caches");
1917        let cleared_stats = tool
1918            .cache_stats()
1919            .expect("Failed to get cleared cache stats");
1920        let cleared_cache_size = cleared_stats
1921            .get("ast_cache_size")
1922            .expect("Missing ast_cache_size")
1923            .as_u64()
1924            .expect("ast_cache_size should be a number") as usize;
1925        assert_eq!(
1926            cleared_cache_size, 0,
1927            "Cache should be empty after clearing"
1928        );
1929    }
1930
1931    #[tokio::test]
1932    async fn test_diff_functionality() {
1933        let tool = TreeTool::new(IntelligenceLevel::Medium).expect("Failed to create TreeTool");
1934
1935        let old_code = r#"
1936            fn calculate(x: i32) -> i32 {
1937                x * 2
1938            }
1939        "#
1940        .to_string();
1941
1942        let new_code = r#"
1943            fn calculate(x: i32, y: i32) -> i32 {
1944                x * y
1945            }
1946            
1947            fn helper() -> String {
1948                "Helper".to_string()
1949            }
1950        "#
1951        .to_string();
1952
1953        let diff_result = tool
1954            .diff(old_code, new_code, Some(SupportedLanguage::Rust))
1955            .await;
1956
1957        assert!(
1958            diff_result.is_ok(),
1959            "Diff computation failed: {:?}",
1960            diff_result.err()
1961        );
1962        let diff = diff_result.unwrap();
1963
1964        // Check that we detected changes
1965        assert!(
1966            !diff.added.is_empty() || !diff.modified.is_empty(),
1967            "Should detect additions or modifications"
1968        );
1969        assert!(
1970            diff.similarity_score >= 0.0 && diff.similarity_score <= 1.0,
1971            "Similarity score should be between 0 and 1"
1972        );
1973    }
1974
1975    #[tokio::test]
1976    async fn test_python_parsing() {
1977        let tool = TreeTool::new(IntelligenceLevel::Medium).expect("Failed to create TreeTool");
1978        let python_code = r#"
1979def greet(name):
1980    return f"Hello, {name}!"
1981
1982class Person:
1983    def __init__(self, name, age):
1984        self.name = name
1985        self.age = age
1986    
1987    def introduce(self):
1988        return f"I'm {self.name}, {self.age} years old"
1989        "#;
1990
1991        let result = tool
1992            .parse(
1993                python_code.to_string(),
1994                Some(SupportedLanguage::Python),
1995                None,
1996            )
1997            .await;
1998
1999        assert!(
2000            result.is_ok(),
2001            "Failed to parse Python code: {:?}",
2002            result.err()
2003        );
2004        let ast = result.unwrap();
2005        assert_eq!(ast.language, SupportedLanguage::Python);
2006        assert!(!ast.has_errors(), "Python AST has parse errors");
2007        assert!(
2008            ast.node_count > 10,
2009            "Expected substantial node count for Python code"
2010        );
2011    }
2012
2013    #[tokio::test]
2014    async fn test_javascript_parsing() {
2015        let tool = TreeTool::new(IntelligenceLevel::Medium).expect("Failed to create TreeTool");
2016        let js_code = r#"
2017function greet(name) {
2018    return `Hello, ${name}!`;
2019}
2020
2021const arrow = (x, y) => x + y;
2022
2023class Calculator {
2024    constructor() {
2025        this.result = 0;
2026    }
2027    
2028    add(value) {
2029        this.result += value;
2030        return this;
2031    }
2032}
2033        "#;
2034
2035        let result = tool
2036            .parse(
2037                js_code.to_string(),
2038                Some(SupportedLanguage::JavaScript),
2039                None,
2040            )
2041            .await;
2042
2043        assert!(
2044            result.is_ok(),
2045            "Failed to parse JavaScript code: {:?}",
2046            result.err()
2047        );
2048        let ast = result.unwrap();
2049        assert_eq!(ast.language, SupportedLanguage::JavaScript);
2050        assert!(!ast.has_errors(), "JavaScript AST has parse errors");
2051        assert!(
2052            ast.node_count > 10,
2053            "Expected substantial node count for JavaScript code"
2054        );
2055    }
2056}