1use crossterm::event::{KeyCode, KeyModifiers};
8use std::collections::HashMap;
9
10#[derive(Debug, Clone)]
12pub struct BufferMode {
13 pub name: String,
15
16 pub parent: Option<String>,
18
19 pub keybindings: HashMap<(KeyCode, KeyModifiers), String>,
21
22 pub chord_keybindings: HashMap<Vec<(KeyCode, KeyModifiers)>, String>,
24
25 pub read_only: bool,
27}
28
29impl BufferMode {
30 pub fn new(name: impl Into<String>) -> Self {
32 Self {
33 name: name.into(),
34 parent: None,
35 keybindings: HashMap::new(),
36 chord_keybindings: HashMap::new(),
37 read_only: false,
38 }
39 }
40
41 pub fn with_parent(mut self, parent: impl Into<String>) -> Self {
43 self.parent = Some(parent.into());
44 self
45 }
46
47 pub fn with_binding(
49 mut self,
50 code: KeyCode,
51 modifiers: KeyModifiers,
52 command: impl Into<String>,
53 ) -> Self {
54 self.keybindings.insert((code, modifiers), command.into());
55 self
56 }
57
58 pub fn with_chord_binding(
60 mut self,
61 sequence: Vec<(KeyCode, KeyModifiers)>,
62 command: impl Into<String>,
63 ) -> Self {
64 self.chord_keybindings.insert(sequence, command.into());
65 self
66 }
67
68 pub fn with_read_only(mut self, read_only: bool) -> Self {
70 self.read_only = read_only;
71 self
72 }
73
74 pub fn with_bindings(mut self, bindings: Vec<(KeyCode, KeyModifiers, String)>) -> Self {
76 for (code, modifiers, command) in bindings {
77 self.keybindings.insert((code, modifiers), command);
78 }
79 self
80 }
81}
82
83#[derive(Debug, Clone)]
87pub struct ModeRegistry {
88 modes: HashMap<String, BufferMode>,
90}
91
92impl ModeRegistry {
93 pub fn new() -> Self {
95 let mut registry = Self {
96 modes: HashMap::new(),
97 };
98
99 let special_mode = BufferMode::new("special")
103 .with_read_only(true)
104 .with_binding(KeyCode::Char('q'), KeyModifiers::NONE, "close")
105 .with_binding(KeyCode::Esc, KeyModifiers::NONE, "close");
106
107 registry.register(special_mode);
108
109 registry
110 }
111
112 pub fn register(&mut self, mode: BufferMode) {
114 self.modes.insert(mode.name.clone(), mode);
115 }
116
117 pub fn get(&self, name: &str) -> Option<&BufferMode> {
119 self.modes.get(name)
120 }
121
122 fn normalize_key(code: KeyCode, modifiers: KeyModifiers) -> (KeyCode, KeyModifiers) {
129 if code == KeyCode::BackTab {
133 return (code, modifiers.difference(KeyModifiers::SHIFT));
134 }
135 if let KeyCode::Char(c) = code {
136 if c.is_ascii_uppercase() {
137 return (
139 KeyCode::Char(c.to_ascii_lowercase()),
140 modifiers | KeyModifiers::SHIFT,
141 );
142 }
143 }
145 (code, modifiers)
146 }
147
148 pub fn resolve_keybinding(
152 &self,
153 mode_name: &str,
154 code: KeyCode,
155 modifiers: KeyModifiers,
156 ) -> Option<String> {
157 let mut current_mode_name = Some(mode_name);
158
159 let (code, modifiers) = Self::normalize_key(code, modifiers);
161
162 while let Some(name) = current_mode_name {
164 if let Some(mode) = self.modes.get(name) {
165 if let Some(command) = mode.keybindings.get(&(code, modifiers)) {
167 return Some(command.clone());
168 }
169
170 current_mode_name = mode.parent.as_deref();
172 } else {
173 break;
175 }
176 }
177
178 None
179 }
180
181 pub fn is_read_only(&self, mode_name: &str) -> bool {
183 let mut current_mode_name = Some(mode_name);
184
185 while let Some(name) = current_mode_name {
187 if let Some(mode) = self.modes.get(name) {
188 if mode.read_only {
189 return true;
190 }
191 current_mode_name = mode.parent.as_deref();
192 } else {
193 break;
194 }
195 }
196
197 false
198 }
199
200 pub fn is_chord_prefix(
205 &self,
206 mode_name: &str,
207 chord_state: &[(KeyCode, KeyModifiers)],
208 code: KeyCode,
209 modifiers: KeyModifiers,
210 ) -> bool {
211 let (code, modifiers) = Self::normalize_key(code, modifiers);
213
214 let mut sequence: Vec<(KeyCode, KeyModifiers)> = chord_state
216 .iter()
217 .map(|(c, m)| Self::normalize_key(*c, *m))
218 .collect();
219 sequence.push((code, modifiers));
220
221 let mut current_mode_name = Some(mode_name);
222
223 while let Some(name) = current_mode_name {
225 if let Some(mode) = self.modes.get(name) {
226 for chord_seq in mode.chord_keybindings.keys() {
228 if chord_seq.len() > sequence.len()
229 && chord_seq[..sequence.len()] == sequence[..]
230 {
231 return true;
232 }
233 }
234 current_mode_name = mode.parent.as_deref();
235 } else {
236 break;
237 }
238 }
239
240 false
241 }
242
243 pub fn resolve_chord_keybinding(
247 &self,
248 mode_name: &str,
249 chord_state: &[(KeyCode, KeyModifiers)],
250 code: KeyCode,
251 modifiers: KeyModifiers,
252 ) -> Option<String> {
253 let (code, modifiers) = Self::normalize_key(code, modifiers);
255
256 let mut sequence: Vec<(KeyCode, KeyModifiers)> = chord_state
258 .iter()
259 .map(|(c, m)| Self::normalize_key(*c, *m))
260 .collect();
261 sequence.push((code, modifiers));
262
263 tracing::trace!(
264 "resolve_chord_keybinding: mode={}, sequence={:?}",
265 mode_name,
266 sequence
267 );
268
269 let mut current_mode_name = Some(mode_name);
270
271 while let Some(name) = current_mode_name {
273 if let Some(mode) = self.modes.get(name) {
274 if let Some(command) = mode.chord_keybindings.get(&sequence) {
276 tracing::trace!(" -> found chord binding: {}", command);
277 return Some(command.clone());
278 }
279 current_mode_name = mode.parent.as_deref();
280 } else {
281 break;
282 }
283 }
284
285 tracing::trace!(" -> no chord binding found");
286 None
287 }
288
289 pub fn list_modes(&self) -> Vec<String> {
291 self.modes.keys().cloned().collect()
292 }
293
294 pub fn has_mode(&self, name: &str) -> bool {
296 self.modes.contains_key(name)
297 }
298
299 pub fn get_all_keybindings(&self, mode_name: &str) -> HashMap<(KeyCode, KeyModifiers), String> {
304 let mut result = HashMap::new();
305 let mut chain = Vec::new();
306
307 let mut current = Some(mode_name);
309 while let Some(name) = current {
310 if let Some(mode) = self.modes.get(name) {
311 chain.push(mode);
312 current = mode.parent.as_deref();
313 } else {
314 break;
315 }
316 }
317
318 for mode in chain.into_iter().rev() {
320 result.extend(mode.keybindings.clone());
321 }
322
323 result
324 }
325}
326
327impl Default for ModeRegistry {
328 fn default() -> Self {
329 Self::new()
330 }
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn test_special_mode_exists() {
339 let registry = ModeRegistry::new();
340 assert!(registry.has_mode("special"));
341 }
342
343 #[test]
344 fn test_special_mode_keybindings() {
345 let registry = ModeRegistry::new();
346 let mode = registry.get("special").unwrap();
347
348 assert_eq!(
349 mode.keybindings
350 .get(&(KeyCode::Char('q'), KeyModifiers::NONE)),
351 Some(&"close".to_string())
352 );
353 assert_eq!(
354 mode.keybindings.get(&(KeyCode::Esc, KeyModifiers::NONE)),
355 Some(&"close".to_string())
356 );
357 }
358
359 #[test]
360 fn test_mode_inheritance() {
361 let mut registry = ModeRegistry::new();
362
363 let diagnostics_mode = BufferMode::new("diagnostics-list")
365 .with_parent("special")
366 .with_binding(KeyCode::Enter, KeyModifiers::NONE, "diagnostics:goto")
367 .with_binding(KeyCode::Char('n'), KeyModifiers::NONE, "next-line");
368
369 registry.register(diagnostics_mode);
370
371 assert_eq!(
373 registry.resolve_keybinding("diagnostics-list", KeyCode::Enter, KeyModifiers::NONE),
374 Some("diagnostics:goto".to_string())
375 );
376
377 assert_eq!(
379 registry.resolve_keybinding("diagnostics-list", KeyCode::Char('q'), KeyModifiers::NONE),
380 Some("close".to_string())
381 );
382
383 assert_eq!(
385 registry.resolve_keybinding("diagnostics-list", KeyCode::Char('x'), KeyModifiers::NONE),
386 None
387 );
388 }
389
390 #[test]
391 fn test_mode_read_only_inheritance() {
392 let mut registry = ModeRegistry::new();
393
394 assert!(registry.is_read_only("special"));
396
397 let child_mode = BufferMode::new("child").with_parent("special");
399 registry.register(child_mode);
400 assert!(registry.is_read_only("child"));
401
402 let editable_mode = BufferMode::new("editable");
404 registry.register(editable_mode);
405 assert!(!registry.is_read_only("editable"));
406 }
407
408 #[test]
409 fn test_get_all_keybindings() {
410 let mut registry = ModeRegistry::new();
411
412 let child_mode = BufferMode::new("child")
413 .with_parent("special")
414 .with_binding(KeyCode::Enter, KeyModifiers::NONE, "child:action")
415 .with_binding(KeyCode::Char('q'), KeyModifiers::NONE, "child:quit");
417
418 registry.register(child_mode);
419
420 let all_bindings = registry.get_all_keybindings("child");
421
422 assert_eq!(
424 all_bindings.get(&(KeyCode::Char('q'), KeyModifiers::NONE)),
425 Some(&"child:quit".to_string())
426 );
427
428 assert_eq!(
430 all_bindings.get(&(KeyCode::Esc, KeyModifiers::NONE)),
431 Some(&"close".to_string())
432 );
433
434 assert_eq!(
436 all_bindings.get(&(KeyCode::Enter, KeyModifiers::NONE)),
437 Some(&"child:action".to_string())
438 );
439 }
440}