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::Char('g'), KeyModifiers::NONE, "revert");
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 let KeyCode::Char(c) = code {
130 if c.is_ascii_uppercase() {
131 return (
133 KeyCode::Char(c.to_ascii_lowercase()),
134 modifiers | KeyModifiers::SHIFT,
135 );
136 }
137 }
139 (code, modifiers)
140 }
141
142 pub fn resolve_keybinding(
146 &self,
147 mode_name: &str,
148 code: KeyCode,
149 modifiers: KeyModifiers,
150 ) -> Option<String> {
151 let mut current_mode_name = Some(mode_name);
152
153 let (code, modifiers) = Self::normalize_key(code, modifiers);
155
156 while let Some(name) = current_mode_name {
158 if let Some(mode) = self.modes.get(name) {
159 if let Some(command) = mode.keybindings.get(&(code, modifiers)) {
161 return Some(command.clone());
162 }
163
164 current_mode_name = mode.parent.as_deref();
166 } else {
167 break;
169 }
170 }
171
172 None
173 }
174
175 pub fn is_read_only(&self, mode_name: &str) -> bool {
177 let mut current_mode_name = Some(mode_name);
178
179 while let Some(name) = current_mode_name {
181 if let Some(mode) = self.modes.get(name) {
182 if mode.read_only {
183 return true;
184 }
185 current_mode_name = mode.parent.as_deref();
186 } else {
187 break;
188 }
189 }
190
191 false
192 }
193
194 pub fn is_chord_prefix(
199 &self,
200 mode_name: &str,
201 chord_state: &[(KeyCode, KeyModifiers)],
202 code: KeyCode,
203 modifiers: KeyModifiers,
204 ) -> bool {
205 let (code, modifiers) = Self::normalize_key(code, modifiers);
207
208 let mut sequence: Vec<(KeyCode, KeyModifiers)> = chord_state
210 .iter()
211 .map(|(c, m)| Self::normalize_key(*c, *m))
212 .collect();
213 sequence.push((code, modifiers));
214
215 let mut current_mode_name = Some(mode_name);
216
217 while let Some(name) = current_mode_name {
219 if let Some(mode) = self.modes.get(name) {
220 for chord_seq in mode.chord_keybindings.keys() {
222 if chord_seq.len() > sequence.len()
223 && chord_seq[..sequence.len()] == sequence[..]
224 {
225 return true;
226 }
227 }
228 current_mode_name = mode.parent.as_deref();
229 } else {
230 break;
231 }
232 }
233
234 false
235 }
236
237 pub fn resolve_chord_keybinding(
241 &self,
242 mode_name: &str,
243 chord_state: &[(KeyCode, KeyModifiers)],
244 code: KeyCode,
245 modifiers: KeyModifiers,
246 ) -> Option<String> {
247 let (code, modifiers) = Self::normalize_key(code, modifiers);
249
250 let mut sequence: Vec<(KeyCode, KeyModifiers)> = chord_state
252 .iter()
253 .map(|(c, m)| Self::normalize_key(*c, *m))
254 .collect();
255 sequence.push((code, modifiers));
256
257 tracing::trace!(
258 "resolve_chord_keybinding: mode={}, sequence={:?}",
259 mode_name,
260 sequence
261 );
262
263 let mut current_mode_name = Some(mode_name);
264
265 while let Some(name) = current_mode_name {
267 if let Some(mode) = self.modes.get(name) {
268 if let Some(command) = mode.chord_keybindings.get(&sequence) {
270 tracing::trace!(" -> found chord binding: {}", command);
271 return Some(command.clone());
272 }
273 current_mode_name = mode.parent.as_deref();
274 } else {
275 break;
276 }
277 }
278
279 tracing::trace!(" -> no chord binding found");
280 None
281 }
282
283 pub fn list_modes(&self) -> Vec<String> {
285 self.modes.keys().cloned().collect()
286 }
287
288 pub fn has_mode(&self, name: &str) -> bool {
290 self.modes.contains_key(name)
291 }
292
293 pub fn get_all_keybindings(&self, mode_name: &str) -> HashMap<(KeyCode, KeyModifiers), String> {
298 let mut result = HashMap::new();
299 let mut chain = Vec::new();
300
301 let mut current = Some(mode_name);
303 while let Some(name) = current {
304 if let Some(mode) = self.modes.get(name) {
305 chain.push(mode);
306 current = mode.parent.as_deref();
307 } else {
308 break;
309 }
310 }
311
312 for mode in chain.into_iter().rev() {
314 result.extend(mode.keybindings.clone());
315 }
316
317 result
318 }
319}
320
321impl Default for ModeRegistry {
322 fn default() -> Self {
323 Self::new()
324 }
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[test]
332 fn test_special_mode_exists() {
333 let registry = ModeRegistry::new();
334 assert!(registry.has_mode("special"));
335 }
336
337 #[test]
338 fn test_special_mode_keybindings() {
339 let registry = ModeRegistry::new();
340 let mode = registry.get("special").unwrap();
341
342 assert_eq!(
343 mode.keybindings
344 .get(&(KeyCode::Char('q'), KeyModifiers::NONE)),
345 Some(&"close".to_string())
346 );
347 assert_eq!(
348 mode.keybindings
349 .get(&(KeyCode::Char('g'), KeyModifiers::NONE)),
350 Some(&"revert".to_string())
351 );
352 }
353
354 #[test]
355 fn test_mode_inheritance() {
356 let mut registry = ModeRegistry::new();
357
358 let diagnostics_mode = BufferMode::new("diagnostics-list")
360 .with_parent("special")
361 .with_binding(KeyCode::Enter, KeyModifiers::NONE, "diagnostics:goto")
362 .with_binding(KeyCode::Char('n'), KeyModifiers::NONE, "next-line");
363
364 registry.register(diagnostics_mode);
365
366 assert_eq!(
368 registry.resolve_keybinding("diagnostics-list", KeyCode::Enter, KeyModifiers::NONE),
369 Some("diagnostics:goto".to_string())
370 );
371
372 assert_eq!(
374 registry.resolve_keybinding("diagnostics-list", KeyCode::Char('q'), KeyModifiers::NONE),
375 Some("close".to_string())
376 );
377
378 assert_eq!(
380 registry.resolve_keybinding("diagnostics-list", KeyCode::Char('x'), KeyModifiers::NONE),
381 None
382 );
383 }
384
385 #[test]
386 fn test_mode_read_only_inheritance() {
387 let mut registry = ModeRegistry::new();
388
389 assert!(registry.is_read_only("special"));
391
392 let child_mode = BufferMode::new("child").with_parent("special");
394 registry.register(child_mode);
395 assert!(registry.is_read_only("child"));
396
397 let editable_mode = BufferMode::new("editable");
399 registry.register(editable_mode);
400 assert!(!registry.is_read_only("editable"));
401 }
402
403 #[test]
404 fn test_get_all_keybindings() {
405 let mut registry = ModeRegistry::new();
406
407 let child_mode = BufferMode::new("child")
408 .with_parent("special")
409 .with_binding(KeyCode::Enter, KeyModifiers::NONE, "child:action")
410 .with_binding(KeyCode::Char('q'), KeyModifiers::NONE, "child:quit");
412
413 registry.register(child_mode);
414
415 let all_bindings = registry.get_all_keybindings("child");
416
417 assert_eq!(
419 all_bindings.get(&(KeyCode::Char('q'), KeyModifiers::NONE)),
420 Some(&"child:quit".to_string())
421 );
422
423 assert_eq!(
425 all_bindings.get(&(KeyCode::Char('g'), KeyModifiers::NONE)),
426 Some(&"revert".to_string())
427 );
428
429 assert_eq!(
431 all_bindings.get(&(KeyCode::Enter, KeyModifiers::NONE)),
432 Some(&"child:action".to_string())
433 );
434 }
435}