@@ -1,8 +1,11 @@
11use crate::commands;
22pub use crate::commands::Command;
3+use crate::config::Config;
34use anyhow::{anyhow, Error, Result};
45use helix_core::hashmap;
56use helix_view::document::Mode;
7+use helix_view::input::{KeyCode, KeyEvent, KeyModifiers};
8+use serde::Deserialize;
69use std::{
710 collections::HashMap,
811 fmt::Display,
@@ -99,14 +102,6 @@ use std::{
99102// D] = last diagnostic
100103// }
101104
102−// #[cfg(feature = "term")]
103−pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
104−
105−#[derive(Clone, Debug)]
106−pub struct Keymap(pub HashMap<KeyEvent, Command>);
107−#[derive(Clone, Debug)]
108−pub struct Keymaps(pub HashMap<Mode, Keymap>);
109−
110105#[macro_export]
111106macro_rules! key {
112107 ($key:ident) => {
@@ -141,9 +136,27 @@ macro_rules! alt {
141136 };
142137}
143138
139+#[derive(Debug, Clone, PartialEq, Deserialize)]
140+#[serde(transparent)]
141+pub struct Keymaps(pub HashMap<Mode, HashMap<KeyEvent, Command>>);
142+
143+impl Deref for Keymaps {
144+ type Target = HashMap<Mode, HashMap<KeyEvent, Command>>;
145+
146+ fn deref(&self) -> &Self::Target {
147+ &self.0
148+ }
149+}
150+
151+impl DerefMut for Keymaps {
152+ fn deref_mut(&mut self) -> &mut Self::Target {
153+ &mut self.0
154+ }
155+}
156+
144157impl Default for Keymaps {
145− fn default() -> Self {
146− let normal = Keymap(hashmap!(
158+ fn default() -> Keymaps {
159+ let normal = hashmap!(
147160 key!('h') => Command::move_char_left,
148161 key!('j') => Command::move_line_down,
149162 key!('k') => Command::move_line_up,
@@ -265,12 +278,12 @@ impl Default for Keymaps {
265278 key!('z') => Command::view_mode,
266279
267280 key!('"') => Command::select_register,
268− ));
281+ );
269282 // TODO: decide whether we want normal mode to also be select mode (kakoune-like), or whether
270283 // we keep this separate select mode. More keys can fit into normal mode then, but it's weird
271284 // because some selection operations can now be done from normal mode, some from select mode.
272285 let mut select = normal.clone();
273− select.0.extend(
286+ select.extend(
274287 hashmap!(
275288 key!('h') => Command::extend_char_left,
276289 key!('j') => Command::extend_line_down,
@@ -303,7 +316,7 @@ impl Default for Keymaps {
303316 // TODO: select could be normal mode with some bindings merged over
304317 Mode::Normal => normal,
305318 Mode::Select => select,
306− Mode::Insert => Keymap(hashmap!(
319+ Mode::Insert => hashmap!(
307320 key!(Esc) => Command::normal_mode as Command,
308321 key!(Backspace) => Command::delete_char_backward,
309322 key!(Delete) => Command::delete_char_forward,
@@ -319,309 +332,66 @@ impl Default for Keymaps {
319332 key!(End) => Command::move_line_end,
320333 ctrl!('x') => Command::completion,
321334 ctrl!('w') => Command::delete_word_backward,
322− )),
335+ ),
323336 ))
324337 }
325338}
326339
327−// Newtype wrapper over keys to allow toml serialization/parsing
328−#[derive(Debug, PartialEq, PartialOrd, Clone, Copy, Hash)]
329−pub struct RepresentableKeyEvent(pub KeyEvent);
330−impl Display for RepresentableKeyEvent {
331− fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
332− let Self(key) = self;
333− f.write_fmt(format_args!(
334− "{}{}{}",
335− if key.modifiers.contains(KeyModifiers::SHIFT) {
336− "S-"
337− } else {
338− ""
339− },
340− if key.modifiers.contains(KeyModifiers::ALT) {
341− "A-"
342− } else {
343− ""
344− },
345− if key.modifiers.contains(KeyModifiers::CONTROL) {
346− "C-"
347− } else {
348− ""
349− },
350− ))?;
351− match key.code {
352− KeyCode::Backspace => f.write_str("backspace")?,
353− KeyCode::Enter => f.write_str("ret")?,
354− KeyCode::Left => f.write_str("left")?,
355− KeyCode::Right => f.write_str("right")?,
356− KeyCode::Up => f.write_str("up")?,
357− KeyCode::Down => f.write_str("down")?,
358− KeyCode::Home => f.write_str("home")?,
359− KeyCode::End => f.write_str("end")?,
360− KeyCode::PageUp => f.write_str("pageup")?,
361− KeyCode::PageDown => f.write_str("pagedown")?,
362− KeyCode::Tab => f.write_str("tab")?,
363− KeyCode::BackTab => f.write_str("backtab")?,
364− KeyCode::Delete => f.write_str("del")?,
365− KeyCode::Insert => f.write_str("ins")?,
366− KeyCode::Null => f.write_str("null")?,
367− KeyCode::Esc => f.write_str("esc")?,
368− KeyCode::Char('<') => f.write_str("lt")?,
369− KeyCode::Char('>') => f.write_str("gt")?,
370− KeyCode::Char('+') => f.write_str("plus")?,
371− KeyCode::Char('-') => f.write_str("minus")?,
372− KeyCode::Char(';') => f.write_str("semicolon")?,
373− KeyCode::Char('%') => f.write_str("percent")?,
374− KeyCode::F(i) => f.write_fmt(format_args!("F{}", i))?,
375− KeyCode::Char(c) => f.write_fmt(format_args!("{}", c))?,
376− };
377− Ok(())
378− }
379−}
380−
381−impl FromStr for RepresentableKeyEvent {
382− type Err = Error;
383−
384− fn from_str(s: &str) -> Result<Self, Self::Err> {
385− let mut tokens: Vec<_> = s.split('-').collect();
386− let code = match tokens.pop().ok_or_else(|| anyhow!("Missing key code"))? {
387− "backspace" => KeyCode::Backspace,
388− "space" => KeyCode::Char(' '),
389− "ret" => KeyCode::Enter,
390− "lt" => KeyCode::Char('<'),
391− "gt" => KeyCode::Char('>'),
392− "plus" => KeyCode::Char('+'),
393− "minus" => KeyCode::Char('-'),
394− "semicolon" => KeyCode::Char(';'),
395− "percent" => KeyCode::Char('%'),
396− "left" => KeyCode::Left,
397− "right" => KeyCode::Right,
398− "up" => KeyCode::Down,
399− "home" => KeyCode::Home,
400− "end" => KeyCode::End,
401− "pageup" => KeyCode::PageUp,
402− "pagedown" => KeyCode::PageDown,
403− "tab" => KeyCode::Tab,
404− "backtab" => KeyCode::BackTab,
405− "del" => KeyCode::Delete,
406− "ins" => KeyCode::Insert,
407− "null" => KeyCode::Null,
408− "esc" => KeyCode::Esc,
409− single if single.len() == 1 => KeyCode::Char(single.chars().next().unwrap()),
410− function if function.len() > 1 && function.starts_with('F') => {
411− let function: String = function.chars().skip(1).collect();
412− let function = str::parse::<u8>(&function)?;
413− (function > 0 && function < 13)
414− .then(|| KeyCode::F(function))
415− .ok_or_else(|| anyhow!("Invalid function key '{}'", function))?
416− }
417− invalid => return Err(anyhow!("Invalid key code '{}'", invalid)),
418− };
419−
420− let mut modifiers = KeyModifiers::empty();
421− for token in tokens {
422− let flag = match token {
423− "S" => KeyModifiers::SHIFT,
424− "A" => KeyModifiers::ALT,
425− "C" => KeyModifiers::CONTROL,
426− _ => return Err(anyhow!("Invalid key modifier '{}-'", token)),
427− };
428−
429− if modifiers.contains(flag) {
430− return Err(anyhow!("Repeated key modifier '{}-'", token));
431− }
432− modifiers.insert(flag);
433− }
434−
435− Ok(RepresentableKeyEvent(KeyEvent { code, modifiers }))
436− }
437−}
438−
439−pub fn parse_keymaps(toml_keymaps: &HashMap<String, HashMap<String, String>>) -> Result<Keymaps> {
440− let mut keymaps = Keymaps::default();
441−
442− for (mode, map) in toml_keymaps {
443− let mode = Mode::from_str(&mode)?;
444− for (key, command) in map {
445− let key = str::parse::<RepresentableKeyEvent>(&key)?;
446− let command = str::parse::<Command>(&command)?;
447− keymaps.0.get_mut(&mode).unwrap().0.insert(key.0, command);
448− }
449− }
450− Ok(keymaps)
451−}
452−
453−impl Deref for Keymap {
454− type Target = HashMap<KeyEvent, Command>;
455− fn deref(&self) -> &Self::Target {
456− &self.0
457− }
458−}
459−
460−impl Deref for Keymaps {
461− type Target = HashMap<Mode, Keymap>;
462− fn deref(&self) -> &Self::Target {
463− &self.0
464− }
465−}
466−
467−impl DerefMut for Keymap {
468− fn deref_mut(&mut self) -> &mut Self::Target {
469− &mut self.0
470− }
471−}
472−
473−impl DerefMut for Keymaps {
474− fn deref_mut(&mut self) -> &mut Self::Target {
475− &mut self.0
340+/// Merge default config keys with user overwritten keys for custom
341+/// user config.
342+pub fn merge_keys(mut config: Config) -> Config {
343+ let mut delta = std::mem::take(&mut config.keys);
344+ for (mode, keys) in &mut *config.keys {
345+ keys.extend(delta.remove(mode).unwrap_or_default());
476346 }
347+ config
477348}
478349
479−#[cfg(test)]
480−mod test {
481− use crate::config::Config;
482−
483− use super::*;
484−
485− impl PartialEq for Command {
486− fn eq(&self, other: &Self) -> bool {
487− self.name() == other.name()
488− }
489− }
490−
491− #[test]
492− fn parsing_keymaps_config_file() {
493− let sample_keymaps = r#"
494− [keys.insert]
495− y = "move_line_down"
496− S-C-a = "delete_selection"
497−
498− [keys.normal]
499− A-F12 = "move_next_word_end"
500− "#;
501−
502− let config: Config = toml::from_str(sample_keymaps).unwrap();
503− assert_eq!(
504− *config
505− .keymaps
506− .0
507− .get(&Mode::Insert)
508− .unwrap()
509− .0
510− .get(&KeyEvent {
511− code: KeyCode::Char('y'),
512− modifiers: KeyModifiers::NONE
513− })
514− .unwrap(),
515− Command::move_line_down
516− );
517− assert_eq!(
518− *config
519− .keymaps
520− .0
521− .get(&Mode::Insert)
522− .unwrap()
523− .0
524− .get(&KeyEvent {
525− code: KeyCode::Char('a'),
526− modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL
527− })
528− .unwrap(),
529− Command::delete_selection
530− );
531− assert_eq!(
532− *config
533− .keymaps
534− .0
535− .get(&Mode::Normal)
536− .unwrap()
537− .0
538− .get(&KeyEvent {
539− code: KeyCode::F(12),
540− modifiers: KeyModifiers::ALT
541− })
542− .unwrap(),
543− Command::move_next_word_end
544− );
545− }
546−
547− #[test]
548− fn parsing_unmodified_keys() {
549− assert_eq!(
550− str::parse::<RepresentableKeyEvent>("backspace").unwrap(),
551− RepresentableKeyEvent(KeyEvent {
552− code: KeyCode::Backspace,
553− modifiers: KeyModifiers::NONE
554− })
555− );
556−
557− assert_eq!(
558− str::parse::<RepresentableKeyEvent>("left").unwrap(),
559− RepresentableKeyEvent(KeyEvent {
560− code: KeyCode::Left,
561− modifiers: KeyModifiers::NONE
562− })
563− );
564−
565− assert_eq!(
566− str::parse::<RepresentableKeyEvent>(",").unwrap(),
567− RepresentableKeyEvent(KeyEvent {
568− code: KeyCode::Char(','),
569− modifiers: KeyModifiers::NONE
570− })
571− );
572−
573− assert_eq!(
574− str::parse::<RepresentableKeyEvent>("w").unwrap(),
575− RepresentableKeyEvent(KeyEvent {
576− code: KeyCode::Char('w'),
350+#[test]
351+fn merge_partial_keys() {
352+ let config = Config {
353+ keys: Keymaps(hashmap! {
354+ Mode::Normal => hashmap! {
355+ KeyEvent {
356+ code: KeyCode::Char('i'),
357+ modifiers: KeyModifiers::NONE,
358+ } => Command::normal_mode,
359+ KeyEvent { // key that does not exist
360+ code: KeyCode::Char('无'),
361+ modifiers: KeyModifiers::NONE,
362+ } => Command::insert_mode,
363+ },
364+ }),
365+ ..Default::default()
366+ };
367+ let merged_config = merge_keys(config.clone());
368+ assert_ne!(config, merged_config);
369+ assert_eq!(
370+ *merged_config
371+ .keys
372+ .0
373+ .get(&Mode::Normal)
374+ .unwrap()
375+ .get(&KeyEvent {
376+ code: KeyCode::Char('i'),
577377 modifiers: KeyModifiers::NONE
578378 })
579− );
580−
581− assert_eq!(
582− str::parse::<RepresentableKeyEvent>("F12").unwrap(),
583− RepresentableKeyEvent(KeyEvent {
584− code: KeyCode::F(12),
379+ .unwrap(),
380+ Command::normal_mode
381+ );
382+ assert_eq!(
383+ *merged_config
384+ .keys
385+ .0
386+ .get(&Mode::Normal)
387+ .unwrap()
388+ .get(&KeyEvent {
389+ code: KeyCode::Char('无'),
585390 modifiers: KeyModifiers::NONE
586391 })
587− );
588− }
589−
590− fn parsing_modified_keys() {
591− assert_eq!(
592− str::parse::<RepresentableKeyEvent>("S-minus").unwrap(),
593− RepresentableKeyEvent(KeyEvent {
594− code: KeyCode::Char('-'),
595− modifiers: KeyModifiers::SHIFT
596− })
597− );
598−
599− assert_eq!(
600− str::parse::<RepresentableKeyEvent>("C-A-S-F12").unwrap(),
601− RepresentableKeyEvent(KeyEvent {
602− code: KeyCode::F(12),
603− modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL | KeyModifiers::ALT
604− })
605− );
606−
607− assert_eq!(
608− str::parse::<RepresentableKeyEvent>("S-C-2").unwrap(),
609− RepresentableKeyEvent(KeyEvent {
610− code: KeyCode::F(2),
611− modifiers: KeyModifiers::SHIFT | KeyModifiers::CONTROL
612− })
613− );
614− }
615−
616− #[test]
617− fn parsing_nonsensical_keys_fails() {
618− assert!(str::parse::<RepresentableKeyEvent>("F13").is_err());
619− assert!(str::parse::<RepresentableKeyEvent>("F0").is_err());
620− assert!(str::parse::<RepresentableKeyEvent>("aaa").is_err());
621− assert!(str::parse::<RepresentableKeyEvent>("S-S-a").is_err());
622− assert!(str::parse::<RepresentableKeyEvent>("C-A-S-C-1").is_err());
623− assert!(str::parse::<RepresentableKeyEvent>("FU").is_err());
624− assert!(str::parse::<RepresentableKeyEvent>("123").is_err());
625− assert!(str::parse::<RepresentableKeyEvent>("S--").is_err());
626− }
392+ .unwrap(),
393+ Command::insert_mode
394+ );
395+ assert!(merged_config.keys.0.get(&Mode::Normal).unwrap().len() > 1);
396+ assert!(merged_config.keys.0.get(&Mode::Insert).unwrap().len() > 0);
627397}