diff options
| author | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 16:42:50 +0530 |
|---|---|---|
| committer | Kunal Mohan <kunalmohan99@gmail.com> | 2021-05-16 21:41:56 +0530 |
| commit | 2038947a14d32d89da2bdbcaa1381c3015ac9c7a (patch) | |
| tree | 5a332470e32b380816098594a89d3c3db1f6eb53 /src/common/input | |
| parent | handle error on termios initialization (#511) (diff) | |
| download | sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.gz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.tar.xz sl0ppij-2038947a14d32d89da2bdbcaa1381c3015ac9c7a.zip | |
Big refactor: separate crates for client, server and utilities
Diffstat (limited to 'src/common/input')
| -rw-r--r-- | src/common/input/actions.rs | 64 | ||||
| -rw-r--r-- | src/common/input/config.rs | 218 | ||||
| -rw-r--r-- | src/common/input/handler.rs | 268 | ||||
| -rw-r--r-- | src/common/input/keybinds.rs | 301 | ||||
| -rw-r--r-- | src/common/input/mod.rs | 7 | ||||
| -rw-r--r-- | src/common/input/options.rs | 45 | ||||
| -rw-r--r-- | src/common/input/unit/keybinds_test.rs | 801 |
7 files changed, 0 insertions, 1704 deletions
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs deleted file mode 100644 index b806aa3f..00000000 --- a/src/common/input/actions.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! Definition of the actions that can be bound to keys. - -use serde::{Deserialize, Serialize}; -use zellij_tile::data::InputMode; - -/// The four directions (left, right, up, down). -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub enum Direction { - Left, - Right, - Up, - Down, -} - -/// Actions that can be bound to keys. -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub enum Action { - /// Quit Zellij. - Quit, - /// Write to the terminal. - Write(Vec<u8>), - /// Switch to the specified input mode. - SwitchToMode(InputMode), - /// Resize focus pane in specified direction. - Resize(Direction), - /// Switch focus to next pane in specified direction. - FocusNextPane, - FocusPreviousPane, - /// Move the focus pane in specified direction. - SwitchFocus, - MoveFocus(Direction), - /// Tries to move the focus pane in specified direction. - /// If there is no pane in the direction, move to previous/next Tab. - MoveFocusOrTab(Direction), - /// Scroll up in focus pane. - ScrollUp, - /// Scroll down in focus pane. - ScrollDown, - /// Scroll up one page in focus pane. - PageScrollUp, - /// Scroll down one page in focus pane. - PageScrollDown, - /// Toggle between fullscreen focus pane and normal layout. - ToggleFocusFullscreen, - /// Toggle between sending text commands to all panes on the current tab and normal mode. - ToggleActiveSyncTab, - /// Open a new pane in the specified direction (relative to focus). - /// If no direction is specified, will try to use the biggest available space. - NewPane(Option<Direction>), - /// Close the focus pane. - CloseFocus, - /// Create a new tab. - NewTab, - /// Do nothing. - NoOp, - /// Go to the next tab. - GoToNextTab, - /// Go to the previous tab. - GoToPreviousTab, - /// Close the current tab. - CloseTab, - GoToTab(u32), - TabNameInput(Vec<u8>), -} diff --git a/src/common/input/config.rs b/src/common/input/config.rs deleted file mode 100644 index 3dffe71c..00000000 --- a/src/common/input/config.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Deserializes configuration options. -use std::error; -use std::fmt::{self, Display}; -use std::fs::File; -use std::io::{self, Read}; -use std::path::{Path, PathBuf}; - -use super::keybinds::{Keybinds, KeybindsFromYaml}; -use super::options::Options; -use crate::cli::{CliArgs, ConfigCli}; -use crate::common::setup; - -use serde::{Deserialize, Serialize}; -use std::convert::TryFrom; - -const DEFAULT_CONFIG_FILE_NAME: &str = "config.yaml"; - -type ConfigResult = Result<Config, ConfigError>; - -/// Intermediate deserialization config struct -#[derive(Debug, Deserialize)] -pub struct ConfigFromYaml { - #[serde(flatten)] - pub options: Option<Options>, - pub keybinds: Option<KeybindsFromYaml>, -} - -/// Main configuration. -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] -pub struct Config { - pub keybinds: Keybinds, - pub options: Options, -} - -#[derive(Debug)] -pub enum ConfigError { - // Deserialization error - Serde(serde_yaml::Error), - // Io error - Io(io::Error), - // Io error with path context - IoPath(io::Error, PathBuf), - // Internal Deserialization Error - FromUtf8(std::string::FromUtf8Error), -} - -impl Default for Config { - fn default() -> Self { - let keybinds = Keybinds::default(); - let options = Options::default(); - Config { keybinds, options } - } -} - -impl TryFrom<&CliArgs> for Config { - type Error = ConfigError; - - fn try_from(opts: &CliArgs) -> ConfigResult { - if let Some(ref path) = opts.config { - return Config::new(&path); - } - - if let Some(ConfigCli::Setup(setup)) = opts.option.clone() { - if setup.clean { - return Config::from_default_assets(); - } - } - - let config_dir = opts - .config_dir - .clone() - .or_else(setup::find_default_config_dir); - - if let Some(ref config) = config_dir { - let path = config.join(DEFAULT_CONFIG_FILE_NAME); - if path.exists() { - Config::new(&path) - } else { - Config::from_default_assets() - } - } else { - Config::from_default_assets() - } - } -} - -impl Config { - /// Uses defaults, but lets config override them. - pub fn from_yaml(yaml_config: &str) -> ConfigResult { - let config_from_yaml: ConfigFromYaml = serde_yaml::from_str(&yaml_config)?; - let keybinds = Keybinds::get_default_keybinds_with_config(config_from_yaml.keybinds); - let options = Options::from_yaml(config_from_yaml.options); - Ok(Config { keybinds, options }) - } - - /// Deserializes from given path. - pub fn new(path: &Path) -> ConfigResult { - match File::open(path) { - Ok(mut file) => { - let mut yaml_config = String::new(); - file.read_to_string(&mut yaml_config) - .map_err(|e| ConfigError::IoPath(e, path.to_path_buf()))?; - Ok(Config::from_yaml(&yaml_config)?) - } - Err(e) => Err(ConfigError::IoPath(e, path.into())), - } - } - - /// Gets default configuration from assets - // TODO Deserialize the Configuration from bytes &[u8], - // once serde-yaml supports zero-copy - pub fn from_default_assets() -> ConfigResult { - Self::from_yaml(String::from_utf8(setup::DEFAULT_CONFIG.to_vec())?.as_str()) - } -} - -impl Display for ConfigError { - fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - match self { - ConfigError::Io(ref err) => write!(formatter, "IoError: {}", err), - ConfigError::IoPath(ref err, ref path) => { - write!(formatter, "IoError: {}, File: {}", err, path.display(),) - } - ConfigError::Serde(ref err) => write!(formatter, "Deserialization error: {}", err), - ConfigError::FromUtf8(ref err) => write!(formatter, "FromUtf8Error: {}", err), - } - } -} - -impl std::error::Error for ConfigError { - fn cause(&self) -> Option<&dyn error::Error> { - match *self { - ConfigError::Io(ref err) => Some(err), - ConfigError::IoPath(ref err, _) => Some(err), - ConfigError::Serde(ref err) => Some(err), - ConfigError::FromUtf8(ref err) => Some(err), - } - } -} - -impl From<io::Error> for ConfigError { - fn from(err: io::Error) -> ConfigError { - ConfigError::Io(err) - } -} - -impl From<serde_yaml::Error> for ConfigError { - fn from(err: serde_yaml::Error) -> ConfigError { - ConfigError::Serde(err) - } -} - -impl From<std::string::FromUtf8Error> for ConfigError { - fn from(err: std::string::FromUtf8Error) -> ConfigError { - ConfigError::FromUtf8(err) - } -} - -// The unit test location. -#[cfg(test)] -mod config_test { - use std::io::Write; - - use tempfile::tempdir; - - use super::*; - - #[test] - fn try_from_cli_args_with_config() { - let arbitrary_config = PathBuf::from("nonexistent.yaml"); - let mut opts = CliArgs::default(); - opts.config = Some(arbitrary_config); - println!("OPTS= {:?}", opts); - let result = Config::try_from(&opts); - assert!(result.is_err()); - } - - #[test] - fn try_from_cli_args_with_option_clean() { - use crate::common::setup::Setup; - let mut opts = CliArgs::default(); - opts.option = Some(ConfigCli::Setup(Setup { - clean: true, - ..Setup::default() - })); - let result = Config::try_from(&opts); - assert!(result.is_ok()); - } - - #[test] - fn try_from_cli_args_with_config_dir() { - let mut opts = CliArgs::default(); - let tmp = tempdir().unwrap(); - File::create(tmp.path().join(DEFAULT_CONFIG_FILE_NAME)) - .unwrap() - .write_all(b"keybinds: invalid\n") - .unwrap(); - opts.config_dir = Some(tmp.path().to_path_buf()); - let result = Config::try_from(&opts); - assert!(result.is_err()); - } - - #[test] - fn try_from_cli_args_with_config_dir_without_config() { - let mut opts = CliArgs::default(); - let tmp = tempdir().unwrap(); - opts.config_dir = Some(tmp.path().to_path_buf()); - let result = Config::try_from(&opts); - assert_eq!(result.unwrap(), Config::default()); - } - - #[test] - fn try_from_cli_args_default() { - let opts = CliArgs::default(); - let result = Config::try_from(&opts); - assert_eq!(result.unwrap(), Config::default()); - } -} diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs deleted file mode 100644 index 4ac2cf83..00000000 --- a/src/common/input/handler.rs +++ /dev/null @@ -1,268 +0,0 @@ -//! Main input logic. - -use super::actions::Action; -use super::keybinds::Keybinds; -use crate::client::ClientInstruction; -use crate::common::input::config::Config; -use crate::common::ipc::ClientToServerMsg; -use crate::common::thread_bus::{SenderWithContext, OPENCALLS}; -use crate::errors::ContextType; -use crate::os_input_output::ClientOsApi; -use crate::CommandIsExecuting; - -use termion::input::{TermRead, TermReadEventsAndRaw}; -use zellij_tile::data::{InputMode, Key, ModeInfo, Palette, PluginCapabilities}; - -/// Handles the dispatching of [`Action`]s according to the current -/// [`InputMode`], and keep tracks of the current [`InputMode`]. -struct InputHandler { - /// The current input mode - mode: InputMode, - os_input: Box<dyn ClientOsApi>, - config: Config, - command_is_executing: CommandIsExecuting, - send_client_instructions: SenderWithContext<ClientInstruction>, - should_exit: bool, - pasting: bool, -} - -impl InputHandler { - /// Returns a new [`InputHandler`] with the attributes specified as arguments. - fn new( - os_input: Box<dyn ClientOsApi>, - command_is_executing: CommandIsExecuting, - config: Config, - send_client_instructions: SenderWithContext<ClientInstruction>, - ) -> Self { - InputHandler { - mode: InputMode::Normal, - os_input, - config, - command_is_executing, - send_client_instructions, - should_exit: false, - pasting: false, - } - } - - /// Main input event loop. Interprets the terminal [`Event`](termion::event::Event)s - /// as [`Action`]s according to the current [`InputMode`], and dispatches those actions. - fn handle_input(&mut self) { - let mut err_ctx = OPENCALLS.with(|ctx| *ctx.borrow()); - err_ctx.add_call(ContextType::StdinHandler); - let alt_left_bracket = vec![27, 91]; - let bracketed_paste_start = vec![27, 91, 50, 48, 48, 126]; // \u{1b}[200~ - let bracketed_paste_end = vec![27, 91, 50, 48, 49, 126]; // \u{1b}[201 - loop { - if self.should_exit { - break; - } - let stdin_buffer = self.os_input.read_from_stdin(); - for key_result in stdin_buffer.events_and_raw() { - match key_result { - Ok((event, raw_bytes)) => match event { - termion::event::Event::Key(key) => { - let key = cast_termion_key(key); - self.handle_key(&key, raw_bytes); - } - termion::event::Event::Unsupported(unsupported_key) => { - // we have to do this because of a bug in termion - // this should be a key event and not an unsupported event - if unsupported_key == alt_left_bracket { - let key = Key::Alt('['); - self.handle_key(&key, raw_bytes); - } else if unsupported_key == bracketed_paste_start { - self.pasting = true; - } else if unsupported_key == bracketed_paste_end { - self.pasting = false; - } else { - // this is a hack because termion doesn't recognize certain keys - // in this case we just forward it to the terminal - self.handle_unknown_key(raw_bytes); - } - } - termion::event::Event::Mouse(_) => { - // Mouse events aren't implemented yet, - // use a NoOp untill then. - } - }, - Err(err) => panic!("Encountered read error: {:?}", err), - } - } - } - } - fn handle_unknown_key(&mut self, raw_bytes: Vec<u8>) { - if self.mode == InputMode::Normal || self.mode == InputMode::Locked { - let action = Action::Write(raw_bytes); - self.dispatch_action(action); - } - } - fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>) { - let keybinds = &self.config.keybinds; - if self.pasting { - // we're inside a paste block, if we're in a mode that allows sending text to the - // terminal, send all text directly without interpreting it - // otherwise, just discard the input - if self.mode == InputMode::Normal || self.mode == InputMode::Locked { - let action = Action::Write(raw_bytes); - self.dispatch_action(action); - } - } else { - for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, keybinds) { - let should_exit = self.dispatch_action(action); - if should_exit { - self.should_exit = true; - } - } - } - } - - /// Dispatches an [`Action`]. - /// - /// This function's body dictates what each [`Action`] actually does when - /// dispatched. - /// - /// # Return value - /// Currently, this function returns a boolean that indicates whether - /// [`Self::handle_input()`] should break after this action is dispatched. - /// This is a temporary measure that is only necessary due to the way that the - /// framework works, and shouldn't be necessary anymore once the test framework - /// is revised. See [issue#183](https://github.com/zellij-org/zellij/issues/183). - fn dispatch_action(&mut self, action: Action) -> bool { - let mut should_break = false; - - match action { - Action::Quit => { - self.exit(); - should_break = true; - } - Action::SwitchToMode(mode) => { - self.mode = mode; - self.os_input - .send_to_server(ClientToServerMsg::Action(action)); - } - Action::CloseFocus - | Action::NewPane(_) - | Action::NewTab - | Action::GoToNextTab - | Action::GoToPreviousTab - | Action::CloseTab - | Action::GoToTab(_) - | Action::MoveFocusOrTab(_) => { - self.command_is_executing.blocking_input_thread(); - self.os_input - .send_to_server(ClientToServerMsg::Action(action)); - self.command_is_executing - .wait_until_input_thread_is_unblocked(); - } - _ => self - .os_input - .send_to_server(ClientToServerMsg::Action(action)), - } - - should_break - } - - /// Routine to be called when the input handler exits (at the moment this is the - /// same as quitting Zellij). - fn exit(&mut self) { - self.send_client_instructions - .send(ClientInstruction::Exit) - .unwrap(); - } -} - -/// Creates a [`Help`] struct indicating the current [`InputMode`] and its keybinds -/// (as pairs of [`String`]s). -// TODO this should probably be automatically generated in some way -pub fn get_mode_info( - mode: InputMode, - palette: Palette, - capabilities: PluginCapabilities, -) -> ModeInfo { - let mut keybinds: Vec<(String, String)> = vec![]; - match mode { - InputMode::Normal | InputMode::Locked => {} - InputMode::Resize => { - keybinds.push(("←↓↑→".to_string(), "Resize".to_string())); - } - InputMode::Pane => { - keybinds.push(("←↓↑→".to_string(), "Move focus".to_string())); - keybinds.push(("p".to_string(), "Next".to_string())); - keybinds.push(("n".to_string(), "New".to_string())); - keybinds.push(("d".to_string(), "Down split".to_string())); - keybinds.push(("r".to_string(), "Right split".to_string())); - keybinds.push(("x".to_string(), "Close".to_string())); - keybinds.push(("f".to_string(), "Fullscreen".to_string())); - } - InputMode::Tab => { - keybinds.push(("←↓↑→".to_string(), "Move focus".to_string())); - keybinds.push(("n".to_string(), "New".to_string())); - keybinds.push(("x".to_string(), "Close".to_string())); - keybinds.push(("r".to_string(), "Rename".to_string())); - keybinds.push(("s".to_string(), "Sync".to_string())); - } - InputMode::Scroll => { - keybinds.push(("↓↑".to_string(), "Scroll".to_string())); - keybinds.push(("PgUp/PgDn".to_string(), "Scroll Page".to_string())); - } - InputMode::RenameTab => { - keybinds.push(("Enter".to_string(), "when done".to_string())); - } - } - ModeInfo { - mode, - keybinds, - palette, - capabilities, - } -} - -/// Entry point to the module. Instantiates an [`InputHandler`] and starts -/// its [`InputHandler::handle_input()`] loop. -pub fn input_loop( - os_input: Box<dyn ClientOsApi>, - config: Config, - command_is_executing: CommandIsExecuting, - send_client_instructions: SenderWithContext<ClientInstruction>, -) { - let _handler = InputHandler::new( - os_input, - command_is_executing, - config, - send_client_instructions, - ) - .handle_input(); -} - -pub fn parse_keys(input_bytes: &[u8]) -> Vec<Key> { - input_bytes.keys().flatten().map(cast_termion_key).collect() -} - -// FIXME: This is an absolutely cursed function that should be destroyed as soon -// as an alternative that doesn't touch zellij-tile can be developed... -fn cast_termion_key(event: termion::event::Key) -> Key { - match event { - termion::event::Key::Backspace => Key::Backspace, - termion::event::Key::Left => Key::Left, - termion::event::Key::Right => Key::Right, - termion::event::Key::Up => Key::Up, - termion::event::Key::Down => Key::Down, - termion::event::Key::Home => Key::Home, - termion::event::Key::End => Key::End, - termion::event::Key::PageUp => Key::PageUp, - termion::event::Key::PageDown => Key::PageDown, - termion::event::Key::BackTab => Key::BackTab, - termion::event::Key::Delete => Key::Delete, - termion::event::Key::Insert => Key::Insert, - termion::event::Key::F(n) => Key::F(n), - termion::event::Key::Char(c) => Key::Char(c), - termion::event::Key::Alt(c) => Key::Alt(c), - termion::event::Key::Ctrl(c) => Key::Ctrl(c), - termion::event::Key::Null => Key::Null, - termion::event::Key::Esc => Key::Esc, - _ => { - unimplemented!("Encountered an unknown key!") - } - } -} diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs deleted file mode 100644 index 8bef47fd..00000000 --- a/src/common/input/keybinds.rs +++ /dev/null @@ -1,301 +0,0 @@ -//! Mapping of inputs to sequences of actions. -use std::collections::HashMap; - -use super::actions::Action; -use super::config; - -use serde::{Deserialize, Serialize}; -use strum::IntoEnumIterator; -use zellij_tile::data::*; - -/// Used in the config struct -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct Keybinds(HashMap<InputMode, ModeKeybinds>); -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] -pub struct ModeKeybinds(HashMap<Key, Vec<Action>>); - -/// Intermediate struct used for deserialisation -/// Used in the config file. -#[derive(Clone, Debug, PartialEq, Deserialize)] -pub struct KeybindsFromYaml { - #[serde(flatten)] - keybinds: HashMap<InputMode, Vec<KeyActionUnbind>>, - #[serde(default)] - unbind: Unbind, -} - -/// Intermediate enum used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize)] -#[serde(untagged)] -enum KeyActionUnbind { - KeyAction(KeyActionFromYaml), - Unbind(UnbindFromYaml), -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize)] -struct KeyActionUnbindFromYaml { - keybinds: Vec<KeyActionFromYaml>, - unbind: Unbind, -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize)] -pub struct KeyActionFromYaml { - action: Vec<Action>, - key: Vec<Key>, -} - -/// Intermediate struct used for deserialisation -#[derive(Clone, Debug, PartialEq, Deserialize)] -struct UnbindFromYaml { - unbind: Unbind, -} - -/// List of keys, for which to disable their respective default actions -/// `All` is a catch all, and will disable the default actions for all keys. -#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize)] -#[serde(untagged)] -enum Unbind { - // This is the correct order, don't rearrange! - // Suspected Bug in the untagged macro. - // 1. Keys - Keys(Vec<Key>), - // 2. All - All(bool), -} - -impl Default for Keybinds { - // Use once per codepath - // TODO investigate why - fn default() -> Keybinds { - Self::from_default_assets() - } -} - -impl Keybinds { - pub fn new() -> Keybinds { - Keybinds(HashMap::<InputMode, ModeKeybinds>::new()) - } - - fn from_default_assets() -> Keybinds { - config::Config::from_default_assets() - .expect("Keybinds from default assets Error!") - .keybinds - } - - /// Entrypoint from the config module - pub fn get_default_keybinds_with_config(from_yaml: Option<KeybindsFromYaml>) -> Keybinds { - let default_keybinds = match from_yaml.clone() { - Some(keybinds) => match keybinds.unbind { - Unbind::All(true) => Keybinds::new(), - Unbind::All(false) | Unbind::Keys(_) => Keybinds::unbind(keybinds), - }, - None => Keybinds::default(), - }; - - if let Some(keybinds) = from_yaml { - default_keybinds.merge_keybinds(Keybinds::from(keybinds)) - } else { - default_keybinds - } - } - - /// Unbinds the default keybindings in relation to their mode - fn unbind(from_yaml: KeybindsFromYaml) -> Keybinds { - let mut keybind_config = Self::new(); - let mut unbind_config: HashMap<InputMode, Unbind> = HashMap::new(); - let keybinds_from_yaml = from_yaml.keybinds; - - for mode in InputMode::iter() { - if let Some(keybinds) = keybinds_from_yaml.get(&mode) { - for keybind in keybinds.iter() { - match keybind { - KeyActionUnbind::Unbind(unbind) => { - unbind_config.insert(mode, unbind.unbind.clone()); - } - KeyActionUnbind::KeyAction(key_action_from_yaml) => { - keybind_config - .0 - .insert(mode, ModeKeybinds::from(key_action_from_yaml.clone())); - } - } - } - } - } - - let mut default = Self::default().unbind_mode(unbind_config); - - // Toplevel Unbinds - if let Unbind::Keys(_) = from_yaml.unbind { - let mut unbind_config: HashMap<InputMode, Unbind> = HashMap::new(); - for mode in InputMode::iter() { - unbind_config.insert(mode, from_yaml.unbind.clone()); - } - default = default.unbind_mode(unbind_config); - }; - - default.merge_keybinds(keybind_config) - } - - /// Unbind [`Key`] bindings respective to their mode - fn unbind_mode(&self, unbind: HashMap<InputMode, Unbind>) -> Keybinds { - let mut keybinds = Keybinds::new(); - - for mode in InputMode::iter() { - if let Some(unbind) = unbind.get(&mode) { - match unbind { - Unbind::All(true) => {} - Unbind::Keys(keys) => { - if let Some(defaults) = self.0.get(&mode) { - keybinds - .0 - .insert(mode, defaults.clone().unbind_keys(keys.to_vec())); - } - } - Unbind::All(false) => { - if let Some(defaults) = self.0.get(&mode) { - keybinds.0.insert(mode, defaults.clone()); - } - } - } - } else if let Some(defaults) = self.0.get(&mode) { - keybinds.0.insert(mode, defaults.clone()); - } - } - keybinds - } - - /// 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::new(); - - for mode in InputMode::iter() { - let mut mode_keybinds = ModeKeybinds::new(); - if let Some(keybind) = self.0.get(&mode) { - mode_keybinds.0.extend(keybind.0.clone()); - }; - if let Some(keybind) = other.0.get(&mode) { - mode_keybinds.0.extend(keybind.0.clone()); - } - if !mode_keybinds.0.is_empty() { - keybinds.0.insert(mode, mode_keybinds); - } - } - keybinds - } - - /// 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 | InputMode::Locked => mode_keybind_or_action(Action::Write(input)), - InputMode::RenameTab => mode_keybind_or_action(Action::TabNameInput(input)), - _ => mode_keybind_or_action(Action::NoOp), - } - } -} - -impl ModeKeybinds { - fn new() -> ModeKeybinds { - ModeKeybinds(HashMap::<Key, Vec<Action>>::new()) - } - - /// Merges `self` with `other`, if keys are the same, `other` overwrites. - fn merge(self, other: ModeKeybinds) -> ModeKeybinds { - let mut merged = self; - merged.0.extend(other.0); - merged - } - - /// Remove [`Key`]'s from [`ModeKeybinds`] - fn unbind_keys(self, unbind: Vec<Key>) -> Self { - let mut keymap = self; - for key in unbind { - keymap.0.remove(&key); - } - keymap - } -} - -impl From<KeybindsFromYaml> for Keybinds { - fn from(keybinds_from_yaml: KeybindsFromYaml) -> Keybinds { - let mut keybinds = Keybinds::new(); - - for mode in InputMode::iter() { - let mut mode_keybinds = ModeKeybinds::new(); - for key_action in keybinds_from_yaml.keybinds.get(&mode).iter() { - for keybind in key_action.iter() { - mode_keybinds = mode_keybinds.merge(ModeKeybinds::from(keybind.clone())); - } - } - keybinds.0.insert(mode, mode_keybinds); - } - keybinds - } -} - -/// For each [`Key`] assigned to [`Action`]s, -/// map the [`Action`]s to the [`Key`] -impl From<KeyActionFromYaml> for ModeKeybinds { - fn from(key_action: KeyActionFromYaml) -> ModeKeybinds { - let actions = key_action.action; - - ModeKeybinds( - key_action - .key - .into_iter() - .map(|k| (k, actions.clone())) - .collect::<HashMap<Key, Vec<Action>>>(), - ) - } -} - -impl From<KeyActionUnbind> for ModeKeybinds { - fn from(key_action_unbind: KeyActionUnbind) -> ModeKeybinds { - match key_action_unbind { - KeyActionUnbind::KeyAction(key_action) => ModeKeybinds::from(key_action), - KeyActionUnbind::Unbind(_) => ModeKeybinds::new(), - } - } -} - -impl From<Vec<KeyActionFromYaml>> for ModeKeybinds { - fn from(key_action_from_yaml: Vec<KeyActionFromYaml>) -> ModeKeybinds { - let mut mode_keybinds = ModeKeybinds::new(); - - for keybind in key_action_from_yaml { - for key in keybind.key { - mode_keybinds.0.insert(key, keybind.action.clone()); - } - } - mode_keybinds - } -} - -impl Default for Unbind { - fn default() -> Unbind { - Unbind::All(false) - } -} - -// The unit test location. -#[cfg(test)] -#[path = "./unit/keybinds_test.rs"] -mod keybinds_test; diff --git a/src/common/input/mod.rs b/src/common/input/mod.rs deleted file mode 100644 index f30ccf23..00000000 --- a/src/common/input/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! The way terminal input is handled. - -pub mod actions; -pub mod config; -pub mod handler; -pub mod keybinds; -pub mod options; diff --git a/src/common/input/options.rs b/src/common/input/options.rs deleted file mode 100644 index 33625b65..00000000 --- a/src/common/input/options.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! Handles cli and configuration options -use crate::cli::ConfigCli; -use serde::{Deserialize, Serialize}; -use structopt::StructOpt; - -#[derive(Clone, Default, Debug, PartialEq, Deserialize, Serialize, StructOpt)] -/// Options that can be set either through the config file, -/// or cli flags -pub struct Options { - /// Allow plugins to use a more simplified layout - /// that is compatible with more fonts - #[structopt(long)] - pub simplified_ui: bool, -} - -impl Options { - pub fn from_yaml(from_yaml: Option<Options>) -> Options { - if let Some(opts) = from_yaml { - opts - } else { - Options::default() - } - } - - /// Merges two [`Options`] structs, a `Some` in `other` - /// will supercede a `Some` in `self` - // TODO: Maybe a good candidate for a macro? - pub fn merge(&self, other: Options) -> Options { - let simplified_ui = if other.simplified_ui { - true - } else { - self.simplified_ui - }; - - Options { simplified_ui } - } - - pub fn from_cli(&self, other: Option<ConfigCli>) -> Options { - if let Some(ConfigCli::Options(options)) = other { - Options::merge(&self, options) - } else { - self.to_owned() - } - } -} diff --git a/src/common/input/unit/keybinds_test.rs b/src/common/input/unit/keybinds_test.rs deleted file mode 100644 index 4767fc15..00000000 --- a/src/common/input/unit/keybinds_test.rs +++ /dev/null @@ -1,801 +0,0 @@ -use super::super::actions::*; -use super::super::keybinds::*; -use zellij_tile::data::Key; - -#[test] -fn merge_keybinds_merges_different_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - - let mut mode_keybinds_expected = ModeKeybinds::new(); - mode_keybinds_expected - .0 - .insert(Key::F(1), vec![Action::NoOp]); - mode_keybinds_expected - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - - let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); - - assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -} - -#[test] -fn merge_mode_keybinds_overwrites_same_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - - let mut mode_keybinds_expected = ModeKeybinds::new(); - mode_keybinds_expected - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - - let mode_keybinds_merged = mode_keybinds_self.merge(mode_keybinds_other); - - assert_eq!(mode_keybinds_expected, mode_keybinds_merged); -} - -#[test] -fn merge_keybinds_merges() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::Backspace, vec![Action::NoOp]); - let mut keybinds_self = Keybinds::new(); - keybinds_self - .0 - .insert(InputMode::Normal, mode_keybinds_self.clone()); - let mut keybinds_other = Keybinds::new(); - keybinds_other - .0 - .insert(InputMode::Resize, mode_keybinds_other.clone()); - let mut keybinds_expected = Keybinds::new(); - keybinds_expected - .0 - .insert(InputMode::Normal, mode_keybinds_self); - keybinds_expected - .0 - .insert(InputMode::Resize, mode_keybinds_other); - - assert_eq!( - keybinds_expected, - keybinds_self.merge_keybinds(keybinds_other) - ) -} - -#[test] -fn merge_keybinds_overwrites_same_keys() { - let mut mode_keybinds_self = ModeKeybinds::new(); - mode_keybinds_self.0.insert(Key::F(1), vec![Action::NoOp]); - mode_keybinds_self.0.insert(Key::F(2), vec![Action::NoOp]); - mode_keybinds_self.0.insert(Key::F(3), vec![Action::NoOp]); - let mut mode_keybinds_other = ModeKeybinds::new(); - mode_keybinds_other - .0 - .insert(Key::F(1), vec![Action::GoToTab(1)]); - mode_keybinds_other - .0 - .insert(Key::F(2), vec![Action::GoToTab(2)]); - mode_keybinds_other - .0 - .insert(Key::F(3), vec![Action::GoToTab(3)]); - let mut keybinds_self = Keybinds::new(); - keybinds_self - .0 - .insert(InputMode::Normal, mode_keybinds_self.clone()); - let mut keybinds_other = Keybinds::new(); - keybinds_other - .0 - .insert(InputMode::Normal, mode_keybinds_other.clone()); - let mut keybinds_expected = Keybinds::new(); - keybinds_expected - .0 - .insert(InputMode::Normal, mode_keybinds_other); - - assert_eq!( - keybinds_expected, - keybinds_self.merge_keybinds(keybinds_other) - ) -} - -#[test] -fn from_keyaction_from_yaml_to_mode_keybindings() { - let actions = vec![Action::NoOp, Action::GoToTab(1)]; - let keyaction = KeyActionFromYaml { - action: actions.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions.clone()); - expected.0.insert(Key::Backspace, actions.clone()); - expected.0.insert(Key::Char('t'), actions); - - assert_eq!(expected, ModeKeybinds::from(keyaction)); -} - -#[test] -fn toplevel_unbind_unbinds_all() { - let from_yaml = KeybindsFromYaml { - unbind: Unbind::All(true), - keybinds: HashMap::new(), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn no_unbind_unbinds_none() { - let from_yaml = KeybindsFromYaml { - unbind: Unbind::All(false), - keybinds: HashMap::new(), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - assert_eq!(keybinds_from_yaml, Keybinds::default()); -} - -#[test] -fn last_keybind_is_taken() { - let actions_1 = vec![Action::NoOp, Action::NewTab]; - let keyaction_1 = KeyActionFromYaml { - action: actions_1.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - let actions_2 = vec![Action::GoToTab(1)]; - let keyaction_2 = KeyActionFromYaml { - action: actions_2.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions_2.clone()); - expected.0.insert(Key::Backspace, actions_2.clone()); - expected.0.insert(Key::Char('t'), actions_2); - - assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -} - -#[test] -fn last_keybind_overwrites() { - let actions_1 = vec![Action::NoOp, Action::NewTab]; - let keyaction_1 = KeyActionFromYaml { - action: actions_1.clone(), - key: vec![Key::F(1), Key::Backspace, Key::Char('t')], - }; - let actions_2 = vec![Action::GoToTab(1)]; - let keyaction_2 = KeyActionFromYaml { - action: actions_2.clone(), - key: vec![Key::F(1), Key::Char('t')], - }; - - let mut expected = ModeKeybinds::new(); - expected.0.insert(Key::F(1), actions_2.clone()); - expected.0.insert(Key::Backspace, actions_1.clone()); - expected.0.insert(Key::Char('t'), actions_2); - - assert_eq!(expected, ModeKeybinds::from(vec![keyaction_1, keyaction_2])); -} - -#[test] -fn unbind_single_mode() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let result = keybinds.0.get(&InputMode::Normal); - assert!(result.is_none()); -} - -#[test] -fn unbind_multiple_modes() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds.clone()); - keys.insert(InputMode::Pane, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let normal = keybinds.0.get(&InputMode::Normal); - let pane = keybinds.0.get(&InputMode::Pane); - assert!(normal.is_none()); - assert!(pane.is_none()); -} - -#[test] -fn unbind_single_keybind_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt('n')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds = keybinds.0.get(&InputMode::Normal); - let result = mode_keybinds - .expect("Mode shouldn't be empty") - .0 - .get(&Key::Alt('n')); - assert!(result.is_none()); -} - -#[test] -fn unbind_single_keybind_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt('n')]); - let unbind_h = Unbind::Keys(vec![Key::Alt('h')]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_n = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_h = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_n); - keys.insert(InputMode::Pane, key_action_unbinds_h); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let normal = keybinds.0.get(&InputMode::Normal); - let pane = keybinds.0.get(&InputMode::Pane); - let result_normal = normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_pane = pane.expect("Mode shouldn't be empty").0.get(&Key::Alt('h')); - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds = keybinds.0.get(&InputMode::Normal); - let result_n = mode_keybinds - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_p = mode_keybinds - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - assert!(result_n.is_none()); - assert!(result_p.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_multiple_modes() { - let unbind_normal = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]); - let unbind_resize = Unbind::Keys(vec![Key::Char('h'), Key::Ctrl('r')]); - let unbind_from_yaml_normal = UnbindFromYaml { - unbind: unbind_normal, - }; - let unbind_from_yaml_resize = UnbindFromYaml { - unbind: unbind_resize, - }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; - let key_action_unbinds_resize = vec![KeyActionUnbind::Unbind(unbind_from_yaml_resize)]; - - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Resize, key_action_unbinds_resize); - - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); - let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); - let result_normal_1 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_2 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_resize_1 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('h')); - let result_resize_2 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('r')); - assert!(result_normal_1.is_none()); - assert!(result_resize_1.is_none()); - assert!(result_normal_2.is_none()); - assert!(result_resize_2.is_none()); -} - -#[test] -fn unbind_multiple_keybinds_all_modes() { - let unbind = Unbind::Keys(vec![Key::Alt('n'), Key::Alt('h')]); - let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - let keybinds_from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind, - }; - - let keybinds = Keybinds::unbind(keybinds_from_yaml); - let mode_keybinds_normal = keybinds.0.get(&InputMode::Normal); - let mode_keybinds_resize = keybinds.0.get(&InputMode::Resize); - let result_normal_1 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_2 = mode_keybinds_normal - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('h')); - let result_resize_1 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('n')); - let result_resize_2 = mode_keybinds_resize - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('h')); - assert!(result_normal_1.is_none()); - assert!(result_resize_1.is_none()); - assert!(result_normal_2.is_none()); - assert!(result_resize_2.is_none()); -} - -#[test] -fn unbind_all_toplevel_single_key_single_mode() { - let unbind = Unbind::Keys(vec![Key::Alt('h')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_single_key_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt('n')]); - let unbind_h = Unbind::Keys(vec![Key::Alt('h')]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_multiple_key_multiple_modes() { - let unbind_n = Unbind::Keys(vec![Key::Alt('n'), Key::Ctrl('p')]); - let unbind_h = Unbind::Keys(vec![Key::Alt('h'), Key::Ctrl('t')]); - let unbind_from_yaml_n = UnbindFromYaml { unbind: unbind_n }; - let unbind_from_yaml_h = UnbindFromYaml { unbind: unbind_h }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_n)]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml_h)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_all_toplevel_all_key_multiple_modes() { - let unbind = Unbind::All(true); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbinds_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml.clone())]; - let key_action_unbinds_pane = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbinds_normal); - keys.insert(InputMode::Pane, key_action_unbinds_pane); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(true), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - assert_eq!(keybinds_from_yaml, Keybinds::new()); -} - -#[test] -fn unbind_single_keybind_all_modes() { - let keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_pane = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_resize = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_tab = keybinds_from_yaml - .0 - .get(&InputMode::Tab) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); - assert!(result_resize.is_none()); - assert!(result_tab.is_none()); -} - -#[test] -fn unbind_single_toplevel_single_key_single_mode_identical() { - let unbind = Unbind::Keys(vec![Key::Alt('n')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_pane = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_resize = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_tab = keybinds_from_yaml - .0 - .get(&InputMode::Tab) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - - assert!(result_normal.is_none()); - assert!(result_pane.is_none()); - assert!(result_resize.is_none()); - assert!(result_tab.is_none()); -} - -#[test] -fn unbind_single_toplevel_single_key_single_mode_differing() { - let unbind = Unbind::Keys(vec![Key::Alt('l')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - let result_resize_n = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_resize_l = keybinds_from_yaml - .0 - .get(&InputMode::Resize) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_resize_n.is_none()); - assert!(result_resize_l.is_some()); -} - -#[test] -fn unbind_single_toplevel_single_key_multiple_modes() { - let unbind = Unbind::Keys(vec![Key::Alt('l')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind.clone()); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - let result_pane_n = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_pane_l = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_pane_n.is_none()); - assert!(result_pane_l.is_none()); -} - -#[test] -fn unbind_single_toplevel_multiple_keys_single_mode() { - let unbind = Unbind::Keys(vec![ - Key::Alt('l'), - Key::Alt('h'), - Key::Alt('j'), - Key::Alt('k'), - ]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind.clone()); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - let result_normal_k = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('k')); - let result_normal_h = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('h')); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_normal_h.is_none()); - assert!(result_normal_k.is_none()); -} - -#[test] -fn unbind_single_toplevel_multiple_keys_multiple_modes() { - let unbind_normal = Unbind::Keys(vec![Key::Alt('l'), Key::Ctrl('p')]); - let unbind_from_yaml_normal = UnbindFromYaml { - unbind: unbind_normal, - }; - let key_action_unbind_normal = vec![KeyActionUnbind::Unbind(unbind_from_yaml_normal)]; - let unbind = Unbind::Keys(vec![Key::Alt('l'), Key::Alt('k')]); - let unbind_from_yaml = UnbindFromYaml { unbind }; - let key_action_unbind = vec![KeyActionUnbind::Unbind(unbind_from_yaml)]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind_normal); - keys.insert(InputMode::Pane, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::Keys(vec![Key::Alt('n')]), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - - let result_normal_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_normal_p = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_normal_l = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - let result_pane_p = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Ctrl('p')); - let result_pane_n = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('n')); - let result_pane_l = keybinds_from_yaml - .0 - .get(&InputMode::Pane) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Alt('l')); - - assert!(result_normal_n.is_none()); - assert!(result_normal_l.is_none()); - assert!(result_normal_p.is_none()); - assert!(result_pane_n.is_none()); - assert!(result_pane_p.is_some()); - assert!(result_pane_l.is_none()); -} - -#[test] -fn uppercase_and_lowercase_are_distinct() { - let key_action_n = KeyActionFromYaml { - key: vec![Key::Char('n')], - action: vec![Action::NewTab], - }; - let key_action_large_n = KeyActionFromYaml { - key: vec![Key::Char('N')], - action: vec![Action::NewPane(None)], - }; - - let key_action_unbind = vec![ - KeyActionUnbind::KeyAction(key_action_n), - KeyActionUnbind::KeyAction(key_action_large_n), - ]; - let mut keys = HashMap::<InputMode, Vec<KeyActionUnbind>>::new(); - keys.insert(InputMode::Normal, key_action_unbind); - let from_yaml = KeybindsFromYaml { - keybinds: keys, - unbind: Unbind::All(false), - }; - - let keybinds_from_yaml = Keybinds::get_default_keybinds_with_config(Some(from_yaml)); - let result_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('n')); - let result_large_n = keybinds_from_yaml - .0 - .get(&InputMode::Normal) - .expect("ModeKeybinds shouldn't be empty") - .0 - .get(&Key::Char('N')); - - assert!(result_n.is_some()); - assert!(result_large_n.is_some()); -} |
