Skip to main content

ass_editor/extensions/builtin/auto_complete/
editor_ext.rs

1//! `EditorExtension` trait implementation for `AutoCompleteExtension`.
2//!
3//! Wires the auto-completion logic into the editor extension lifecycle:
4//! initialization, configuration loading, command execution, and schema.
5
6use super::extension::AutoCompleteExtension;
7use crate::core::{Position, Result};
8use crate::extensions::{
9    EditorExtension, ExtensionCommand, ExtensionContext, ExtensionInfo, ExtensionResult,
10    ExtensionState, MessageLevel,
11};
12
13#[cfg(not(feature = "std"))]
14use alloc::{
15    collections::BTreeMap as HashMap,
16    format,
17    string::{String, ToString},
18    vec,
19    vec::Vec,
20};
21#[cfg(feature = "std")]
22use std::collections::HashMap;
23
24impl EditorExtension for AutoCompleteExtension {
25    fn info(&self) -> &ExtensionInfo {
26        &self.info
27    }
28
29    fn initialize(&mut self, context: &mut dyn ExtensionContext) -> Result<()> {
30        self.state = ExtensionState::Active;
31
32        // Load configuration
33        if let Some(fields) = context.get_config("autocomplete.complete_fields") {
34            self.config.complete_fields = fields == "true";
35        }
36        if let Some(styles) = context.get_config("autocomplete.complete_styles") {
37            self.config.complete_styles = styles == "true";
38        }
39        if let Some(tags) = context.get_config("autocomplete.complete_tags") {
40            self.config.complete_tags = tags == "true";
41        }
42        if let Some(values) = context.get_config("autocomplete.complete_values") {
43            self.config.complete_values = values == "true";
44        }
45        if let Some(max) = context.get_config("autocomplete.max_suggestions") {
46            if let Ok(max_val) = max.parse() {
47                self.config.max_suggestions = max_val;
48            }
49        }
50
51        context.show_message("Auto-completion initialized", MessageLevel::Info)?;
52        Ok(())
53    }
54
55    fn shutdown(&mut self, _context: &mut dyn ExtensionContext) -> Result<()> {
56        self.state = ExtensionState::Shutdown;
57        self.style_names.clear();
58        Ok(())
59    }
60
61    fn state(&self) -> ExtensionState {
62        self.state
63    }
64
65    fn execute_command(
66        &mut self,
67        command_id: &str,
68        args: &HashMap<String, String>,
69        context: &mut dyn ExtensionContext,
70    ) -> Result<ExtensionResult> {
71        match command_id {
72            "autocomplete.trigger" => {
73                if let Some(doc) = context.current_document() {
74                    // Get position from args or use end of document
75                    let position = if let Some(offset_str) = args.get("position") {
76                        if let Ok(offset) = offset_str.parse() {
77                            Position::new(offset)
78                        } else {
79                            Position::new(doc.len_bytes())
80                        }
81                    } else {
82                        Position::new(doc.len_bytes())
83                    };
84
85                    let completions = self.get_completions(doc, position)?;
86                    let mut result = ExtensionResult::success_with_message(format!(
87                        "Found {} completions",
88                        completions.len()
89                    ));
90
91                    // Add completion data
92                    for (i, completion) in completions.iter().take(10).enumerate() {
93                        result
94                            .data
95                            .insert(format!("completion_{i}"), completion.insert_text.clone());
96                    }
97
98                    Ok(result)
99                } else {
100                    Ok(ExtensionResult::failure("No active document".to_string()))
101                }
102            }
103            "autocomplete.update_styles" => {
104                if let Some(doc) = context.current_document() {
105                    self.update_style_names(doc)?;
106                    Ok(ExtensionResult::success_with_message(format!(
107                        "Updated {} style names",
108                        self.style_names.len()
109                    )))
110                } else {
111                    Ok(ExtensionResult::failure("No active document".to_string()))
112                }
113            }
114            _ => Ok(ExtensionResult::failure(format!(
115                "Unknown command: {command_id}"
116            ))),
117        }
118    }
119
120    fn commands(&self) -> Vec<ExtensionCommand> {
121        vec![
122            ExtensionCommand::new(
123                "autocomplete.trigger".to_string(),
124                "Trigger Completion".to_string(),
125                "Get completion suggestions at cursor position".to_string(),
126            )
127            .with_category("Completion".to_string()),
128            ExtensionCommand::new(
129                "autocomplete.update_styles".to_string(),
130                "Update Style Names".to_string(),
131                "Update known style names from document".to_string(),
132            )
133            .with_category("Completion".to_string()),
134        ]
135    }
136
137    fn config_schema(&self) -> HashMap<String, String> {
138        let mut schema = HashMap::new();
139        schema.insert(
140            "autocomplete.complete_fields".to_string(),
141            "boolean".to_string(),
142        );
143        schema.insert(
144            "autocomplete.complete_styles".to_string(),
145            "boolean".to_string(),
146        );
147        schema.insert(
148            "autocomplete.complete_tags".to_string(),
149            "boolean".to_string(),
150        );
151        schema.insert(
152            "autocomplete.complete_values".to_string(),
153            "boolean".to_string(),
154        );
155        schema.insert(
156            "autocomplete.max_suggestions".to_string(),
157            "number".to_string(),
158        );
159        schema.insert("autocomplete.min_chars".to_string(), "number".to_string());
160        schema
161    }
162}