Skip to main content

ass_editor/extensions/builtin/auto_complete/
extension.rs

1//! `AutoCompleteExtension` definition and completion orchestration.
2//!
3//! Holds the extension struct, its constructor, the top-level
4//! `get_completions` entry point, and document style-name extraction. The
5//! per-context completion generators live in sibling submodules.
6
7use super::types::{AutoCompleteConfig, CompletionItem};
8use crate::core::{EditorDocument, Position, Result};
9use crate::extensions::{ExtensionCapability, ExtensionInfo, ExtensionState};
10use ass_core::parser::{Script, Section};
11
12#[cfg(not(feature = "std"))]
13use alloc::{
14    string::{String, ToString},
15    vec::Vec,
16};
17
18/// Auto-completion extension
19pub struct AutoCompleteExtension {
20    pub(super) info: ExtensionInfo,
21    pub(super) state: ExtensionState,
22    /// Known style names from the document
23    pub(super) style_names: Vec<String>,
24    /// Configuration
25    pub(super) config: AutoCompleteConfig,
26}
27
28impl AutoCompleteExtension {
29    /// Create a new auto-complete extension
30    pub fn new() -> Self {
31        let info = ExtensionInfo::new(
32            "auto-complete".to_string(),
33            "1.0.0".to_string(),
34            "ASS-RS Team".to_string(),
35            "Built-in auto-completion for ASS/SSA files".to_string(),
36        )
37        .with_capability(ExtensionCapability::CodeCompletion)
38        .with_license("MIT".to_string());
39
40        Self {
41            info,
42            state: ExtensionState::Uninitialized,
43            style_names: Vec::new(),
44            config: AutoCompleteConfig::default(),
45        }
46    }
47
48    /// Get completions at a position
49    pub fn get_completions(
50        &mut self,
51        document: &EditorDocument,
52        position: Position,
53    ) -> Result<Vec<CompletionItem>> {
54        // Update style names from document
55        self.update_style_names(document)?;
56
57        // Get completion context
58        let context = self.get_completion_context(document, position)?;
59
60        // Generate completions based on context
61        let mut completions = Vec::new();
62
63        // Section completions
64        if context.line.is_empty() || context.line.starts_with('[') {
65            completions.extend(self.get_section_completions(&context));
66        }
67
68        // Field completions
69        if let Some(ref section) = context.section {
70            if !context.in_override_tag && self.config.complete_fields {
71                completions.extend(self.get_field_completions(section, &context));
72            }
73        }
74
75        // Override tag completions
76        if context.in_override_tag && self.config.complete_tags {
77            completions.extend(self.get_tag_completions(&context));
78        }
79
80        // Style reference completions
81        if self.should_complete_style(&context) && self.config.complete_styles {
82            completions.extend(self.get_style_completions(&context));
83        }
84
85        // Sort and limit completions
86        completions.sort_by_key(|c| c.sort_order);
87        completions.truncate(self.config.max_suggestions);
88
89        Ok(completions)
90    }
91
92    /// Update known style names from document
93    pub(super) fn update_style_names(&mut self, document: &EditorDocument) -> Result<()> {
94        self.style_names.clear();
95
96        if let Ok(script) = Script::parse(&document.text()) {
97            for section in script.sections() {
98                if let Section::Styles(styles) = section {
99                    for style in styles {
100                        self.style_names.push(style.name.to_string());
101                    }
102                }
103            }
104        }
105
106        Ok(())
107    }
108}
109
110impl Default for AutoCompleteExtension {
111    fn default() -> Self {
112        Self::new()
113    }
114}