diff --git a/Cargo.lock b/Cargo.lock index ebc26dc..2067332 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1039,14 +1039,6 @@ dependencies = [ "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "toml" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "serde 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "typenum" version = "1.10.0" @@ -1137,7 +1129,6 @@ dependencies = [ "serde_derive 1.0.90 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "xi-core-lib 0.2.0 (git+https://github.com/xi-editor/xi-editor)", "xi-rpc 0.2.0 (git+https://github.com/xi-editor/xi-editor)", @@ -1428,7 +1419,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" -"checksum toml 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "87c5890a989fa47ecdc7bcb4c63a77a82c18f306714104b1decfd722db17b39e" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" diff --git a/Cargo.toml b/Cargo.toml index f6f8551..212d970 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ serde = "1.0.89" serde_derive = "1.0" serde_json = "1.0" termion = "1.5.1" -toml = "0.5.0" [dependencies.clap] features = ["suggestions", "color", "wrap_help"] diff --git a/README.md b/README.md index 14e0328..12734dd 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,8 @@ Vim used by 80% of users. ## Configuration -All the keybinding presented below can be overriden via the `keyboard.toml` -configuration file. You can find an [example of keyboard.toml config](./resources/keyboard.toml) +All the keybinding presented below can be overriden via the `keyboard.vim` +configuration file. You can find an [example of keyboard.toml config](./resources/keyboard.vim) which will overrid all the default values with the exact same values. Those configuration files should live inside a `vixi` folder placed into the diff --git a/resources/keyboard.toml b/resources/keyboard.toml deleted file mode 100644 index 9bce167..0000000 --- a/resources/keyboard.toml +++ /dev/null @@ -1,63 +0,0 @@ -[normal_mode] -switch_to_insert_mode = "i" -switch_to_visual_mode = "v" -switch_to_action_mode = "" - -paste = "p" - -insert_line_below = "o" -insert_line_above = "O" - -# The "classic" move keys -move_up = "" -move_down = "" -move_left = "" -move_right = "" - -# The "vim-like" move keys -move_up = "k" -move_down = "j" -move_left = "h" -move_right = "l" - -page_up = "" -page_down = "" - - -[visual_mode] -switch_to_normal_mode = "" -yank_selection = "y" -delete_selection = "d" -paste = "p" - -# The "classic" move keys -move_up_and_select = "" -move_down_and_select = "" -move_left_and_select = "" -move_right_and_select = "" - -# The "vim-like" move keys -move_up_and_select = "k" -move_down_and_select = "j" -move_left_and_select = "h" -move_right_and_select = "l" - - -[insert_mode] -switch_to_normal_mode = "" - -# The "classic" move keys -move_up = "" -move_down = "" -move_left = "" -move_right = "" - -page_up = "" -page_down = "" - -delete_forward = "" -delete_backward = "" - -[action_mode] -quit = "q" -write_to_file = "w" diff --git a/resources/keyboard.vim b/resources/keyboard.vim new file mode 100644 index 0000000..f06a8dc --- /dev/null +++ b/resources/keyboard.vim @@ -0,0 +1,70 @@ +" This is a sample of vixi keyboard configuration. +" +" It redefine all the default configurations with the same values. This +" content should be set into: +" - Linux => /etc/vixi/keyboard.vim or ~/.config/vixi/keyboard.vim +" - Windows => C:\Users\Alice\AppData\vixi\keyboard.vim +" - MacOS => /Users/Alice/Library/Preferences/vixi/keyboard.vim +" +" +" The configuration format is: {cmd} {lhs} {rhs} +" where: +" {cmd} is one of 'map', 'nmap', 'vmap', 'imap' +" {lhs} left hand side, is a sequence of one or more keys that you will use +" in your new shortcut. +" {rhs} right hand side, is the sequence of keys that the {lhs} shortcut keys +" will execute when entered. A shortcut starting with ':' indicate a +" command. +" +" +" {cmd} defines the mode in which you keymap will be available: +" +" | Command | Modes | +" |:-------:|:--------------:| +" | map | Normal, Visual | +" | nmap | Normal | +" | vmap | Visual | +" | imap | Insert | + + +" Leader key definition +set leader + +" List of key available the Normal and Visual mode +map :switch_to_normal_mode +map :move_up +map :move_down +map :move_left +map :move_right +map :page_up +map :page_down +map q :quit +map w :write_to_file + +" Normal mode +nmap h :move_left +nmap j :move_down +nmap k :move_up +nmap l :move_right +nmap i :switch_to_insert_mode +nmap v :switch_to_visual_mode +nmap p :paste +nmap o :open_line_below +nmap O :open_line_above + +" Visual mode +vmap :switch_to_normal_mode +vmap y :yank_selection +vmap d :delete_selection +vmap p :past + +" Insert mode mode +imap :delete_forward +imap :delete_backward +imap :switch_to_normal_mode +imap :move_up +imap :move_down +imap :move_left +imap :move_right +imap :page_up +imap :page_down diff --git a/src/input_controller/actions/mod.rs b/src/input_controller/actions/mod.rs deleted file mode 100644 index bc7ec1b..0000000 --- a/src/input_controller/actions/mod.rs +++ /dev/null @@ -1,136 +0,0 @@ -mod rpc; - -use super::KeyStroke; -use crate::core::ClientToClientWriter; - -use xi_rpc::Peer; - -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum Response { - Continue, - Stop, - SwitchToInsertMode, - SwitchToNormalMode, - SwitchToVisualMode, - SwitchToActionMode, -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub enum Action { - WriteToFile, - Quite, - - SwitchToInsertMode, - SwitchToVisualMode, - SwitchToActionMode, - SwitchToNormalMode, - - MoveUp, - MoveDown, - MoveLeft, - MoveRight, - PageUp, - PageDown, - - MoveUpAndSelect, - MoveDownAndSelect, - MoveLeftAndSelect, - MoveRightAndSelect, - - YankSelection, - DeleteSelection, - DeleteSelectionAndPaste, - - Paste, - - InsertLineBelow, - InsertLineAbove, - - DeleteBackward, - DeleteForward, - - // Custom for the insert mode. Not configurable - InsertKeyStroke(KeyStroke), -} - -impl Action { - pub fn execute( - self, - view_id: &str, - core: &dyn Peer, - front_event_writer: &mut ClientToClientWriter, - ) -> Response { - match self { - Action::WriteToFile => rpc::write_to_file(view_id, front_event_writer), - Action::Quite => rpc::quite(view_id, core), - - Action::SwitchToInsertMode => Response::SwitchToInsertMode, - Action::SwitchToVisualMode => Response::SwitchToVisualMode, - Action::SwitchToActionMode => Response::SwitchToActionMode, - Action::SwitchToNormalMode => Response::SwitchToNormalMode, - - Action::MoveUp => rpc::move_up(view_id, core), - Action::MoveDown => rpc::move_down(view_id, core), - Action::MoveLeft => rpc::move_left(view_id, core), - Action::MoveRight => rpc::move_right(view_id, core), - Action::PageUp => rpc::page_up(view_id, core), - Action::PageDown => rpc::page_down(view_id, core), - - Action::MoveUpAndSelect => rpc::move_up_and_select(view_id, core), - Action::MoveDownAndSelect => rpc::move_down_and_select(view_id, core), - Action::MoveLeftAndSelect => rpc::move_left_and_select(view_id, core), - Action::MoveRightAndSelect => rpc::move_right_and_select(view_id, core), - - Action::YankSelection => rpc::yank_selection(view_id, core), - Action::DeleteSelection => rpc::cute_selection(view_id, core), - Action::DeleteSelectionAndPaste => rpc::cute_selection_and_paste(view_id, core), - - Action::Paste => rpc::paste(view_id, core), - - Action::InsertKeyStroke(k) => rpc::insert_keystroke(view_id, k, core), - Action::InsertLineBelow => rpc::insert_line_below(view_id, core), - Action::InsertLineAbove => rpc::insert_line_above(view_id, core), - - Action::DeleteBackward => rpc::delete_backward(view_id, core), - Action::DeleteForward => rpc::delete_forward(view_id, core), - } - } - - pub fn from_description(desc: &str) -> Option { - match desc { - "write_to_file" => Some(Action::WriteToFile), - "quit" => Some(Action::Quite), - - "switch_to_insert_mode" => Some(Action::SwitchToInsertMode), - "switch_to_visual_mode" => Some(Action::SwitchToVisualMode), - "switch_to_action_mode" => Some(Action::SwitchToActionMode), - "switch_to_normal_mode" => Some(Action::SwitchToNormalMode), - - "move_up" => Some(Action::MoveUp), - "move_down" => Some(Action::MoveDown), - "move_left" => Some(Action::MoveLeft), - "move_right" => Some(Action::MoveRight), - "page_up" => Some(Action::PageUp), - "page_down" => Some(Action::PageDown), - - "move_up_and_select" => Some(Action::MoveUpAndSelect), - "move_down_and_select" => Some(Action::MoveDownAndSelect), - "move_left_and_select" => Some(Action::MoveLeftAndSelect), - "move_right_and_select" => Some(Action::MoveRightAndSelect), - - "yank_selection" => Some(Action::YankSelection), - "delete_selection" => Some(Action::DeleteSelection), - "delete_selection_and_past" => Some(Action::DeleteSelectionAndPaste), - - "paste" => Some(Action::Paste), - - "insert_line_below" => Some(Action::InsertLineBelow), - "insert_line_above" => Some(Action::InsertLineAbove), - - "delete_backward" => Some(Action::DeleteBackward), - "delete_forward" => Some(Action::DeleteForward), - - _ => None, - } - } -} diff --git a/src/input_controller/commands/mod.rs b/src/input_controller/commands/mod.rs new file mode 100644 index 0000000..c88c804 --- /dev/null +++ b/src/input_controller/commands/mod.rs @@ -0,0 +1,136 @@ +mod rpc; + +use super::KeyStroke; +use crate::core::ClientToClientWriter; + +use xi_rpc::Peer; + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Response { + Continue, + Stop, + SwitchToInsertMode, + SwitchToNormalMode, + SwitchToVisualMode, + SwitchToActionMode, +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Command { + WriteToFile, + Quite, + + SwitchToInsertMode, + SwitchToVisualMode, + SwitchToActionMode, + SwitchToNormalMode, + + MoveUp, + MoveDown, + MoveLeft, + MoveRight, + PageUp, + PageDown, + + MoveUpAndSelect, + MoveDownAndSelect, + MoveLeftAndSelect, + MoveRightAndSelect, + + YankSelection, + DeleteSelection, + DeleteSelectionAndPaste, + + Paste, + + InsertLineBelow, + InsertLineAbove, + + DeleteBackward, + DeleteForward, + + // Custom for the insert mode. Not configurable + InsertKeyStroke(KeyStroke), +} + +impl Command { + pub fn execute( + self, + view_id: &str, + core: &dyn Peer, + front_event_writer: &mut ClientToClientWriter, + ) -> Response { + match self { + Command::WriteToFile => rpc::write_to_file(view_id, front_event_writer), + Command::Quite => rpc::quite(view_id, core), + + Command::SwitchToInsertMode => Response::SwitchToInsertMode, + Command::SwitchToVisualMode => Response::SwitchToVisualMode, + Command::SwitchToActionMode => Response::SwitchToActionMode, + Command::SwitchToNormalMode => Response::SwitchToNormalMode, + + Command::MoveUp => rpc::move_up(view_id, core), + Command::MoveDown => rpc::move_down(view_id, core), + Command::MoveLeft => rpc::move_left(view_id, core), + Command::MoveRight => rpc::move_right(view_id, core), + Command::PageUp => rpc::page_up(view_id, core), + Command::PageDown => rpc::page_down(view_id, core), + + Command::MoveUpAndSelect => rpc::move_up_and_select(view_id, core), + Command::MoveDownAndSelect => rpc::move_down_and_select(view_id, core), + Command::MoveLeftAndSelect => rpc::move_left_and_select(view_id, core), + Command::MoveRightAndSelect => rpc::move_right_and_select(view_id, core), + + Command::YankSelection => rpc::yank_selection(view_id, core), + Command::DeleteSelection => rpc::cute_selection(view_id, core), + Command::DeleteSelectionAndPaste => rpc::cute_selection_and_paste(view_id, core), + + Command::Paste => rpc::paste(view_id, core), + + Command::InsertKeyStroke(k) => rpc::insert_keystroke(view_id, k, core), + Command::InsertLineBelow => rpc::insert_line_below(view_id, core), + Command::InsertLineAbove => rpc::insert_line_above(view_id, core), + + Command::DeleteBackward => rpc::delete_backward(view_id, core), + Command::DeleteForward => rpc::delete_forward(view_id, core), + } + } + + pub fn from_description(desc: &str) -> Option { + match desc { + ":write_to_file" => Some(Command::WriteToFile), + ":quit" => Some(Command::Quite), + + ":switch_to_insert_mode" => Some(Command::SwitchToInsertMode), + ":switch_to_visual_mode" => Some(Command::SwitchToVisualMode), + ":switch_to_action_mode" => Some(Command::SwitchToActionMode), + ":switch_to_normal_mode" => Some(Command::SwitchToNormalMode), + + ":move_up" => Some(Command::MoveUp), + ":move_down" => Some(Command::MoveDown), + ":move_left" => Some(Command::MoveLeft), + ":move_right" => Some(Command::MoveRight), + ":page_up" => Some(Command::PageUp), + ":page_down" => Some(Command::PageDown), + + ":move_up_and_select" => Some(Command::MoveUpAndSelect), + ":move_down_and_select" => Some(Command::MoveDownAndSelect), + ":move_left_and_select" => Some(Command::MoveLeftAndSelect), + ":move_right_and_select" => Some(Command::MoveRightAndSelect), + + ":yank_selection" => Some(Command::YankSelection), + ":delete_selection" => Some(Command::DeleteSelection), + ":delete_selection_and_past" => Some(Command::DeleteSelectionAndPaste), + + ":paste" => Some(Command::Paste), + + ":insert_line_below" => Some(Command::InsertLineBelow), + ":insert_line_above" => Some(Command::InsertLineAbove), + + ":delete_backward" => Some(Command::DeleteBackward), + ":delete_forward" => Some(Command::DeleteForward), + + _ => None, + } + } +} diff --git a/src/input_controller/actions/rpc.rs b/src/input_controller/commands/rpc.rs similarity index 100% rename from src/input_controller/actions/rpc.rs rename to src/input_controller/commands/rpc.rs diff --git a/src/input_controller/config.rs b/src/input_controller/config.rs new file mode 100644 index 0000000..dcf660e --- /dev/null +++ b/src/input_controller/config.rs @@ -0,0 +1,87 @@ +use std::collections::HashMap; +use std::fs::File; +use std::io::{self, Read}; +use std::path::Path; +use std::str::FromStr; + +use super::commands::Command; +use super::keyboard::KeyStroke; + +#[derive(Debug, Default)] +pub struct Config { + pub leader: Option, + pub normal_mode: HashMap, + pub insert_mode: HashMap, + pub visual_mode: HashMap, + pub action_mode: HashMap, +} + +impl Config { + pub fn from_config_dir(config_dir: &Path) -> Result { + let mut file = File::open(config_dir.join("keyboard.vim"))?; + let mut raw_contents = String::new(); + file.read_to_string(&mut raw_contents)?; + + Self::from_str(&raw_contents) + } + + fn parse_map(&mut self, line: &str) { + let elements: Vec<&str> = line.split(' ').collect(); + + if elements.len() < 3 { + return; + } + + let key = match KeyStroke::from_description(elements[1]) { + Some(key) => key, + None => return, + }; + + let command = match Command::from_description(elements[2]) { + Some(cmd) => cmd, + None => return, + }; + + match elements[0] { + "imap" => self.insert_mode.insert(key, command), + "nmap" => self.normal_mode.insert(key, command), + "vmap" => self.visual_mode.insert(key, command), + "map" => { + self.normal_mode.insert(key, command); + self.visual_mode.insert(key, command) + } + _ => return, + }; + } + + fn parse_set(&mut self, line: &str) { + let elements: Vec<&str> = line.split(' ').collect(); + + if elements.len() != 3 { + return; + } + + match elements[1] { + "leader" => self.leader = KeyStroke::from_description(elements[2]), + _ => return, + } + } +} + +impl FromStr for Config { + type Err = io::Error; + + fn from_str(raw: &str) -> Result { + let mut config = Self::default(); + + for line in raw.lines() { + match line.splitn(1, ' ').nth(0).unwrap_or_default() { + "map" | "nmap" | "vmap" | "imap" => config.parse_map(line), + "set" => config.parse_set(line), + _ => (), + } + } + + Ok(config) + } +} diff --git a/src/input_controller/mod.rs b/src/input_controller/mod.rs index 9401ba5..8bf9ae8 100644 --- a/src/input_controller/mod.rs +++ b/src/input_controller/mod.rs @@ -1,13 +1,14 @@ -mod actions; +mod commands; +pub mod config; pub mod keyboard; -mod mode_actions; +mod modes; -use std::collections::HashMap; use std::sync::{Arc, Mutex}; -use self::actions::{Action, Response}; +use self::commands::{Command, Response}; +use self::config::Config; use self::keyboard::{KeyStroke, Keyboard}; -use self::mode_actions::ModeActions; +use self::modes::ModeCommands; use crate::core::ClientToClientWriter; use failure::Error; @@ -17,18 +18,6 @@ lazy_static! { static ref PASTE_BUFFER: Arc>> = Arc::new(Mutex::new(None)); } -#[derive(Debug, Default, Deserialize)] -pub struct Config { - #[serde(default)] - normal_mode: HashMap, - #[serde(default)] - insert_mode: HashMap, - #[serde(default)] - visual_mode: HashMap, - #[serde(default)] - action_mode: HashMap, -} - #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Mode { Normal, @@ -52,10 +41,10 @@ impl Mode { pub struct InputController { keyboard: Box, view_id: String, - normal_mode: ModeActions, - insert_mode: ModeActions, - visual_mode: ModeActions, - action_mode: ModeActions, + normal_mode: ModeCommands, + insert_mode: ModeCommands, + visual_mode: ModeCommands, + action_mode: ModeCommands, mode: Mode, front_event_writer: ClientToClientWriter, } @@ -69,10 +58,10 @@ impl InputController { Self { keyboard, view_id: String::new(), - normal_mode: ModeActions::setup(Mode::Normal, &config.normal_mode), - insert_mode: ModeActions::setup(Mode::Insert, &config.insert_mode), - visual_mode: ModeActions::setup(Mode::Visual, &config.visual_mode), - action_mode: ModeActions::setup(Mode::Action, &config.action_mode), + normal_mode: ModeCommands::setup(Mode::Normal, &config.normal_mode), + insert_mode: ModeCommands::setup(Mode::Insert, &config.insert_mode), + visual_mode: ModeCommands::setup(Mode::Visual, &config.visual_mode), + action_mode: ModeCommands::setup(Mode::Action, &config.action_mode), mode: Mode::Normal, front_event_writer: client_to_client_writer, } @@ -112,14 +101,14 @@ impl InputController { if let Some(key) = key_res { let mut action = match self.mode { - Mode::Normal => self.normal_mode.get_action_from_keystroke(key), - Mode::Insert => self.insert_mode.get_action_from_keystroke(key), - Mode::Visual => self.visual_mode.get_action_from_keystroke(key), - Mode::Action => self.action_mode.get_action_from_keystroke(key), + Mode::Normal => self.normal_mode.get_command_from_keystroke(key), + Mode::Insert => self.insert_mode.get_command_from_keystroke(key), + Mode::Visual => self.visual_mode.get_command_from_keystroke(key), + Mode::Action => self.action_mode.get_command_from_keystroke(key), }; if action.is_none() && self.mode == Mode::Insert { - action = Some(Action::InsertKeyStroke(key)); + action = Some(Command::InsertKeyStroke(key)); } else if action.is_none() { continue; } @@ -159,25 +148,3 @@ impl InputController { Ok(()) } } - -#[cfg(test)] -mod tests { - use super::Config; - - #[test] - fn test_config_deserialization() { - let config: Config = toml::from_str( - r#" - [visual_mode] - move_down = "" - "#, - ) - .unwrap(); - - assert_eq!( - String::from(""), - config.visual_mode[&String::from("move_down")] - ); - } - -} diff --git a/src/input_controller/mode_actions.rs b/src/input_controller/mode_actions.rs deleted file mode 100644 index 3223232..0000000 --- a/src/input_controller/mode_actions.rs +++ /dev/null @@ -1,123 +0,0 @@ -use std::collections::HashMap; - -use super::actions::Action; -use super::keyboard::KeyStroke; -use super::Mode; - -#[derive(Debug)] -pub struct ModeActions(HashMap); - -impl ModeActions { - pub fn setup(mode: Mode, config_map: &HashMap) -> Self { - let mut actions = match mode { - Mode::Normal => defaults::DEFAULT_NORMAL_MODE_ACTIONS.clone(), - Mode::Insert => defaults::DEFAULT_INSERT_MODE_ACTIONS.clone(), - Mode::Visual => defaults::DEFAULT_VISUAL_MODE_ACTIONS.clone(), - Mode::Action => defaults::DEFAULT_ACTION_MODE_ACTIONS.clone(), - }; - - for (action_desc, key_desc) in config_map.iter() { - let key = KeyStroke::from_description(&key_desc); - if key.is_none() { - continue; - } - - let action = Action::from_description(action_desc); - if action.is_none() { - continue; - } - - actions.insert(key.unwrap(), action.unwrap()); - } - - Self(actions) - } - - pub fn get_action_from_keystroke(&self, keystroke: KeyStroke) -> Option { - self.0.get(&keystroke).cloned() - } -} - -pub mod defaults { - use std::collections::HashMap; - - use super::super::actions::Action; - use super::super::keyboard::KeyStroke; - - lazy_static! { - pub static ref DEFAULT_NORMAL_MODE_ACTIONS: HashMap = { - let mut actions = HashMap::with_capacity(12); - - // The classic arrow keys. - actions.insert(KeyStroke::KeyUp, Action::MoveUpAndSelect); - actions.insert(KeyStroke::KeyDown, Action::MoveDownAndSelect); - actions.insert(KeyStroke::KeyLeft, Action::MoveLeftAndSelect); - actions.insert(KeyStroke::KeyRight, Action::MoveRightAndSelect); - - // The "vim like" keys. - actions.insert(KeyStroke::Char('k'), Action::MoveUpAndSelect); - actions.insert(KeyStroke::Char('j'), Action::MoveDownAndSelect); - actions.insert(KeyStroke::Char('h'), Action::MoveLeftAndSelect); - actions.insert(KeyStroke::Char('l'), Action::MoveRightAndSelect); - - actions.insert(KeyStroke::Char('i'), Action::SwitchToInsertMode); - actions.insert(KeyStroke::Char('v'), Action::SwitchToVisualMode); - - actions.insert(KeyStroke::Char('o'), Action::InsertLineBelow); - actions.insert(KeyStroke::Char('O'), Action::InsertLineAbove); - - actions - }; - - pub static ref DEFAULT_ACTION_MODE_ACTIONS: HashMap = { - let mut actions = HashMap::with_capacity(3); - - actions.insert(KeyStroke::KeyEscape, Action::SwitchToNormalMode); - actions.insert(KeyStroke::Char('q'), Action::Quite); - actions.insert(KeyStroke::Char('w'), Action::WriteToFile); - - actions - }; - - pub static ref DEFAULT_INSERT_MODE_ACTIONS: HashMap = { - let mut actions = HashMap::with_capacity(12); - - actions.insert(KeyStroke::KeyEscape, Action::SwitchToNormalMode); - actions.insert(KeyStroke::KeyBackSpace, Action::DeleteBackward); - actions.insert(KeyStroke::KeyDelete, Action::DeleteForward); - - // The classic arrow keys. - actions.insert(KeyStroke::KeyUp, Action::MoveUp); - actions.insert(KeyStroke::KeyDown, Action::MoveDown); - actions.insert(KeyStroke::KeyLeft, Action::MoveLeft); - actions.insert(KeyStroke::KeyRight, Action::MoveRight); - actions.insert(KeyStroke::KeyPreviousPage, Action::PageUp); - actions.insert(KeyStroke::KeyNextPage, Action::PageDown); - - actions - }; - - pub static ref DEFAULT_VISUAL_MODE_ACTIONS: HashMap = { - let mut actions = HashMap::with_capacity(1); - - actions.insert(KeyStroke::KeyEscape, Action::SwitchToNormalMode); - actions.insert(KeyStroke::Char('y'), Action::YankSelection); - actions.insert(KeyStroke::Char('d'), Action::DeleteSelection); - actions.insert(KeyStroke::Char('p'), Action::DeleteSelectionAndPaste); - - // The classic arrow keys. - actions.insert(KeyStroke::KeyUp, Action::MoveUpAndSelect); - actions.insert(KeyStroke::KeyDown, Action::MoveDownAndSelect); - actions.insert(KeyStroke::KeyLeft, Action::MoveLeftAndSelect); - actions.insert(KeyStroke::KeyRight, Action::MoveRightAndSelect); - - // The "vim like" keys. - actions.insert(KeyStroke::Char('k'), Action::MoveUpAndSelect); - actions.insert(KeyStroke::Char('j'), Action::MoveDownAndSelect); - actions.insert(KeyStroke::Char('h'), Action::MoveLeftAndSelect); - actions.insert(KeyStroke::Char('l'), Action::MoveRightAndSelect); - - actions - }; - } -} diff --git a/src/input_controller/modes.rs b/src/input_controller/modes.rs new file mode 100644 index 0000000..84bd21c --- /dev/null +++ b/src/input_controller/modes.rs @@ -0,0 +1,113 @@ +use std::collections::HashMap; + +use super::commands::Command; +use super::keyboard::KeyStroke; +use super::Mode; + +#[derive(Debug, Default)] +pub struct ModeCommands(HashMap); + +impl ModeCommands { + pub fn setup(mode: Mode, config_map: &HashMap) -> Self { + let mut commands = match mode { + Mode::Normal => defaults::DEFAULT_NORMAL_MODE_COMMANDS.clone(), + Mode::Insert => defaults::DEFAULT_INSERT_MODE_COMMANDS.clone(), + Mode::Visual => defaults::DEFAULT_VISUAL_MODE_COMMANDS.clone(), + Mode::Action => defaults::DEFAULT_ACTION_MODE_COMMANDS.clone(), + }; + + for (key, command) in config_map.iter() { + commands.insert(*key, *command); + } + + Self(commands) + } + + pub fn get_command_from_keystroke(&self, keystroke: KeyStroke) -> Option { + self.0.get(&keystroke).cloned() + } +} + +pub mod defaults { + use std::collections::HashMap; + + use super::super::commands::Command; + use super::super::keyboard::KeyStroke; + + lazy_static! { + pub static ref DEFAULT_NORMAL_MODE_COMMANDS: HashMap = { + let mut commands = HashMap::with_capacity(12); + + // The classic arrow keys. + commands.insert(KeyStroke::KeyUp, Command::MoveUpAndSelect); + commands.insert(KeyStroke::KeyDown, Command::MoveDownAndSelect); + commands.insert(KeyStroke::KeyLeft, Command::MoveLeftAndSelect); + commands.insert(KeyStroke::KeyRight, Command::MoveRightAndSelect); + + // The "vim like" keys. + commands.insert(KeyStroke::Char('k'), Command::MoveUpAndSelect); + commands.insert(KeyStroke::Char('j'), Command::MoveDownAndSelect); + commands.insert(KeyStroke::Char('h'), Command::MoveLeftAndSelect); + commands.insert(KeyStroke::Char('l'), Command::MoveRightAndSelect); + + commands.insert(KeyStroke::Char('i'), Command::SwitchToInsertMode); + commands.insert(KeyStroke::Char('v'), Command::SwitchToVisualMode); + + commands.insert(KeyStroke::Char('o'), Command::InsertLineBelow); + commands.insert(KeyStroke::Char('O'), Command::InsertLineAbove); + + commands + }; + + pub static ref DEFAULT_ACTION_MODE_COMMANDS: HashMap = { + let mut commands = HashMap::with_capacity(3); + + commands.insert(KeyStroke::KeyEscape, Command::SwitchToNormalMode); + commands.insert(KeyStroke::Char('q'), Command::Quite); + commands.insert(KeyStroke::Char('w'), Command::WriteToFile); + + commands + }; + + pub static ref DEFAULT_INSERT_MODE_COMMANDS: HashMap = { + let mut commands = HashMap::with_capacity(12); + + commands.insert(KeyStroke::KeyEscape, Command::SwitchToNormalMode); + commands.insert(KeyStroke::KeyBackSpace, Command::DeleteBackward); + commands.insert(KeyStroke::KeyDelete, Command::DeleteForward); + + // The classic arrow keys. + commands.insert(KeyStroke::KeyUp, Command::MoveUp); + commands.insert(KeyStroke::KeyDown, Command::MoveDown); + commands.insert(KeyStroke::KeyLeft, Command::MoveLeft); + commands.insert(KeyStroke::KeyRight, Command::MoveRight); + commands.insert(KeyStroke::KeyPreviousPage, Command::PageUp); + commands.insert(KeyStroke::KeyNextPage, Command::PageDown); + + commands + }; + + pub static ref DEFAULT_VISUAL_MODE_COMMANDS: HashMap = { + let mut commands = HashMap::with_capacity(1); + + commands.insert(KeyStroke::KeyEscape, Command::SwitchToNormalMode); + commands.insert(KeyStroke::Char('y'), Command::YankSelection); + commands.insert(KeyStroke::Char('d'), Command::DeleteSelection); + commands.insert(KeyStroke::Char('p'), Command::DeleteSelectionAndPaste); + + // The classic arrow keys. + commands.insert(KeyStroke::KeyUp, Command::MoveUpAndSelect); + commands.insert(KeyStroke::KeyDown, Command::MoveDownAndSelect); + commands.insert(KeyStroke::KeyLeft, Command::MoveLeftAndSelect); + commands.insert(KeyStroke::KeyRight, Command::MoveRightAndSelect); + + // The "vim like" keys. + commands.insert(KeyStroke::Char('k'), Command::MoveUpAndSelect); + commands.insert(KeyStroke::Char('j'), Command::MoveDownAndSelect); + commands.insert(KeyStroke::Char('h'), Command::MoveLeftAndSelect); + commands.insert(KeyStroke::Char('l'), Command::MoveRightAndSelect); + + commands + }; + } +} diff --git a/src/main.rs b/src/main.rs index f6967fe..719ee03 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,7 +17,6 @@ extern crate failure; #[macro_use] extern crate lazy_static; extern crate termion; -extern crate toml; #[cfg(feature = "tracing")] extern crate xi_trace; @@ -30,9 +29,8 @@ mod logging; mod trace; use std::cell::RefCell; -use std::fs::File; -use std::io::prelude::*; use std::io::stdin; +use std::path::PathBuf; use std::process::exit; use std::rc::Rc; use std::thread; @@ -40,8 +38,9 @@ use std::thread; use event_controller::style::TermionStyles; use event_controller::window::TermionLayout; use event_controller::{EventController, Styles}; +use input_controller::config; use input_controller::keyboard::TermionKeyboard; -use input_controller::{Config, InputController}; +use input_controller::InputController; use failure::Error; use xi_rpc::{Peer, RpcLoop}; @@ -54,7 +53,7 @@ fn setup_logger() { logging::setup(&logging_path).expect("failed to set the logger") } -fn setup_config(core: &dyn Peer) -> Result { +fn setup_config(core: &dyn Peer) -> Result { let config_dir = dirs::config_dir().ok_or_else(|| format_err!("config dir not found"))?; let mut xi_config_dir = config_dir.clone(); @@ -67,12 +66,7 @@ fn setup_config(core: &dyn Peer) -> Result { let mut vixi_config_dir = config_dir.clone(); vixi_config_dir.push("vixi"); - let mut keyboard_config_file = File::open(vixi_config_dir.join("keyboard.toml"))?; - let mut keyboard_config_contents = String::new(); - keyboard_config_file.read_to_string(&mut keyboard_config_contents)?; - let config: Config = toml::from_str(&keyboard_config_contents)?; - - Ok(config) + Ok(vixi_config_dir.to_path_buf()) } fn main() { @@ -92,7 +86,7 @@ fn main() { let mut front_event_loop = RpcLoop::new(client_to_core_writer); let raw_peer = front_event_loop.get_raw_peer(); - let config = match setup_config(&raw_peer) { + let config_dir = match setup_config(&raw_peer) { Ok(config) => config, Err(err) => { println!("failed to load the configuration: {}", err); @@ -112,10 +106,22 @@ fn main() { .unwrap(); }); + let keyboard_config = match config::Config::from_config_dir(&config_dir) { + Ok(config) => config, + Err(err) => { + println!( + "failed to parse the keyboard config in {}: {}", + config_dir.to_str().unwrap(), + err + ); + exit(1); + } + }; + let mut input_controller = InputController::new( Box::new(TermionKeyboard::from_reader(stdin())), client_to_client_writer, - &config, + &keyboard_config, ); if let Err(err) = input_controller.open_file(&raw_peer, file_path) {