scribe_analysis/language_support/
mod.rs

1//! # Multi-Language AST Analysis Support
2//!
3//! This module provides comprehensive language support for AST-based analysis,
4//! significantly expanding beyond the basic regex-based parsing. Implements
5//! proper tree-sitter AST parsing for 20+ programming languages.
6//!
7//! ## Supported Languages
8//!
9//! ### Tier 1: Full AST Support (Tree-sitter)
10//! - Python, JavaScript, TypeScript, Go, Rust (existing)
11//! - Java, C#, C, C++, PHP, Ruby, Swift, Kotlin (to be added)
12//!
13//! ### Tier 2: Syntax-Aware Support (Tree-sitter)
14//! - HTML, CSS, JSON, YAML, XML, Markdown
15//! - SQL, Bash, PowerShell, Dockerfile
16//!
17//! ### Tier 3: Basic Structure Analysis (Regex + Heuristics)
18//! - Scala, Dart, Perl, R, Julia, Elixir, Lua, Haskell
19//!
20//! ## Key Features
21//!
22//! - **Function/Class Extraction**: Identify all functions, classes, and methods
23//! - **Documentation Coverage**: Analyze docstrings, comments, and documentation
24//! - **Language-Specific Complexity**: Tailored complexity metrics per language
25//! - **Import/Dependency Analysis**: Accurate dependency graph construction
26//! - **Symbol Resolution**: Variable, function, and class usage tracking
27
28pub mod ast_language;
29pub mod documentation_analysis;
30pub mod function_extraction;
31pub mod language_metrics;
32pub mod symbol_analysis;
33
34pub use ast_language::{AstLanguage, LanguageFeatures, LanguageTier};
35pub use documentation_analysis::{DocumentationAnalyzer, DocumentationCoverage};
36pub use function_extraction::{ClassInfo, FunctionExtractor, FunctionInfo};
37pub use language_metrics::{LanguageMetrics, LanguageSpecificComplexity};
38pub use symbol_analysis::{SymbolAnalyzer, SymbolType, SymbolUsage};
39
40use scribe_core::Result;
41use std::collections::HashMap;
42
43/// Main language support coordinator
44pub struct LanguageSupport {
45    /// Function extractors by language
46    function_extractors: HashMap<AstLanguage, FunctionExtractor>,
47    /// Documentation analyzers by language
48    doc_analyzers: HashMap<AstLanguage, DocumentationAnalyzer>,
49    /// Symbol analyzers by language
50    symbol_analyzers: HashMap<AstLanguage, SymbolAnalyzer>,
51}
52
53impl LanguageSupport {
54    /// Create a new language support system
55    pub fn new() -> Result<Self> {
56        let mut support = Self {
57            function_extractors: HashMap::new(),
58            doc_analyzers: HashMap::new(),
59            symbol_analyzers: HashMap::new(),
60        };
61
62        // Initialize extractors for all supported languages
63        support.initialize_extractors()?;
64
65        Ok(support)
66    }
67
68    /// Initialize extractors for all supported languages
69    fn initialize_extractors(&mut self) -> Result<()> {
70        for language in AstLanguage::all_supported() {
71            self.function_extractors
72                .insert(language, FunctionExtractor::new(language)?);
73            self.doc_analyzers
74                .insert(language, DocumentationAnalyzer::new(language)?);
75            self.symbol_analyzers
76                .insert(language, SymbolAnalyzer::new(language)?);
77        }
78        Ok(())
79    }
80
81    /// Get language features and capabilities
82    pub fn get_language_features(&self, language: AstLanguage) -> LanguageFeatures {
83        language.features()
84    }
85
86    /// Extract all functions and classes from source code
87    pub fn extract_functions(
88        &mut self,
89        content: &str,
90        language: AstLanguage,
91    ) -> Result<Vec<FunctionInfo>> {
92        if let Some(extractor) = self.function_extractors.get_mut(&language) {
93            extractor.extract_functions(content)
94        } else {
95            Ok(Vec::new())
96        }
97    }
98
99    /// Analyze documentation coverage
100    pub fn analyze_documentation(
101        &self,
102        content: &str,
103        language: AstLanguage,
104    ) -> Result<DocumentationCoverage> {
105        if let Some(analyzer) = self.doc_analyzers.get(&language) {
106            analyzer.analyze_coverage(content)
107        } else {
108            Ok(DocumentationCoverage::default())
109        }
110    }
111
112    /// Analyze symbol usage patterns
113    pub fn analyze_symbols(
114        &self,
115        content: &str,
116        language: AstLanguage,
117    ) -> Result<Vec<SymbolUsage>> {
118        if let Some(analyzer) = self.symbol_analyzers.get(&language) {
119            analyzer.analyze_symbols(content)
120        } else {
121            Ok(Vec::new())
122        }
123    }
124
125    /// Get language-specific complexity metrics
126    pub fn calculate_language_metrics(
127        &self,
128        content: &str,
129        language: AstLanguage,
130    ) -> Result<LanguageMetrics> {
131        LanguageMetrics::calculate(content, language)
132    }
133
134    /// Check if a language is supported
135    pub fn is_supported(&self, language: AstLanguage) -> bool {
136        self.function_extractors.contains_key(&language)
137    }
138
139    /// Get all supported languages
140    pub fn supported_languages(&self) -> Vec<AstLanguage> {
141        self.function_extractors.keys().copied().collect()
142    }
143}
144
145impl Default for LanguageSupport {
146    fn default() -> Self {
147        Self::new().expect("Failed to create LanguageSupport")
148    }
149}
150
151/// Comprehensive analysis result for a source file
152#[derive(Debug, Clone)]
153pub struct LanguageAnalysisResult {
154    /// Detected programming language
155    pub language: AstLanguage,
156    /// Language tier and capabilities
157    pub tier: LanguageTier,
158    /// Extracted functions and classes
159    pub functions: Vec<FunctionInfo>,
160    /// Documentation coverage analysis
161    pub documentation: DocumentationCoverage,
162    /// Symbol usage patterns
163    pub symbols: Vec<SymbolUsage>,
164    /// Language-specific metrics
165    pub metrics: LanguageMetrics,
166}
167
168/// Main entry point for language analysis
169pub fn analyze_file_language(
170    content: &str,
171    file_extension: &str,
172    language_support: &mut LanguageSupport,
173) -> Result<Option<LanguageAnalysisResult>> {
174    let language = match AstLanguage::from_extension(file_extension) {
175        Some(lang) => lang,
176        None => return Ok(None),
177    };
178
179    if !language_support.is_supported(language) {
180        return Ok(None);
181    }
182
183    let functions = language_support.extract_functions(content, language)?;
184    let documentation = language_support.analyze_documentation(content, language)?;
185    let symbols = language_support.analyze_symbols(content, language)?;
186    let metrics = language_support.calculate_language_metrics(content, language)?;
187
188    Ok(Some(LanguageAnalysisResult {
189        language,
190        tier: language.tier(),
191        functions,
192        documentation,
193        symbols,
194        metrics,
195    }))
196}
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn test_language_support_creation() {
204        let support = LanguageSupport::new();
205        if let Err(e) = &support {
206            println!("Error creating LanguageSupport: {:?}", e);
207        }
208        assert!(support.is_ok());
209    }
210
211    #[test]
212    fn test_supported_languages() {
213        let support = LanguageSupport::new().unwrap();
214        let languages = support.supported_languages();
215
216        // Should have at least the core languages
217        assert!(languages.contains(&AstLanguage::Python));
218        assert!(languages.contains(&AstLanguage::JavaScript));
219        assert!(languages.contains(&AstLanguage::Rust));
220    }
221
222    #[test]
223    fn test_python_function_extraction() {
224        let mut support = LanguageSupport::new().unwrap();
225        let python_code = r#"
226def hello_world():
227    """A simple function."""
228    print("Hello, World!")
229
230class Calculator:
231    """A simple calculator class."""
232    
233    def add(self, a, b):
234        """Add two numbers."""
235        return a + b
236"#;
237
238        let functions = support.extract_functions(python_code, AstLanguage::Python);
239        assert!(functions.is_ok());
240        let functions = functions.unwrap();
241        assert!(!functions.is_empty());
242    }
243
244    #[test]
245    fn test_language_analysis() {
246        let mut support = LanguageSupport::new().unwrap();
247        let result = analyze_file_language("def test(): pass", "py", &mut support);
248
249        assert!(result.is_ok());
250        let result = result.unwrap();
251        assert!(result.is_some());
252        assert_eq!(result.unwrap().language, AstLanguage::Python);
253    }
254}