ricecoder_lsp/
providers.rs

1//! Provider traits for pluggable language-specific implementations
2//!
3//! This module defines traits for pluggable providers that enable
4//! language-agnostic architecture with configuration-driven behavior.
5
6use crate::config::LanguageConfig;
7use crate::types::{Diagnostic, Position, SemanticInfo, Symbol};
8
9/// Error type for provider operations
10#[derive(Debug, Clone, thiserror::Error)]
11pub enum ProviderError {
12    /// Provider not found
13    #[error("Provider not found: {0}")]
14    NotFound(String),
15
16    /// Provider error
17    #[error("Provider error: {0}")]
18    Error(String),
19
20    /// Unsupported operation
21    #[error("Unsupported operation: {0}")]
22    UnsupportedOperation(String),
23}
24
25/// Result type for provider operations
26pub type ProviderResult<T> = Result<T, ProviderError>;
27
28/// Trait for language-specific semantic analysis providers
29pub trait SemanticAnalyzerProvider: Send + Sync {
30    /// Get the language identifier this provider handles
31    fn language(&self) -> &str;
32
33    /// Analyze code and extract semantic information
34    fn analyze(&self, code: &str) -> ProviderResult<SemanticInfo>;
35
36    /// Extract symbols from code
37    fn extract_symbols(&self, code: &str) -> ProviderResult<Vec<Symbol>>;
38
39    /// Get hover information at a specific position
40    fn get_hover_info(&self, code: &str, position: Position) -> ProviderResult<Option<String>>;
41}
42
43/// Trait for language-specific diagnostic rule providers
44pub trait DiagnosticsProvider: Send + Sync {
45    /// Get the language identifier this provider handles
46    fn language(&self) -> &str;
47
48    /// Generate diagnostics for the given code
49    fn generate_diagnostics(&self, code: &str) -> ProviderResult<Vec<Diagnostic>>;
50
51    /// Get the configuration for this provider
52    fn config(&self) -> Option<&LanguageConfig>;
53}
54
55/// Trait for language-specific code action providers
56pub trait CodeActionProvider: Send + Sync {
57    /// Get the language identifier this provider handles
58    fn language(&self) -> &str;
59
60    /// Suggest code actions for a diagnostic
61    fn suggest_actions(&self, diagnostic: &Diagnostic, code: &str) -> ProviderResult<Vec<String>>;
62
63    /// Apply a code action to code
64    fn apply_action(&self, code: &str, action: &str) -> ProviderResult<String>;
65
66    /// Get the configuration for this provider
67    fn config(&self) -> Option<&LanguageConfig>;
68}
69
70/// Registry for semantic analyzer providers
71pub struct SemanticAnalyzerRegistry {
72    providers: std::collections::HashMap<String, Box<dyn SemanticAnalyzerProvider>>,
73}
74
75impl SemanticAnalyzerRegistry {
76    /// Create a new semantic analyzer registry
77    pub fn new() -> Self {
78        Self {
79            providers: std::collections::HashMap::new(),
80        }
81    }
82
83    /// Register a semantic analyzer provider
84    pub fn register(&mut self, provider: Box<dyn SemanticAnalyzerProvider>) {
85        let language = provider.language().to_string();
86        self.providers.insert(language, provider);
87    }
88
89    /// Get a semantic analyzer provider by language
90    pub fn get(&self, language: &str) -> Option<&dyn SemanticAnalyzerProvider> {
91        self.providers.get(language).map(|p| p.as_ref())
92    }
93
94    /// Check if a provider is registered for a language
95    pub fn has_provider(&self, language: &str) -> bool {
96        self.providers.contains_key(language)
97    }
98
99    /// List all registered languages
100    pub fn languages(&self) -> Vec<&str> {
101        self.providers.keys().map(|s| s.as_str()).collect()
102    }
103}
104
105impl Default for SemanticAnalyzerRegistry {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111/// Registry for diagnostics providers
112pub struct DiagnosticsRegistry {
113    providers: std::collections::HashMap<String, Box<dyn DiagnosticsProvider>>,
114}
115
116impl DiagnosticsRegistry {
117    /// Create a new diagnostics registry
118    pub fn new() -> Self {
119        Self {
120            providers: std::collections::HashMap::new(),
121        }
122    }
123
124    /// Register a diagnostics provider
125    pub fn register(&mut self, provider: Box<dyn DiagnosticsProvider>) {
126        let language = provider.language().to_string();
127        self.providers.insert(language, provider);
128    }
129
130    /// Get a diagnostics provider by language
131    pub fn get(&self, language: &str) -> Option<&dyn DiagnosticsProvider> {
132        self.providers.get(language).map(|p| p.as_ref())
133    }
134
135    /// Check if a provider is registered for a language
136    pub fn has_provider(&self, language: &str) -> bool {
137        self.providers.contains_key(language)
138    }
139
140    /// List all registered languages
141    pub fn languages(&self) -> Vec<&str> {
142        self.providers.keys().map(|s| s.as_str()).collect()
143    }
144}
145
146impl Default for DiagnosticsRegistry {
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152/// Registry for code action providers
153pub struct CodeActionRegistry {
154    providers: std::collections::HashMap<String, Box<dyn CodeActionProvider>>,
155}
156
157impl CodeActionRegistry {
158    /// Create a new code action registry
159    pub fn new() -> Self {
160        Self {
161            providers: std::collections::HashMap::new(),
162        }
163    }
164
165    /// Register a code action provider
166    pub fn register(&mut self, provider: Box<dyn CodeActionProvider>) {
167        let language = provider.language().to_string();
168        self.providers.insert(language, provider);
169    }
170
171    /// Get a code action provider by language
172    pub fn get(&self, language: &str) -> Option<&dyn CodeActionProvider> {
173        self.providers.get(language).map(|p| p.as_ref())
174    }
175
176    /// Check if a provider is registered for a language
177    pub fn has_provider(&self, language: &str) -> bool {
178        self.providers.contains_key(language)
179    }
180
181    /// List all registered languages
182    pub fn languages(&self) -> Vec<&str> {
183        self.providers.keys().map(|s| s.as_str()).collect()
184    }
185}
186
187impl Default for CodeActionRegistry {
188    fn default() -> Self {
189        Self::new()
190    }
191}
192
193#[cfg(test)]
194mod tests {
195    use super::*;
196
197    struct MockSemanticProvider;
198
199    impl SemanticAnalyzerProvider for MockSemanticProvider {
200        fn language(&self) -> &str {
201            "mock"
202        }
203
204        fn analyze(&self, _code: &str) -> ProviderResult<SemanticInfo> {
205            Ok(SemanticInfo {
206                symbols: vec![],
207                imports: vec![],
208                definitions: vec![],
209                references: vec![],
210            })
211        }
212
213        fn extract_symbols(&self, _code: &str) -> ProviderResult<Vec<Symbol>> {
214            Ok(vec![])
215        }
216
217        fn get_hover_info(
218            &self,
219            _code: &str,
220            _position: Position,
221        ) -> ProviderResult<Option<String>> {
222            Ok(None)
223        }
224    }
225
226    #[test]
227    fn test_semantic_analyzer_registry() {
228        let mut registry = SemanticAnalyzerRegistry::new();
229        let provider = Box::new(MockSemanticProvider);
230
231        registry.register(provider);
232
233        assert!(registry.has_provider("mock"));
234        assert!(registry.get("mock").is_some());
235        assert!(!registry.has_provider("unknown"));
236    }
237
238    #[test]
239    fn test_semantic_analyzer_registry_languages() {
240        let mut registry = SemanticAnalyzerRegistry::new();
241        let provider = Box::new(MockSemanticProvider);
242
243        registry.register(provider);
244
245        let languages = registry.languages();
246        assert_eq!(languages.len(), 1);
247        assert!(languages.contains(&"mock"));
248    }
249}