Skip to main content

reovim_module_vim/resolvers/
commandline.rs

1//! Vim command-line mode key resolver.
2//!
3//! In command-line mode (`:`, `/`, `?`), typed characters accumulate in
4//! the command-line buffer. Enter executes, Escape cancels.
5
6use {
7    reovim_driver_input::{
8        KeyCode, KeyEvent, KeyLookupState, KeySequence, ModeKeyResolver, ModeState, Modifiers,
9        ResolveContext, ResolveInput, ResolveResult,
10    },
11    reovim_kernel::api::v1::ModeId,
12    reovim_module_cmdline::CmdlineState,
13};
14
15use crate::modes::VimMode;
16
17/// Vim command-line mode key resolver.
18///
19/// Handles input for `:` (Ex commands), `/` (forward search), and `?` (backward search).
20/// Characters are accumulated in the command-line buffer until Enter or Escape.
21///
22/// # Behavior
23///
24/// - Printable characters → `InsertChar` (goes to cmdline buffer)
25/// - Enter → `NotHandled` (keybinding executes command/search)
26/// - Escape → `NotHandled` (keybinding cancels)
27/// - Backspace → `NotHandled` (keybinding deletes char)
28pub struct VimCommandLineResolver {
29    mode_id: ModeId,
30}
31
32impl VimCommandLineResolver {
33    /// Create a new command-line mode resolver.
34    #[must_use]
35    pub const fn new() -> Self {
36        Self {
37            mode_id: VimMode::COMMANDLINE_ID,
38        }
39    }
40
41    /// Check if a key should insert a character into the command-line buffer.
42    const fn is_insertable(key: &KeyEvent) -> Option<char> {
43        // Only consider keys without control/alt modifiers for insertion
44        if key.modifiers.contains(Modifiers::CTRL) || key.modifiers.contains(Modifiers::ALT) {
45            return None;
46        }
47
48        match key.code {
49            KeyCode::Char(c) => Some(c),
50            // Space is insertable
51            // Tab could be for completion (future)
52            // Enter is NOT insertable - it executes
53            _ => None,
54        }
55    }
56}
57
58impl Default for VimCommandLineResolver {
59    fn default() -> Self {
60        Self::new()
61    }
62}
63
64#[cfg_attr(coverage_nightly, coverage(off))]
65impl ModeKeyResolver for VimCommandLineResolver {
66    fn resolve_with_keymap(
67        &self,
68        key: &KeyEvent,
69        _state: &mut ModeState,
70        input: &ResolveInput<'_>,
71    ) -> ResolveResult {
72        // Check for insertable character first
73        // Route to CmdlineState extension (#482 - Generic Input Target)
74        if let Some(c) = Self::is_insertable(key) {
75            return ResolveResult::insert_char_to::<CmdlineState>(c);
76        }
77
78        // For non-insertable keys (Escape, Enter, Backspace, etc.), look up in keymap
79        let mut keys = KeySequence::new();
80        keys.push(*key);
81        let lookup_state = input.keymap.query(input.mode, &keys);
82
83        match lookup_state {
84            KeyLookupState::ExactWithLonger { exact, .. } | KeyLookupState::ExactOnly(exact) => {
85                // Execute the command
86                ResolveResult::Execute(exact, ResolveContext::default())
87            }
88            KeyLookupState::PrefixOnly => {
89                // Wait for more keys
90                ResolveResult::Pending
91            }
92            KeyLookupState::NotFound => {
93                // No binding found
94                ResolveResult::NotHandled
95            }
96        }
97    }
98
99    fn mode_id(&self) -> &ModeId {
100        &self.mode_id
101    }
102
103    fn inherits_from(&self) -> Option<&ModeId> {
104        // Command-line mode doesn't inherit from other modes
105        None
106    }
107
108    fn reset(&mut self) {
109        // No state to reset
110    }
111}