ass_editor/extensions/
mod.rs

1//! Extension system for editor functionality
2//!
3//! Provides the `EditorExtension` trait for extending editor capabilities
4//! with custom functionality. Supports both synchronous and asynchronous
5//! operations, lifecycle management, and inter-extension communication.
6
7pub mod builtin;
8pub mod registry_integration;
9
10#[cfg(not(feature = "std"))]
11extern crate alloc;
12
13use crate::core::{EditorDocument, Result};
14use crate::events::DocumentEvent;
15use core::fmt;
16
17#[cfg(feature = "std")]
18use std::collections::HashMap;
19
20#[cfg(not(feature = "std"))]
21use alloc::collections::BTreeMap as HashMap;
22
23#[cfg(not(feature = "std"))]
24use alloc::{
25    boxed::Box,
26    format,
27    string::{String, ToString},
28    vec::Vec,
29};
30
31#[cfg(feature = "multi-thread")]
32use std::sync::Arc;
33
34#[cfg(not(feature = "multi-thread"))]
35use core::cell::RefCell;
36
37#[cfg(all(not(feature = "multi-thread"), not(feature = "std")))]
38use alloc::rc::Rc;
39
40#[cfg(all(not(feature = "multi-thread"), feature = "std"))]
41use std::rc::Rc;
42
43/// Extension capabilities that can be provided
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum ExtensionCapability {
46    /// Text processing and transformation
47    TextProcessing,
48    /// Syntax highlighting and theming
49    SyntaxHighlighting,
50    /// Code completion and suggestions  
51    CodeCompletion,
52    /// Linting and validation
53    Linting,
54    /// Import/export format support
55    FormatSupport,
56    /// Custom commands and shortcuts
57    CustomCommands,
58    /// UI enhancements and widgets
59    UserInterface,
60    /// External tool integration
61    ToolIntegration,
62    /// Custom event handling
63    EventHandling,
64    /// Performance monitoring
65    Performance,
66}
67
68impl ExtensionCapability {
69    /// Get a human-readable description of the capability
70    pub fn description(&self) -> &'static str {
71        match self {
72            Self::TextProcessing => "Text processing and transformation",
73            Self::SyntaxHighlighting => "Syntax highlighting and theming",
74            Self::CodeCompletion => "Code completion and suggestions",
75            Self::Linting => "Linting and validation",
76            Self::FormatSupport => "Import/export format support",
77            Self::CustomCommands => "Custom commands and shortcuts",
78            Self::UserInterface => "UI enhancements and widgets",
79            Self::ToolIntegration => "External tool integration",
80            Self::EventHandling => "Custom event handling",
81            Self::Performance => "Performance monitoring",
82        }
83    }
84}
85
86/// Extension metadata and information
87#[derive(Debug, Clone, PartialEq, Eq)]
88pub struct ExtensionInfo {
89    /// Extension name
90    pub name: String,
91    /// Extension version
92    pub version: String,
93    /// Extension author
94    pub author: String,
95    /// Extension description
96    pub description: String,
97    /// Capabilities provided by this extension
98    pub capabilities: Vec<ExtensionCapability>,
99    /// Dependencies on other extensions
100    pub dependencies: Vec<String>,
101    /// Optional extension website/homepage
102    pub homepage: Option<String>,
103    /// License identifier
104    pub license: Option<String>,
105}
106
107impl ExtensionInfo {
108    /// Create a new extension info
109    pub fn new(name: String, version: String, author: String, description: String) -> Self {
110        Self {
111            name,
112            version,
113            author,
114            description,
115            capabilities: Vec::new(),
116            dependencies: Vec::new(),
117            homepage: None,
118            license: None,
119        }
120    }
121
122    /// Add a capability to this extension
123    pub fn with_capability(mut self, capability: ExtensionCapability) -> Self {
124        self.capabilities.push(capability);
125        self
126    }
127
128    /// Add multiple capabilities
129    pub fn with_capabilities(mut self, capabilities: Vec<ExtensionCapability>) -> Self {
130        self.capabilities.extend(capabilities);
131        self
132    }
133
134    /// Add a dependency on another extension
135    pub fn with_dependency(mut self, dependency: String) -> Self {
136        self.dependencies.push(dependency);
137        self
138    }
139
140    /// Set the homepage URL
141    pub fn with_homepage(mut self, homepage: String) -> Self {
142        self.homepage = Some(homepage);
143        self
144    }
145
146    /// Set the license
147    pub fn with_license(mut self, license: String) -> Self {
148        self.license = Some(license);
149        self
150    }
151
152    /// Check if this extension provides a specific capability
153    pub fn has_capability(&self, capability: &ExtensionCapability) -> bool {
154        self.capabilities.contains(capability)
155    }
156}
157
158/// Extension lifecycle state
159#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum ExtensionState {
161    /// Extension is uninitialized
162    Uninitialized,
163    /// Extension is being initialized
164    Initializing,
165    /// Extension is active and running
166    Active,
167    /// Extension is paused/suspended
168    Paused,
169    /// Extension encountered an error
170    Error,
171    /// Extension is being shut down
172    ShuttingDown,
173    /// Extension has been shut down
174    Shutdown,
175}
176
177impl ExtensionState {
178    /// Check if the extension is in an active state
179    pub fn is_active(&self) -> bool {
180        matches!(self, Self::Active)
181    }
182
183    /// Check if the extension can be used
184    pub fn is_usable(&self) -> bool {
185        matches!(self, Self::Active | Self::Paused)
186    }
187
188    /// Check if the extension is in an error state
189    pub fn is_error(&self) -> bool {
190        matches!(self, Self::Error)
191    }
192}
193
194/// Extension command that can be executed
195#[derive(Debug, Clone)]
196pub struct ExtensionCommand {
197    /// Command identifier
198    pub id: String,
199    /// Human-readable command name
200    pub name: String,
201    /// Command description
202    pub description: String,
203    /// Keyboard shortcut (if any)
204    pub shortcut: Option<String>,
205    /// Command category for organization
206    pub category: String,
207    /// Whether the command requires a document
208    pub requires_document: bool,
209}
210
211impl ExtensionCommand {
212    /// Create a new extension command
213    pub fn new(id: String, name: String, description: String) -> Self {
214        Self {
215            id,
216            name,
217            description,
218            shortcut: None,
219            category: "General".to_string(),
220            requires_document: true,
221        }
222    }
223
224    /// Set the keyboard shortcut
225    pub fn with_shortcut(mut self, shortcut: String) -> Self {
226        self.shortcut = Some(shortcut);
227        self
228    }
229
230    /// Set the command category
231    pub fn with_category(mut self, category: String) -> Self {
232        self.category = category;
233        self
234    }
235
236    /// Set whether the command requires a document
237    pub fn requires_document(mut self, requires: bool) -> Self {
238        self.requires_document = requires;
239        self
240    }
241}
242
243/// Extension context providing access to editor functionality
244pub trait ExtensionContext {
245    /// Get the current document (if any)
246    fn current_document(&self) -> Option<&EditorDocument>;
247
248    /// Get a mutable reference to the current document
249    fn current_document_mut(&mut self) -> Option<&mut EditorDocument>;
250
251    /// Send an event to the event system
252    fn send_event(&mut self, event: DocumentEvent) -> Result<()>;
253
254    /// Get configuration value
255    fn get_config(&self, key: &str) -> Option<String>;
256
257    /// Set configuration value
258    fn set_config(&mut self, key: String, value: String) -> Result<()>;
259
260    /// Register a command with the editor
261    fn register_command(&mut self, command: ExtensionCommand) -> Result<()>;
262
263    /// Show a message to the user
264    fn show_message(&mut self, message: &str, level: MessageLevel) -> Result<()>;
265
266    /// Get data from another extension
267    fn get_extension_data(&self, extension_name: &str, key: &str) -> Option<String>;
268
269    /// Set data for inter-extension communication
270    fn set_extension_data(&mut self, key: String, value: String) -> Result<()>;
271}
272
273/// Message levels for user notifications
274#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275pub enum MessageLevel {
276    /// Informational message
277    Info,
278    /// Warning message
279    Warning,
280    /// Error message
281    Error,
282    /// Success message
283    Success,
284}
285
286/// Result of extension command execution
287#[derive(Debug, Clone)]
288pub struct ExtensionResult {
289    /// Whether the command succeeded
290    pub success: bool,
291    /// Optional result message
292    pub message: Option<String>,
293    /// Optional result data
294    pub data: HashMap<String, String>,
295}
296
297impl ExtensionResult {
298    /// Create a successful result
299    pub fn success() -> Self {
300        Self {
301            success: true,
302            message: None,
303            data: HashMap::new(),
304        }
305    }
306
307    /// Create a successful result with message
308    pub fn success_with_message(message: String) -> Self {
309        Self {
310            success: true,
311            message: Some(message),
312            data: HashMap::new(),
313        }
314    }
315
316    /// Create a failure result
317    pub fn failure(message: String) -> Self {
318        Self {
319            success: false,
320            message: Some(message),
321            data: HashMap::new(),
322        }
323    }
324
325    /// Add data to the result
326    pub fn with_data(mut self, key: String, value: String) -> Self {
327        self.data.insert(key, value);
328        self
329    }
330}
331
332/// Main extension trait that extensions must implement
333pub trait EditorExtension: Send + Sync {
334    /// Get extension metadata
335    fn info(&self) -> &ExtensionInfo;
336
337    /// Initialize the extension
338    fn initialize(&mut self, context: &mut dyn ExtensionContext) -> Result<()>;
339
340    /// Shutdown the extension
341    fn shutdown(&mut self, context: &mut dyn ExtensionContext) -> Result<()>;
342
343    /// Get the current state of the extension
344    fn state(&self) -> ExtensionState;
345
346    /// Execute a command provided by this extension
347    fn execute_command(
348        &mut self,
349        command_id: &str,
350        args: &HashMap<String, String>,
351        context: &mut dyn ExtensionContext,
352    ) -> Result<ExtensionResult>;
353
354    /// Get commands provided by this extension
355    fn commands(&self) -> Vec<ExtensionCommand> {
356        Vec::new()
357    }
358
359    /// Handle a document event (optional)
360    fn handle_event(
361        &mut self,
362        _event: &DocumentEvent,
363        _context: &mut dyn ExtensionContext,
364    ) -> Result<()> {
365        // Default implementation does nothing
366        Ok(())
367    }
368
369    /// Get configuration schema (optional)
370    fn config_schema(&self) -> HashMap<String, String> {
371        HashMap::new()
372    }
373
374    /// Validate configuration (optional)
375    fn validate_config(&self, _config: &HashMap<String, String>) -> Result<()> {
376        Ok(())
377    }
378
379    /// Pause the extension (optional)
380    fn pause(&mut self) -> Result<()> {
381        Ok(())
382    }
383
384    /// Resume the extension (optional)
385    fn resume(&mut self) -> Result<()> {
386        Ok(())
387    }
388
389    /// Get extension-specific data (optional)
390    fn get_data(&self, _key: &str) -> Option<String> {
391        None
392    }
393
394    /// Set extension-specific data (optional)
395    fn set_data(&mut self, _key: String, _value: String) -> Result<()> {
396        Ok(())
397    }
398}
399
400/// Extension manager for loading and managing extensions
401/// Internal storage for ExtensionManager data
402struct ExtensionManagerInner {
403    /// Loaded extensions
404    extensions: HashMap<String, Box<dyn EditorExtension>>,
405
406    /// Extension states
407    extension_states: HashMap<String, ExtensionState>,
408
409    /// Available commands from all extensions
410    commands: HashMap<String, (String, ExtensionCommand)>, // command_id -> (extension_name, command)
411
412    /// Configuration storage
413    config: HashMap<String, String>,
414
415    /// Inter-extension data storage
416    extension_data: HashMap<String, HashMap<String, String>>,
417
418    /// Event channel for sending events
419    #[cfg(feature = "std")]
420    #[allow(dead_code)]
421    event_tx: EventSender,
422
423    /// Message handler for user notifications
424    #[allow(dead_code)]
425    message_handler: Box<dyn MessageHandler>,
426}
427
428/// Extension manager with built-in thread safety
429#[cfg(feature = "multi-thread")]
430use parking_lot::Mutex;
431
432/// Single unified ExtensionManager that is always thread-safe when multi-thread feature is enabled
433pub struct ExtensionManager {
434    #[cfg(feature = "multi-thread")]
435    inner: Arc<Mutex<ExtensionManagerInner>>,
436    #[cfg(not(feature = "multi-thread"))]
437    inner: RefCell<ExtensionManagerInner>,
438}
439
440// ExtensionManager is cloneable when multi-thread feature is enabled
441#[cfg(feature = "multi-thread")]
442impl Clone for ExtensionManager {
443    fn clone(&self) -> Self {
444        Self {
445            inner: self.inner.clone(),
446        }
447    }
448}
449
450// Note: ExtensionManager does not implement Clone without multi-thread feature
451// This is intentional - cloning requires Arc<Mutex<T>> which needs multi-thread
452
453impl fmt::Debug for ExtensionManager {
454    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
455        #[cfg(feature = "multi-thread")]
456        {
457            let inner = self.inner.lock();
458            f.debug_struct("ExtensionManager")
459                .field("extension_states", &inner.extension_states)
460                .field("commands", &inner.commands.keys().collect::<Vec<_>>())
461                .field("config", &inner.config)
462                .field("extension_data", &inner.extension_data)
463                .field("extensions", &"<HashMap<String, Box<dyn EditorExtension>>>")
464                .finish()
465        }
466        #[cfg(not(feature = "multi-thread"))]
467        {
468            let inner = self.inner.borrow();
469            f.debug_struct("ExtensionManager")
470                .field("extension_states", &inner.extension_states)
471                .field("commands", &inner.commands.keys().collect::<Vec<_>>())
472                .field("config", &inner.config)
473                .field("extension_data", &inner.extension_data)
474                .field("extensions", &"<HashMap<String, Box<dyn EditorExtension>>>")
475                .finish()
476        }
477    }
478}
479
480impl ExtensionManagerInner {
481    /// Create a new inner manager
482    fn new() -> Self {
483        #[cfg(feature = "std")]
484        let (tx, _rx) = mpsc::channel();
485
486        Self {
487            extensions: HashMap::new(),
488            extension_states: HashMap::new(),
489            commands: HashMap::new(),
490            config: HashMap::new(),
491            extension_data: HashMap::new(),
492            #[cfg(feature = "std")]
493            event_tx: tx,
494            #[cfg(feature = "std")]
495            message_handler: Box::new(StdMessageHandler),
496            #[cfg(not(feature = "std"))]
497            message_handler: Box::new(NoOpMessageHandler),
498        }
499    }
500}
501
502impl ExtensionManager {
503    /// Helper method for accessing inner data mutably
504    #[cfg(feature = "multi-thread")]
505    fn with_inner_mut<F, R>(&self, f: F) -> R
506    where
507        F: FnOnce(&mut ExtensionManagerInner) -> R,
508    {
509        let mut inner = self.inner.lock();
510        f(&mut inner)
511    }
512
513    /// Helper method for accessing inner data immutably
514    #[cfg(feature = "multi-thread")]
515    fn with_inner<F, R>(&self, f: F) -> R
516    where
517        F: FnOnce(&ExtensionManagerInner) -> R,
518    {
519        let inner = self.inner.lock();
520        f(&inner)
521    }
522
523    /// Helper method for accessing inner data mutably
524    #[cfg(not(feature = "multi-thread"))]
525    fn with_inner_mut<F, R>(&self, f: F) -> R
526    where
527        F: FnOnce(&mut ExtensionManagerInner) -> R,
528    {
529        let mut inner = self.inner.borrow_mut();
530        f(&mut inner)
531    }
532
533    /// Helper method for accessing inner data immutably
534    #[cfg(not(feature = "multi-thread"))]
535    fn with_inner<F, R>(&self, f: F) -> R
536    where
537        F: FnOnce(&ExtensionManagerInner) -> R,
538    {
539        let inner = self.inner.borrow();
540        f(&inner)
541    }
542
543    /// Create a new extension manager
544    pub fn new() -> Self {
545        #[cfg(feature = "multi-thread")]
546        {
547            Self {
548                inner: Arc::new(Mutex::new(ExtensionManagerInner::new())),
549            }
550        }
551        #[cfg(not(feature = "multi-thread"))]
552        {
553            Self {
554                inner: RefCell::new(ExtensionManagerInner::new()),
555            }
556        }
557    }
558
559    /// Create a new extension manager with custom event sender and message handler
560    #[cfg(feature = "std")]
561    pub fn with_event_channel(
562        event_tx: EventSender,
563        message_handler: Box<dyn MessageHandler>,
564    ) -> Self {
565        let inner = ExtensionManagerInner {
566            extensions: HashMap::new(),
567            extension_states: HashMap::new(),
568            commands: HashMap::new(),
569            config: HashMap::new(),
570            extension_data: HashMap::new(),
571            event_tx,
572            message_handler,
573        };
574
575        #[cfg(feature = "multi-thread")]
576        {
577            Self {
578                inner: Arc::new(Mutex::new(inner)),
579            }
580        }
581        #[cfg(not(feature = "multi-thread"))]
582        {
583            Self {
584                inner: RefCell::new(inner),
585            }
586        }
587    }
588
589    /// Create an extension context for use by extensions
590    pub fn create_context<'a>(
591        &'a mut self,
592        extension_name: String,
593        document: Option<&'a mut EditorDocument>,
594    ) -> Result<Box<dyn ExtensionContext + 'a>> {
595        #[cfg(feature = "multi-thread")]
596        {
597            Ok(Box::new(EditorContext {
598                document,
599                manager: self.clone(),
600                extension_name,
601            }))
602        }
603
604        #[cfg(not(feature = "multi-thread"))]
605        {
606            // In single-threaded mode, we share the config state via Rc<RefCell>
607            let config_clone = self.inner.borrow().config.clone();
608            let shared_config = Rc::new(RefCell::new(config_clone));
609
610            Ok(Box::new(EditorContext {
611                document,
612                manager: self,
613                manager_mut_state: shared_config,
614                extension_name,
615            }))
616        }
617    }
618
619    /// Load an extension
620    pub fn load_extension(&mut self, extension: Box<dyn EditorExtension>) -> Result<()> {
621        let extension_name = extension.info().name.clone();
622        let dependencies = extension.info().dependencies.clone();
623
624        // Check for dependency conflicts
625        let has_deps = self.with_inner(|inner| {
626            dependencies
627                .iter()
628                .all(|dep| inner.extension_states.contains_key(dep))
629        });
630
631        if !has_deps {
632            return Err(crate::core::EditorError::CommandFailed {
633                message: format!("Extension '{extension_name}' has unmet dependencies"),
634            });
635        }
636
637        self.with_inner_mut(|inner| {
638            if inner.extensions.contains_key(&extension_name) {
639                return Err(crate::core::EditorError::CommandFailed {
640                    message: format!("Extension '{extension_name}' is already loaded"),
641                });
642            }
643
644            inner.extensions.insert(extension_name.clone(), extension);
645            inner
646                .extension_states
647                .insert(extension_name.clone(), ExtensionState::Uninitialized);
648            Ok(())
649        })
650    }
651
652    /// Initialize an extension
653    pub fn initialize_extension(
654        &mut self,
655        extension_name: &str,
656        context: &mut dyn ExtensionContext,
657    ) -> Result<()> {
658        self.with_inner_mut(|inner| {
659            inner
660                .extension_states
661                .insert(extension_name.to_string(), ExtensionState::Initializing);
662
663            if let Some(extension) = inner.extensions.get_mut(extension_name) {
664                match extension.initialize(context) {
665                    Ok(()) => {
666                        inner
667                            .extension_states
668                            .insert(extension_name.to_string(), ExtensionState::Active);
669
670                        // Register commands
671                        for command in extension.commands() {
672                            inner
673                                .commands
674                                .insert(command.id.clone(), (extension_name.to_string(), command));
675                        }
676
677                        Ok(())
678                    }
679                    Err(e) => {
680                        inner
681                            .extension_states
682                            .insert(extension_name.to_string(), ExtensionState::Error);
683                        Err(e)
684                    }
685                }
686            } else {
687                Err(crate::core::EditorError::CommandFailed {
688                    message: format!("Extension '{extension_name}' not found"),
689                })
690            }
691        })
692    }
693
694    /// Unload an extension
695    pub fn unload_extension(
696        &mut self,
697        extension_name: &str,
698        context: &mut dyn ExtensionContext,
699    ) -> Result<()> {
700        // Shutdown the extension first
701        self.shutdown_extension(extension_name, context)?;
702
703        self.with_inner_mut(|inner| {
704            // Remove the extension
705            inner.extensions.remove(extension_name);
706            inner.extension_states.remove(extension_name);
707
708            // Remove commands
709            inner
710                .commands
711                .retain(|_, (ext_name, _)| ext_name != extension_name);
712
713            // Remove extension data
714            inner.extension_data.remove(extension_name);
715        });
716
717        Ok(())
718    }
719
720    /// Shutdown an extension
721    fn shutdown_extension(
722        &mut self,
723        extension_name: &str,
724        context: &mut dyn ExtensionContext,
725    ) -> Result<()> {
726        self.with_inner_mut(|inner| {
727            inner
728                .extension_states
729                .insert(extension_name.to_string(), ExtensionState::ShuttingDown);
730
731            if let Some(extension) = inner.extensions.get_mut(extension_name) {
732                extension.shutdown(context)?;
733                inner
734                    .extension_states
735                    .insert(extension_name.to_string(), ExtensionState::Shutdown);
736            }
737            Ok(())
738        })
739    }
740
741    /// Execute a command from an extension
742    pub fn execute_command(
743        &mut self,
744        command_id: &str,
745        args: &HashMap<String, String>,
746        context: &mut dyn ExtensionContext,
747    ) -> Result<ExtensionResult> {
748        let extension_name = self
749            .with_inner(|inner| inner.commands.get(command_id).map(|(name, _)| name.clone()))
750            .ok_or_else(|| crate::core::EditorError::CommandFailed {
751                message: format!("Command '{command_id}' not found"),
752            })?;
753
754        self.with_inner_mut(|inner| {
755            if let Some(extension) = inner.extensions.get_mut(&extension_name) {
756                extension.execute_command(command_id, args, context)
757            } else {
758                Err(crate::core::EditorError::CommandFailed {
759                    message: format!("Extension '{extension_name}' not found"),
760                })
761            }
762        })
763    }
764
765    /// Get list of loaded extensions
766    pub fn list_extensions(&self) -> Vec<String> {
767        self.with_inner(|inner| inner.extensions.keys().cloned().collect())
768    }
769
770    /// Get extension state
771    pub fn get_extension_state(&self, extension_name: &str) -> Option<ExtensionState> {
772        self.with_inner(|inner| inner.extension_states.get(extension_name).copied())
773    }
774
775    /// Get all available commands
776    pub fn list_commands(&self) -> Vec<String> {
777        self.with_inner(|inner| inner.commands.keys().cloned().collect())
778    }
779
780    /// Get configuration value
781    pub fn get_config(&self, key: &str) -> Option<String> {
782        self.with_inner(|inner| inner.config.get(key).cloned())
783    }
784
785    /// Set configuration value
786    pub fn set_config(&mut self, key: String, value: String) {
787        self.with_inner_mut(|inner| {
788            inner.config.insert(key, value);
789        });
790    }
791
792    /// Get extension data
793    pub fn get_extension_data(&self, extension_name: &str, key: &str) -> Option<String> {
794        self.with_inner(|inner| {
795            inner
796                .extension_data
797                .get(extension_name)
798                .and_then(|data| data.get(key))
799                .cloned()
800        })
801    }
802
803    /// Set extension data
804    pub fn set_extension_data(&mut self, extension_name: String, key: String, value: String) {
805        self.with_inner_mut(|inner| {
806            inner
807                .extension_data
808                .entry(extension_name)
809                .or_default()
810                .insert(key, value);
811        });
812    }
813}
814
815impl Default for ExtensionManager {
816    fn default() -> Self {
817        Self::new()
818    }
819}
820
821/// Message handler trait for showing messages to users
822pub trait MessageHandler: Send + Sync {
823    /// Show a message to the user
824    fn show(&mut self, message: &str, level: MessageLevel) -> Result<()>;
825}
826
827/// Default message handler implementation for std environments
828#[cfg(feature = "std")]
829pub struct StdMessageHandler;
830
831#[cfg(feature = "std")]
832impl MessageHandler for StdMessageHandler {
833    fn show(&mut self, message: &str, level: MessageLevel) -> Result<()> {
834        match level {
835            MessageLevel::Error => eprintln!("[ERROR] {message}"),
836            MessageLevel::Warning => eprintln!("[WARN] {message}"),
837            MessageLevel::Info => println!("[INFO] {message}"),
838            MessageLevel::Success => println!("[SUCCESS] {message}"),
839        }
840        Ok(())
841    }
842}
843
844/// No-op message handler for no_std environments
845#[cfg(not(feature = "std"))]
846pub struct NoOpMessageHandler;
847
848#[cfg(not(feature = "std"))]
849impl MessageHandler for NoOpMessageHandler {
850    fn show(&mut self, _message: &str, _level: MessageLevel) -> Result<()> {
851        Ok(())
852    }
853}
854
855/// Event sender type for channel communication
856#[cfg(feature = "std")]
857use std::sync::mpsc::{self, Sender};
858
859#[cfg(feature = "std")]
860pub type EventSender = Sender<DocumentEvent>;
861
862#[cfg(not(feature = "std"))]
863pub type EventSender = (); // No-op for no_std environments
864
865/// Editor context providing access to editor functionality
866/// Adapts to available features automatically
867pub struct EditorContext<'a> {
868    /// Current document (if any)
869    pub document: Option<&'a mut EditorDocument>,
870    /// Reference to the extension manager
871    #[cfg(feature = "multi-thread")]
872    pub manager: ExtensionManager,
873    /// Reference to the extension manager for single-threaded builds
874    #[cfg(not(feature = "multi-thread"))]
875    pub manager: &'a mut ExtensionManager,
876    /// Mutable state for single-threaded builds (config updates)
877    #[cfg(not(feature = "multi-thread"))]
878    pub manager_mut_state: alloc::rc::Rc<core::cell::RefCell<HashMap<String, String>>>,
879    /// Name of the current extension
880    pub extension_name: String,
881}
882
883impl<'a> ExtensionContext for EditorContext<'a> {
884    fn current_document(&self) -> Option<&EditorDocument> {
885        self.document.as_deref()
886    }
887
888    fn current_document_mut(&mut self) -> Option<&mut EditorDocument> {
889        self.document.as_deref_mut()
890    }
891
892    fn send_event(&mut self, _event: DocumentEvent) -> Result<()> {
893        // For now, we'll just log the event
894        // In a real implementation, this would use the event system
895        #[cfg(feature = "std")]
896        {
897            eprintln!("Extension {} sent event: {:?}", self.extension_name, _event);
898        }
899        Ok(())
900    }
901
902    fn get_config(&self, key: &str) -> Option<String> {
903        #[cfg(feature = "multi-thread")]
904        {
905            self.manager.get_config(key)
906        }
907        #[cfg(not(feature = "multi-thread"))]
908        {
909            self.manager.get_config(key)
910        }
911    }
912
913    fn set_config(&mut self, key: String, value: String) -> Result<()> {
914        #[cfg(feature = "multi-thread")]
915        {
916            self.manager.set_config(key.clone(), value.clone());
917        }
918        #[cfg(not(feature = "multi-thread"))]
919        {
920            // In single-threaded mode, update the shared config state
921            self.manager_mut_state.borrow_mut().insert(key, value);
922        }
923        Ok(())
924    }
925
926    fn register_command(&mut self, _command: ExtensionCommand) -> Result<()> {
927        // For now, just acknowledge the command in both modes
928        // In a real implementation, this would register with a command system
929        #[cfg(feature = "std")]
930        {
931            eprintln!(
932                "Extension {} registered command: {}",
933                self.extension_name, _command.id
934            );
935        }
936        Ok(())
937    }
938
939    fn show_message(&mut self, _message: &str, _level: MessageLevel) -> Result<()> {
940        // Simple console output for now
941        #[cfg(feature = "std")]
942        {
943            match _level {
944                MessageLevel::Info => eprintln!("[INFO] {}: {}", self.extension_name, _message),
945                MessageLevel::Warning => eprintln!("[WARN] {}: {}", self.extension_name, _message),
946                MessageLevel::Error => eprintln!("[ERROR] {}: {}", self.extension_name, _message),
947                MessageLevel::Success => {
948                    eprintln!("[SUCCESS] {}: {}", self.extension_name, _message)
949                }
950            }
951        }
952        Ok(())
953    }
954
955    fn get_extension_data(&self, extension_name: &str, key: &str) -> Option<String> {
956        self.manager.get_extension_data(extension_name, key)
957    }
958
959    fn set_extension_data(&mut self, key: String, value: String) -> Result<()> {
960        self.manager
961            .set_extension_data(self.extension_name.clone(), key, value);
962        Ok(())
963    }
964}
965
966#[cfg(test)]
967mod tests {
968    use super::*;
969    #[cfg(not(feature = "std"))]
970    use alloc::{string::ToString, vec};
971
972    #[test]
973    fn extension_info_creation() {
974        let info = ExtensionInfo::new(
975            "test-extension".to_string(),
976            "1.0.0".to_string(),
977            "Test Author".to_string(),
978            "A test extension".to_string(),
979        )
980        .with_capability(ExtensionCapability::TextProcessing)
981        .with_dependency("core-extension".to_string())
982        .with_homepage("https://example.com".to_string())
983        .with_license("MIT".to_string());
984
985        assert_eq!(info.name, "test-extension");
986        assert_eq!(info.version, "1.0.0");
987        assert!(info.has_capability(&ExtensionCapability::TextProcessing));
988        assert_eq!(info.dependencies.len(), 1);
989        assert_eq!(info.homepage, Some("https://example.com".to_string()));
990        assert_eq!(info.license, Some("MIT".to_string()));
991    }
992
993    #[test]
994    fn extension_capability_description() {
995        let capability = ExtensionCapability::TextProcessing;
996        assert_eq!(
997            capability.description(),
998            "Text processing and transformation"
999        );
1000    }
1001
1002    #[test]
1003    fn extension_state_checks() {
1004        let state = ExtensionState::Active;
1005        assert!(state.is_active());
1006        assert!(state.is_usable());
1007        assert!(!state.is_error());
1008
1009        let error_state = ExtensionState::Error;
1010        assert!(!error_state.is_active());
1011        assert!(!error_state.is_usable());
1012        assert!(error_state.is_error());
1013    }
1014
1015    #[test]
1016    fn extension_command_creation() {
1017        let command = ExtensionCommand::new(
1018            "test-command".to_string(),
1019            "Test Command".to_string(),
1020            "A test command".to_string(),
1021        )
1022        .with_shortcut("Ctrl+T".to_string())
1023        .with_category("Testing".to_string())
1024        .requires_document(false);
1025
1026        assert_eq!(command.id, "test-command");
1027        assert_eq!(command.shortcut, Some("Ctrl+T".to_string()));
1028        assert_eq!(command.category, "Testing");
1029        assert!(!command.requires_document);
1030    }
1031
1032    #[test]
1033    fn extension_result_creation() {
1034        let success = ExtensionResult::success_with_message("Success!".to_string())
1035            .with_data("key".to_string(), "value".to_string());
1036
1037        assert!(success.success);
1038        assert_eq!(success.message, Some("Success!".to_string()));
1039        assert_eq!(success.data.get("key"), Some(&"value".to_string()));
1040
1041        let failure = ExtensionResult::failure("Failed!".to_string());
1042        assert!(!failure.success);
1043        assert_eq!(failure.message, Some("Failed!".to_string()));
1044    }
1045
1046    #[test]
1047    fn extension_manager_creation() {
1048        let manager = ExtensionManager::new();
1049        assert_eq!(manager.list_extensions().len(), 0);
1050        assert_eq!(manager.list_commands().len(), 0);
1051    }
1052
1053    #[test]
1054    fn extension_manager_config() {
1055        let mut manager = ExtensionManager::new();
1056
1057        manager.set_config("test_key".to_string(), "test_value".to_string());
1058        assert_eq!(
1059            manager.get_config("test_key"),
1060            Some("test_value".to_string())
1061        );
1062        assert_eq!(manager.get_config("nonexistent"), None);
1063    }
1064
1065    #[test]
1066    fn extension_manager_data() {
1067        let mut manager = ExtensionManager::new();
1068
1069        manager.set_extension_data("ext1".to_string(), "key".to_string(), "value".to_string());
1070        assert_eq!(
1071            manager.get_extension_data("ext1", "key"),
1072            Some("value".to_string())
1073        );
1074        assert_eq!(manager.get_extension_data("ext1", "nonexistent"), None);
1075        assert_eq!(manager.get_extension_data("ext2", "key"), None);
1076    }
1077
1078    // Mock extension for testing
1079    struct TestExtension {
1080        info: ExtensionInfo,
1081        state: ExtensionState,
1082        data: HashMap<String, String>,
1083    }
1084
1085    impl TestExtension {
1086        fn new(name: &str) -> Self {
1087            Self {
1088                info: ExtensionInfo::new(
1089                    name.to_string(),
1090                    "1.0.0".to_string(),
1091                    "Test".to_string(),
1092                    "Test extension".to_string(),
1093                ),
1094                state: ExtensionState::Uninitialized,
1095                data: HashMap::new(),
1096            }
1097        }
1098    }
1099
1100    impl EditorExtension for TestExtension {
1101        fn info(&self) -> &ExtensionInfo {
1102            &self.info
1103        }
1104
1105        fn initialize(&mut self, _context: &mut dyn ExtensionContext) -> Result<()> {
1106            self.state = ExtensionState::Active;
1107            Ok(())
1108        }
1109
1110        fn shutdown(&mut self, _context: &mut dyn ExtensionContext) -> Result<()> {
1111            self.state = ExtensionState::Shutdown;
1112            Ok(())
1113        }
1114
1115        fn state(&self) -> ExtensionState {
1116            self.state
1117        }
1118
1119        fn execute_command(
1120            &mut self,
1121            command_id: &str,
1122            _args: &HashMap<String, String>,
1123            _context: &mut dyn ExtensionContext,
1124        ) -> Result<ExtensionResult> {
1125            match command_id {
1126                "test-command" => Ok(ExtensionResult::success_with_message(
1127                    "Command executed".to_string(),
1128                )),
1129                _ => Ok(ExtensionResult::failure("Unknown command".to_string())),
1130            }
1131        }
1132
1133        fn commands(&self) -> Vec<ExtensionCommand> {
1134            vec![ExtensionCommand::new(
1135                "test-command".to_string(),
1136                "Test Command".to_string(),
1137                "A test command".to_string(),
1138            )]
1139        }
1140
1141        fn get_data(&self, key: &str) -> Option<String> {
1142            self.data.get(key).cloned()
1143        }
1144
1145        fn set_data(&mut self, key: String, value: String) -> Result<()> {
1146            self.data.insert(key, value);
1147            Ok(())
1148        }
1149    }
1150
1151    #[test]
1152    fn extension_manager_lifecycle() {
1153        let mut manager = ExtensionManager::new();
1154        let _doc = EditorDocument::new();
1155
1156        let extension = Box::new(TestExtension::new("test-ext"));
1157        manager.load_extension(extension).unwrap();
1158
1159        assert_eq!(manager.list_extensions().len(), 1);
1160        assert_eq!(
1161            manager.get_extension_state("test-ext"),
1162            Some(ExtensionState::Uninitialized)
1163        );
1164
1165        {
1166            // We can't directly test initialization with the new context due to borrowing constraints
1167            // The manager needs mutable access to create the context
1168        }
1169
1170        // Test that we can check states after operations
1171        let extension_exists = manager.list_extensions().contains(&"test-ext".to_string());
1172        assert!(extension_exists);
1173    }
1174
1175    #[test]
1176    fn editor_context() {
1177        let mut manager = ExtensionManager::new();
1178
1179        // Test config through manager first
1180        manager.set_config("test".to_string(), "value".to_string());
1181        assert_eq!(manager.get_config("test"), Some("value".to_string()));
1182
1183        // Test extension data through manager
1184        manager.set_extension_data("default".to_string(), "key".to_string(), "data".to_string());
1185        assert_eq!(
1186            manager.get_extension_data("default", "key"),
1187            Some("data".to_string())
1188        );
1189    }
1190}