Skip to main content

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;