Skip to main content

metarepo_core/tui/
keybindings.rs

1//! Vim-like keybindings and key handling
2
3use super::modes::Mode;
4use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
5
6/// Actions that can be triggered by key presses
7#[derive(Debug, Clone, PartialEq)]
8pub enum KeyAction {
9    // Mode transitions
10    EnterNormalMode,
11    EnterInsertMode,
12    EnterVisualMode,
13    EnterCommandMode,
14
15    // Navigation (vim-style)
16    MoveUp,
17    MoveDown,
18    MoveLeft,
19    MoveRight,
20    MoveToTop,
21    MoveToBottom,
22    MovePageUp,
23    MovePageDown,
24
25    // Tree navigation
26    ExpandNode,
27    CollapseNode,
28    ToggleNode,
29
30    // Editing
31    DeleteChar,
32    DeleteLine,
33    DeleteNode,
34    InsertChar(char),
35    NewLine,
36    Backspace,
37
38    // Commands
39    Save,
40    Quit,
41    SaveAndQuit,
42    ForceQuit,
43    ExecuteCommand(String),
44
45    // Visual mode
46    SelectUp,
47    SelectDown,
48    SelectAll,
49
50    // Misc
51    ShowHelp,
52    Refresh,
53    Undo,
54    Redo,
55    Repeat,
56    None,
57}
58
59/// Key handler that maps key events to actions based on current mode
60pub struct KeyHandler;
61
62impl KeyHandler {
63    /// Handle a key event in the given mode and return the appropriate action
64    pub fn handle(key: KeyEvent, mode: Mode) -> KeyAction {
65        match mode {
66            Mode::Normal => Self::handle_normal_mode(key),
67            Mode::Insert => Self::handle_insert_mode(key),
68            Mode::Visual => Self::handle_visual_mode(key),
69            Mode::Command => Self::handle_command_mode(key),
70        }
71    }
72
73    /// Handle keys in Normal mode
74    fn handle_normal_mode(key: KeyEvent) -> KeyAction {
75        match (key.code, key.modifiers) {
76            // Mode transitions
77            (KeyCode::Char('i'), KeyModifiers::NONE) => KeyAction::EnterInsertMode,
78            (KeyCode::Char('v'), KeyModifiers::NONE) => KeyAction::EnterVisualMode,
79            (KeyCode::Char(':'), KeyModifiers::NONE) => KeyAction::EnterCommandMode,
80
81            // Navigation - vim style (hjkl)
82            (KeyCode::Char('h'), KeyModifiers::NONE) => KeyAction::MoveLeft,
83            (KeyCode::Char('j'), KeyModifiers::NONE) => KeyAction::MoveDown,
84            (KeyCode::Char('k'), KeyModifiers::NONE) => KeyAction::MoveUp,
85            (KeyCode::Char('l'), KeyModifiers::NONE) => KeyAction::MoveRight,
86
87            // Navigation - arrow keys
88            (KeyCode::Left, _) => KeyAction::MoveLeft,
89            (KeyCode::Down, _) => KeyAction::MoveDown,
90            (KeyCode::Up, _) => KeyAction::MoveUp,
91            (KeyCode::Right, _) => KeyAction::MoveRight,
92
93            // Navigation - page/home/end
94            (KeyCode::Char('g'), KeyModifiers::NONE) => KeyAction::MoveToTop,
95            (KeyCode::Char('G'), KeyModifiers::SHIFT) => KeyAction::MoveToBottom,
96            (KeyCode::PageUp, _) | (KeyCode::Char('u'), KeyModifiers::CONTROL) => {
97                KeyAction::MovePageUp
98            }
99            (KeyCode::PageDown, _) | (KeyCode::Char('d'), KeyModifiers::CONTROL) => {
100                KeyAction::MovePageDown
101            }
102            (KeyCode::Home, _) => KeyAction::MoveToTop,
103            (KeyCode::End, _) => KeyAction::MoveToBottom,
104
105            // Tree operations
106            (KeyCode::Enter, _) | (KeyCode::Char('o'), KeyModifiers::NONE) => KeyAction::ToggleNode,
107            (KeyCode::Char('O'), KeyModifiers::SHIFT) => KeyAction::ExpandNode,
108            (KeyCode::Char('C'), KeyModifiers::SHIFT) => KeyAction::CollapseNode,
109
110            // Editing operations
111            (KeyCode::Char('d'), KeyModifiers::NONE) => KeyAction::DeleteNode,
112            (KeyCode::Char('x'), KeyModifiers::NONE) => KeyAction::DeleteChar,
113            (KeyCode::Delete, _) => KeyAction::DeleteChar,
114
115            // Quick commands
116            (KeyCode::Char('w'), KeyModifiers::CONTROL) => KeyAction::Save,
117            (KeyCode::Char('q'), KeyModifiers::CONTROL) => KeyAction::Quit,
118            (KeyCode::Char('c'), KeyModifiers::CONTROL) => KeyAction::ForceQuit,
119            (KeyCode::Esc, KeyModifiers::NONE) => KeyAction::Quit,
120
121            // Undo/Redo
122            (KeyCode::Char('u'), KeyModifiers::NONE) => KeyAction::Undo,
123            (KeyCode::Char('r'), KeyModifiers::CONTROL) => KeyAction::Redo,
124            (KeyCode::Char('.'), KeyModifiers::NONE) => KeyAction::Repeat,
125
126            // Help
127            (KeyCode::Char('?'), KeyModifiers::NONE) => KeyAction::ShowHelp,
128
129            // Refresh
130            (KeyCode::Char('r'), KeyModifiers::NONE) => KeyAction::Refresh,
131
132            _ => KeyAction::None,
133        }
134    }
135
136    /// Handle keys in Insert mode
137    fn handle_insert_mode(key: KeyEvent) -> KeyAction {
138        match (key.code, key.modifiers) {
139            // Exit insert mode
140            (KeyCode::Esc, _) => KeyAction::EnterNormalMode,
141
142            // Text editing
143            (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => {
144                KeyAction::InsertChar(c)
145            }
146            (KeyCode::Backspace, _) => KeyAction::Backspace,
147            (KeyCode::Delete, _) => KeyAction::DeleteChar,
148            (KeyCode::Enter, _) => KeyAction::NewLine,
149
150            // Navigation in insert mode
151            (KeyCode::Left, _) => KeyAction::MoveLeft,
152            (KeyCode::Right, _) => KeyAction::MoveRight,
153            (KeyCode::Up, _) => KeyAction::MoveUp,
154            (KeyCode::Down, _) => KeyAction::MoveDown,
155
156            _ => KeyAction::None,
157        }
158    }
159
160    /// Handle keys in Visual mode
161    fn handle_visual_mode(key: KeyEvent) -> KeyAction {
162        match (key.code, key.modifiers) {
163            // Exit visual mode
164            (KeyCode::Esc, _) | (KeyCode::Char('v'), KeyModifiers::NONE) => {
165                KeyAction::EnterNormalMode
166            }
167
168            // Selection navigation - vim style
169            (KeyCode::Char('h'), KeyModifiers::NONE) => KeyAction::MoveLeft,
170            (KeyCode::Char('j'), KeyModifiers::NONE) => KeyAction::SelectDown,
171            (KeyCode::Char('k'), KeyModifiers::NONE) => KeyAction::SelectUp,
172            (KeyCode::Char('l'), KeyModifiers::NONE) => KeyAction::MoveRight,
173
174            // Selection navigation - arrow keys
175            (KeyCode::Up, _) => KeyAction::SelectUp,
176            (KeyCode::Down, _) => KeyAction::SelectDown,
177
178            // Select all
179            (KeyCode::Char('a'), KeyModifiers::CONTROL) => KeyAction::SelectAll,
180
181            // Delete selection
182            (KeyCode::Char('d'), KeyModifiers::NONE) => KeyAction::DeleteNode,
183            (KeyCode::Delete, _) | (KeyCode::Char('x'), KeyModifiers::NONE) => {
184                KeyAction::DeleteNode
185            }
186
187            _ => KeyAction::None,
188        }
189    }
190
191    /// Handle keys in Command mode
192    fn handle_command_mode(key: KeyEvent) -> KeyAction {
193        match (key.code, key.modifiers) {
194            // Exit command mode
195            (KeyCode::Esc, _) => KeyAction::EnterNormalMode,
196
197            // Execute command
198            (KeyCode::Enter, _) => KeyAction::ExecuteCommand(String::new()), // Command will be filled by app
199
200            // Edit command
201            (KeyCode::Char(c), KeyModifiers::NONE | KeyModifiers::SHIFT) => {
202                KeyAction::InsertChar(c)
203            }
204            (KeyCode::Backspace, _) => KeyAction::Backspace,
205
206            _ => KeyAction::None,
207        }
208    }
209
210    /// Parse a vim command string (e.g., "w", "q", "wq", "q!")
211    pub fn parse_command(cmd: &str) -> KeyAction {
212        let cmd = cmd.trim();
213        match cmd {
214            "w" | "write" => KeyAction::Save,
215            "q" | "quit" => KeyAction::Quit,
216            "wq" | "x" => KeyAction::SaveAndQuit,
217            "q!" | "quit!" => KeyAction::ForceQuit,
218            _ => KeyAction::None,
219        }
220    }
221}