1use std::collections::HashMap;
36use std::fmt;
37use std::sync::RwLock;
38
39use crate::event::Key;
40
41pub fn keys(names: &str) -> Vec<Key> {
47 try_keys(names).unwrap_or_else(|name| panic!("unknown key {name:?}"))
48}
49
50pub fn try_keys(names: &str) -> Result<Vec<Key>, String> {
67 let names = key_names(names)?;
68 names
69 .iter()
70 .map(|name| Key::parse(name).ok_or_else(|| name.clone()))
71 .collect()
72}
73
74fn key_names(text: &str) -> Result<Vec<String>, String> {
77 let mut names = Vec::new();
78 let mut name = String::new();
79 let mut chars = text.chars();
80 while let Some(c) = chars.next() {
81 match c {
82 '"' | '\'' if name.is_empty() => {
83 let mut quoted = String::new();
84 let mut closed = false;
85 for d in chars.by_ref() {
86 if d == c {
87 closed = true;
88 break;
89 }
90 quoted.push(d);
91 }
92 if !closed || quoted.is_empty() {
93 return Err(text.trim().to_string());
94 }
95 names.push(quoted);
96 }
97 c if c.is_whitespace() || c == ',' => {
98 if !name.is_empty() {
99 names.push(std::mem::take(&mut name));
100 }
101 }
102 c => name.push(c),
103 }
104 }
105 if !name.is_empty() {
106 names.push(name);
107 }
108 if names.is_empty() && !text.trim().is_empty() {
109 return Err(text.trim().to_string());
110 }
111 Ok(names)
112}
113
114#[derive(Clone, Debug, PartialEq, Eq)]
116pub struct Binding {
117 pub context: String,
119 pub action: String,
121 pub keys: Vec<Key>,
123 pub description: String,
125 pub sequence: bool,
129 pub unavailable: Option<String>,
132}
133
134impl Binding {
135 pub fn new(
136 context: impl Into<String>,
137 action: impl Into<String>,
138 keys: impl IntoIterator<Item = Key>,
139 description: impl Into<String>,
140 ) -> Binding {
141 Binding {
142 context: context.into(),
143 action: action.into(),
144 keys: keys.into_iter().collect(),
145 description: description.into(),
146 sequence: false,
147 unavailable: None,
148 }
149 }
150
151 pub fn sequence(
161 context: impl Into<String>,
162 action: impl Into<String>,
163 keys: impl IntoIterator<Item = Key>,
164 description: impl Into<String>,
165 ) -> Binding {
166 Binding {
167 sequence: true,
168 ..Binding::new(context, action, keys, description)
169 }
170 }
171
172 #[must_use]
182 pub fn unavailable(mut self, reason: impl Into<String>) -> Binding {
183 self.unavailable = Some(reason.into());
184 self
185 }
186
187 pub fn id(&self) -> String {
189 format!("{}.{}", self.context, self.action)
190 }
191
192 pub fn keys_label(&self) -> String {
195 self.keys
196 .iter()
197 .map(Key::to_string)
198 .collect::<Vec<_>>()
199 .join(if self.sequence { " " } else { "/" })
200 }
201
202 pub fn hint(&self) -> String {
205 crate::chrome::keys_hint_of(&self.keys, self.sequence)
206 }
207}
208
209impl fmt::Display for Binding {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "{} {}", self.keys_label(), self.description)
212 }
213}
214
215#[derive(Clone, Debug, Default)]
217pub struct Keymap {
218 context: String,
219 bindings: Vec<Binding>,
220 rebound: HashMap<String, Vec<Key>>,
222}
223
224impl Keymap {
225 pub fn new(context: impl Into<String>) -> Keymap {
227 Keymap {
228 context: context.into(),
229 ..Keymap::default()
230 }
231 }
232
233 pub fn context(&self) -> &str {
235 &self.context
236 }
237
238 #[must_use]
240 pub fn bind(
241 mut self,
242 action: impl Into<String>,
243 keys: impl IntoIterator<Item = Key>,
244 description: impl Into<String>,
245 ) -> Keymap {
246 let binding = Binding::new(self.context.clone(), action, keys, description);
247 self.add(binding);
248 self
249 }
250
251 pub fn add(&mut self, binding: Binding) {
254 match self
255 .bindings
256 .iter_mut()
257 .find(|b| b.context == binding.context && b.action == binding.action)
258 {
259 Some(existing) => *existing = binding,
260 None => self.bindings.push(binding),
261 }
262 }
263
264 pub fn extend(&mut self, other: Keymap) {
268 for (id, keys) in other.rebound {
269 self.rebound.entry(id).or_insert(keys);
270 }
271 for binding in other.bindings {
272 if !self
273 .bindings
274 .iter()
275 .any(|b| b.context == binding.context && b.action == binding.action)
276 {
277 self.bindings.push(binding);
278 }
279 }
280 }
281
282 pub fn rebind(&mut self, action: &str, keys: impl IntoIterator<Item = Key>) {
285 let id = format!("{}.{action}", self.context);
286 self.rebound.insert(id, keys.into_iter().collect());
287 }
288
289 #[must_use]
291 pub fn rebound(mut self, action: &str, keys: impl IntoIterator<Item = Key>) -> Keymap {
292 self.rebind(action, keys);
293 self
294 }
295
296 pub fn apply(&mut self, overrides: &Overrides) {
298 for (id, keys) in &overrides.keys {
299 self.rebound.insert(id.clone(), keys.clone());
300 }
301 }
302
303 fn source(&self, binding: &Binding) -> (u8, Vec<Key>) {
306 if self.plain() {
307 return (0, binding.keys.clone());
308 }
309 let id = binding.id();
310 if let Some(keys) = self.rebound.get(&id) {
311 return (2, keys.clone());
312 }
313 if let Some(keys) = installed_keys(&id) {
314 return (1, keys);
315 }
316 (0, binding.keys.clone())
317 }
318
319 fn plain(&self) -> bool {
322 self.rebound.is_empty() && !any_installed()
323 }
324
325 fn effective(&self, binding: &Binding) -> Vec<Key> {
329 if self.plain() {
330 return binding.keys.clone();
332 }
333 let (level, mut keys) = self.source(binding);
334 if level < 2 {
335 for other in &self.bindings {
336 if other.context != binding.context || other.action == binding.action {
337 continue;
338 }
339 let (other_level, taken) = self.source(other);
340 if other_level > level {
341 keys.retain(|key| !taken.contains(key));
342 }
343 }
344 }
345 keys
346 }
347
348 fn find(&self, key: Key) -> Option<usize> {
353 self.find_by(|keys| keys.contains(&key))
354 .or_else(|| self.find_by(|keys| key.matches_any(keys)))
355 }
356
357 fn find_by(&self, fires: impl Fn(&[Key]) -> bool) -> Option<usize> {
358 if self.plain() {
359 return (self.bindings.iter())
362 .position(|binding| !binding.sequence && fires(&binding.keys));
363 }
364 let mut found: Option<(u8, usize)> = None;
365 for (index, binding) in self.bindings.iter().enumerate() {
366 if binding.sequence {
367 continue;
368 }
369 let (level, _) = self.source(binding);
370 if found.is_some_and(|(best, _)| best >= level) {
371 continue;
372 }
373 if fires(&self.effective(binding)) {
374 found = Some((level, index));
375 }
376 }
377 found.map(|(_, index)| index)
378 }
379
380 pub fn lookup(&self, key: Key) -> Option<Binding> {
382 self.find(key).map(|index| {
383 let binding = &self.bindings[index];
384 Binding {
385 keys: self.effective(binding),
386 ..binding.clone()
387 }
388 })
389 }
390
391 pub fn action(&self, key: Key) -> Option<&str> {
396 self.find(key)
397 .map(|index| self.bindings[index].action.as_str())
398 }
399
400 pub fn is(&self, key: Key, action: &str) -> bool {
402 key.matches_any(&self.keys(action))
403 }
404
405 pub fn keys(&self, action: &str) -> Vec<Key> {
407 self.bindings
408 .iter()
409 .find(|binding| binding.action == action)
410 .map(|binding| self.effective(binding))
411 .unwrap_or_default()
412 }
413
414 pub fn key(&self, action: &str) -> Option<Key> {
416 self.keys(action).first().copied()
417 }
418
419 pub fn bindings(&self) -> Vec<Binding> {
421 self.bindings
422 .iter()
423 .map(|binding| Binding {
424 keys: self.effective(binding),
425 ..binding.clone()
426 })
427 .collect()
428 }
429
430 pub fn declared(&self) -> &[Binding] {
432 &self.bindings
433 }
434
435 pub fn is_empty(&self) -> bool {
436 self.bindings.is_empty()
437 }
438
439 pub fn len(&self) -> usize {
440 self.bindings.len()
441 }
442}
443
444#[derive(Clone, Debug, Default, PartialEq, Eq)]
446pub struct Overrides {
447 keys: HashMap<String, Vec<Key>>,
448}
449
450impl Overrides {
451 pub fn new() -> Overrides {
452 Overrides::default()
453 }
454
455 pub fn set(
457 &mut self,
458 context: &str,
459 action: &str,
460 keys: impl IntoIterator<Item = Key>,
461 ) -> &mut Self {
462 self.keys
463 .insert(format!("{context}.{action}"), keys.into_iter().collect());
464 self
465 }
466
467 pub fn get(&self, id: &str) -> Option<&[Key]> {
469 self.keys.get(id).map(Vec::as_slice)
470 }
471
472 pub fn is_empty(&self) -> bool {
473 self.keys.is_empty()
474 }
475
476 pub fn from_pairs<I, A, K>(pairs: I) -> Result<Overrides, String>
479 where
480 I: IntoIterator<Item = (A, K)>,
481 A: AsRef<str>,
482 K: AsRef<str>,
483 {
484 let mut overrides = Overrides::new();
485 for (id, names) in pairs {
486 let id = id.as_ref().trim();
487 let Some((context, action)) = id.rsplit_once('.') else {
488 return Err(format!("{id:?} is not context.action"));
489 };
490 if context.is_empty() || action.is_empty() {
491 return Err(format!("{id:?} is not context.action"));
492 }
493 let keys = try_keys(names.as_ref()).map_err(|name| {
494 if name.contains([',', '"', '\'']) {
495 format!("{id}: no key in {name:?} (the comma key is `comma`, or quoted: \",\")")
496 } else {
497 format!("{id}: unknown key {name:?}")
498 }
499 })?;
500 overrides.set(context, action, keys);
501 }
502 Ok(overrides)
503 }
504
505 pub fn parse(text: &str) -> Result<Overrides, String> {
521 let mut pairs = Vec::new();
522 for (number, line) in text.lines().enumerate() {
523 let line = strip_comment(line).trim();
524 if line.is_empty() {
525 continue;
526 }
527 let Some((id, names)) = line.split_once('=') else {
528 return Err(format!(
529 "line {}: expected context.action = keys",
530 number + 1
531 ));
532 };
533 let (id, names) = (id.trim(), names.trim());
534 if names.is_empty() {
535 return Err(format!(
536 "line {}: {id} has no keys (quote a \"#\" key; `none` unbinds)",
537 number + 1
538 ));
539 }
540 let names = if names.eq_ignore_ascii_case("none") {
541 ""
542 } else {
543 names
544 };
545 pairs.push((id.to_string(), names.to_string()));
546 }
547 Overrides::from_pairs(pairs)
548 }
549}
550
551fn strip_comment(line: &str) -> &str {
554 let mut quote = None;
555 let mut previous = ' ';
556 for (index, c) in line.char_indices() {
557 match quote {
558 Some(open) if c == open => quote = None,
559 Some(_) => {}
560 None if c == '"' || c == '\'' => quote = Some(c),
561 None if c == '#' && previous.is_whitespace() => return &line[..index],
562 None => {}
563 }
564 previous = c;
565 }
566 line
567}
568
569static INSTALLED: RwLock<Option<Overrides>> = RwLock::new(None);
570
571pub fn install(overrides: Overrides) {
574 let mut installed = INSTALLED.write().unwrap_or_else(|e| e.into_inner());
575 *installed = (!overrides.is_empty()).then_some(overrides);
576}
577
578pub fn installed() -> Overrides {
580 INSTALLED
581 .read()
582 .unwrap_or_else(|e| e.into_inner())
583 .clone()
584 .unwrap_or_default()
585}
586
587fn any_installed() -> bool {
589 let installed = INSTALLED.read().unwrap_or_else(|e| e.into_inner());
590 installed.as_ref().is_some_and(|keys| !keys.is_empty())
591}
592
593fn installed_keys(id: &str) -> Option<Vec<Key>> {
594 let installed = INSTALLED.read().unwrap_or_else(|e| e.into_inner());
595 installed.as_ref()?.get(id).map(<[Key]>::to_vec)
596}
597
598#[cfg(test)]
599mod tests {
600 use super::*;
601
602 #[test]
603 fn parses_overrides() {
604 let overrides = Overrides::parse(
605 "# mine\nselect.down = ctrl+n, down\n\ntabs.next = alt+l # comment\nx.y = none\n",
606 )
607 .unwrap();
608 assert_eq!(
609 overrides.get("select.down").unwrap(),
610 [Key::ctrl('n'), Key::new(crate::KeyCode::Down)]
611 );
612 assert_eq!(overrides.get("x.y").unwrap(), []);
613 assert!(Overrides::parse("nodot = up").is_err());
614 assert!(Overrides::parse("a.b = nokey").is_err());
615 assert!(Overrides::parse("a.b up").is_err());
616 }
617
618 #[test]
619 fn installed_overrides_apply_below_local_ones() {
620 let keymap = Keymap::new("keymap-test-context").bind("go", keys("g"), "go");
621 let mut overrides = Overrides::new();
622 overrides.set("keymap-test-context", "go", keys("h"));
623 install(overrides);
624 assert_eq!(keymap.action(Key::char('h')), Some("go"));
625 assert_eq!(keymap.action(Key::char('g')), None);
626 let local = keymap.clone().rebound("go", keys("j"));
627 assert_eq!(local.action(Key::char('j')), Some("go"));
628 install(Overrides::new());
629 assert_eq!(keymap.action(Key::char('g')), Some("go"));
630 }
631
632 #[test]
633 fn extends_and_lists() {
634 let mut outer = Keymap::new("outer").bind("quit", keys("q"), "quit");
635 let inner = Keymap::new("inner")
636 .bind("down", keys("down j"), "move down")
637 .rebound("down", keys("n"));
638 outer.extend(inner);
639 let listed: Vec<String> = outer.bindings().iter().map(ToString::to_string).collect();
640 assert_eq!(listed, ["q quit", "n move down"]);
641 assert_eq!(outer.lookup(Key::char('n')).unwrap().id(), "inner.down");
642 }
643
644 #[test]
645 fn a_legacy_key_does_the_action_of_any_key_it_could_be() {
646 use crate::KeyCode;
647 let keymap = Keymap::new("legacy-test")
648 .bind("indent", keys("ctrl+i"), "indent")
649 .bind("submit", keys("ctrl+m"), "submit");
650 let tab = Key::new(KeyCode::Tab);
651 assert_eq!(keymap.action(tab), Some("indent"));
652 assert!(keymap.is(tab, "indent"));
653 assert_eq!(keymap.action(Key::new(KeyCode::Enter)), Some("submit"));
654 assert_eq!(keymap.action(tab.exact()), None);
656 assert!(!keymap.is(tab.exact(), "indent"));
657 assert_eq!(keymap.action(Key::ctrl('i').exact()), Some("indent"));
658 let keymap = keymap.bind("next", keys("tab"), "next field");
660 assert_eq!(keymap.action(tab), Some("next"));
661 assert_eq!(keymap.action(Key::ctrl('i')), Some("indent"));
662 }
663
664 #[test]
665 fn a_rebound_key_moves_from_an_earlier_action() {
666 let keymap = Keymap::new("move-test")
667 .bind("next", keys("n"), "next match")
668 .bind("down", keys("down j"), "scroll down")
669 .rebound("down", keys("n"));
670 assert_eq!(keymap.action(Key::char('n')), Some("down"));
671 assert_eq!(keymap.lookup(Key::char('n')).unwrap().action, "down");
672 assert_eq!(keymap.keys("next"), []);
674 assert!(!keymap.is(Key::char('n'), "next"));
675 let keymap = keymap.rebound("next", keys("m"));
677 assert_eq!(keymap.action(Key::char('m')), Some("next"));
678 }
679}