aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/input
diff options
context:
space:
mode:
authora-kenji <aks.kenji@protonmail.com>2021-03-05 22:40:42 +0100
committera-kenji <aks.kenji@protonmail.com>2021-03-09 19:16:15 +0100
commitc97c553870380956904ab7d2daa36e5ce0aef010 (patch)
treef84ac83057df1a5da72af1733ffc048555ad8b40 /src/common/input
parentpoking (diff)
downloadsl0ppij-c97c553870380956904ab7d2daa36e5ce0aef010.tar.gz
sl0ppij-c97c553870380956904ab7d2daa36e5ce0aef010.tar.xz
sl0ppij-c97c553870380956904ab7d2daa36e5ce0aef010.zip
feature(input): add config file
* use a simple platform dependent config location `ProjectDir` * deserialise from yaml iterate more on config format, maybe be more verbose? * make keybinds a tuple struct same size as newtype, but makes impls easier to add * merge optionally multiple keys for one keybinding easier configuration
Diffstat (limited to 'src/common/input')
-rw-r--r--src/common/input/actions.rs6
-rw-r--r--src/common/input/handler.rs16
-rw-r--r--src/common/input/keybinds.rs414
-rw-r--r--src/common/input/macros.rs12
-rw-r--r--src/common/input/mod.rs3
-rw-r--r--src/common/input/ut/config_test.rs51
-rw-r--r--src/common/input/ut/keybinds_test.rs0
7 files changed, 331 insertions, 171 deletions
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
index d03ca859..6d5c5892 100644
--- a/src/common/input/actions.rs
+++ b/src/common/input/actions.rs
@@ -2,10 +2,10 @@
use super::handler;
//use super::macros;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
/// The four directions (left, right, up, down).
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Direction {
Left,
Right,
@@ -14,7 +14,7 @@ pub enum Direction {
}
/// Actions that can be bound to keys.
-#[derive(Clone, Debug, Deserialize)]
+#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Action {
/// Quit Zellij.
Quit,
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index 9d8b2cf2..9b08491b 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -1,7 +1,7 @@
//! Main input logic.
use super::actions::Action;
-use super::keybinds::get_default_keybinds;
+use super::keybinds::Keybinds;
use crate::common::config::Config;
use crate::common::{update_state, AppInstruction, AppState, SenderWithContext, OPENCALLS};
use crate::errors::ContextType;
@@ -15,8 +15,6 @@ use serde::{Deserialize, Serialize};
use strum_macros::EnumIter;
use termion::input::TermReadEventsAndRaw;
-use super::keybinds::key_to_actions;
-
/// Handles the dispatching of [`Action`]s according to the current
/// [`InputMode`], and keep tracks of the current [`InputMode`].
struct InputHandler {
@@ -62,9 +60,8 @@ impl InputHandler {
self.send_pty_instructions.update(err_ctx);
self.send_app_instructions.update(err_ctx);
self.send_screen_instructions.update(err_ctx);
- if let Ok(keybinds) = get_default_keybinds() {
- let mut merged_keybinds = keybinds;
- merged_keybinds.extend(self.config.keybinds.clone().into_iter());
+ if let Ok(_keybinds) = Keybinds::get_default_keybinds() {
+ let keybinds = self.config.keybinds.clone();
'input_loop: loop {
//@@@ I think this should actually just iterate over stdin directly
let stdin_buffer = self.os_input.read_from_stdin();
@@ -84,11 +81,8 @@ impl InputHandler {
let mut should_break = false;
// Hacked on way to have a means of testing Macros, needs to
// get properly integrated
- for action in key_to_actions(
- &key,
- raw_bytes,
- &self.mode,
- &merged_keybinds,
+ for action in Keybinds::key_to_actions(
+ &key, raw_bytes, &self.mode, &keybinds,
) {
should_break |= self.dispatch_action(action);
}
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
index 8799649d..8b089ecf 100644
--- a/src/common/input/keybinds.rs
+++ b/src/common/input/keybinds.rs
@@ -1,183 +1,311 @@
//! Mapping of inputs to sequences of actions.
+use std::collections::HashMap;
use super::actions::{Action, Direction};
use super::handler::InputMode;
-use std::collections::HashMap;
-
+use serde::Deserialize;
use strum::IntoEnumIterator;
use termion::event::Key;
-pub type Keybinds = HashMap<InputMode, ModeKeybinds>;
-type ModeKeybinds = HashMap<Key, Vec<Action>>;
+#[derive(Clone, Debug, PartialEq, Deserialize)]
+pub struct Keybinds(HashMap<InputMode, ModeKeybinds>);
+#[derive(Clone, Debug, Default, PartialEq, Deserialize)]
+pub struct ModeKeybinds(HashMap<Key, Vec<Action>>);
-/// Populates the default hashmap of keybinds.
-/// @@@khs26 What about an input config file?
-pub fn get_default_keybinds() -> Result<Keybinds, String> {
- let mut defaults = Keybinds::new();
+/// Intermediate struct for configuration.
+#[derive(Clone, Debug, Default, PartialEq, Deserialize)]
+pub struct MultipleKeybinds(HashMap<InputMode, Vec<ModeKeybind>>);
+/// Intermediate struct for configuration.
+#[derive(Clone, Debug, Default, PartialEq, Deserialize)]
+struct MultipleModeKeybinds(HashMap<Vec<Key>, Vec<Action>>);
- for mode in InputMode::iter() {
- defaults.insert(mode, get_defaults_for_mode(&mode)?);
- }
+/// Intermediate enum for configuration.
+#[derive(Debug, Clone, PartialEq, Deserialize)]
+#[serde(untagged)]
+enum ModeKeybind {
+ ModeKeybinds(ModeKeybinds),
+ MultipleModeKeybinds(MultipleModeKeybinds),
+}
- Ok(defaults)
+impl Default for Keybinds {
+ fn default() -> Keybinds {
+ Keybinds::get_default_keybinds().expect("Something is wrong with the default Keybinds")
+ }
}
-/// Returns the default keybinds for a given [`InputMode`].
-fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> {
- let mut defaults = ModeKeybinds::new();
+impl Keybinds {
+ pub fn new() -> Keybinds {
+ Keybinds::from(HashMap::<InputMode, ModeKeybinds>::new())
+ }
- match *mode {
- InputMode::Normal => {
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Command)],
- );
- }
- InputMode::Command => {
- defaults.insert(
- Key::Char('r'),
- vec![Action::SwitchToMode(InputMode::Resize)],
- );
- defaults.insert(Key::Char('p'), vec![Action::SwitchToMode(InputMode::Pane)]);
- defaults.insert(Key::Char('t'), vec![Action::SwitchToMode(InputMode::Tab)]);
- defaults.insert(
- Key::Char('s'),
- vec![Action::SwitchToMode(InputMode::Scroll)],
- );
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Normal)],
- );
- defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]);
- defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ pub fn get_default_keybinds() -> Result<Keybinds, String> {
+ let mut defaults = Keybinds::new();
+
+ for mode in InputMode::iter() {
+ defaults
+ .0
+ .insert(mode, Keybinds::get_defaults_for_mode(&mode)?);
}
- InputMode::Resize => {
- defaults.insert(Key::Char('h'), vec![Action::Resize(Direction::Left)]);
- defaults.insert(Key::Char('j'), vec![Action::Resize(Direction::Down)]);
- defaults.insert(Key::Char('k'), vec![Action::Resize(Direction::Up)]);
- defaults.insert(Key::Char('l'), vec![Action::Resize(Direction::Right)]);
- defaults.insert(Key::Left, vec![Action::Resize(Direction::Left)]);
- defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]);
- defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]);
- defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]);
+ Ok(defaults)
+ }
+
+ pub fn get_default_keybinds_with_config(keybinds: Option<MultipleKeybinds>) -> Keybinds {
+ let default_keybinds = Keybinds::default();
+ if let Some(keybinds) = keybinds {
+ default_keybinds.merge_keybinds(Keybinds::from(keybinds))
+ } else {
+ default_keybinds
+ }
+ }
- defaults.insert(Key::Ctrl('b'), vec![Action::Resize(Direction::Left)]);
- defaults.insert(Key::Ctrl('n'), vec![Action::Resize(Direction::Down)]);
- defaults.insert(Key::Ctrl('p'), vec![Action::Resize(Direction::Up)]);
- defaults.insert(Key::Ctrl('f'), vec![Action::Resize(Direction::Right)]);
+ /// Merges two Keybinds structs into one Keybinds struct
+ /// `other` overrides the ModeKeybinds of `self`.
+ fn merge_keybinds(&self, other: Keybinds) -> Keybinds {
+ let mut keybinds = Keybinds::default();
- defaults.insert(Key::Char('q'), vec![Action::Quit]);
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Normal)],
- );
- defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
+ for mode in InputMode::iter() {
+ let mut mode_keybinds: ModeKeybinds = if let Some(keybind) = self.0.get(&mode) {
+ keybind.clone()
+ } else {
+ ModeKeybinds::default()
+ };
+ if let Some(keybind) = other.0.get(&mode) {
+ mode_keybinds.0.extend(keybind.0.clone());
+ }
+ keybinds.0.insert(mode, mode_keybinds);
}
- InputMode::Pane => {
- defaults.insert(Key::Char('h'), vec![Action::MoveFocus(Direction::Left)]);
- defaults.insert(Key::Char('j'), vec![Action::MoveFocus(Direction::Down)]);
- defaults.insert(Key::Char('k'), vec![Action::MoveFocus(Direction::Up)]);
- defaults.insert(Key::Char('l'), vec![Action::MoveFocus(Direction::Right)]);
+ keybinds
+ }
+
+ /// Returns the default keybinds for a given [`InputMode`].
+ fn get_defaults_for_mode(mode: &InputMode) -> Result<ModeKeybinds, String> {
+ let mut defaults = HashMap::new();
- defaults.insert(Key::Left, vec![Action::MoveFocus(Direction::Left)]);
- defaults.insert(Key::Down, vec![Action::MoveFocus(Direction::Down)]);
- defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]);
- defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]);
+ match *mode {
+ InputMode::Normal => {
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Command)],
+ );
+ }
+ InputMode::Command => {
+ defaults.insert(
+ Key::Char('r'),
+ vec![Action::SwitchToMode(InputMode::Resize)],
+ );
+ defaults.insert(Key::Char('p'), vec![Action::SwitchToMode(InputMode::Pane)]);
+ defaults.insert(Key::Char('t'), vec![Action::SwitchToMode(InputMode::Tab)]);
+ defaults.insert(
+ Key::Char('s'),
+ vec![Action::SwitchToMode(InputMode::Scroll)],
+ );
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Normal)],
+ );
+ defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Normal)]);
+ defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ }
+ InputMode::Resize => {
+ defaults.insert(Key::Char('h'), vec![Action::Resize(Direction::Left)]);
+ defaults.insert(Key::Char('j'), vec![Action::Resize(Direction::Down)]);
+ defaults.insert(Key::Char('k'), vec![Action::Resize(Direction::Up)]);
+ defaults.insert(Key::Char('l'), vec![Action::Resize(Direction::Right)]);
- defaults.insert(Key::Ctrl('b'), vec![Action::MoveFocus(Direction::Left)]);
- defaults.insert(Key::Ctrl('n'), vec![Action::MoveFocus(Direction::Down)]);
- defaults.insert(Key::Ctrl('p'), vec![Action::MoveFocus(Direction::Up)]);
- defaults.insert(Key::Ctrl('f'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Left, vec![Action::Resize(Direction::Left)]);
+ defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]);
+ defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]);
+ defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]);
- defaults.insert(Key::Char('p'), vec![Action::SwitchFocus(Direction::Right)]);
- defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]);
- defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]);
- defaults.insert(
- Key::Char('r'),
- vec![Action::NewPane(Some(Direction::Right))],
- );
- defaults.insert(Key::Char('x'), vec![Action::CloseFocus]);
+ defaults.insert(Key::Ctrl('b'), vec![Action::Resize(Direction::Left)]);
+ defaults.insert(Key::Ctrl('n'), vec![Action::Resize(Direction::Down)]);
+ defaults.insert(Key::Ctrl('p'), vec![Action::Resize(Direction::Up)]);
+ defaults.insert(Key::Ctrl('f'), vec![Action::Resize(Direction::Right)]);
- defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]);
+ defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Normal)],
+ );
+ defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
+ }
+ InputMode::Pane => {
+ defaults.insert(Key::Char('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Char('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Char('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Char('l'), vec![Action::MoveFocus(Direction::Right)]);
- defaults.insert(Key::Char('q'), vec![Action::Quit]);
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Normal)],
- );
- defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
- }
- InputMode::Tab => {
- defaults.insert(Key::Char('h'), vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Char('j'), vec![Action::GoToNextTab]);
- defaults.insert(Key::Char('k'), vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Char('l'), vec![Action::GoToNextTab]);
+ defaults.insert(Key::Left, vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Down, vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]);
- defaults.insert(Key::Left, vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Down, vec![Action::GoToNextTab]);
- defaults.insert(Key::Up, vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Right, vec![Action::GoToNextTab]);
+ defaults.insert(Key::Ctrl('b'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Ctrl('n'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Ctrl('p'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Ctrl('f'), vec![Action::MoveFocus(Direction::Right)]);
- defaults.insert(Key::Ctrl('b'), vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Ctrl('n'), vec![Action::GoToNextTab]);
- defaults.insert(Key::Ctrl('p'), vec![Action::GoToPreviousTab]);
- defaults.insert(Key::Ctrl('f'), vec![Action::GoToNextTab]);
+ defaults.insert(Key::Char('p'), vec![Action::SwitchFocus(Direction::Right)]);
+ defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]);
+ defaults.insert(
+ Key::Char('r'),
+ vec![Action::NewPane(Some(Direction::Right))],
+ );
+ defaults.insert(Key::Char('x'), vec![Action::CloseFocus]);
- defaults.insert(Key::Char('n'), vec![Action::NewTab]);
- defaults.insert(Key::Char('x'), vec![Action::CloseTab]);
+ defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]);
- defaults.insert(Key::Char('q'), vec![Action::Quit]);
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Normal)],
- );
- for i in '1'..='9' {
- defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]);
+ defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Normal)],
+ );
+ defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
}
- defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
- }
- InputMode::Scroll => {
- defaults.insert(Key::Char('j'), vec![Action::ScrollDown]);
- defaults.insert(Key::Char('k'), vec![Action::ScrollUp]);
+ InputMode::Tab => {
+ defaults.insert(Key::Char('h'), vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Char('j'), vec![Action::GoToNextTab]);
+ defaults.insert(Key::Char('k'), vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Char('l'), vec![Action::GoToNextTab]);
+
+ defaults.insert(Key::Left, vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Down, vec![Action::GoToNextTab]);
+ defaults.insert(Key::Up, vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Right, vec![Action::GoToNextTab]);
+
+ defaults.insert(Key::Ctrl('b'), vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Ctrl('n'), vec![Action::GoToNextTab]);
+ defaults.insert(Key::Ctrl('p'), vec![Action::GoToPreviousTab]);
+ defaults.insert(Key::Ctrl('f'), vec![Action::GoToNextTab]);
- defaults.insert(Key::Down, vec![Action::ScrollDown]);
- defaults.insert(Key::Up, vec![Action::ScrollUp]);
+ defaults.insert(Key::Char('n'), vec![Action::NewTab]);
+ defaults.insert(Key::Char('x'), vec![Action::CloseTab]);
- defaults.insert(Key::Ctrl('n'), vec![Action::ScrollDown]);
- defaults.insert(Key::Ctrl('p'), vec![Action::ScrollUp]);
+ defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Normal)],
+ );
+ for i in '1'..='9' {
+ defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]);
+ }
+ defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
+ }
+ InputMode::Scroll => {
+ defaults.insert(Key::Char('j'), vec![Action::ScrollDown]);
+ defaults.insert(Key::Char('k'), vec![Action::ScrollUp]);
+
+ defaults.insert(Key::Down, vec![Action::ScrollDown]);
+ defaults.insert(Key::Up, vec![Action::ScrollUp]);
+
+ defaults.insert(Key::Ctrl('n'), vec![Action::ScrollDown]);
+ defaults.insert(Key::Ctrl('p'), vec![Action::ScrollUp]);
+
+ defaults.insert(Key::Char('q'), vec![Action::Quit]);
+ defaults.insert(
+ Key::Ctrl('g'),
+ vec![Action::SwitchToMode(InputMode::Normal)],
+ );
+ defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
+ }
+ }
- defaults.insert(Key::Char('q'), vec![Action::Quit]);
- defaults.insert(
- Key::Ctrl('g'),
- vec![Action::SwitchToMode(InputMode::Normal)],
- );
- defaults.insert(Key::Esc, vec![Action::SwitchToMode(InputMode::Command)]);
+ Ok(ModeKeybinds::from(defaults))
+ }
+
+ /// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current
+ /// [`InputMode`] and [`Keybinds`].
+ pub fn key_to_actions(
+ key: &Key,
+ input: Vec<u8>,
+ mode: &InputMode,
+ keybinds: &Keybinds,
+ ) -> Vec<Action> {
+ let mode_keybind_or_action = |action: Action| {
+ keybinds
+ .0
+ .get(mode)
+ .unwrap_or_else(|| unreachable!("Unrecognized mode: {:?}", mode))
+ .0
+ .get(key)
+ .cloned()
+ .unwrap_or_else(|| vec![action])
+ };
+ match *mode {
+ InputMode::Normal => mode_keybind_or_action(Action::Write(input)),
+ _ => mode_keybind_or_action(Action::NoOp),
}
}
+}
+
+impl ModeKeybinds {
+ pub fn new() -> ModeKeybinds {
+ ModeKeybinds::from(HashMap::<Key, Vec<Action>>::new())
+ }
- Ok(defaults)
+ /// Merges `self` with `other`, if keys are the same, `other` overwrites.
+ pub fn merge(self, other: ModeKeybinds) -> ModeKeybinds {
+ let mut merged = self;
+ merged.0.extend(other.0.clone());
+ merged
+ }
}
-/// Converts a [`Key`] terminal event to a sequence of [`Action`]s according to the current
-/// [`InputMode`] and [`Keybinds`].
-pub fn key_to_actions(
- key: &Key,
- input: Vec<u8>,
- mode: &InputMode,
- keybinds: &Keybinds,
-) -> Vec<Action> {
- let mode_keybind_or_action = |action: Action| {
+/// Converts from the `MultipleKeybinds` struct to a `Keybinds` struct.
+/// TODO @a-kenji Error on conflicting keybinds?
+/// Atm the Lower Keybind will take precedence and will overwrite the
+/// one further up.
+impl From<MultipleKeybinds> for Keybinds {
+ fn from(multiple: MultipleKeybinds) -> Keybinds {
+ let mut keybinds = Keybinds::new();
+
+ for mode in InputMode::iter() {
+ let mut mode_keybinds = ModeKeybinds::new();
+ for mode_keybind in multiple.0.get(&mode).iter() {
+ for keybind in mode_keybind.iter() {
+ match keybind {
+ ModeKeybind::ModeKeybinds(k) => {
+ mode_keybinds = mode_keybinds.clone().merge(k.clone());
+ }
+ ModeKeybind::MultipleModeKeybinds(k) => {
+ mode_keybinds =
+ mode_keybinds.clone().merge(ModeKeybinds::from(k.clone()));
+ }
+ }
+ }
+ }
+ keybinds.0.insert(mode, mode_keybinds);
+ }
keybinds
- .get(mode)
- .unwrap_or_else(|| unreachable!("Unrecognized mode: {:?}", mode))
- .get(key)
- .cloned()
- .unwrap_or_else(|| vec![action])
- };
- match *mode {
- InputMode::Normal => mode_keybind_or_action(Action::Write(input)),
- _ => mode_keybind_or_action(Action::NoOp),
}
}
+
+impl From<HashMap<InputMode, ModeKeybinds>> for Keybinds {
+ fn from(map: HashMap<InputMode, ModeKeybinds>) -> Keybinds {
+ Keybinds(map)
+ }
+}
+impl From<HashMap<Key, Vec<Action>>> for ModeKeybinds {
+ fn from(map: HashMap<Key, Vec<Action>>) -> ModeKeybinds {
+ ModeKeybinds(map)
+ }
+}
+
+impl From<MultipleModeKeybinds> for ModeKeybinds {
+ fn from(multiple: MultipleModeKeybinds) -> ModeKeybinds {
+ let single: HashMap<Key, Vec<Action>> = multiple
+ .0
+ .into_iter()
+ .flat_map(|(k, v)| (k.into_iter().map(move |k| (k, v.clone()))))
+ .collect();
+ ModeKeybinds::from(single)
+ }
+}
+
+// The unit test location.
+#[cfg(test)]
+#[path = "./ut/keybinds_test.rs"]
+mod keybinds_test;
diff --git a/src/common/input/macros.rs b/src/common/input/macros.rs
deleted file mode 100644
index 4b7c6df1..00000000
--- a/src/common/input/macros.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//! Use a list of commands and execute them in a
-//! defined predictable order.
-
-use super::actions::Action;
-
-use serde::Deserialize;
-
-#[derive(Debug, Clone, Deserialize)]
-pub struct Macro {
- name: Option<String>,
- sequence: Vec<Action>,
-}
diff --git a/src/common/input/mod.rs b/src/common/input/mod.rs
index 438df224..a7dca7ed 100644
--- a/src/common/input/mod.rs
+++ b/src/common/input/mod.rs
@@ -1,6 +1,5 @@
-//! The way terminal iput is handled.
+//! The way terminal input is handled.
pub mod actions;
pub mod handler;
pub mod keybinds;
-pub mod macros;
diff --git a/src/common/input/ut/config_test.rs b/src/common/input/ut/config_test.rs
new file mode 100644
index 00000000..001aab15
--- /dev/null
+++ b/src/common/input/ut/config_test.rs
@@ -0,0 +1,51 @@
+//! For Configuration Options
+
+use super::super::config::*;
+use crate::common::input::keybinds::*;
+use crate::common::input::actions::*;
+use std::path::PathBuf;
+
+use termion::event::Key;
+
+
+#[test]
+fn no_config_file_equals_default_config() {
+ let no_file = PathBuf::from(r"../fixtures/config/config.yamlll");
+ let config = Config::from_option_or_default(Some(no_file)).unwrap();
+ let default = Config::default();
+ assert_eq!(config, default);
+}
+
+#[test]
+fn no_config_option_file_equals_default_config() {
+ let config = Config::from_option_or_default(None).unwrap();
+ let default = Config::default();
+ assert_eq!(config, default);
+}
+
+#[test]
+fn multiple_keys_mapped_to_one_action() {
+ let options = r"
+ ---
+keybindings:
+ Normal:
+ - ? - F: 6
+ - F: 7
+ - F: 8
+ : - {GoToTab: 5}
+ ";
+
+ let config_options = Config::from_yaml(&options).unwrap();
+
+ assert_eq!(config_options, config_options)
+}
+
+//#[test]
+//fn merge_keybinds_merges(){
+ //let mut self_keybinds = Keybinds::new();
+ //let mut self_mode_keybinds = ModeKeybinds::new();
+ //self_mode_keybinds.0.insert(Key::F(1), vec![Action::GoToTab(5)]);
+ //let mut other_keybinds = Keybinds::new();
+ //let mut self_mode_keybinds = ModeKeybinds::new();
+ //let mut expected = Keybinds::new();
+//}
diff --git a/src/common/input/ut/keybinds_test.rs b/src/common/input/ut/keybinds_test.rs
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/common/input/ut/keybinds_test.rs