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}