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
27pub mod ast_language;
28pub mod documentation_analysis;
29pub mod function_extraction;
30pub mod language_metrics;
31
32pub use ast_language::{AstLanguage, LanguageFeatures, LanguageTier};
33pub use documentation_analysis::{DocumentationAnalyzer, DocumentationCoverage};
34pub use function_extraction::{ClassInfo, FunctionExtractor, FunctionInfo};
35pub use language_metrics::{LanguageMetrics, LanguageSpecificComplexity};
36
37use scribe_core::Result;
38use std::collections::HashMap;
39
40/// Main language support coordinator
41pub struct LanguageSupport {
42    /// Function extractors by language
43    function_extractors: HashMap<AstLanguage, FunctionExtractor>,
44    /// Documentation analyzers by language
45    doc_analyzers: HashMap<AstLanguage, DocumentationAnalyzer>,
46}
47
48impl LanguageSupport {
49    /// Create a new language support system
50    pub fn new() -> Result<Self> {
51        let mut support = Self {
52            function_extractors: HashMap::new(),
53            doc_analyzers: HashMap::new(),
54        };
55
56        // Initialize extractors for all supported languages
57        support.initialize_extractors()?;
58
59        Ok(support)
60    }
61
62    /// Initialize extractors for all supported languages
63    fn initialize_extractors(&mut self) -> Result<()> {
64        for language in AstLanguage::all_supported() {
65            self.function_extractors
66                .insert(language, FunctionExtractor::new(language)?);
67            self.doc_analyzers
68                .insert(language, DocumentationAnalyzer::new(language)?);
69        }
70        Ok(())
71    }
72
73    /// Get language features and capabilities
74    pub fn get_language_features(&self, language: AstLanguage) -> LanguageFeatures {
75        language.features()
76    }
77
78    /// Extract all functions and classes from source code
79    pub fn extract_functions(
80        &mut self,
81        content: &str,
82        language: AstLanguage,
83    ) -> Result<Vec<FunctionInfo>> {
84        if let Some(extractor) = self.function_extractors.get_mut(&language) {
85            extractor.extract_functions(content)
86        } else {
87            Ok(Vec::new())
88        }
89    }
90
91    /// Analyze documentation coverage
92    pub fn analyze_documentation(
93        &self,
94        content: &str,
95        language: AstLanguage,
96    ) -> Result<DocumentationCoverage> {
97        if let Some(analyzer) = self.doc_analyzers.get(&language) {
98            analyzer.analyze_coverage(content)
99        } else {
100            Ok(DocumentationCoverage::default())
101        }
102    }
103
104    /// Get language-specific complexity metrics
105    pub fn calculate_language_metrics(
106        &self,
107        content: &str,
108        language: AstLanguage,
109    ) -> Result<LanguageMetrics> {
110        LanguageMetrics::calculate(content, language)
111    }
112
113    /// Check if a language is supported
114    pub fn is_supported(&self, language: AstLanguage) -> bool {
115        self.function_extractors.contains_key(&language)
116    }
117
118    /// Get all supported languages
119    pub fn supported_languages(&self) -> Vec<AstLanguage> {
120        self.function_extractors.keys().copied().collect()
121    }
122}
123
124impl Default for LanguageSupport {
125    fn default() -> Self {
126        Self::new().expect("Failed to create LanguageSupport")
127    }
128}
129
130/// Comprehensive analysis result for a source file
131#[derive(Debug, Clone)]
132pub struct LanguageAnalysisResult {
133    /// Detected programming language
134    pub language: AstLanguage,
135    /// Language tier and capabilities
136    pub tier: LanguageTier,
137    /// Extracted functions and classes
138    pub functions: Vec<FunctionInfo>,
139    /// Documentation coverage analysis
140    pub documentation: DocumentationCoverage,
141    /// Language-specific metrics
142    pub metrics: LanguageMetrics,
143}
144
145/// Main entry point for language analysis
146pub fn analyze_file_language(
147    content: &str,
148    file_extension: &str,
149    language_support: &mut LanguageSupport,
150) -> Result<Option<LanguageAnalysisResult>> {
151    let language = match AstLanguage::from_extension(file_extension) {
152        Some(lang) => lang,
153        None => return Ok(None),
154    };
155
156    if !language_support.is_supported(language) {
157        return Ok(None);
158    }
159
160    let functions = language_support.extract_functions(content, language)?;
161    let documentation = language_support.analyze_documentation(content, language)?;
162    let metrics = language_support.calculate_language_metrics(content, language)?;
163
164    Ok(Some(LanguageAnalysisResult {
165        language,
166        tier: language.tier(),
167        functions,
168        documentation,
169        metrics,
170    }))
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    #[test]
178    fn test_language_support_creation() {
179        let support = LanguageSupport::new();
180        if let Err(e) = &support {
181            println!("Error creating LanguageSupport: {:?}", e);
182        }
183        assert!(support.is_ok());
184    }
185
186    #[test]
187    fn test_supported_languages() {
188        let support = LanguageSupport::new().unwrap();
189        let languages = support.supported_languages();
190
191        // Should have at least the core languages
192        assert!(languages.contains(&AstLanguage::Python));
193        assert!(languages.contains(&AstLanguage::JavaScript));
194        assert!(languages.contains(&AstLanguage::Rust));
195    }
196
197    #[test]
198    fn test_python_function_extraction() {
199        let mut support = LanguageSupport::new().unwrap();
200        let python_code = r#"
201        def hello_world():
202            """A simple function."""
203            print("Hello, World!")
204
205        class Calculator:
206            """A simple calculator class."""
207            
208            def add(self, a, b):
209                """Add two numbers."""
210                return a + b
211        "#;
212
213        let functions = support.extract_functions(python_code, AstLanguage::Python);
214        assert!(functions.is_ok());
215        let functions = functions.unwrap();
216        assert!(!functions.is_empty());
217    }
218
219    #[test]
220    fn test_language_analysis() {
221        let mut support = LanguageSupport::new().unwrap();
222        let result = analyze_file_language("def test(): pass", "py", &mut support);
223
224        assert!(result.is_ok());
225        let result = result.unwrap();
226        assert!(result.is_some());
227        assert_eq!(result.unwrap().language, AstLanguage::Python);
228    }
229}