1use std::collections::HashMap;
2
3use thiserror::Error;
4
5use crate::events::Key;
6
7#[derive(Error, Debug, Clone, PartialEq)]
9pub enum KeybindingError {
10 #[error("conflict: {0} is already bound to {1}")]
12 Conflict(String, String),
13}
14
15pub struct KeybindingsManager {
20 bindings: HashMap<String, Key>,
21 reverse: HashMap<Key, String>,
22}
23
24pub fn default_bindings() -> Vec<(String, Key)> {
51 vec![
52 ("editor.cursorLeft".into(), Key::left()),
53 ("editor.cursorRight".into(), Key::right()),
54 ("editor.cursorUp".into(), Key::up()),
55 ("editor.cursorDown".into(), Key::down()),
56 ("editor.wordForward".into(), Key::ctrl('f')),
57 ("editor.wordBackward".into(), Key::ctrl('b')),
58 ("editor.lineStart".into(), Key::home()),
59 ("editor.lineEnd".into(), Key::end()),
60 ("editor.deleteChar".into(), Key::delete()),
61 ("editor.undo".into(), Key::ctrl('-')),
62 ("editor.redo".into(), Key::ctrl_shift('z')),
63 ("editor.yankLine".into(), Key::ctrl('y')),
64 ("editor.paste".into(), Key::ctrl('v')),
65 ("editor.insertMode".into(), Key::char('i')),
66 ("editor.normalMode".into(), Key::esc()),
67 ("input.submit".into(), Key::enter()),
68 ("input.backspace".into(), Key::backspace()),
69 ("input.delete".into(), Key::delete()),
70 ("select.next".into(), Key::down()),
71 ("select.prev".into(), Key::up()),
72 ("select.confirm".into(), Key::enter()),
73 ("select.cancel".into(), Key::esc()),
74 ]
75}
76
77impl Default for KeybindingsManager {
78 fn default() -> Self {
79 let mut mgr = Self {
80 bindings: HashMap::new(),
81 reverse: HashMap::new(),
82 };
83 for (action, key) in default_bindings() {
84 mgr.bindings.insert(action.clone(), key.clone());
85 mgr.reverse.insert(key, action);
86 }
87 mgr
88 }
89}
90
91impl KeybindingsManager {
92 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn get(&self, action: &str) -> Option<&Key> {
99 self.bindings.get(action)
100 }
101
102 pub fn set(&mut self, action: &str, key: Key) -> Result<(), KeybindingError> {
106 if let Some(existing_action) = self.reverse.get(&key) &&
107 existing_action != action
108 {
109 return Err(KeybindingError::Conflict(
110 format!("{:?}", key),
111 existing_action.clone(),
112 ));
113 }
114 if let Some(old_key) = self.bindings.get(action) {
115 self.reverse.remove(old_key);
116 }
117 self.bindings.insert(action.to_string(), key.clone());
118 self.reverse.insert(key, action.to_string());
119 Ok(())
120 }
121
122 pub fn detect_conflicts(&self) -> Vec<(String, String)> {
126 let mut conflicts = Vec::new();
127 let mut seen: HashMap<&Key, &String> = HashMap::new();
128 for (action, key) in &self.bindings {
129 if let Some(other) = seen.get(key) {
130 conflicts.push((action.clone(), other.to_string()));
131 } else {
132 seen.insert(key, action);
133 }
134 }
135 conflicts
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn default_bindings_contains_navigation() {
145 let bindings = default_bindings();
146 let actions: Vec<_> = bindings.iter().map(|(a, _)| a.clone()).collect();
147 assert!(actions.contains(&"editor.cursorLeft".to_string()));
148 assert!(actions.contains(&"editor.cursorRight".to_string()));
149 assert!(actions.contains(&"input.submit".to_string()));
150 }
151
152 #[test]
153 fn manager_default_populated() {
154 let mgr = KeybindingsManager::new();
155 assert!(mgr.get("editor.cursorLeft").is_some());
156 assert!(mgr.get("input.submit").is_some());
157 }
158
159 #[test]
160 fn manager_set_and_get() {
161 let mut mgr = KeybindingsManager::new();
162 mgr.set("test.action", Key::ctrl('t')).unwrap();
163 assert_eq!(mgr.get("test.action"), Some(&Key::ctrl('t')));
164 }
165
166 #[test]
167 fn manager_detects_conflicts_in_defaults() {
168 let mgr = KeybindingsManager::new();
169 let conflicts = mgr.detect_conflicts();
170 assert!(!conflicts.is_empty());
171 }
172
173 #[test]
174 fn manager_set_conflict_error() {
175 let mut mgr = KeybindingsManager::new();
176 mgr.set("action1", Key::ctrl('x')).unwrap();
177 let result = mgr.set("action2", Key::ctrl('x'));
178 assert!(result.is_err());
179 }
180
181 #[test]
182 fn manager_overwrite_same_action() {
183 let mut mgr = KeybindingsManager::new();
184 mgr.set("action1", Key::ctrl('x')).unwrap();
185 mgr.set("action1", Key::ctrl('q')).unwrap();
186 assert_eq!(mgr.get("action1"), Some(&Key::ctrl('q')));
187 }
188}