ricecoder_completion/
engine.rs

1/// Core completion engine with language-agnostic architecture
2///
3/// This module provides the main completion engine and related traits for generating,
4/// ranking, and managing code completions. The engine is designed to be language-agnostic
5/// with pluggable providers for language-specific behavior and external LSP integration.
6///
7/// # Architecture
8///
9/// The completion engine follows a pipeline architecture with external LSP routing:
10///
11/// 1. **External LSP Routing** (Primary): Route to external LSP server if available and configured
12/// 2. **Context Analysis**: Analyze code context to determine available symbols and expected types
13/// 3. **Completion Generation**: Generate suggestions using language-specific provider or generic generator
14/// 4. **Merging**: Merge external LSP completions with internal completions (external takes priority)
15/// 5. **Ranking**: Rank completions by relevance, frequency, and recency
16///
17/// # External LSP Integration
18///
19/// The completion engine integrates with external LSP servers through the `ExternalLspCompletionProxy`.
20/// When a completion request is made:
21///
22/// 1. If an external LSP server is configured for the language, the request is forwarded to it
23/// 2. The external LSP response is transformed to ricecoder's internal model
24/// 3. External completions are merged with internal completions:
25///    - External completions have higher priority (appear first)
26///    - Internal completions are added if they don't duplicate external ones
27///    - Results are sorted by relevance score
28/// 4. If the external LSP is unavailable or times out, the system falls back to internal providers
29///
30/// # Merge Strategy
31///
32/// The merge strategy for combining external and internal completions:
33///
34/// - **Priority**: External completions are prioritized over internal ones
35/// - **Deduplication**: Completions with the same label are deduplicated (external wins)
36/// - **Sorting**: All completions are sorted by relevance score
37/// - **Fallback**: If external LSP fails, internal completions are used as fallback
38///
39/// This ensures users get the best available completions while maintaining graceful degradation.
40///
41/// # Example
42///
43/// ```ignore
44/// use ricecoder_completion::engine::*;
45/// use ricecoder_completion::types::*;
46/// use std::sync::Arc;
47///
48/// // Create engine components
49/// let context_analyzer = Arc::new(GenericContextAnalyzer);
50/// let generator = Arc::new(BasicCompletionGenerator);
51/// let ranker = Arc::new(BasicCompletionRanker::default_weights());
52/// let registry = ProviderRegistry::new();
53///
54/// // Create engine
55/// let engine = GenericCompletionEngine::new(
56///     context_analyzer,
57///     generator,
58///     ranker,
59///     registry,
60/// );
61///
62/// // Generate completions
63/// let completions = engine.generate_completions(
64///     "fn main() { let x = ",
65///     Position::new(0, 20),
66///     "rust",
67/// ).await?;
68/// ```
69use crate::context::ContextAnalyzer;
70use crate::types::*;
71use async_trait::async_trait;
72use std::sync::Arc;
73
74/// Main completion engine trait
75///
76/// Implementations of this trait orchestrate the completion process by coordinating
77/// context analysis, completion generation, and ranking.
78///
79/// # Async Behavior
80///
81/// All methods are async to support non-blocking I/O and streaming responses.
82/// Implementations should handle cancellation gracefully.
83#[async_trait]
84pub trait CompletionEngine: Send + Sync {
85    /// Generate completion suggestions for the given code at the specified position
86    ///
87    /// # Arguments
88    ///
89    /// * `code` - The source code to analyze
90    /// * `position` - The cursor position where completions are requested
91    /// * `language` - The programming language identifier (e.g., "rust", "typescript", "python")
92    ///
93    /// # Returns
94    ///
95    /// A vector of completion items ranked by relevance, or an error if generation fails.
96    ///
97    /// # Errors
98    ///
99    /// Returns `CompletionError` if:
100    /// - Context analysis fails
101    /// - Completion generation fails
102    /// - Ranking fails
103    /// - The language is not supported
104    async fn generate_completions(
105        &self,
106        code: &str,
107        position: Position,
108        language: &str,
109    ) -> CompletionResult<Vec<CompletionItem>>;
110
111    /// Resolve additional details for a completion item
112    ///
113    /// This method is called when the user selects a completion item to resolve
114    /// additional details like documentation, type information, or additional edits.
115    ///
116    /// # Arguments
117    ///
118    /// * `item` - The completion item to resolve
119    ///
120    /// # Returns
121    ///
122    /// The resolved completion item with additional details, or an error if resolution fails.
123    async fn resolve_completion(&self, item: &CompletionItem) -> CompletionResult<CompletionItem>;
124}
125
126/// Generic completion engine implementation
127///
128/// This is the main implementation of the completion engine. It coordinates
129/// external LSP routing, context analysis, completion generation, and ranking
130/// to produce ranked completion suggestions.
131///
132/// # Completion Flow
133///
134/// The engine follows this flow for each completion request:
135///
136/// 1. **External LSP Check**: Check if an external LSP server is configured for the language
137/// 2. **Context Analysis**: Analyze code context to determine available symbols and expected types
138/// 3. **Completion Generation**: Generate suggestions using:
139///    - External LSP server (if available and configured)
140///    - Language-specific provider (if registered)
141///    - Generic completion generator (fallback)
142/// 4. **Merging**: Merge external and internal completions (external takes priority)
143/// 5. **Ranking**: Rank all completions by relevance, frequency, and recency
144///
145/// # Language Support
146///
147/// The engine supports multiple languages through a pluggable provider system:
148/// - If an external LSP server is configured, it will be used for semantic completions
149/// - If a language-specific provider is registered, it will be used as fallback
150/// - Otherwise, the generic completion generator is used as a fallback
151///
152/// # Graceful Degradation
153///
154/// If the external LSP server is unavailable or times out:
155/// - The system falls back to language-specific providers
156/// - If no provider is available, the generic generator is used
157/// - Users always get some completions, even if not semantic
158///
159/// # Example
160///
161/// ```ignore
162/// use ricecoder_completion::engine::*;
163/// use ricecoder_completion::types::*;
164/// use std::sync::Arc;
165///
166/// let engine = GenericCompletionEngine::new(
167///     Arc::new(GenericContextAnalyzer),
168///     Arc::new(BasicCompletionGenerator),
169///     Arc::new(BasicCompletionRanker::default_weights()),
170///     ProviderRegistry::new(),
171/// );
172/// ```
173pub struct GenericCompletionEngine {
174    context_analyzer: Arc<dyn ContextAnalyzer>,
175    generator: Arc<dyn CompletionGenerator>,
176    ranker: Arc<dyn CompletionRanker>,
177    provider_registry: ProviderRegistry,
178}
179
180impl GenericCompletionEngine {
181    /// Create a new completion engine
182    ///
183    /// # Arguments
184    ///
185    /// * `context_analyzer` - Analyzer for determining code context
186    /// * `generator` - Generic completion generator (used as fallback)
187    /// * `ranker` - Ranker for sorting completions
188    /// * `provider_registry` - Registry of language-specific providers
189    pub fn new(
190        context_analyzer: Arc<dyn ContextAnalyzer>,
191        generator: Arc<dyn CompletionGenerator>,
192        ranker: Arc<dyn CompletionRanker>,
193        provider_registry: ProviderRegistry,
194    ) -> Self {
195        Self {
196            context_analyzer,
197            generator,
198            ranker,
199            provider_registry,
200        }
201    }
202}
203
204#[async_trait]
205impl CompletionEngine for GenericCompletionEngine {
206    async fn generate_completions(
207        &self,
208        code: &str,
209        position: Position,
210        language: &str,
211    ) -> CompletionResult<Vec<CompletionItem>> {
212        // Analyze context
213        let context = self
214            .context_analyzer
215            .analyze_context(code, position, language)
216            .await?;
217
218        // Generate completions using language-specific provider if available
219        let mut completions = if let Some(provider) = self.provider_registry.get_provider(language)
220        {
221            provider
222                .generate_completions(code, position, &context)
223                .await?
224        } else {
225            // Fall back to generic completion
226            self.generator
227                .generate_completions(code, position, &context)
228                .await?
229        };
230
231        // Rank completions
232        completions = self.ranker.rank_completions(completions, &context);
233
234        Ok(completions)
235    }
236
237    async fn resolve_completion(&self, item: &CompletionItem) -> CompletionResult<CompletionItem> {
238        Ok(item.clone())
239    }
240}
241
242/// Completion generator trait
243///
244/// Implementations generate completion suggestions based on code context.
245/// This is typically used as a fallback when no language-specific provider is available.
246///
247/// # Implementations
248///
249/// - `BasicCompletionGenerator`: Generic text-based completion
250/// - Language-specific providers: Rust, TypeScript, Python
251#[async_trait]
252pub trait CompletionGenerator: Send + Sync {
253    /// Generate completion suggestions
254    ///
255    /// # Arguments
256    ///
257    /// * `code` - The source code to analyze
258    /// * `position` - The cursor position where completions are requested
259    /// * `context` - The analyzed code context
260    ///
261    /// # Returns
262    ///
263    /// A vector of completion items (not yet ranked), or an error if generation fails.
264    async fn generate_completions(
265        &self,
266        code: &str,
267        position: Position,
268        context: &CompletionContext,
269    ) -> CompletionResult<Vec<CompletionItem>>;
270}
271
272/// Completion ranker trait
273///
274/// Implementations rank completion suggestions by relevance, frequency, and recency.
275/// The ranker is responsible for sorting completions so the most relevant appear first.
276///
277/// # Scoring
278///
279/// Rankers typically combine multiple scoring factors:
280/// - **Relevance**: How well the completion matches the context
281/// - **Frequency**: How often the completion is used
282/// - **Recency**: How recently the completion was used
283///
284/// # Implementations
285///
286/// - `BasicCompletionRanker`: Prefix matching and fuzzy matching
287/// - `AdvancedCompletionRanker`: Advanced scoring with frequency and recency
288pub trait CompletionRanker: Send + Sync {
289    /// Rank completions by relevance and frequency
290    ///
291    /// # Arguments
292    ///
293    /// * `items` - Unranked completion items
294    /// * `context` - The analyzed code context
295    ///
296    /// # Returns
297    ///
298    /// The same completion items, sorted by relevance (highest first).
299    fn rank_completions(
300        &self,
301        items: Vec<CompletionItem>,
302        context: &CompletionContext,
303    ) -> Vec<CompletionItem>;
304
305    /// Score relevance of a completion item
306    ///
307    /// # Arguments
308    ///
309    /// * `item` - The completion item to score
310    /// * `context` - The analyzed code context
311    ///
312    /// # Returns
313    ///
314    /// A relevance score (typically 0.0 to 1.0, where 1.0 is most relevant).
315    fn score_relevance(&self, item: &CompletionItem, context: &CompletionContext) -> f32;
316
317    /// Score frequency of a completion item
318    ///
319    /// # Arguments
320    ///
321    /// * `item` - The completion item to score
322    ///
323    /// # Returns
324    ///
325    /// A frequency score (typically 0.0 to 1.0, where 1.0 is most frequently used).
326    fn score_frequency(&self, item: &CompletionItem) -> f32;
327}
328
329/// Pluggable completion provider for language-specific behavior
330///
331/// Implementations provide language-specific completion suggestions. Providers are
332/// registered in the `ProviderRegistry` and selected based on the language identifier.
333///
334/// # Language Support
335///
336/// Each provider supports a single language. The engine queries the registry to find
337/// the appropriate provider for the current language.
338///
339/// # Implementations
340///
341/// - `RustCompletionProvider`: Rust-specific completions
342/// - `TypeScriptCompletionProvider`: TypeScript-specific completions
343/// - `PythonCompletionProvider`: Python-specific completions
344/// - `GenericTextProvider`: Generic text-based completions
345///
346/// # Example
347///
348/// ```ignore
349/// use ricecoder_completion::providers::RustCompletionProvider;
350/// use ricecoder_completion::engine::CompletionProvider;
351///
352/// let provider = RustCompletionProvider;
353/// assert_eq!(provider.language(), "rust");
354/// ```
355#[async_trait]
356pub trait CompletionProvider: Send + Sync {
357    /// Get the language this provider supports
358    ///
359    /// # Returns
360    ///
361    /// A language identifier string (e.g., "rust", "typescript", "python").
362    fn language(&self) -> &str;
363
364    /// Generate completions for this language
365    ///
366    /// # Arguments
367    ///
368    /// * `code` - The source code to analyze
369    /// * `position` - The cursor position where completions are requested
370    /// * `context` - The analyzed code context
371    ///
372    /// # Returns
373    ///
374    /// A vector of language-specific completion items (not yet ranked), or an error if generation fails.
375    async fn generate_completions(
376        &self,
377        code: &str,
378        position: Position,
379        context: &CompletionContext,
380    ) -> CompletionResult<Vec<CompletionItem>>;
381}
382
383/// Registry for completion providers
384///
385/// The provider registry manages language-specific completion providers.
386/// It allows registering, retrieving, and listing available providers.
387///
388/// # Example
389///
390/// ```ignore
391/// use ricecoder_completion::engine::ProviderRegistry;
392/// use ricecoder_completion::providers::RustCompletionProvider;
393/// use std::sync::Arc;
394///
395/// let mut registry = ProviderRegistry::new();
396/// registry.register(Arc::new(RustCompletionProvider));
397///
398/// // Get provider for Rust
399/// let provider = registry.get_provider("rust");
400/// assert!(provider.is_some());
401///
402/// // List all supported languages
403/// let languages = registry.list_languages();
404/// assert!(languages.contains(&"rust".to_string()));
405/// ```
406pub struct ProviderRegistry {
407    providers: std::collections::HashMap<String, Arc<dyn CompletionProvider>>,
408}
409
410impl ProviderRegistry {
411    /// Create a new empty provider registry
412    pub fn new() -> Self {
413        Self {
414            providers: std::collections::HashMap::new(),
415        }
416    }
417
418    /// Create a new provider registry with all built-in providers registered
419    ///
420    /// This is a convenience method that automatically registers all language-specific
421    /// providers (Rust, TypeScript, Python, Go, Java, Kotlin, Dart).
422    pub fn with_builtin_providers() -> Self {
423        let mut registry = Self::new();
424        registry.register_builtin_providers();
425        registry
426    }
427
428    /// Register all built-in language providers
429    ///
430    /// This method registers providers for all supported languages:
431    /// - Rust
432    /// - TypeScript
433    /// - Python
434    /// - Go
435    /// - Java
436    /// - Kotlin
437    /// - Dart
438    pub fn register_builtin_providers(&mut self) {
439        use crate::providers::*;
440
441        self.register(Arc::new(RustCompletionProvider));
442        self.register(Arc::new(TypeScriptCompletionProvider));
443        self.register(Arc::new(PythonCompletionProvider));
444        self.register(Arc::new(GoCompletionProvider));
445        self.register(Arc::new(JavaCompletionProvider));
446        self.register(Arc::new(KotlinCompletionProvider));
447        self.register(Arc::new(DartCompletionProvider));
448    }
449
450    /// Register a completion provider
451    ///
452    /// # Arguments
453    ///
454    /// * `provider` - The provider to register
455    ///
456    /// # Behavior
457    ///
458    /// If a provider for the same language already exists, it will be replaced.
459    pub fn register(&mut self, provider: Arc<dyn CompletionProvider>) {
460        self.providers
461            .insert(provider.language().to_string(), provider);
462    }
463
464    /// Get a completion provider for a language
465    ///
466    /// # Arguments
467    ///
468    /// * `language` - The language identifier
469    ///
470    /// # Returns
471    ///
472    /// The provider for the language, or `None` if no provider is registered.
473    pub fn get_provider(&self, language: &str) -> Option<Arc<dyn CompletionProvider>> {
474        self.providers.get(language).cloned()
475    }
476
477    /// Unregister a completion provider
478    ///
479    /// # Arguments
480    ///
481    /// * `language` - The language identifier
482    ///
483    /// # Returns
484    ///
485    /// The unregistered provider, or `None` if no provider was registered.
486    pub fn unregister(&mut self, language: &str) -> Option<Arc<dyn CompletionProvider>> {
487        self.providers.remove(language)
488    }
489
490    /// List all supported languages
491    ///
492    /// # Returns
493    ///
494    /// A vector of language identifiers for all registered providers.
495    pub fn list_languages(&self) -> Vec<String> {
496        self.providers.keys().cloned().collect()
497    }
498}
499
500impl Default for ProviderRegistry {
501    fn default() -> Self {
502        Self::new()
503    }
504}
505
506#[cfg(test)]
507mod tests {
508    use super::*;
509
510    #[test]
511    fn test_provider_registry_register() {
512        let registry = ProviderRegistry::new();
513        assert_eq!(registry.list_languages().len(), 0);
514    }
515
516    #[test]
517    fn test_provider_registry_get_nonexistent() {
518        let registry = ProviderRegistry::new();
519        assert!(registry.get_provider("rust").is_none());
520    }
521}