Skip to main content

reovim_kernel/ipc/
context.rs

1//! Handler context for event dispatch.
2//!
3//! `HandlerContext` provides event handlers with the ability to:
4//! - Emit new events (with automatic scope inheritance)
5//! - Request render updates
6//! - Request application quit
7//!
8//! # Design Philosophy
9//!
10//! Following "mechanism, not policy":
11//! - Context provides emission and signaling mechanisms
12//! - Policy-specific methods (mode changes, etc.) stay in modules as extension traits
13//!
14//! # Example
15//!
16//! ```ignore
17//! bus.subscribe_with_context::<MyEvent, _>(100, |event, ctx| {
18//!     // Emit a follow-up event (scope is inherited automatically)
19//!     ctx.emit(FollowUpEvent { id: event.id });
20//!
21//!     // Request a render after processing
22//!     ctx.request_render();
23//!
24//!     EventResult::Handled
25//! });
26//! ```
27
28use super::{
29    event::{DynEvent, Event, EventResult},
30    event_bus::EventSender,
31    scope::EventScope,
32};
33
34/// Context passed to event handlers during dispatch.
35///
36/// `HandlerContext` enables handlers to interact with the event system:
37/// - Emit new events with automatic scope tracking
38/// - Signal render requests
39/// - Signal quit requests
40///
41/// # Emission Modes
42///
43/// `HandlerContext` supports two emission modes:
44///
45/// 1. **Collect mode** (default): Events are collected in a Vec and returned
46///    via `take_emitted_events()` after the handler completes.
47///
48/// 2. **Direct mode** (with sender): Events are sent directly to a channel
49///    via the attached `EventSender`. Use `with_sender()` to enable this.
50///
51/// # Scope Inheritance
52///
53/// When `emit()` is called and the context has an attached scope,
54/// the new event inherits the scope. This ensures proper lifecycle
55/// tracking for chains of related events.
56///
57/// # Thread Safety
58///
59/// `HandlerContext` is not `Send` or `Sync` - it's designed to be used
60/// within a single handler invocation.
61pub struct HandlerContext<'a> {
62    /// Flag: render was requested by handler
63    render_requested: bool,
64
65    /// Flag: quit was requested by handler
66    quit_requested: bool,
67
68    /// Current scope for lifecycle tracking (inherited by emitted events)
69    scope: Option<EventScope>,
70
71    /// Events emitted during handler execution (collect mode)
72    /// These are collected and dispatched after the handler returns
73    emitted_events: Vec<DynEvent>,
74
75    /// Optional sender for direct emission mode
76    /// When present, `emit()` sends directly instead of collecting
77    sender: Option<&'a EventSender>,
78}
79
80impl<'a> HandlerContext<'a> {
81    /// Create a new handler context in collect mode.
82    ///
83    /// Events emitted via `emit()` are collected and can be retrieved
84    /// with `take_emitted_events()` after the handler completes.
85    ///
86    /// Typically called by the event processor before invoking handlers.
87    #[must_use]
88    pub const fn new() -> Self {
89        Self {
90            render_requested: false,
91            quit_requested: false,
92            scope: None,
93            emitted_events: Vec::new(),
94            sender: None,
95        }
96    }
97
98    /// Attach an event sender for direct emission mode.
99    ///
100    /// When a sender is attached, `emit()` sends events directly to the
101    /// channel instead of collecting them. This matches the runtime's
102    /// expected behavior where emitted events re-enter the event queue.
103    ///
104    /// # Example
105    ///
106    /// ```ignore
107    /// let sender = bus.sender().unwrap();
108    /// let ctx = HandlerContext::new().with_sender(&sender);
109    /// // Now ctx.emit() sends directly via channel
110    /// ```
111    #[must_use]
112    pub const fn with_sender(mut self, sender: &'a EventSender) -> Self {
113        self.sender = Some(sender);
114        self
115    }
116
117    /// Attach a scope for lifecycle tracking.
118    ///
119    /// Events emitted via `emit()` will inherit this scope.
120    #[must_use]
121    pub fn with_scope(mut self, scope: Option<EventScope>) -> Self {
122        self.scope = scope;
123        self
124    }
125
126    /// Get a reference to the current scope, if any.
127    #[must_use]
128    pub const fn scope(&self) -> Option<&EventScope> {
129        self.scope.as_ref()
130    }
131
132    /// Emit a new event from within a handler.
133    ///
134    /// Behavior depends on mode:
135    /// - **Collect mode** (no sender): Event is collected in internal Vec
136    /// - **Direct mode** (with sender): Event is sent directly to channel
137    ///
138    /// If this context has a scope, the event inherits it for proper
139    /// lifecycle tracking.
140    ///
141    /// # Scope Tracking
142    ///
143    /// When a scope is present:
144    /// 1. `scope.increment()` is called
145    /// 2. Event is wrapped with the scope
146    /// 3. After dispatch, `scope.decrement()` is called by the processor
147    ///
148    /// # Example
149    ///
150    /// ```ignore
151    /// ctx.emit(BufferModified { buffer_id: 1 });
152    /// ```
153    pub fn emit<E: Event>(&mut self, event: E) {
154        let dyn_event = if let Some(ref scope) = self.scope {
155            scope.increment();
156            DynEvent::new(event).with_scope(scope.clone())
157        } else {
158            DynEvent::new(event)
159        };
160
161        // Direct mode: send via channel
162        if let Some(sender) = self.sender {
163            sender.send_dyn(dyn_event);
164        } else {
165            // Collect mode: store for later retrieval
166            self.emitted_events.push(dyn_event);
167        }
168    }
169
170    /// Emit a pre-boxed dynamic event.
171    ///
172    /// Useful when you already have a `DynEvent` and want to emit it.
173    /// The event's existing scope (if any) is preserved.
174    pub fn emit_dyn(&mut self, event: DynEvent) {
175        if let Some(sender) = self.sender {
176            sender.send_dyn(event);
177        } else {
178            self.emitted_events.push(event);
179        }
180    }
181
182    /// Request a render update after event processing completes.
183    ///
184    /// This is a hint to the runtime that the UI should be refreshed.
185    /// The actual render is performed by the runtime, not the kernel.
186    pub const fn request_render(&mut self) {
187        self.render_requested = true;
188    }
189
190    /// Request application quit.
191    ///
192    /// This signals that the handler wants the application to terminate.
193    /// The runtime decides how to handle this request.
194    pub const fn request_quit(&mut self) {
195        self.quit_requested = true;
196    }
197
198    /// Check if render was requested.
199    #[must_use]
200    pub const fn render_requested(&self) -> bool {
201        self.render_requested
202    }
203
204    /// Check if quit was requested.
205    #[must_use]
206    pub const fn quit_requested(&self) -> bool {
207        self.quit_requested
208    }
209
210    /// Take collected emitted events.
211    ///
212    /// Called by the event processor after handler execution to get
213    /// the events that need to be dispatched.
214    ///
215    /// Note: In direct mode (with sender), this will always return
216    /// an empty Vec since events are sent immediately.
217    #[must_use]
218    pub fn take_emitted_events(&mut self) -> Vec<DynEvent> {
219        std::mem::take(&mut self.emitted_events)
220    }
221
222    /// Check if any events were emitted (collect mode only).
223    #[must_use]
224    pub const fn has_emitted_events(&self) -> bool {
225        !self.emitted_events.is_empty()
226    }
227
228    /// Get the number of emitted events (collect mode only).
229    #[must_use]
230    pub const fn emitted_event_count(&self) -> usize {
231        self.emitted_events.len()
232    }
233
234    /// Check if this context is in direct emission mode.
235    #[must_use]
236    pub const fn has_sender(&self) -> bool {
237        self.sender.is_some()
238    }
239}
240
241impl Default for HandlerContext<'_> {
242    fn default() -> Self {
243        Self::new()
244    }
245}
246
247impl std::fmt::Debug for HandlerContext<'_> {
248    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249        f.debug_struct("HandlerContext")
250            .field("render_requested", &self.render_requested)
251            .field("quit_requested", &self.quit_requested)
252            .field("has_scope", &self.scope.is_some())
253            .field("has_sender", &self.sender.is_some())
254            .field("emitted_events", &self.emitted_events.len())
255            .finish()
256    }
257}
258
259/// Result of dispatching with context.
260///
261/// Contains both the event result and any side effects from handlers.
262#[derive(Debug)]
263pub struct DispatchResult {
264    /// The event handling result
265    pub result: EventResult,
266
267    /// Whether any handler requested a render
268    pub render_requested: bool,
269
270    /// Whether any handler requested quit
271    pub quit_requested: bool,
272
273    /// Events emitted by handlers (need to be dispatched)
274    pub emitted_events: Vec<DynEvent>,
275}
276
277impl DispatchResult {
278    /// Create a new dispatch result.
279    #[must_use]
280    pub fn new(result: EventResult, ctx: &mut HandlerContext<'_>) -> Self {
281        Self {
282            result,
283            render_requested: ctx.render_requested(),
284            quit_requested: ctx.quit_requested(),
285            emitted_events: ctx.take_emitted_events(),
286        }
287    }
288
289    /// Create a result indicating no handlers matched.
290    #[must_use]
291    pub const fn not_handled() -> Self {
292        Self {
293            result: EventResult::NotHandled,
294            render_requested: false,
295            quit_requested: false,
296            emitted_events: Vec::new(),
297        }
298    }
299}