reovim_core/plugin/
traits.rs

1//! Core plugin traits and types
2
3use std::{any::TypeId, sync::Arc};
4
5use tokio::sync::mpsc;
6
7use crate::{event::InnerEvent, event_bus::EventBus};
8
9use super::{PluginContext, PluginStateRegistry};
10
11/// Unique identifier for a plugin
12///
13/// Plugin IDs use a namespace:name format, e.g., "reovim:core" or "community:git".
14#[derive(Debug, Clone, PartialEq, Eq, Hash)]
15pub struct PluginId(&'static str);
16
17impl PluginId {
18    /// Create a new plugin ID
19    ///
20    /// Convention: Use "namespace:name" format
21    /// - "reovim:*" for built-in plugins
22    /// - "community:*" for community plugins
23    #[must_use]
24    pub const fn new(name: &'static str) -> Self {
25        Self(name)
26    }
27
28    /// Get the plugin ID as a string slice
29    #[must_use]
30    pub const fn as_str(&self) -> &'static str {
31        self.0
32    }
33}
34
35impl std::fmt::Display for PluginId {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        write!(f, "{}", self.0)
38    }
39}
40
41/// Trait for plugins that provide modular functionality
42///
43/// Plugins encapsulate initialization logic and can declare dependencies
44/// on other plugins. The plugin system resolves dependencies and ensures
45/// plugins are initialized in the correct order.
46///
47/// # Example
48///
49/// ```ignore
50/// pub struct MyPlugin;
51///
52/// impl Plugin for MyPlugin {
53///     fn id(&self) -> PluginId {
54///         PluginId::new("my:plugin")
55///     }
56///
57///     fn build(&self, ctx: &mut PluginContext) {
58///         // Register commands, interactors, keybindings, etc.
59///         ctx.register_command(MyCommand);
60///     }
61///
62///     fn dependencies(&self) -> Vec<TypeId> {
63///         // Declare that this plugin requires CorePlugin
64///         vec![TypeId::of::<CorePlugin>()]
65///     }
66/// }
67/// ```
68pub trait Plugin: Send + Sync + 'static {
69    /// Unique identifier for this plugin
70    fn id(&self) -> PluginId;
71
72    /// Human-readable name for this plugin
73    ///
74    /// Defaults to the type name.
75    fn name(&self) -> &'static str {
76        std::any::type_name::<Self>()
77    }
78
79    /// Description of what this plugin provides
80    fn description(&self) -> &'static str {
81        ""
82    }
83
84    /// Build phase: Register components with the plugin context
85    ///
86    /// This is where plugins register their commands, interactors,
87    /// keybindings, pickers, and handlers.
88    fn build(&self, ctx: &mut PluginContext);
89
90    /// Finalize phase after all plugins are built
91    ///
92    /// Called after all plugins have been built, useful for
93    /// initialization that depends on other plugins being present.
94    fn finish(&self, _ctx: &mut PluginContext) {}
95
96    /// Declare plugin dependencies
97    ///
98    /// Returns a list of `TypeId`s for plugins this plugin depends on.
99    /// The plugin system ensures dependencies are loaded first.
100    ///
101    /// # Example
102    ///
103    /// ```ignore
104    /// fn dependencies(&self) -> Vec<TypeId> {
105    ///     vec![TypeId::of::<CorePlugin>()]
106    /// }
107    /// ```
108    fn dependencies(&self) -> Vec<TypeId> {
109        vec![]
110    }
111
112    /// Optional plugins that should be loaded before this one if present
113    ///
114    /// Unlike dependencies, these are not required to be present.
115    fn optional_dependencies(&self) -> Vec<TypeId> {
116        vec![]
117    }
118
119    // =========================================================================
120    // NEW: Runtime Lifecycle Methods
121    // =========================================================================
122
123    /// Initialize plugin state
124    ///
125    /// Called after all plugins are built, before the event loop starts.
126    /// Plugins should register their state with the registry here.
127    ///
128    /// # Example
129    ///
130    /// ```ignore
131    /// fn init_state(&self, registry: &PluginStateRegistry) {
132    ///     registry.register(MyPluginState::new());
133    /// }
134    /// ```
135    fn init_state(&self, _registry: &PluginStateRegistry) {
136        // Default: no state to initialize
137    }
138
139    /// Subscribe to events via the event bus
140    ///
141    /// Called after all plugins have initialized their state.
142    /// Plugins should register event handlers here.
143    ///
144    /// # Arguments
145    ///
146    /// * `bus` - The event bus to subscribe to
147    /// * `state` - Shared reference to the plugin state registry
148    ///
149    /// # Example
150    ///
151    /// ```ignore
152    /// fn subscribe(&self, bus: &EventBus, state: Arc<PluginStateRegistry>) {
153    ///     let state_clone = state.clone();
154    ///     bus.subscribe::<MyEvent, _>(100, move |event, ctx| {
155    ///         state_clone.with_mut::<MyPluginState, _, _>(|s| {
156    ///             s.handle_event(event);
157    ///         });
158    ///         EventResult::Handled
159    ///     });
160    /// }
161    /// ```
162    fn subscribe(&self, _bus: &EventBus, _state: Arc<PluginStateRegistry>) {
163        // Default: no event subscriptions
164    }
165
166    /// Boot phase: Called after `EventBus` is active and initial events processed
167    ///
168    /// This is the final initialization phase, guaranteed to run after:
169    /// - All plugins have subscribed to events
170    /// - `EventBus` processor is running
171    /// - Initial events (like `RegisterLanguage`) have been dispatched
172    ///
173    /// Use this for initialization that requires the event bus to be live
174    /// and all plugins to be fully initialized.
175    ///
176    /// # Arguments
177    ///
178    /// * `bus` - The event bus (now active and processing events)
179    /// * `state` - Shared reference to the plugin state registry
180    /// * `event_tx` - Optional sender for `InnerEvent` (for spawning background tasks
181    ///   that need to signal re-renders). Plugins that don't need this can ignore it.
182    fn boot(
183        &self,
184        _bus: &EventBus,
185        _state: Arc<PluginStateRegistry>,
186        _event_tx: Option<mpsc::Sender<InnerEvent>>,
187    ) {
188        // Default: no boot-time initialization
189    }
190}