1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::time::Duration;
9
10pub mod defaults;
11pub mod leader;
12pub mod stack;
13
14pub use defaults::{
15 default_copy_mode_table, default_resize_mode_table, default_search_mode_table, KeyTableRegistry,
16};
17pub use leader::{LeaderKeyConfig, LeaderKeyState};
18pub use stack::{KeyTableActivation, KeyTableStack};
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct KeyTable {
23 pub name: String,
25 pub bindings: HashMap<KeyCombo, KeyAction>,
27}
28
29impl KeyTable {
30 pub fn new(name: impl Into<String>) -> Self {
32 Self {
33 name: name.into(),
34 bindings: HashMap::new(),
35 }
36 }
37
38 pub fn bind(&mut self, combo: KeyCombo, action: KeyAction) {
40 self.bindings.insert(combo, action);
41 }
42
43 pub fn get(&self, combo: &KeyCombo) -> Option<&KeyAction> {
45 self.bindings.get(combo)
46 }
47}
48
49#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
51pub struct KeyCombo {
52 pub key: KeyCode,
54 pub mods: KeyModifiers,
56}
57
58impl KeyCombo {
59 pub fn new(key: KeyCode, mods: KeyModifiers) -> Self {
61 Self { key, mods }
62 }
63
64 pub fn key(key: KeyCode) -> Self {
66 Self {
67 key,
68 mods: KeyModifiers::NONE,
69 }
70 }
71
72 pub fn ctrl(key: KeyCode) -> Self {
74 Self {
75 key,
76 mods: KeyModifiers::CTRL,
77 }
78 }
79
80 pub fn shift(key: KeyCode) -> Self {
82 Self {
83 key,
84 mods: KeyModifiers::SHIFT,
85 }
86 }
87
88 pub fn alt(key: KeyCode) -> Self {
90 Self {
91 key,
92 mods: KeyModifiers::ALT,
93 }
94 }
95
96 pub fn super_key(key: KeyCode) -> Self {
98 Self {
99 key,
100 mods: KeyModifiers::SUPER,
101 }
102 }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
108pub enum KeyCode {
109 KeyA,
111 KeyB,
112 KeyC,
113 KeyD,
114 KeyE,
115 KeyF,
116 KeyG,
117 KeyH,
118 KeyI,
119 KeyJ,
120 KeyK,
121 KeyL,
122 KeyM,
123 KeyN,
124 KeyO,
125 KeyP,
126 KeyQ,
127 KeyR,
128 KeyS,
129 KeyT,
130 KeyU,
131 KeyV,
132 KeyW,
133 KeyX,
134 KeyY,
135 KeyZ,
136
137 Digit0,
139 Digit1,
140 Digit2,
141 Digit3,
142 Digit4,
143 Digit5,
144 Digit6,
145 Digit7,
146 Digit8,
147 Digit9,
148
149 F1,
151 F2,
152 F3,
153 F4,
154 F5,
155 F6,
156 F7,
157 F8,
158 F9,
159 F10,
160 F11,
161 F12,
162
163 Escape,
165 Enter,
166 Tab,
167 Backspace,
168 Space,
169 Slash,
170
171 Left,
173 Right,
174 Up,
175 Down,
176
177 Home,
179 End,
180 PageUp,
181 PageDown,
182 Insert,
183 Delete,
184
185 ControlLeft,
187 ControlRight,
188 AltLeft,
189 AltRight,
190 ShiftLeft,
191 ShiftRight,
192 SuperLeft,
193 SuperRight,
194}
195
196bitflags::bitflags! {
197 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
199 pub struct KeyModifiers: u8 {
200 const NONE = 0;
202 const CTRL = 1 << 0;
204 const ALT = 1 << 1;
206 const SHIFT = 1 << 2;
208 const SUPER = 1 << 3;
210 const LEADER = 1 << 4;
212 }
213}
214
215impl KeyModifiers {
216 pub fn ctrl(self) -> bool {
218 self.contains(KeyModifiers::CTRL)
219 }
220
221 pub fn alt(self) -> bool {
223 self.contains(KeyModifiers::ALT)
224 }
225
226 pub fn shift(self) -> bool {
228 self.contains(KeyModifiers::SHIFT)
229 }
230
231 pub fn super_key(self) -> bool {
233 self.contains(KeyModifiers::SUPER)
234 }
235
236 pub fn leader(self) -> bool {
238 self.contains(KeyModifiers::LEADER)
239 }
240}
241
242#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
244pub enum ActivateKeyTableMode {
245 Persistent,
247 OneShot,
249 Timeout(Duration),
251 UntilAction(Box<KeyAction>),
253}
254
255#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
257pub enum KeyAction {
258 ActivateKeyTable {
261 name: String,
262 mode: ActivateKeyTableMode,
263 replace_current: bool,
264 },
265 PopKeyTable,
267 ClearKeyTableStack,
269
270 ActivatePaneDirection(Direction),
273 AdjustPaneSize { direction: Direction, amount: i32 },
275 SplitPane { direction: SplitDirection },
277 ClosePane,
279 ZoomPane,
281
282 ActivateTab(i32),
285 ActivateTabRelative(i32),
287 SpawnTab,
289 CloseTab,
291 MoveTab(i32),
293
294 SpawnWindow,
297 CloseWindow,
299 ToggleFullscreen,
301
302 SendString(String),
305 SendKey { key: KeyCode, mods: KeyModifiers },
307 ScrollByPage(i32),
309 ScrollByLine(i32),
311 ScrollToTop,
313 ScrollToBottom,
315 ClearScrollback,
317
318 Copy,
321 Paste,
323 CopyTo(ClipboardKind),
325 PasteFrom(ClipboardKind),
327
328 ActivateCopyMode,
331 ActivateSearchMode,
333 CopyMode(CopyModeAction),
335 Search(SearchAction),
337
338 EmitEvent { event: String, args: Vec<String> },
341 RunCommand(String),
343 Noop,
345}
346
347#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
349pub enum Direction {
350 Left,
351 Right,
352 Up,
353 Down,
354}
355
356#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
358pub enum SplitDirection {
359 Horizontal,
360 Vertical,
361}
362
363#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
365pub enum ClipboardKind {
366 Primary,
368 Clipboard,
370}
371
372#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
374pub enum CopyModeAction {
375 MoveLeft,
377 MoveRight,
378 MoveUp,
379 MoveDown,
380 MoveWordForward,
381 MoveWordBackward,
382 MoveToLineStart,
383 MoveToLineEnd,
384 MoveToTop,
385 MoveToBottom,
386
387 ToggleSelection,
389 ToggleLineSelection,
390 ToggleBlockSelection,
391
392 SearchForward,
394 SearchBackward,
395 NextMatch,
396 PrevMatch,
397
398 CopyAndExit,
400 Exit,
401}
402
403#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
405pub enum SearchAction {
406 Confirm,
408 Cancel,
410 NextMatch,
412 PrevMatch,
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419
420 #[test]
421 fn test_key_modifiers_flags() {
422 let mods = KeyModifiers::CTRL | KeyModifiers::SHIFT;
423 assert!(mods.ctrl());
424 assert!(mods.shift());
425 assert!(!mods.alt());
426 assert!(!mods.super_key());
427 assert!(!mods.leader());
428 }
429
430 #[test]
431 fn test_key_modifiers_none() {
432 let mods = KeyModifiers::NONE;
433 assert!(!mods.ctrl());
434 assert!(!mods.alt());
435 assert!(!mods.shift());
436 assert!(!mods.super_key());
437 assert!(!mods.leader());
438 }
439
440 #[test]
441 fn test_key_combo_equality() {
442 let combo1 = KeyCombo::new(KeyCode::KeyH, KeyModifiers::CTRL);
443 let combo2 = KeyCombo::new(KeyCode::KeyH, KeyModifiers::CTRL);
444 let combo3 = KeyCombo::new(KeyCode::KeyH, KeyModifiers::ALT);
445
446 assert_eq!(combo1, combo2);
447 assert_ne!(combo1, combo3);
448 }
449
450 #[test]
451 fn test_key_table_binding() {
452 let mut table = KeyTable::new("test");
453 let combo = KeyCombo::new(KeyCode::KeyH, KeyModifiers::NONE);
454 let action = KeyAction::Noop;
455
456 table.bind(combo.clone(), action.clone());
457
458 assert_eq!(table.get(&combo), Some(&action));
459 }
460
461 #[test]
462 fn test_key_combo_hash() {
463 use std::collections::HashSet;
464
465 let mut set = HashSet::new();
466 let combo = KeyCombo::new(KeyCode::KeyA, KeyModifiers::CTRL);
467
468 set.insert(combo.clone());
469 assert!(set.contains(&combo));
470 }
471}