reovim_driver_session/api/mode.rs
1//! Mode stack operations.
2//!
3//! This module provides the [`ModeApi`] trait for mode stack manipulation.
4//! Resolvers and commands use this to push, pop, and query modes.
5//!
6//! # Design
7//!
8//! Following Unix philosophy: this trait does ONE thing well - mode management.
9//!
10//! # Example
11//!
12//! ```ignore
13//! use reovim_driver_session::api::ModeApi;
14//!
15//! fn handle_escape<S: ModeApi>(session: &mut S) {
16//! // Pop back to previous mode (or stay at home mode)
17//! let _ = session.pop_mode(None);
18//! }
19//! ```
20
21use reovim_kernel::api::v1::ModeId;
22
23// Re-export transition types for convenience
24pub use crate::transition::{PopResult, TransitionContext};
25
26/// Mode stack operations.
27///
28/// Provides access to the editing mode stack for resolvers and commands.
29/// The mode stack follows vim-style semantics: modes can be pushed and
30/// popped, with a "home" mode that cannot be removed.
31pub trait ModeApi: Send {
32 /// Get the current mode (top of stack).
33 fn current_mode(&self) -> &ModeId;
34
35 /// Get the home mode (bottom of stack, cannot be popped).
36 fn home_mode(&self) -> &ModeId;
37
38 /// Get the mode stack depth.
39 fn mode_depth(&self) -> usize;
40
41 /// Check if a mode is anywhere on the stack.
42 fn is_mode_active(&self, mode: &ModeId) -> bool;
43
44 /// Get the full mode stack as a snapshot (bottom to top).
45 fn mode_stack(&self) -> Vec<ModeId>;
46
47 /// Push a mode onto the stack.
48 ///
49 /// The new mode becomes the current mode. The previous mode
50 /// remains on the stack and can be returned to via `pop_mode`.
51 fn push_mode(&mut self, mode: ModeId, ctx: TransitionContext);
52
53 /// Pop the current mode from the stack.
54 ///
55 /// Returns `Ok(())` if successful, or `Err(ModeError::CannotPopHomeMode)`
56 /// if trying to pop the home mode.
57 ///
58 /// # Arguments
59 ///
60 /// * `result` - Optional result to pass to the parent mode
61 ///
62 /// # Errors
63 ///
64 /// Returns [`ModeError::CannotPopHomeMode`] if only the home mode remains.
65 fn pop_mode(&mut self, result: Option<PopResult>) -> Result<(), ModeError>;
66
67 /// Replace the current mode (pop + push atomically).
68 ///
69 /// This is equivalent to popping and pushing, but works even when
70 /// only the home mode exists.
71 fn set_mode(&mut self, mode: ModeId, ctx: TransitionContext);
72}
73
74/// Errors from mode operations.
75#[derive(Debug, Clone, PartialEq, Eq)]
76pub enum ModeError {
77 /// Cannot pop the home mode.
78 CannotPopHomeMode,
79}
80
81impl std::fmt::Display for ModeError {
82 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83 match self {
84 Self::CannotPopHomeMode => write!(f, "cannot pop home mode"),
85 }
86 }
87}
88
89impl std::error::Error for ModeError {}
90#[cfg(test)]
91#[path = "tests/mode.rs"]
92mod tests;