diff --git a/Cargo.toml b/Cargo.toml index 143574b339..8588eca9d8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ maintenance = { status = "actively-developed" } members = ["rustyline-derive"] [dependencies] +bitflags = "1.2" cfg-if = "0.1.6" dirs-next = { version = "1.0", optional = true } fs2 = "0.4" diff --git a/examples/example.rs b/examples/example.rs index 4826f4d878..c627471ac8 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -6,7 +6,7 @@ use rustyline::error::ReadlineError; use rustyline::highlight::{Highlighter, MatchingBracketHighlighter}; use rustyline::hint::{Hinter, HistoryHinter}; use rustyline::validate::{self, MatchingBracketValidator, Validator}; -use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyPress}; +use rustyline::{Cmd, CompletionType, Config, Context, EditMode, Editor, KeyEvent}; use rustyline_derive::Helper; #[derive(Helper)] @@ -97,8 +97,8 @@ fn main() -> rustyline::Result<()> { }; let mut rl = Editor::with_config(config); rl.set_helper(Some(h)); - rl.bind_sequence(KeyPress::Meta('N'), Cmd::HistorySearchForward); - rl.bind_sequence(KeyPress::Meta('P'), Cmd::HistorySearchBackward); + rl.bind_sequence(KeyEvent::alt('N'), Cmd::HistorySearchForward); + rl.bind_sequence(KeyEvent::alt('P'), Cmd::HistorySearchBackward); if rl.load_history("history.txt").is_err() { println!("No previous history."); } diff --git a/src/keymap.rs b/src/keymap.rs index 094384919f..1b891c871e 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -7,7 +7,7 @@ use log::debug; use super::Result; use crate::config::Config; use crate::config::EditMode; -use crate::keys::KeyPress; +use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M}; use crate::tty::{RawReader, Term, Terminal}; /// The number of times one command should be repeated. @@ -333,7 +333,7 @@ enum InputMode { /// Transform key(s) to commands based on current input mode pub struct InputState { mode: EditMode, - custom_bindings: Arc>>, + custom_bindings: Arc>>, input_mode: InputMode, // vi only ? // numeric arguments: http://web.mit.edu/gnu/doc/html/rlman_1.html#SEC7 num_args: i16, @@ -370,7 +370,7 @@ pub trait Refresher { } impl InputState { - pub fn new(config: &Config, custom_bindings: Arc>>) -> Self { + pub fn new(config: &Config, custom_bindings: Arc>>) -> Self { Self { mode: config.edit_mode(), custom_bindings, @@ -415,7 +415,7 @@ impl InputState { rdr: &mut R, wrt: &mut dyn Refresher, digit: char, - ) -> Result { + ) -> Result { #[allow(clippy::cast_possible_truncation)] match digit { '0'..='9' => { @@ -431,7 +431,7 @@ impl InputState { let key = rdr.next_key(true)?; #[allow(clippy::cast_possible_truncation)] match key { - KeyPress::Char(digit @ '0'..='9') | KeyPress::Meta(digit @ '0'..='9') => { + E(K::Char(digit @ '0'..='9'), m) if m == M::NONE || m == M::ALT => { if self.num_args == -1 { self.num_args *= digit.to_digit(10).unwrap() as i16; } else if self.num_args.abs() < 1000 { @@ -442,7 +442,7 @@ impl InputState { .saturating_add(digit.to_digit(10).unwrap() as i16); } } - KeyPress::Char('-') | KeyPress::Meta('-') => {} + E(K::Char('-'), m) if m == M::NONE || m == M::ALT => {} _ => { wrt.refresh_line()?; return Ok(key); @@ -455,11 +455,11 @@ impl InputState { &mut self, rdr: &mut R, wrt: &mut dyn Refresher, - mut key: KeyPress, + mut key: KeyEvent, ) -> Result { - if let KeyPress::Meta(digit @ '-') = key { + if let E(K::Char(digit @ '-'), M::ALT) = key { key = self.emacs_digit_argument(rdr, wrt, digit)?; - } else if let KeyPress::Meta(digit @ '0'..='9') = key { + } else if let E(K::Char(digit @ '0'..='9'), M::ALT) = key { key = self.emacs_digit_argument(rdr, wrt, digit)?; } let (n, positive) = self.emacs_num_args(); // consume them in all cases @@ -475,39 +475,39 @@ impl InputState { } } let cmd = match key { - KeyPress::Char(c) => { + E(K::Char(c), M::NONE) => { if positive { Cmd::SelfInsert(n, c) } else { Cmd::Unknown } } - KeyPress::Ctrl('A') => Cmd::Move(Movement::BeginningOfLine), - KeyPress::Ctrl('B') => { + E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine), + E(K::Char('B'), M::CTRL) => { if positive { Cmd::Move(Movement::BackwardChar(n)) } else { Cmd::Move(Movement::ForwardChar(n)) } } - KeyPress::Ctrl('E') => Cmd::Move(Movement::EndOfLine), - KeyPress::Ctrl('F') => { + E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine), + E(K::Char('F'), M::CTRL) => { if positive { Cmd::Move(Movement::ForwardChar(n)) } else { Cmd::Move(Movement::BackwardChar(n)) } } - KeyPress::Ctrl('G') | KeyPress::Esc | KeyPress::Meta('\x07') => Cmd::Abort, - KeyPress::Ctrl('H') | KeyPress::Backspace => { + E(K::Char('G'), M::CTRL) | E::ESC | E(K::Char('G'), M::CTRL_ALT) => Cmd::Abort, + E(K::Char('H'), M::CTRL) | E::BACKSPACE => { if positive { Cmd::Kill(Movement::BackwardChar(n)) } else { Cmd::Kill(Movement::ForwardChar(n)) } } - KeyPress::BackTab => Cmd::CompleteBackward, - KeyPress::Tab => { + E(K::BackTab, M::NONE) => Cmd::CompleteBackward, + E(K::Tab, M::NONE) => { if positive { Cmd::Complete } else { @@ -515,60 +515,61 @@ impl InputState { } } // Don't complete hints when the cursor is not at the end of a line - KeyPress::Right if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint, - KeyPress::Ctrl('K') => { + E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint, + E(K::Char('K'), M::CTRL) => { if positive { Cmd::Kill(Movement::EndOfLine) } else { Cmd::Kill(Movement::BeginningOfLine) } } - KeyPress::Ctrl('L') => Cmd::ClearScreen, - KeyPress::Ctrl('N') => Cmd::NextHistory, - KeyPress::Ctrl('P') => Cmd::PreviousHistory, - KeyPress::Ctrl('X') => { + E(K::Char('L'), M::CTRL) => Cmd::ClearScreen, + E(K::Char('N'), M::CTRL) => Cmd::NextHistory, + E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory, + E(K::Char('X'), M::CTRL) => { let snd_key = rdr.next_key(true)?; match snd_key { - KeyPress::Ctrl('G') | KeyPress::Esc => Cmd::Abort, - KeyPress::Ctrl('U') => Cmd::Undo(n), + E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort, + E(K::Char('U'), M::CTRL) => Cmd::Undo(n), _ => Cmd::Unknown, } } - KeyPress::Meta('\x08') | KeyPress::Meta('\x7f') => { + E(K::Backspace, M::ALT) => { if positive { Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) } else { Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) } } - KeyPress::Meta('<') => Cmd::BeginningOfHistory, - KeyPress::Meta('>') => Cmd::EndOfHistory, - KeyPress::Meta('B') | KeyPress::Meta('b') => { + E(K::Char('<'), M::ALT) => Cmd::BeginningOfHistory, + E(K::Char('>'), M::ALT) => Cmd::EndOfHistory, + E(K::Char('B'), M::ALT) | E(K::Char('b'), M::ALT) => { if positive { Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) } else { Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) } } - KeyPress::Meta('C') | KeyPress::Meta('c') => Cmd::CapitalizeWord, - KeyPress::Meta('D') | KeyPress::Meta('d') => { + E(K::Char('C'), M::ALT) | E(K::Char('c'), M::ALT) => Cmd::CapitalizeWord, + E(K::Char('D'), M::ALT) | E(K::Char('d'), M::ALT) => { if positive { Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) } else { Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) } } - KeyPress::Meta('F') | KeyPress::Meta('f') => { + E(K::Char('F'), M::ALT) | E(K::Char('f'), M::ALT) => { if positive { Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) } else { Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) } } - KeyPress::Meta('L') | KeyPress::Meta('l') => Cmd::DowncaseWord, - KeyPress::Meta('T') | KeyPress::Meta('t') => Cmd::TransposeWords(n), - KeyPress::Meta('U') | KeyPress::Meta('u') => Cmd::UpcaseWord, - KeyPress::Meta('Y') | KeyPress::Meta('y') => Cmd::YankPop, + E(K::Char('L'), M::ALT) | E(K::Char('l'), M::ALT) => Cmd::DowncaseWord, + E(K::Char('T'), M::ALT) | E(K::Char('t'), M::ALT) => Cmd::TransposeWords(n), + // TODO ESC-R (r): Undo all changes made to this line. + E(K::Char('U'), M::ALT) | E(K::Char('u'), M::ALT) => Cmd::UpcaseWord, + E(K::Char('Y'), M::ALT) | E(K::Char('y'), M::ALT) => Cmd::YankPop, _ => self.common(rdr, key, n, positive)?, }; debug!(target: "rustyline", "Emacs command: {:?}", cmd); @@ -581,12 +582,12 @@ impl InputState { rdr: &mut R, wrt: &mut dyn Refresher, digit: char, - ) -> Result { + ) -> Result { self.num_args = digit.to_digit(10).unwrap() as i16; loop { wrt.refresh_prompt_and_line(&format!("(arg: {}) ", self.num_args))?; let key = rdr.next_key(false)?; - if let KeyPress::Char(digit @ '0'..='9') = key { + if let E(K::Char(digit @ '0'..='9'), M::NONE) = key { if self.num_args.abs() < 1000 { // shouldn't ever need more than 4 digits self.num_args = self @@ -605,9 +606,9 @@ impl InputState { &mut self, rdr: &mut R, wrt: &mut dyn Refresher, - mut key: KeyPress, + mut key: KeyEvent, ) -> Result { - if let KeyPress::Char(digit @ '1'..='9') = key { + if let E(K::Char(digit @ '1'..='9'), M::NONE) = key { key = self.vi_arg_digit(rdr, wrt, digit)?; } let no_num_args = self.num_args == 0; @@ -628,8 +629,8 @@ impl InputState { } } let cmd = match key { - KeyPress::Char('$') | KeyPress::End => Cmd::Move(Movement::EndOfLine), - KeyPress::Char('.') => { + E(K::Char('$'), M::NONE) | E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine), + E(K::Char('.'), M::NONE) => { // vi-redo (repeat last command) if no_num_args { self.last_cmd.redo(None, wrt) @@ -637,55 +638,59 @@ impl InputState { self.last_cmd.redo(Some(n), wrt) } } - // TODO KeyPress::Char('%') => Cmd::???, Move to the corresponding opening/closing + // TODO E(K::Char('%'), M::NONE) => Cmd::???, Move to the corresponding opening/closing // bracket - KeyPress::Char('0') => Cmd::Move(Movement::BeginningOfLine), - KeyPress::Char('^') => Cmd::Move(Movement::ViFirstPrint), - KeyPress::Char('a') => { + E(K::Char('0'), M::NONE) => Cmd::Move(Movement::BeginningOfLine), + E(K::Char('^'), M::NONE) => Cmd::Move(Movement::ViFirstPrint), + E(K::Char('a'), M::NONE) => { // vi-append-mode self.input_mode = InputMode::Insert; wrt.doing_insert(); Cmd::Move(Movement::ForwardChar(n)) } - KeyPress::Char('A') => { + E(K::Char('A'), M::NONE) => { // vi-append-eol self.input_mode = InputMode::Insert; wrt.doing_insert(); Cmd::Move(Movement::EndOfLine) } - KeyPress::Char('b') => Cmd::Move(Movement::BackwardWord(n, Word::Vi)), // vi-prev-word - KeyPress::Char('B') => Cmd::Move(Movement::BackwardWord(n, Word::Big)), - KeyPress::Char('c') => { + E(K::Char('b'), M::NONE) => Cmd::Move(Movement::BackwardWord(n, Word::Vi)), /* vi-prev-word */ + E(K::Char('B'), M::NONE) => Cmd::Move(Movement::BackwardWord(n, Word::Big)), + E(K::Char('c'), M::NONE) => { self.input_mode = InputMode::Insert; match self.vi_cmd_motion(rdr, wrt, key, n)? { Some(mvt) => Cmd::Replace(mvt, None), None => Cmd::Unknown, } } - KeyPress::Char('C') => { + E(K::Char('C'), M::NONE) => { self.input_mode = InputMode::Insert; Cmd::Replace(Movement::EndOfLine, None) } - KeyPress::Char('d') => match self.vi_cmd_motion(rdr, wrt, key, n)? { + E(K::Char('d'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? { Some(mvt) => Cmd::Kill(mvt), None => Cmd::Unknown, }, - KeyPress::Char('D') | KeyPress::Ctrl('K') => Cmd::Kill(Movement::EndOfLine), - KeyPress::Char('e') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi)), - KeyPress::Char('E') => Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big)), - KeyPress::Char('i') => { + E(K::Char('D'), M::NONE) | E(K::Char('K'), M::CTRL) => Cmd::Kill(Movement::EndOfLine), + E(K::Char('e'), M::NONE) => { + Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Vi)) + } + E(K::Char('E'), M::NONE) => { + Cmd::Move(Movement::ForwardWord(n, At::BeforeEnd, Word::Big)) + } + E(K::Char('i'), M::NONE) => { // vi-insertion-mode self.input_mode = InputMode::Insert; wrt.doing_insert(); Cmd::Noop } - KeyPress::Char('I') => { + E(K::Char('I'), M::NONE) => { // vi-insert-beg self.input_mode = InputMode::Insert; wrt.doing_insert(); Cmd::Move(Movement::BeginningOfLine) } - KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => { + E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => { // vi-char-search let cs = self.vi_char_search(rdr, c)?; match cs { @@ -693,74 +698,76 @@ impl InputState { None => Cmd::Unknown, } } - KeyPress::Char(';') => match self.last_char_search { + E(K::Char(';'), M::NONE) => match self.last_char_search { Some(cs) => Cmd::Move(Movement::ViCharSearch(n, cs)), None => Cmd::Noop, }, - KeyPress::Char(',') => match self.last_char_search { + E(K::Char(','), M::NONE) => match self.last_char_search { Some(ref cs) => Cmd::Move(Movement::ViCharSearch(n, cs.opposite())), None => Cmd::Noop, }, - // TODO KeyPress::Char('G') => Cmd::???, Move to the history line n - KeyPress::Char('p') => Cmd::Yank(n, Anchor::After), // vi-put - KeyPress::Char('P') => Cmd::Yank(n, Anchor::Before), // vi-put - KeyPress::Char('r') => { + // TODO E(K::Char('G'), M::NONE) => Cmd::???, Move to the history line n + E(K::Char('p'), M::NONE) => Cmd::Yank(n, Anchor::After), // vi-put + E(K::Char('P'), M::NONE) => Cmd::Yank(n, Anchor::Before), // vi-put + E(K::Char('r'), M::NONE) => { // vi-replace-char: let ch = rdr.next_key(false)?; match ch { - KeyPress::Char(c) => Cmd::ReplaceChar(n, c), - KeyPress::Esc => Cmd::Noop, + E(K::Char(c), M::NONE) => Cmd::ReplaceChar(n, c), + E::ESC => Cmd::Noop, _ => Cmd::Unknown, } } - KeyPress::Char('R') => { + E(K::Char('R'), M::NONE) => { // vi-replace-mode (overwrite-mode) self.input_mode = InputMode::Replace; Cmd::Replace(Movement::ForwardChar(0), None) } - KeyPress::Char('s') => { + E(K::Char('s'), M::NONE) => { // vi-substitute-char: self.input_mode = InputMode::Insert; Cmd::Replace(Movement::ForwardChar(n), None) } - KeyPress::Char('S') => { + E(K::Char('S'), M::NONE) => { // vi-substitute-line: self.input_mode = InputMode::Insert; Cmd::Replace(Movement::WholeLine, None) } - KeyPress::Char('u') => Cmd::Undo(n), - // KeyPress::Char('U') => Cmd::???, // revert-line - KeyPress::Char('w') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Vi)), /* vi-next-word */ - KeyPress::Char('W') => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Big)), /* vi-next-word */ + E(K::Char('u'), M::NONE) => Cmd::Undo(n), + // E(K::Char('U'), M::NONE) => Cmd::???, // revert-line + E(K::Char('w'), M::NONE) => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Vi)), /* vi-next-word */ + E(K::Char('W'), M::NONE) => Cmd::Move(Movement::ForwardWord(n, At::Start, Word::Big)), /* vi-next-word */ // TODO move backward if eol - KeyPress::Char('x') => Cmd::Kill(Movement::ForwardChar(n)), // vi-delete - KeyPress::Char('X') => Cmd::Kill(Movement::BackwardChar(n)), // vi-rubout - KeyPress::Char('y') => match self.vi_cmd_motion(rdr, wrt, key, n)? { + E(K::Char('x'), M::NONE) => Cmd::Kill(Movement::ForwardChar(n)), // vi-delete + E(K::Char('X'), M::NONE) => Cmd::Kill(Movement::BackwardChar(n)), // vi-rubout + E(K::Char('y'), M::NONE) => match self.vi_cmd_motion(rdr, wrt, key, n)? { Some(mvt) => Cmd::ViYankTo(mvt), None => Cmd::Unknown, }, - // KeyPress::Char('Y') => Cmd::???, // vi-yank-to - KeyPress::Char('h') | KeyPress::Ctrl('H') | KeyPress::Backspace => { + // E(K::Char('Y'), M::NONE) => Cmd::???, // vi-yank-to + E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => { Cmd::Move(Movement::BackwardChar(n)) } - KeyPress::Ctrl('G') => Cmd::Abort, - KeyPress::Char('l') | KeyPress::Char(' ') => Cmd::Move(Movement::ForwardChar(n)), - KeyPress::Ctrl('L') => Cmd::ClearScreen, - KeyPress::Char('+') | KeyPress::Char('j') => Cmd::LineDownOrNextHistory(n), + E(K::Char('G'), M::CTRL) => Cmd::Abort, + E(K::Char('l'), M::NONE) | E(K::Char(' '), M::NONE) => { + Cmd::Move(Movement::ForwardChar(n)) + } + E(K::Char('L'), M::CTRL) => Cmd::ClearScreen, + E(K::Char('+'), M::NONE) | E(K::Char('j'), M::NONE) => Cmd::LineDownOrNextHistory(n), // TODO: move to the start of the line. - KeyPress::Ctrl('N') => Cmd::NextHistory, - KeyPress::Char('-') | KeyPress::Char('k') => Cmd::LineUpOrPreviousHistory(n), + E(K::Char('N'), M::CTRL) => Cmd::NextHistory, + E(K::Char('-'), M::NONE) | E(K::Char('k'), M::NONE) => Cmd::LineUpOrPreviousHistory(n), // TODO: move to the start of the line. - KeyPress::Ctrl('P') => Cmd::PreviousHistory, - KeyPress::Ctrl('R') => { + E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory, + E(K::Char('R'), M::CTRL) => { self.input_mode = InputMode::Insert; // TODO Validate Cmd::ReverseSearchHistory } - KeyPress::Ctrl('S') => { + E(K::Char('S'), M::CTRL) => { self.input_mode = InputMode::Insert; // TODO Validate Cmd::ForwardSearchHistory } - KeyPress::Esc => Cmd::Noop, + E::ESC => Cmd::Noop, _ => self.common(rdr, key, n, true)?, }; debug!(target: "rustyline", "Vi command: {:?}", cmd); @@ -774,7 +781,7 @@ impl InputState { &mut self, rdr: &mut R, wrt: &mut dyn Refresher, - key: KeyPress, + key: KeyEvent, ) -> Result { { let bindings = self.custom_bindings.read().unwrap(); @@ -788,26 +795,26 @@ impl InputState { } } let cmd = match key { - KeyPress::Char(c) => { + E(K::Char(c), M::NONE) => { if self.input_mode == InputMode::Replace { Cmd::Overwrite(c) } else { Cmd::SelfInsert(1, c) } } - KeyPress::Ctrl('H') | KeyPress::Backspace => Cmd::Kill(Movement::BackwardChar(1)), - KeyPress::BackTab => Cmd::CompleteBackward, - KeyPress::Tab => Cmd::Complete, + E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(Movement::BackwardChar(1)), + E(K::BackTab, M::NONE) => Cmd::CompleteBackward, + E(K::Tab, M::NONE) => Cmd::Complete, // Don't complete hints when the cursor is not at the end of a line - KeyPress::Right if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint, - KeyPress::Meta(k) => { + E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint, + E(K::Char(k), M::ALT) => { debug!(target: "rustyline", "Vi fast command mode: {}", k); self.input_mode = InputMode::Command; wrt.done_inserting(); - self.vi_command(rdr, wrt, KeyPress::Char(k))? + self.vi_command(rdr, wrt, E(K::Char(k), M::NONE))? } - KeyPress::Esc => { + E::ESC => { // vi-movement-mode/vi-command-mode self.input_mode = InputMode::Command; wrt.done_inserting(); @@ -832,7 +839,7 @@ impl InputState { &mut self, rdr: &mut R, wrt: &mut dyn Refresher, - key: KeyPress, + key: KeyEvent, n: RepeatCount, ) -> Result> { let mut mvt = rdr.next_key(false)?; @@ -840,51 +847,51 @@ impl InputState { return Ok(Some(Movement::WholeLine)); } let mut n = n; - if let KeyPress::Char(digit @ '1'..='9') = mvt { + if let E(K::Char(digit @ '1'..='9'), M::NONE) = mvt { // vi-arg-digit mvt = self.vi_arg_digit(rdr, wrt, digit)?; n = self.vi_num_args().saturating_mul(n); } Ok(match mvt { - KeyPress::Char('$') => Some(Movement::EndOfLine), - KeyPress::Char('0') => Some(Movement::BeginningOfLine), - KeyPress::Char('^') => Some(Movement::ViFirstPrint), - KeyPress::Char('b') => Some(Movement::BackwardWord(n, Word::Vi)), - KeyPress::Char('B') => Some(Movement::BackwardWord(n, Word::Big)), - KeyPress::Char('e') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)), - KeyPress::Char('E') => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)), - KeyPress::Char(c) if c == 'f' || c == 'F' || c == 't' || c == 'T' => { + E(K::Char('$'), M::NONE) => Some(Movement::EndOfLine), + E(K::Char('0'), M::NONE) => Some(Movement::BeginningOfLine), + E(K::Char('^'), M::NONE) => Some(Movement::ViFirstPrint), + E(K::Char('b'), M::NONE) => Some(Movement::BackwardWord(n, Word::Vi)), + E(K::Char('B'), M::NONE) => Some(Movement::BackwardWord(n, Word::Big)), + E(K::Char('e'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)), + E(K::Char('E'), M::NONE) => Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)), + E(K::Char(c), M::NONE) if c == 'f' || c == 'F' || c == 't' || c == 'T' => { let cs = self.vi_char_search(rdr, c)?; match cs { Some(cs) => Some(Movement::ViCharSearch(n, cs)), None => None, } } - KeyPress::Char(';') => match self.last_char_search { + E(K::Char(';'), M::NONE) => match self.last_char_search { Some(cs) => Some(Movement::ViCharSearch(n, cs)), None => None, }, - KeyPress::Char(',') => match self.last_char_search { + E(K::Char(','), M::NONE) => match self.last_char_search { Some(ref cs) => Some(Movement::ViCharSearch(n, cs.opposite())), None => None, }, - KeyPress::Char('h') | KeyPress::Ctrl('H') | KeyPress::Backspace => { + E(K::Char('h'), M::NONE) | E(K::Char('H'), M::CTRL) | E::BACKSPACE => { Some(Movement::BackwardChar(n)) } - KeyPress::Char('l') | KeyPress::Char(' ') => Some(Movement::ForwardChar(n)), - KeyPress::Char('j') | KeyPress::Char('+') => Some(Movement::LineDown(n)), - KeyPress::Char('k') | KeyPress::Char('-') => Some(Movement::LineUp(n)), - KeyPress::Char('w') => { + E(K::Char('l'), M::NONE) | E(K::Char(' '), M::NONE) => Some(Movement::ForwardChar(n)), + E(K::Char('j'), M::NONE) | E(K::Char('+'), M::NONE) => Some(Movement::LineDown(n)), + E(K::Char('k'), M::NONE) | E(K::Char('-'), M::NONE) => Some(Movement::LineUp(n)), + E(K::Char('w'), M::NONE) => { // 'cw' is 'ce' - if key == KeyPress::Char('c') { + if key == E(K::Char('c'), M::NONE) { Some(Movement::ForwardWord(n, At::AfterEnd, Word::Vi)) } else { Some(Movement::ForwardWord(n, At::Start, Word::Vi)) } } - KeyPress::Char('W') => { + E(K::Char('W'), M::NONE) => { // 'cW' is 'cE' - if key == KeyPress::Char('c') { + if key == E(K::Char('c'), M::NONE) { Some(Movement::ForwardWord(n, At::AfterEnd, Word::Big)) } else { Some(Movement::ForwardWord(n, At::Start, Word::Big)) @@ -901,7 +908,7 @@ impl InputState { ) -> Result> { let ch = rdr.next_key(false)?; Ok(match ch { - KeyPress::Char(ch) => { + E(K::Char(ch), M::NONE) => { let cs = match cmd { 'f' => CharSearch::Forward(ch), 't' => CharSearch::ForwardBefore(ch), @@ -919,72 +926,72 @@ impl InputState { fn common( &mut self, rdr: &mut R, - key: KeyPress, + key: KeyEvent, n: RepeatCount, positive: bool, ) -> Result { Ok(match key { - KeyPress::Home => Cmd::Move(Movement::BeginningOfLine), - KeyPress::Left => { + E(K::Home, M::NONE) => Cmd::Move(Movement::BeginningOfLine), + E(K::Left, M::NONE) => { if positive { Cmd::Move(Movement::BackwardChar(n)) } else { Cmd::Move(Movement::ForwardChar(n)) } } - KeyPress::Ctrl('C') => Cmd::Interrupt, - KeyPress::Ctrl('D') => Cmd::EndOfFile, - KeyPress::Delete => { + E(K::Char('C'), M::CTRL) => Cmd::Interrupt, + E(K::Char('D'), M::CTRL) => Cmd::EndOfFile, + E(K::Delete, M::NONE) => { if positive { Cmd::Kill(Movement::ForwardChar(n)) } else { Cmd::Kill(Movement::BackwardChar(n)) } } - KeyPress::End => Cmd::Move(Movement::EndOfLine), - KeyPress::Right => { + E(K::End, M::NONE) => Cmd::Move(Movement::EndOfLine), + E(K::Right, M::NONE) => { if positive { Cmd::Move(Movement::ForwardChar(n)) } else { Cmd::Move(Movement::BackwardChar(n)) } } - KeyPress::Ctrl('J') | - KeyPress::Enter => { + E(K::Char('J'), M::CTRL) | + E::ENTER => { Cmd::AcceptOrInsertLine { accept_in_the_middle: true } } - KeyPress::Down => Cmd::LineDownOrNextHistory(1), - KeyPress::Up => Cmd::LineUpOrPreviousHistory(1), - KeyPress::Ctrl('R') => Cmd::ReverseSearchHistory, - KeyPress::Ctrl('S') => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution - KeyPress::Ctrl('T') => Cmd::TransposeChars, - KeyPress::Ctrl('U') => { + E(K::Down, M::NONE) => Cmd::LineDownOrNextHistory(1), + E(K::Up, M::NONE) => Cmd::LineUpOrPreviousHistory(1), + E(K::Char('R'), M::CTRL) => Cmd::ReverseSearchHistory, + E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory, // most terminals override Ctrl+S to suspend execution + E(K::Char('T'), M::CTRL) => Cmd::TransposeChars, + E(K::Char('U'), M::CTRL) => { if positive { Cmd::Kill(Movement::BeginningOfLine) } else { Cmd::Kill(Movement::EndOfLine) } }, - KeyPress::Ctrl('Q') | // most terminals override Ctrl+Q to resume execution - KeyPress::Ctrl('V') => Cmd::QuotedInsert, - KeyPress::Ctrl('W') => { + E(K::Char('Q'), M::CTRL) | // most terminals override Ctrl+Q to resume execution + E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert, + E(K::Char('W'), M::CTRL) => { if positive { Cmd::Kill(Movement::BackwardWord(n, Word::Big)) } else { Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Big)) } } - KeyPress::Ctrl('Y') => { + E(K::Char('Y'), M::CTRL) => { if positive { Cmd::Yank(n, Anchor::Before) } else { Cmd::Unknown // TODO Validate } } - KeyPress::Ctrl('Z') => Cmd::Suspend, - KeyPress::Ctrl('_') => Cmd::Undo(n), - KeyPress::UnknownEscSeq => Cmd::Noop, - KeyPress::BracketedPasteStart => { + E(K::Char('Z'), M::CTRL) => Cmd::Suspend, + E(K::Char('_'), M::CTRL) => Cmd::Undo(n), + E(K::UnknownEscSeq, M::NONE) => Cmd::Noop, + E(K::BracketedPasteStart, M::NONE) => { let paste = rdr.read_pasted_text()?; Cmd::Insert(1, paste) }, diff --git a/src/keys.rs b/src/keys.rs index bf066368f1..c147e49d77 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -1,12 +1,119 @@ //! Key constants +/// Input key pressed and modifiers +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct KeyEvent(pub KeyCode, pub Modifiers); + +impl KeyEvent { + /// Constant value representing an unmodified press of `KeyCode::Backspace`. + pub(crate) const BACKSPACE: Self = Self(KeyCode::Backspace, Modifiers::NONE); + /// Constant value representing an unmodified press of `KeyCode::Enter`. + pub(crate) const ENTER: Self = Self(KeyCode::Enter, Modifiers::NONE); + /// Constant value representing an unmodified press of `KeyCode::Esc`. + pub(crate) const ESC: Self = Self(KeyCode::Esc, Modifiers::NONE); + + /// Constructor from `char` and modifiers + pub fn new(c: char, mut mods: Modifiers) -> Self { + use {KeyCode as K, KeyEvent as E, Modifiers as M}; + + if !c.is_control() { + if !mods.is_empty() { + mods.remove(M::SHIFT); // TODO Validate: no SHIFT even if + // `c` is uppercase + } + return E(K::Char(c), mods); + } + #[allow(clippy::match_same_arms)] + match c { + '\x00' => E(K::Char(' '), mods | M::CTRL), + '\x01' => E(K::Char('A'), mods | M::CTRL), + '\x02' => E(K::Char('B'), mods | M::CTRL), + '\x03' => E(K::Char('C'), mods | M::CTRL), + '\x04' => E(K::Char('D'), mods | M::CTRL), + '\x05' => E(K::Char('E'), mods | M::CTRL), + '\x06' => E(K::Char('F'), mods | M::CTRL), + '\x07' => E(K::Char('G'), mods | M::CTRL), + '\x08' => E(K::Backspace, mods), // '\b' + '\x09' => { + // '\t' + if mods.contains(M::SHIFT) { + mods.remove(M::SHIFT); + E(K::BackTab, mods) + } else { + E(K::Tab, mods) + } + } + '\x0a' => E(K::Char('J'), mods | M::CTRL), // '\n' (10) + '\x0b' => E(K::Char('K'), mods | M::CTRL), + '\x0c' => E(K::Char('L'), mods | M::CTRL), + '\x0d' => E(K::Enter, mods), // '\r' (13) + '\x0e' => E(K::Char('N'), mods | M::CTRL), + '\x0f' => E(K::Char('O'), mods | M::CTRL), + '\x10' => E(K::Char('P'), mods | M::CTRL), + '\x11' => E(K::Char('Q'), mods | M::CTRL), + '\x12' => E(K::Char('R'), mods | M::CTRL), + '\x13' => E(K::Char('S'), mods | M::CTRL), + '\x14' => E(K::Char('T'), mods | M::CTRL), + '\x15' => E(K::Char('U'), mods | M::CTRL), + '\x16' => E(K::Char('V'), mods | M::CTRL), + '\x17' => E(K::Char('W'), mods | M::CTRL), + '\x18' => E(K::Char('X'), mods | M::CTRL), + '\x19' => E(K::Char('Y'), mods | M::CTRL), + '\x1a' => E(K::Char('Z'), mods | M::CTRL), + '\x1b' => E(K::Esc, mods), // Ctrl-[ + '\x1c' => E(K::Char('\\'), mods | M::CTRL), + '\x1d' => E(K::Char(']'), mods | M::CTRL), + '\x1e' => E(K::Char('^'), mods | M::CTRL), + '\x1f' => E(K::Char('_'), mods | M::CTRL), + '\x7f' => E(K::Backspace, mods), // Rubout + '\u{9b}' => E(K::Esc, mods | M::SHIFT), + _ => E(K::Null, mods), + } + } + + /// Constructor from `char` with Ctrl modifier + pub fn ctrl(c: char) -> Self { + Self::new(c, Modifiers::CTRL) + } + + /// Constructor from `char` with Alt modifier + pub fn alt(c: char) -> Self { + Self::new(c, Modifiers::ALT) + } + + /// ctrl-a => ctrl-A (uppercase) + /// shift-A => A (no SHIFT modifier) + /// shift-Tab => BackTab + pub fn normalize(e: Self) -> Self { + use {KeyCode as K, KeyEvent as E, Modifiers as M}; + + match e { + E(K::Char(c), m) if c.is_ascii_control() => Self::new(c, m), + E(K::Char(c), m) if c.is_ascii_lowercase() && m.contains(M::CTRL) => { + E(K::Char(c.to_ascii_uppercase()), m) + } + E(K::Char(c), m) if c.is_ascii_uppercase() && m.contains(M::SHIFT) => { + E(K::Char(c), m ^ M::SHIFT) + } + E(K::Tab, m) if m.contains(M::SHIFT) => E(K::BackTab, m ^ M::SHIFT), + _ => e, + } + } +} + +impl From for KeyEvent { + fn from(c: char) -> Self { + Self::new(c, Modifiers::NONE) + } +} + /// Input key pressed #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] -pub enum KeyPress { +pub enum KeyCode { /// Unsupported escape sequence (on unix platform) UnknownEscSeq, - /// ⌫ or `KeyPress::Ctrl('H')` + /// ⌫ or Ctrl-H Backspace, /// ⇤ (usually Shift-Tab) BackTab, @@ -16,25 +123,15 @@ pub enum KeyPress { BracketedPasteEnd, /// Single char Char(char), - /// Ctrl-↓ - ControlDown, - /// Ctrl-← - ControlLeft, - /// Ctrl-→ - ControlRight, - /// Ctrl-↑ - ControlUp, - /// Ctrl-char - Ctrl(char), /// ⌦ Delete, /// ↓ arrow key Down, /// ⇲ End, - /// ↵ or `KeyPress::Ctrl('M')` + /// ↵ or Ctrl-M Enter, - /// Escape or `KeyPress::Ctrl('[')` + /// Escape or Ctrl-[ Esc, /// Function key F(u8), @@ -44,9 +141,7 @@ pub enum KeyPress { Insert, /// ← arrow key Left, - /// Escape-char or Alt-char - Meta(char), - /// `KeyPress::Char('\0')` + /// \0 Null, /// ⇟ PageDown, @@ -54,69 +149,49 @@ pub enum KeyPress { PageUp, /// → arrow key Right, - /// Shift-↓ - ShiftDown, - /// Shift-← - ShiftLeft, - /// Shift-→ - ShiftRight, - /// Shift-↑ - ShiftUp, - /// ⇥ or `KeyPress::Ctrl('I')` + /// ⇥ or Ctrl-I Tab, /// ↑ arrow key Up, } -#[cfg(any(windows, unix))] -pub fn char_to_key_press(c: char) -> KeyPress { - if !c.is_control() { - return KeyPress::Char(c); - } - #[allow(clippy::match_same_arms)] - match c { - '\x00' => KeyPress::Ctrl(' '), - '\x01' => KeyPress::Ctrl('A'), - '\x02' => KeyPress::Ctrl('B'), - '\x03' => KeyPress::Ctrl('C'), - '\x04' => KeyPress::Ctrl('D'), - '\x05' => KeyPress::Ctrl('E'), - '\x06' => KeyPress::Ctrl('F'), - '\x07' => KeyPress::Ctrl('G'), - '\x08' => KeyPress::Backspace, // '\b' - '\x09' => KeyPress::Tab, // '\t' - '\x0a' => KeyPress::Ctrl('J'), // '\n' (10) - '\x0b' => KeyPress::Ctrl('K'), - '\x0c' => KeyPress::Ctrl('L'), - '\x0d' => KeyPress::Enter, // '\r' (13) - '\x0e' => KeyPress::Ctrl('N'), - '\x0f' => KeyPress::Ctrl('O'), - '\x10' => KeyPress::Ctrl('P'), - '\x12' => KeyPress::Ctrl('R'), - '\x13' => KeyPress::Ctrl('S'), - '\x14' => KeyPress::Ctrl('T'), - '\x15' => KeyPress::Ctrl('U'), - '\x16' => KeyPress::Ctrl('V'), - '\x17' => KeyPress::Ctrl('W'), - '\x18' => KeyPress::Ctrl('X'), - '\x19' => KeyPress::Ctrl('Y'), - '\x1a' => KeyPress::Ctrl('Z'), - '\x1b' => KeyPress::Esc, // Ctrl-[ - '\x1c' => KeyPress::Ctrl('\\'), - '\x1d' => KeyPress::Ctrl(']'), - '\x1e' => KeyPress::Ctrl('^'), - '\x1f' => KeyPress::Ctrl('_'), - '\x7f' => KeyPress::Backspace, // Rubout - _ => KeyPress::Null, +bitflags::bitflags! { + /// The set of modifier keys that were triggered along with a key press. + pub struct Modifiers: u8 { + /// Control modifier + const CTRL = 1<<3; + /// Escape or Alt modifier + const ALT = 1<<2; + /// Shift modifier + const SHIFT = 1<<1; + + /// No modifier + const NONE = 0; + /// Ctrl + Shift + const CTRL_SHIFT = Self::CTRL.bits | Self::SHIFT.bits; + /// Alt + Shift + const ALT_SHIFT = Self::ALT.bits | Self::SHIFT.bits; + /// Ctrl + Alt + const CTRL_ALT = Self::CTRL.bits | Self::ALT.bits; + /// Ctrl + Alt + Shift + const CTRL_ALT_SHIFT = Self::CTRL.bits | Self::ALT.bits | Self::SHIFT.bits; } } #[cfg(test)] mod tests { - use super::{char_to_key_press, KeyPress}; + use super::{KeyCode as K, KeyEvent as E, Modifiers as M}; + + #[test] + fn new() { + assert_eq!(E::ESC, E::new('\x1b', M::NONE)); + } #[test] - fn char_to_key() { - assert_eq!(KeyPress::Esc, char_to_key_press('\x1b')); + fn normalize() { + assert_eq!(E::ctrl('A'), E::normalize(E(K::Char('\x01'), M::NONE))); + assert_eq!(E::ctrl('A'), E::normalize(E::ctrl('a'))); + assert_eq!(E::from('A'), E::normalize(E(K::Char('A'), M::SHIFT))); + assert_eq!(E(K::BackTab, M::NONE), E::normalize(E(K::Tab, M::SHIFT))); } } diff --git a/src/lib.rs b/src/lib.rs index 1d070f511a..a4e98f9591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,7 @@ use crate::hint::Hinter; use crate::history::{Direction, History}; pub use crate::keymap::{Anchor, At, CharSearch, Cmd, Movement, RepeatCount, Word}; use crate::keymap::{InputState, Refresher}; -pub use crate::keys::KeyPress; +pub use crate::keys::{KeyCode, KeyEvent, Modifiers}; use crate::kill_ring::{KillRing, Mode}; use crate::line_buffer::WordAction; use crate::validate::Validator; @@ -804,7 +804,7 @@ pub struct Editor { helper: Option, kill_ring: Arc>, config: Config, - custom_bindings: Arc>>, + custom_bindings: Arc>>, } #[allow(clippy::new_without_default)] @@ -923,18 +923,18 @@ impl Editor { } /// Bind a sequence to a command. - pub fn bind_sequence(&mut self, key_seq: KeyPress, cmd: Cmd) -> Option { + pub fn bind_sequence(&mut self, key_seq: KeyEvent, cmd: Cmd) -> Option { if let Ok(mut bindings) = self.custom_bindings.write() { - bindings.insert(key_seq, cmd) + bindings.insert(KeyEvent::normalize(key_seq), cmd) } else { None } } /// Remove a binding for the given sequence. - pub fn unbind_sequence(&mut self, key_seq: KeyPress) -> Option { + pub fn unbind_sequence(&mut self, key_seq: KeyEvent) -> Option { if let Ok(mut bindings) = self.custom_bindings.write() { - bindings.remove(&key_seq) + bindings.remove(&KeyEvent::normalize(key_seq)) } else { None } diff --git a/src/test/common.rs b/src/test/common.rs index 798d61904c..0f31dcefa0 100644 --- a/src/test/common.rs +++ b/src/test/common.rs @@ -2,21 +2,16 @@ use super::{assert_cursor, assert_line, assert_line_with_initial, init_editor}; use crate::config::EditMode; use crate::error::ReadlineError; -use crate::keys::KeyPress; +use crate::keys::{KeyCode as K, KeyEvent as E, Modifiers as M}; #[test] fn home_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_cursor( - *mode, - ("", ""), - &[KeyPress::Home, KeyPress::Enter], - ("", ""), - ); + assert_cursor(*mode, ("", ""), &[E(K::Home, M::NONE), E::ENTER], ("", "")); assert_cursor( *mode, ("Hi", ""), - &[KeyPress::Home, KeyPress::Enter], + &[E(K::Home, M::NONE), E::ENTER], ("", "Hi"), ); if *mode == EditMode::Vi { @@ -24,7 +19,7 @@ fn home_key() { assert_cursor( *mode, ("Hi", ""), - &[KeyPress::Esc, KeyPress::Home, KeyPress::Enter], + &[E::ESC, E(K::Home, M::NONE), E::ENTER], ("", "Hi"), ); } @@ -34,17 +29,17 @@ fn home_key() { #[test] fn end_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_cursor(*mode, ("", ""), &[KeyPress::End, KeyPress::Enter], ("", "")); + assert_cursor(*mode, ("", ""), &[E(K::End, M::NONE), E::ENTER], ("", "")); assert_cursor( *mode, ("H", "i"), - &[KeyPress::End, KeyPress::Enter], + &[E(K::End, M::NONE), E::ENTER], ("Hi", ""), ); assert_cursor( *mode, ("", "Hi"), - &[KeyPress::End, KeyPress::Enter], + &[E(K::End, M::NONE), E::ENTER], ("Hi", ""), ); if *mode == EditMode::Vi { @@ -52,7 +47,7 @@ fn end_key() { assert_cursor( *mode, ("", "Hi"), - &[KeyPress::Esc, KeyPress::End, KeyPress::Enter], + &[E::ESC, E(K::End, M::NONE), E::ENTER], ("Hi", ""), ); } @@ -65,19 +60,19 @@ fn left_key() { assert_cursor( *mode, ("Hi", ""), - &[KeyPress::Left, KeyPress::Enter], + &[E(K::Left, M::NONE), E::ENTER], ("H", "i"), ); assert_cursor( *mode, ("H", "i"), - &[KeyPress::Left, KeyPress::Enter], + &[E(K::Left, M::NONE), E::ENTER], ("", "Hi"), ); assert_cursor( *mode, ("", "Hi"), - &[KeyPress::Left, KeyPress::Enter], + &[E(K::Left, M::NONE), E::ENTER], ("", "Hi"), ); if *mode == EditMode::Vi { @@ -85,7 +80,7 @@ fn left_key() { assert_cursor( *mode, ("Bye", ""), - &[KeyPress::Esc, KeyPress::Left, KeyPress::Enter], + &[E::ESC, E(K::Left, M::NONE), E::ENTER], ("B", "ye"), ); } @@ -95,28 +90,23 @@ fn left_key() { #[test] fn right_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_cursor( - *mode, - ("", ""), - &[KeyPress::Right, KeyPress::Enter], - ("", ""), - ); + assert_cursor(*mode, ("", ""), &[E(K::Right, M::NONE), E::ENTER], ("", "")); assert_cursor( *mode, ("", "Hi"), - &[KeyPress::Right, KeyPress::Enter], + &[E(K::Right, M::NONE), E::ENTER], ("H", "i"), ); assert_cursor( *mode, ("B", "ye"), - &[KeyPress::Right, KeyPress::Enter], + &[E(K::Right, M::NONE), E::ENTER], ("By", "e"), ); assert_cursor( *mode, ("H", "i"), - &[KeyPress::Right, KeyPress::Enter], + &[E(K::Right, M::NONE), E::ENTER], ("Hi", ""), ); if *mode == EditMode::Vi { @@ -124,7 +114,7 @@ fn right_key() { assert_cursor( *mode, ("", "Hi"), - &[KeyPress::Esc, KeyPress::Right, KeyPress::Enter], + &[E::ESC, E(K::Right, M::NONE), E::ENTER], ("H", "i"), ); } @@ -134,22 +124,18 @@ fn right_key() { #[test] fn enter_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_line(*mode, &[KeyPress::Enter], ""); - assert_line(*mode, &[KeyPress::Char('a'), KeyPress::Enter], "a"); - assert_line_with_initial(*mode, ("Hi", ""), &[KeyPress::Enter], "Hi"); - assert_line_with_initial(*mode, ("", "Hi"), &[KeyPress::Enter], "Hi"); - assert_line_with_initial(*mode, ("H", "i"), &[KeyPress::Enter], "Hi"); + assert_line(*mode, &[E::ENTER], ""); + assert_line(*mode, &[E::from('a'), E::ENTER], "a"); + assert_line_with_initial(*mode, ("Hi", ""), &[E::ENTER], "Hi"); + assert_line_with_initial(*mode, ("", "Hi"), &[E::ENTER], "Hi"); + assert_line_with_initial(*mode, ("H", "i"), &[E::ENTER], "Hi"); if *mode == EditMode::Vi { // vi command mode - assert_line(*mode, &[KeyPress::Esc, KeyPress::Enter], ""); - assert_line( - *mode, - &[KeyPress::Char('a'), KeyPress::Esc, KeyPress::Enter], - "a", - ); - assert_line_with_initial(*mode, ("Hi", ""), &[KeyPress::Esc, KeyPress::Enter], "Hi"); - assert_line_with_initial(*mode, ("", "Hi"), &[KeyPress::Esc, KeyPress::Enter], "Hi"); - assert_line_with_initial(*mode, ("H", "i"), &[KeyPress::Esc, KeyPress::Enter], "Hi"); + assert_line(*mode, &[E::ESC, E::ENTER], ""); + assert_line(*mode, &[E::from('a'), E::ESC, E::ENTER], "a"); + assert_line_with_initial(*mode, ("Hi", ""), &[E::ESC, E::ENTER], "Hi"); + assert_line_with_initial(*mode, ("", "Hi"), &[E::ESC, E::ENTER], "Hi"); + assert_line_with_initial(*mode, ("H", "i"), &[E::ESC, E::ENTER], "Hi"); } } } @@ -157,16 +143,12 @@ fn enter_key() { #[test] fn newline_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_line(*mode, &[KeyPress::Ctrl('J')], ""); - assert_line(*mode, &[KeyPress::Char('a'), KeyPress::Ctrl('J')], "a"); + assert_line(*mode, &[E::ctrl('J')], ""); + assert_line(*mode, &[E::from('a'), E::ctrl('J')], "a"); if *mode == EditMode::Vi { // vi command mode - assert_line(*mode, &[KeyPress::Esc, KeyPress::Ctrl('J')], ""); - assert_line( - *mode, - &[KeyPress::Char('a'), KeyPress::Esc, KeyPress::Ctrl('J')], - "a", - ); + assert_line(*mode, &[E::ESC, E::ctrl('J')], ""); + assert_line(*mode, &[E::from('a'), E::ESC, E::ctrl('J')], "a"); } } } @@ -174,53 +156,35 @@ fn newline_key() { #[test] fn eof_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - let mut editor = init_editor(*mode, &[KeyPress::Ctrl('D')]); + let mut editor = init_editor(*mode, &[E::ctrl('D')]); let err = editor.readline(">>"); assert_matches!(err, Err(ReadlineError::Eof)); } assert_line( EditMode::Emacs, - &[KeyPress::Char('a'), KeyPress::Ctrl('D'), KeyPress::Enter], - "a", - ); - assert_line( - EditMode::Vi, - &[KeyPress::Char('a'), KeyPress::Ctrl('D')], + &[E::from('a'), E::ctrl('D'), E::ENTER], "a", ); - assert_line( - EditMode::Vi, - &[KeyPress::Char('a'), KeyPress::Esc, KeyPress::Ctrl('D')], - "a", - ); - assert_line_with_initial( - EditMode::Emacs, - ("", "Hi"), - &[KeyPress::Ctrl('D'), KeyPress::Enter], - "i", - ); - assert_line_with_initial(EditMode::Vi, ("", "Hi"), &[KeyPress::Ctrl('D')], "Hi"); - assert_line_with_initial( - EditMode::Vi, - ("", "Hi"), - &[KeyPress::Esc, KeyPress::Ctrl('D')], - "Hi", - ); + assert_line(EditMode::Vi, &[E::from('a'), E::ctrl('D')], "a"); + assert_line(EditMode::Vi, &[E::from('a'), E::ESC, E::ctrl('D')], "a"); + assert_line_with_initial(EditMode::Emacs, ("", "Hi"), &[E::ctrl('D'), E::ENTER], "i"); + assert_line_with_initial(EditMode::Vi, ("", "Hi"), &[E::ctrl('D')], "Hi"); + assert_line_with_initial(EditMode::Vi, ("", "Hi"), &[E::ESC, E::ctrl('D')], "Hi"); } #[test] fn interrupt_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - let mut editor = init_editor(*mode, &[KeyPress::Ctrl('C')]); + let mut editor = init_editor(*mode, &[E::ctrl('C')]); let err = editor.readline(">>"); assert_matches!(err, Err(ReadlineError::Interrupted)); - let mut editor = init_editor(*mode, &[KeyPress::Ctrl('C')]); + let mut editor = init_editor(*mode, &[E::ctrl('C')]); let err = editor.readline_with_initial(">>", ("Hi", "")); assert_matches!(err, Err(ReadlineError::Interrupted)); if *mode == EditMode::Vi { // vi command mode - let mut editor = init_editor(*mode, &[KeyPress::Esc, KeyPress::Ctrl('C')]); + let mut editor = init_editor(*mode, &[E::ESC, E::ctrl('C')]); let err = editor.readline_with_initial(">>", ("Hi", "")); assert_matches!(err, Err(ReadlineError::Interrupted)); } @@ -233,13 +197,13 @@ fn delete_key() { assert_cursor( *mode, ("a", ""), - &[KeyPress::Delete, KeyPress::Enter], + &[E(K::Delete, M::NONE), E::ENTER], ("a", ""), ); assert_cursor( *mode, ("", "a"), - &[KeyPress::Delete, KeyPress::Enter], + &[E(K::Delete, M::NONE), E::ENTER], ("", ""), ); if *mode == EditMode::Vi { @@ -247,7 +211,7 @@ fn delete_key() { assert_cursor( *mode, ("", "a"), - &[KeyPress::Esc, KeyPress::Delete, KeyPress::Enter], + &[E::ESC, E(K::Delete, M::NONE), E::ENTER], ("", ""), ); } @@ -257,24 +221,14 @@ fn delete_key() { #[test] fn ctrl_t() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_cursor( - *mode, - ("a", "b"), - &[KeyPress::Ctrl('T'), KeyPress::Enter], - ("ba", ""), - ); - assert_cursor( - *mode, - ("ab", "cd"), - &[KeyPress::Ctrl('T'), KeyPress::Enter], - ("acb", "d"), - ); + assert_cursor(*mode, ("a", "b"), &[E::ctrl('T'), E::ENTER], ("ba", "")); + assert_cursor(*mode, ("ab", "cd"), &[E::ctrl('T'), E::ENTER], ("acb", "d")); if *mode == EditMode::Vi { // vi command mode assert_cursor( *mode, ("ab", ""), - &[KeyPress::Esc, KeyPress::Ctrl('T'), KeyPress::Enter], + &[E::ESC, E::ctrl('T'), E::ENTER], ("ba", ""), ); } @@ -287,21 +241,16 @@ fn ctrl_u() { assert_cursor( *mode, ("start of line ", "end"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], - ("", "end"), - ); - assert_cursor( - *mode, - ("", "end"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("", "end"), ); + assert_cursor(*mode, ("", "end"), &[E::ctrl('U'), E::ENTER], ("", "end")); if *mode == EditMode::Vi { // vi command mode assert_cursor( *mode, ("start of line ", "end"), - &[KeyPress::Esc, KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ESC, E::ctrl('U'), E::ENTER], ("", " end"), ); } @@ -315,7 +264,7 @@ fn ctrl_v() { assert_cursor( *mode, ("", ""), - &[KeyPress::Ctrl('V'), KeyPress::Char('\t'), KeyPress::Enter], + &[E::ctrl('V'), E(K::Char('\t'), M::NONE), E::ENTER], ("\t", ""), ); if *mode == EditMode::Vi { @@ -323,12 +272,7 @@ fn ctrl_v() { assert_cursor( *mode, ("", ""), - &[ - KeyPress::Esc, - KeyPress::Ctrl('V'), - KeyPress::Char('\t'), - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('V'), E(K::Char('\t'), M::NONE), E::ENTER], ("\t", ""), ); } @@ -341,13 +285,13 @@ fn ctrl_w() { assert_cursor( *mode, ("Hello, ", "world"), - &[KeyPress::Ctrl('W'), KeyPress::Enter], + &[E::ctrl('W'), E::ENTER], ("", "world"), ); assert_cursor( *mode, ("Hello, world.", ""), - &[KeyPress::Ctrl('W'), KeyPress::Enter], + &[E::ctrl('W'), E::ENTER], ("Hello, ", ""), ); if *mode == EditMode::Vi { @@ -355,7 +299,7 @@ fn ctrl_w() { assert_cursor( *mode, ("Hello, world.", ""), - &[KeyPress::Esc, KeyPress::Ctrl('W'), KeyPress::Enter], + &[E::ESC, E::ctrl('W'), E::ENTER], ("Hello, ", "."), ); } @@ -368,7 +312,7 @@ fn ctrl_y() { assert_cursor( *mode, ("Hello, ", "world"), - &[KeyPress::Ctrl('W'), KeyPress::Ctrl('Y'), KeyPress::Enter], + &[E::ctrl('W'), E::ctrl('Y'), E::ENTER], ("Hello, ", "world"), ); } @@ -380,7 +324,7 @@ fn ctrl__() { assert_cursor( *mode, ("Hello, ", "world"), - &[KeyPress::Ctrl('W'), KeyPress::Ctrl('_'), KeyPress::Enter], + &[E::ctrl('W'), E::ctrl('_'), E::ENTER], ("Hello, ", "world"), ); if *mode == EditMode::Vi { @@ -388,12 +332,7 @@ fn ctrl__() { assert_cursor( *mode, ("Hello, ", "world"), - &[ - KeyPress::Esc, - KeyPress::Ctrl('W'), - KeyPress::Ctrl('_'), - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('W'), E::ctrl('_'), E::ENTER], ("Hello,", " world"), ); } diff --git a/src/test/emacs.rs b/src/test/emacs.rs index 9fd395e54c..aa3e0b66d6 100644 --- a/src/test/emacs.rs +++ b/src/test/emacs.rs @@ -1,20 +1,20 @@ //! Emacs specific key bindings use super::{assert_cursor, assert_history}; use crate::config::EditMode; -use crate::keys::KeyPress; +use crate::keys::{KeyCode as K, KeyEvent as E, Modifiers as M}; #[test] fn ctrl_a() { assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Ctrl('A'), KeyPress::Enter], + &[E::ctrl('A'), E::ENTER], ("", "Hi"), ); assert_cursor( EditMode::Emacs, ("test test\n123", "foo"), - &[KeyPress::Ctrl('A'), KeyPress::Enter], + &[E::ctrl('A'), E::ENTER], ("test test\n", "123foo"), ); } @@ -24,13 +24,13 @@ fn ctrl_e() { assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Ctrl('E'), KeyPress::Enter], + &[E::ctrl('E'), E::ENTER], ("Hi", ""), ); assert_cursor( EditMode::Emacs, ("foo", "test test\n123"), - &[KeyPress::Ctrl('E'), KeyPress::Enter], + &[E::ctrl('E'), E::ENTER], ("footest test", "\n123"), ); } @@ -40,24 +40,19 @@ fn ctrl_b() { assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Ctrl('B'), KeyPress::Enter], + &[E::ctrl('B'), E::ENTER], ("H", "i"), ); assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Meta('2'), KeyPress::Ctrl('B'), KeyPress::Enter], + &[E::alt('2'), E::ctrl('B'), E::ENTER], ("", "Hi"), ); assert_cursor( EditMode::Emacs, ("", "Hi"), - &[ - KeyPress::Meta('-'), - KeyPress::Meta('2'), - KeyPress::Ctrl('B'), - KeyPress::Enter, - ], + &[E::alt('-'), E::alt('2'), E::ctrl('B'), E::ENTER], ("Hi", ""), ); } @@ -67,24 +62,19 @@ fn ctrl_f() { assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Ctrl('F'), KeyPress::Enter], + &[E::ctrl('F'), E::ENTER], ("H", "i"), ); assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Meta('2'), KeyPress::Ctrl('F'), KeyPress::Enter], + &[E::alt('2'), E::ctrl('F'), E::ENTER], ("Hi", ""), ); assert_cursor( EditMode::Emacs, ("Hi", ""), - &[ - KeyPress::Meta('-'), - KeyPress::Meta('2'), - KeyPress::Ctrl('F'), - KeyPress::Enter, - ], + &[E::alt('-'), E::alt('2'), E::ctrl('F'), E::ENTER], ("", "Hi"), ); } @@ -94,24 +84,19 @@ fn ctrl_h() { assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Ctrl('H'), KeyPress::Enter], + &[E::ctrl('H'), E::ENTER], ("H", ""), ); assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Meta('2'), KeyPress::Ctrl('H'), KeyPress::Enter], + &[E::alt('2'), E::ctrl('H'), E::ENTER], ("", ""), ); assert_cursor( EditMode::Emacs, ("", "Hi"), - &[ - KeyPress::Meta('-'), - KeyPress::Meta('2'), - KeyPress::Ctrl('H'), - KeyPress::Enter, - ], + &[E::alt('-'), E::alt('2'), E::ctrl('H'), E::ENTER], ("", ""), ); } @@ -121,19 +106,19 @@ fn backspace() { assert_cursor( EditMode::Emacs, ("", ""), - &[KeyPress::Backspace, KeyPress::Enter], + &[E::BACKSPACE, E::ENTER], ("", ""), ); assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Backspace, KeyPress::Enter], + &[E::BACKSPACE, E::ENTER], ("H", ""), ); assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Backspace, KeyPress::Enter], + &[E::BACKSPACE, E::ENTER], ("", "Hi"), ); } @@ -143,37 +128,37 @@ fn ctrl_k() { assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("Hi", ""), ); assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("", ""), ); assert_cursor( EditMode::Emacs, ("B", "ye"), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("B", ""), ); assert_cursor( EditMode::Emacs, ("Hi", "foo\nbar"), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("Hi", "\nbar"), ); assert_cursor( EditMode::Emacs, ("Hi", "\nbar"), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("Hi", "bar"), ); assert_cursor( EditMode::Emacs, ("Hi", "bar"), - &[KeyPress::Ctrl('K'), KeyPress::Enter], + &[E::ctrl('K'), E::ENTER], ("Hi", ""), ); } @@ -183,37 +168,37 @@ fn ctrl_u() { assert_cursor( EditMode::Emacs, ("", "Hi"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("", "Hi"), ); assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("", ""), ); assert_cursor( EditMode::Emacs, ("B", "ye"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("", "ye"), ); assert_cursor( EditMode::Emacs, ("foo\nbar", "Hi"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("foo\n", "Hi"), ); assert_cursor( EditMode::Emacs, ("foo\n", "Hi"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("foo", "Hi"), ); assert_cursor( EditMode::Emacs, ("foo", "Hi"), - &[KeyPress::Ctrl('U'), KeyPress::Enter], + &[E::ctrl('U'), E::ENTER], ("", "Hi"), ); } @@ -222,12 +207,7 @@ fn ctrl_n() { assert_history( EditMode::Emacs, &["line1", "line2"], - &[ - KeyPress::Ctrl('P'), - KeyPress::Ctrl('P'), - KeyPress::Ctrl('N'), - KeyPress::Enter, - ], + &[E::ctrl('P'), E::ctrl('P'), E::ctrl('N'), E::ENTER], "", ("line2", ""), ); @@ -238,7 +218,7 @@ fn ctrl_p() { assert_history( EditMode::Emacs, &["line1"], - &[KeyPress::Ctrl('P'), KeyPress::Enter], + &[E::ctrl('P'), E::ENTER], "", ("line1", ""), ); @@ -249,7 +229,7 @@ fn ctrl_t() { /* FIXME assert_cursor( ("ab", "cd"), - &[KeyPress::Meta('2'), KeyPress::Ctrl('T'), KeyPress::Enter], + &[E::alt('2'), E::ctrl('T'), E::ENTER], ("acdb", ""), );*/ } @@ -259,12 +239,7 @@ fn ctrl_x_ctrl_u() { assert_cursor( EditMode::Emacs, ("Hello, ", "world"), - &[ - KeyPress::Ctrl('W'), - KeyPress::Ctrl('X'), - KeyPress::Ctrl('U'), - KeyPress::Enter, - ], + &[E::ctrl('W'), E::ctrl('X'), E::ctrl('U'), E::ENTER], ("Hello, ", "world"), ); } @@ -274,19 +249,19 @@ fn meta_b() { assert_cursor( EditMode::Emacs, ("Hello, world!", ""), - &[KeyPress::Meta('B'), KeyPress::Enter], + &[E::alt('B'), E::ENTER], ("Hello, ", "world!"), ); assert_cursor( EditMode::Emacs, ("Hello, world!", ""), - &[KeyPress::Meta('2'), KeyPress::Meta('B'), KeyPress::Enter], + &[E::alt('2'), E::alt('B'), E::ENTER], ("", "Hello, world!"), ); assert_cursor( EditMode::Emacs, ("", "Hello, world!"), - &[KeyPress::Meta('-'), KeyPress::Meta('B'), KeyPress::Enter], + &[E::alt('-'), E::alt('B'), E::ENTER], ("Hello", ", world!"), ); } @@ -296,19 +271,19 @@ fn meta_f() { assert_cursor( EditMode::Emacs, ("", "Hello, world!"), - &[KeyPress::Meta('F'), KeyPress::Enter], + &[E::alt('F'), E::ENTER], ("Hello", ", world!"), ); assert_cursor( EditMode::Emacs, ("", "Hello, world!"), - &[KeyPress::Meta('2'), KeyPress::Meta('F'), KeyPress::Enter], + &[E::alt('2'), E::alt('F'), E::ENTER], ("Hello, world", "!"), ); assert_cursor( EditMode::Emacs, ("Hello, world!", ""), - &[KeyPress::Meta('-'), KeyPress::Meta('F'), KeyPress::Enter], + &[E::alt('-'), E::alt('F'), E::ENTER], ("Hello, ", "world!"), ); } @@ -318,19 +293,19 @@ fn meta_c() { assert_cursor( EditMode::Emacs, ("hi", ""), - &[KeyPress::Meta('C'), KeyPress::Enter], + &[E::alt('C'), E::ENTER], ("hi", ""), ); assert_cursor( EditMode::Emacs, ("", "hi"), - &[KeyPress::Meta('C'), KeyPress::Enter], + &[E::alt('C'), E::ENTER], ("Hi", ""), ); /* FIXME assert_cursor( ("", "hi test"), - &[KeyPress::Meta('2'), KeyPress::Meta('C'), KeyPress::Enter], + &[E::alt('2'), E::alt('C'), E::ENTER], ("Hi Test", ""), );*/ } @@ -340,19 +315,19 @@ fn meta_l() { assert_cursor( EditMode::Emacs, ("Hi", ""), - &[KeyPress::Meta('L'), KeyPress::Enter], + &[E::alt('L'), E::ENTER], ("Hi", ""), ); assert_cursor( EditMode::Emacs, ("", "HI"), - &[KeyPress::Meta('L'), KeyPress::Enter], + &[E::alt('L'), E::ENTER], ("hi", ""), ); /* FIXME assert_cursor( ("", "HI TEST"), - &[KeyPress::Meta('2'), KeyPress::Meta('L'), KeyPress::Enter], + &[E::alt('2'), E::alt('L'), E::ENTER], ("hi test", ""), );*/ } @@ -362,19 +337,19 @@ fn meta_u() { assert_cursor( EditMode::Emacs, ("hi", ""), - &[KeyPress::Meta('U'), KeyPress::Enter], + &[E::alt('U'), E::ENTER], ("hi", ""), ); assert_cursor( EditMode::Emacs, ("", "hi"), - &[KeyPress::Meta('U'), KeyPress::Enter], + &[E::alt('U'), E::ENTER], ("HI", ""), ); /* FIXME assert_cursor( ("", "hi test"), - &[KeyPress::Meta('2'), KeyPress::Meta('U'), KeyPress::Enter], + &[E::alt('2'), E::alt('U'), E::ENTER], ("HI TEST", ""), );*/ } @@ -384,13 +359,13 @@ fn meta_d() { assert_cursor( EditMode::Emacs, ("Hello", ", world!"), - &[KeyPress::Meta('D'), KeyPress::Enter], + &[E::alt('D'), E::ENTER], ("Hello", "!"), ); assert_cursor( EditMode::Emacs, ("Hello", ", world!"), - &[KeyPress::Meta('2'), KeyPress::Meta('D'), KeyPress::Enter], + &[E::alt('2'), E::alt('D'), E::ENTER], ("Hello", ""), ); } @@ -400,13 +375,13 @@ fn meta_t() { assert_cursor( EditMode::Emacs, ("Hello", ", world!"), - &[KeyPress::Meta('T'), KeyPress::Enter], + &[E::alt('T'), E::ENTER], ("world, Hello", "!"), ); /* FIXME assert_cursor( ("One Two", " Three Four"), - &[KeyPress::Meta('T'), KeyPress::Enter], + &[E::alt('T'), E::ENTER], ("One Four Three Two", ""), );*/ } @@ -417,12 +392,12 @@ fn meta_y() { EditMode::Emacs, ("Hello, world", "!"), &[ - KeyPress::Ctrl('W'), - KeyPress::Left, - KeyPress::Ctrl('W'), - KeyPress::Ctrl('Y'), - KeyPress::Meta('Y'), - KeyPress::Enter, + E::ctrl('W'), + E(K::Left, M::NONE), + E::ctrl('W'), + E::ctrl('Y'), + E::alt('Y'), + E::ENTER, ], ("world", " !"), ); @@ -433,7 +408,7 @@ fn meta_backspace() { assert_cursor( EditMode::Emacs, ("Hello, wor", "ld!"), - &[KeyPress::Meta('\x08'), KeyPress::Enter], + &[E(K::Backspace, M::ALT), E::ENTER], ("Hello, ", "ld!"), ); } @@ -443,7 +418,7 @@ fn meta_digit() { assert_cursor( EditMode::Emacs, ("", ""), - &[KeyPress::Meta('3'), KeyPress::Char('h'), KeyPress::Enter], + &[E::alt('3'), E::from('h'), E::ENTER], ("hhh", ""), ); } diff --git a/src/test/history.rs b/src/test/history.rs index 88cb3b5b87..ab869249c6 100644 --- a/src/test/history.rs +++ b/src/test/history.rs @@ -1,7 +1,7 @@ //! History related commands tests use super::assert_history; use crate::config::EditMode; -use crate::keys::KeyPress; +use crate::keys::{KeyCode as K, KeyEvent as E, Modifiers as M}; #[test] fn down_key() { @@ -9,14 +9,19 @@ fn down_key() { assert_history( *mode, &["line1"], - &[KeyPress::Down, KeyPress::Enter], + &[E(K::Down, M::NONE), E::ENTER], "", ("", ""), ); assert_history( *mode, &["line1", "line2"], - &[KeyPress::Up, KeyPress::Up, KeyPress::Down, KeyPress::Enter], + &[ + E(K::Up, M::NONE), + E(K::Up, M::NONE), + E(K::Down, M::NONE), + E::ENTER, + ], "", ("line2", ""), ); @@ -24,10 +29,10 @@ fn down_key() { *mode, &["line1"], &[ - KeyPress::Char('a'), - KeyPress::Up, - KeyPress::Down, // restore original line - KeyPress::Enter, + E::from('a'), + E(K::Up, M::NONE), + E(K::Down, M::NONE), // restore original line + E::ENTER, ], "", ("a", ""), @@ -36,9 +41,9 @@ fn down_key() { *mode, &["line1"], &[ - KeyPress::Char('a'), - KeyPress::Down, // noop - KeyPress::Enter, + E::from('a'), + E(K::Down, M::NONE), // noop + E::ENTER, ], "", ("a", ""), @@ -49,18 +54,18 @@ fn down_key() { #[test] fn up_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_history(*mode, &[], &[KeyPress::Up, KeyPress::Enter], "", ("", "")); + assert_history(*mode, &[], &[E(K::Up, M::NONE), E::ENTER], "", ("", "")); assert_history( *mode, &["line1"], - &[KeyPress::Up, KeyPress::Enter], + &[E(K::Up, M::NONE), E::ENTER], "", ("line1", ""), ); assert_history( *mode, &["line1", "line2"], - &[KeyPress::Up, KeyPress::Up, KeyPress::Enter], + &[E(K::Up, M::NONE), E(K::Up, M::NONE), E::ENTER], "", ("line1", ""), ); @@ -73,7 +78,7 @@ fn ctrl_r() { assert_history( *mode, &[], - &[KeyPress::Ctrl('R'), KeyPress::Char('o'), KeyPress::Enter], + &[E::ctrl('R'), E::from('o'), E::ENTER], "", ("o", ""), ); @@ -81,10 +86,10 @@ fn ctrl_r() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('o'), - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('o'), + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("cargo", ""), @@ -93,10 +98,10 @@ fn ctrl_r() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('u'), - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('u'), + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("ru", "stc"), @@ -105,11 +110,11 @@ fn ctrl_r() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('r'), - KeyPress::Char('u'), - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('r'), + E::from('u'), + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("r", "ustc"), @@ -118,11 +123,11 @@ fn ctrl_r() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('r'), - KeyPress::Ctrl('R'), - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('r'), + E::ctrl('R'), + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("r", "ustc"), @@ -131,11 +136,11 @@ fn ctrl_r() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('r'), - KeyPress::Char('z'), // no match - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('r'), + E::from('z'), // no match + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("car", "go"), @@ -144,11 +149,11 @@ fn ctrl_r() { EditMode::Emacs, &["rustc", "cargo"], &[ - KeyPress::Char('a'), - KeyPress::Ctrl('R'), - KeyPress::Char('r'), - KeyPress::Ctrl('G'), // abort (FIXME: doesn't work with vi mode) - KeyPress::Enter, + E::from('a'), + E::ctrl('R'), + E::from('r'), + E::ctrl('G'), // abort (FIXME: doesn't work with vi mode) + E::ENTER, ], "", ("a", ""), @@ -162,7 +167,7 @@ fn ctrl_r_with_long_prompt() { assert_history( *mode, &["rustc", "cargo"], - &[KeyPress::Ctrl('R'), KeyPress::Char('o'), KeyPress::Enter], + &[E::ctrl('R'), E::from('o'), E::ENTER], ">>>>>>>>>>>>>>>>>>>>>>>>>>> ", ("cargo", ""), ); @@ -176,12 +181,12 @@ fn ctrl_s() { *mode, &["rustc", "cargo"], &[ - KeyPress::Ctrl('R'), - KeyPress::Char('r'), - KeyPress::Ctrl('R'), - KeyPress::Ctrl('S'), - KeyPress::Right, // just to assert cursor pos - KeyPress::Enter, + E::ctrl('R'), + E::from('r'), + E::ctrl('R'), + E::ctrl('S'), + E(K::Right, M::NONE), // just to assert cursor pos + E::ENTER, ], "", ("car", "go"), @@ -194,14 +199,14 @@ fn meta_lt() { assert_history( EditMode::Emacs, &[""], - &[KeyPress::Meta('<'), KeyPress::Enter], + &[E::alt('<'), E::ENTER], "", ("", ""), ); assert_history( EditMode::Emacs, &["rustc", "cargo"], - &[KeyPress::Meta('<'), KeyPress::Enter], + &[E::alt('<'), E::ENTER], "", ("rustc", ""), ); @@ -212,14 +217,14 @@ fn meta_gt() { assert_history( EditMode::Emacs, &[""], - &[KeyPress::Meta('>'), KeyPress::Enter], + &[E::alt('>'), E::ENTER], "", ("", ""), ); assert_history( EditMode::Emacs, &["rustc", "cargo"], - &[KeyPress::Meta('<'), KeyPress::Meta('>'), KeyPress::Enter], + &[E::alt('<'), E::alt('>'), E::ENTER], "", ("", ""), ); @@ -227,10 +232,10 @@ fn meta_gt() { EditMode::Emacs, &["rustc", "cargo"], &[ - KeyPress::Char('a'), - KeyPress::Meta('<'), - KeyPress::Meta('>'), // restore original line - KeyPress::Enter, + E::from('a'), + E::alt('<'), + E::alt('>'), // restore original line + E::ENTER, ], "", ("a", ""), diff --git a/src/test/mod.rs b/src/test/mod.rs index f89bcf5621..a9d42bf8ac 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -8,7 +8,7 @@ use crate::edit::init_state; use crate::highlight::Highlighter; use crate::hint::Hinter; use crate::keymap::{Cmd, InputState}; -use crate::keys::KeyPress; +use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M}; use crate::tty::Sink; use crate::validate::Validator; use crate::{Context, Editor, Helper, Result}; @@ -19,7 +19,7 @@ mod history; mod vi_cmd; mod vi_insert; -fn init_editor(mode: EditMode, keys: &[KeyPress]) -> Editor<()> { +fn init_editor(mode: EditMode, keys: &[KeyEvent]) -> Editor<()> { let config = Config::builder().edit_mode(mode).build(); let mut editor = Editor::<()>::with_config(config); editor.term.keys.extend(keys.iter().cloned()); @@ -59,8 +59,8 @@ fn complete_line() { let mut s = init_state(&mut out, "rus", 3, helper.as_ref(), &history); let config = Config::default(); let mut input_state = InputState::new(&config, Arc::new(RwLock::new(HashMap::new()))); - let keys = vec![KeyPress::Enter]; - let mut rdr: IntoIter = keys.into_iter(); + let keys = vec![E::ENTER]; + let mut rdr: IntoIter = keys.into_iter(); let cmd = super::complete_line(&mut rdr, &mut s, &mut input_state, &Config::default()).unwrap(); assert_eq!( Some(Cmd::AcceptOrInsertLine { @@ -74,7 +74,7 @@ fn complete_line() { // `keys`: keys to press // `expected_line`: line after enter key -fn assert_line(mode: EditMode, keys: &[KeyPress], expected_line: &str) { +fn assert_line(mode: EditMode, keys: &[KeyEvent], expected_line: &str) { let mut editor = init_editor(mode, keys); let actual_line = editor.readline(">>").unwrap(); assert_eq!(expected_line, actual_line); @@ -86,7 +86,7 @@ fn assert_line(mode: EditMode, keys: &[KeyPress], expected_line: &str) { fn assert_line_with_initial( mode: EditMode, initial: (&str, &str), - keys: &[KeyPress], + keys: &[KeyEvent], expected_line: &str, ) { let mut editor = init_editor(mode, keys); @@ -97,7 +97,7 @@ fn assert_line_with_initial( // `initial`: line status before `keys` pressed: strings before and after cursor // `keys`: keys to press // `expected`: line status before enter key: strings before and after cursor -fn assert_cursor(mode: EditMode, initial: (&str, &str), keys: &[KeyPress], expected: (&str, &str)) { +fn assert_cursor(mode: EditMode, initial: (&str, &str), keys: &[KeyEvent], expected: (&str, &str)) { let mut editor = init_editor(mode, keys); let actual_line = editor.readline_with_initial("", initial).unwrap(); assert_eq!(expected.0.to_owned() + expected.1, actual_line); @@ -110,7 +110,7 @@ fn assert_cursor(mode: EditMode, initial: (&str, &str), keys: &[KeyPress], expec fn assert_history( mode: EditMode, entries: &[&str], - keys: &[KeyPress], + keys: &[KeyEvent], prompt: &str, expected: (&str, &str), ) { @@ -128,7 +128,7 @@ fn assert_history( #[test] fn unknown_esc_key() { for mode in &[EditMode::Emacs, EditMode::Vi] { - assert_line(*mode, &[KeyPress::UnknownEscSeq, KeyPress::Enter], ""); + assert_line(*mode, &[E(K::UnknownEscSeq, M::NONE), E::ENTER], ""); } } diff --git a/src/test/vi_cmd.rs b/src/test/vi_cmd.rs index 02c5c0965a..ec5234d9c8 100644 --- a/src/test/vi_cmd.rs +++ b/src/test/vi_cmd.rs @@ -1,14 +1,14 @@ //! Vi command mode specific key bindings use super::{assert_cursor, assert_history}; use crate::config::EditMode; -use crate::keys::KeyPress; +use crate::keys::KeyEvent as E; #[test] fn dollar() { assert_cursor( EditMode::Vi, ("", "Hi"), - &[KeyPress::Esc, KeyPress::Char('$'), KeyPress::Enter], + &[E::ESC, E::from('$'), E::ENTER], ("Hi", ""), // FIXME ); } @@ -23,13 +23,7 @@ fn semi_colon() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('f'), - KeyPress::Char('o'), - KeyPress::Char(';'), - KeyPress::Enter, - ], + &[E::ESC, E::from('f'), E::from('o'), E::from(';'), E::ENTER], ("Hello, w", "orld!"), ); } @@ -39,13 +33,7 @@ fn comma() { assert_cursor( EditMode::Vi, ("Hello, w", "orld!"), - &[ - KeyPress::Esc, - KeyPress::Char('f'), - KeyPress::Char('l'), - KeyPress::Char(','), - KeyPress::Enter, - ], + &[E::ESC, E::from('f'), E::from('l'), E::from(','), E::ENTER], ("Hel", "lo, world!"), ); } @@ -55,7 +43,7 @@ fn zero() { assert_cursor( EditMode::Vi, ("Hi", ""), - &[KeyPress::Esc, KeyPress::Char('0'), KeyPress::Enter], + &[E::ESC, E::from('0'), E::ENTER], ("", "Hi"), ); } @@ -65,7 +53,7 @@ fn caret() { assert_cursor( EditMode::Vi, (" Hi", ""), - &[KeyPress::Esc, KeyPress::Char('^'), KeyPress::Enter], + &[E::ESC, E::from('^'), E::ENTER], (" ", "Hi"), ); } @@ -75,12 +63,7 @@ fn a() { assert_cursor( EditMode::Vi, ("B", "e"), - &[ - KeyPress::Esc, - KeyPress::Char('a'), - KeyPress::Char('y'), - KeyPress::Enter, - ], + &[E::ESC, E::from('a'), E::from('y'), E::ENTER], ("By", "e"), ); } @@ -90,12 +73,7 @@ fn uppercase_a() { assert_cursor( EditMode::Vi, ("", "By"), - &[ - KeyPress::Esc, - KeyPress::Char('A'), - KeyPress::Char('e'), - KeyPress::Enter, - ], + &[E::ESC, E::from('A'), E::from('e'), E::ENTER], ("Bye", ""), ); } @@ -105,18 +83,13 @@ fn b() { assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[KeyPress::Esc, KeyPress::Char('b'), KeyPress::Enter], + &[E::ESC, E::from('b'), E::ENTER], ("Hello, ", "world!"), ); assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('b'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('b'), E::ENTER], ("Hello", ", world!"), ); } @@ -126,18 +99,13 @@ fn uppercase_b() { assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[KeyPress::Esc, KeyPress::Char('B'), KeyPress::Enter], + &[E::ESC, E::from('B'), E::ENTER], ("Hello, ", "world!"), ); assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('B'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('B'), E::ENTER], ("", "Hello, world!"), ); } @@ -147,35 +115,30 @@ fn uppercase_c() { assert_cursor( EditMode::Vi, ("Hello, w", "orld!"), - &[ - KeyPress::Esc, - KeyPress::Char('C'), - KeyPress::Char('i'), - KeyPress::Enter, - ], + &[E::ESC, E::from('C'), E::from('i'), E::ENTER], ("Hello, i", ""), ); } #[test] fn ctrl_k() { - for key in &[KeyPress::Char('D'), KeyPress::Ctrl('K')] { + for key in &[E::from('D'), E::ctrl('K')] { assert_cursor( EditMode::Vi, ("Hi", ""), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("H", ""), ); assert_cursor( EditMode::Vi, ("", "Hi"), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("", ""), ); assert_cursor( EditMode::Vi, ("By", "e"), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("B", ""), ); } @@ -186,18 +149,13 @@ fn e() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[KeyPress::Esc, KeyPress::Char('e'), KeyPress::Enter], + &[E::ESC, E::from('e'), E::ENTER], ("Hell", "o, world!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('e'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('e'), E::ENTER], ("Hello, worl", "d!"), ); } @@ -207,18 +165,13 @@ fn uppercase_e() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[KeyPress::Esc, KeyPress::Char('E'), KeyPress::Enter], + &[E::ESC, E::from('E'), E::ENTER], ("Hello", ", world!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('E'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('E'), E::ENTER], ("Hello, world", "!"), ); } @@ -228,24 +181,13 @@ fn f() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('f'), - KeyPress::Char('r'), - KeyPress::Enter, - ], + &[E::ESC, E::from('f'), E::from('r'), E::ENTER], ("Hello, wo", "rld!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('3'), - KeyPress::Char('f'), - KeyPress::Char('l'), - KeyPress::Enter, - ], + &[E::ESC, E::from('3'), E::from('f'), E::from('l'), E::ENTER], ("Hello, wor", "ld!"), ); } @@ -255,24 +197,13 @@ fn uppercase_f() { assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('F'), - KeyPress::Char('r'), - KeyPress::Enter, - ], + &[E::ESC, E::from('F'), E::from('r'), E::ENTER], ("Hello, wo", "rld!"), ); assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('3'), - KeyPress::Char('F'), - KeyPress::Char('l'), - KeyPress::Enter, - ], + &[E::ESC, E::from('3'), E::from('F'), E::from('l'), E::ENTER], ("He", "llo, world!"), ); } @@ -282,12 +213,7 @@ fn i() { assert_cursor( EditMode::Vi, ("Be", ""), - &[ - KeyPress::Esc, - KeyPress::Char('i'), - KeyPress::Char('y'), - KeyPress::Enter, - ], + &[E::ESC, E::from('i'), E::from('y'), E::ENTER], ("By", "e"), ); } @@ -297,12 +223,7 @@ fn uppercase_i() { assert_cursor( EditMode::Vi, ("Be", ""), - &[ - KeyPress::Esc, - KeyPress::Char('I'), - KeyPress::Char('y'), - KeyPress::Enter, - ], + &[E::ESC, E::from('I'), E::from('y'), E::ENTER], ("y", "Be"), ); } @@ -312,12 +233,7 @@ fn u() { assert_cursor( EditMode::Vi, ("Hello, ", "world"), - &[ - KeyPress::Esc, - KeyPress::Ctrl('W'), - KeyPress::Char('u'), - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('W'), E::from('u'), E::ENTER], ("Hello,", " world"), ); } @@ -327,18 +243,13 @@ fn w() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[KeyPress::Esc, KeyPress::Char('w'), KeyPress::Enter], + &[E::ESC, E::from('w'), E::ENTER], ("Hello", ", world!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('w'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('w'), E::ENTER], ("Hello, ", "world!"), ); } @@ -348,18 +259,13 @@ fn uppercase_w() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[KeyPress::Esc, KeyPress::Char('W'), KeyPress::Enter], + &[E::ESC, E::from('W'), E::ENTER], ("Hello, ", "world!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('2'), - KeyPress::Char('W'), - KeyPress::Enter, - ], + &[E::ESC, E::from('2'), E::from('W'), E::ENTER], ("Hello, world", "!"), ); } @@ -369,7 +275,7 @@ fn x() { assert_cursor( EditMode::Vi, ("", "a"), - &[KeyPress::Esc, KeyPress::Char('x'), KeyPress::Enter], + &[E::ESC, E::from('x'), E::ENTER], ("", ""), ); } @@ -379,28 +285,24 @@ fn uppercase_x() { assert_cursor( EditMode::Vi, ("Hi", ""), - &[KeyPress::Esc, KeyPress::Char('X'), KeyPress::Enter], + &[E::ESC, E::from('X'), E::ENTER], ("", "i"), ); } #[test] fn h() { - for key in &[ - KeyPress::Char('h'), - KeyPress::Ctrl('H'), - KeyPress::Backspace, - ] { + for key in &[E::from('h'), E::ctrl('H'), E::BACKSPACE] { assert_cursor( EditMode::Vi, ("Bye", ""), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("B", "ye"), ); assert_cursor( EditMode::Vi, ("Bye", ""), - &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter], + &[E::ESC, E::from('2'), *key, E::ENTER], ("", "Bye"), ); } @@ -408,17 +310,17 @@ fn h() { #[test] fn l() { - for key in &[KeyPress::Char('l'), KeyPress::Char(' ')] { + for key in &[E::from('l'), E::from(' ')] { assert_cursor( EditMode::Vi, ("", "Hi"), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("H", "i"), ); assert_cursor( EditMode::Vi, ("", "Hi"), - &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter], + &[E::ESC, E::from('2'), *key, E::ENTER], ("Hi", ""), ); } @@ -426,25 +328,25 @@ fn l() { #[test] fn j() { - for key in &[KeyPress::Char('j'), KeyPress::Char('+')] { + for key in &[E::from('j'), E::from('+')] { assert_cursor( EditMode::Vi, ("Hel", "lo,\nworld!"), // NOTE: escape moves backwards on char - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("Hello,\nwo", "rld!"), ); assert_cursor( EditMode::Vi, ("", "One\nTwo\nThree"), - &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter], + &[E::ESC, E::from('2'), *key, E::ENTER], ("One\nTwo\n", "Three"), ); assert_cursor( EditMode::Vi, ("Hel", "lo,\nworld!"), // NOTE: escape moves backwards on char - &[KeyPress::Esc, KeyPress::Char('7'), *key, KeyPress::Enter], + &[E::ESC, E::from('7'), *key, E::ENTER], ("Hello,\nwo", "rld!"), ); } @@ -452,32 +354,32 @@ fn j() { #[test] fn k() { - for key in &[KeyPress::Char('k'), KeyPress::Char('-')] { + for key in &[E::from('k'), E::from('-')] { assert_cursor( EditMode::Vi, ("Hello,\nworl", "d!"), // NOTE: escape moves backwards on char - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("Hel", "lo,\nworld!"), ); assert_cursor( EditMode::Vi, ("One\nTwo\nT", "hree"), // NOTE: escape moves backwards on char - &[KeyPress::Esc, KeyPress::Char('2'), *key, KeyPress::Enter], + &[E::ESC, E::from('2'), *key, E::ENTER], ("", "One\nTwo\nThree"), ); assert_cursor( EditMode::Vi, ("Hello,\nworl", "d!"), // NOTE: escape moves backwards on char - &[KeyPress::Esc, KeyPress::Char('5'), *key, KeyPress::Enter], + &[E::ESC, E::from('5'), *key, E::ENTER], ("Hel", "lo,\nworld!"), ); assert_cursor( EditMode::Vi, ("first line\nshort\nlong line", ""), - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], ("first line\nshort", "\nlong line"), ); } @@ -485,17 +387,11 @@ fn k() { #[test] fn ctrl_n() { - for key in &[KeyPress::Ctrl('N')] { + for key in &[E::ctrl('N')] { assert_history( EditMode::Vi, &["line1", "line2"], - &[ - KeyPress::Esc, - KeyPress::Ctrl('P'), - KeyPress::Ctrl('P'), - *key, - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('P'), E::ctrl('P'), *key, E::ENTER], "", ("line2", ""), ); @@ -504,11 +400,11 @@ fn ctrl_n() { #[test] fn ctrl_p() { - for key in &[KeyPress::Ctrl('P')] { + for key in &[E::ctrl('P')] { assert_history( EditMode::Vi, &["line1"], - &[KeyPress::Esc, *key, KeyPress::Enter], + &[E::ESC, *key, E::ENTER], "", ("line1", ""), ); @@ -520,12 +416,7 @@ fn p() { assert_cursor( EditMode::Vi, ("Hello, ", "world"), - &[ - KeyPress::Esc, - KeyPress::Ctrl('W'), - KeyPress::Char('p'), - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('W'), E::from('p'), E::ENTER], (" Hello", ",world"), ); } @@ -535,12 +426,7 @@ fn uppercase_p() { assert_cursor( EditMode::Vi, ("Hello, ", "world"), - &[ - KeyPress::Esc, - KeyPress::Ctrl('W'), - KeyPress::Char('P'), - KeyPress::Enter, - ], + &[E::ESC, E::ctrl('W'), E::from('P'), E::ENTER], ("Hello", ", world"), ); } @@ -550,24 +436,13 @@ fn r() { assert_cursor( EditMode::Vi, ("Hi", ", world!"), - &[ - KeyPress::Esc, - KeyPress::Char('r'), - KeyPress::Char('o'), - KeyPress::Enter, - ], + &[E::ESC, E::from('r'), E::from('o'), E::ENTER], ("H", "o, world!"), ); assert_cursor( EditMode::Vi, ("He", "llo, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('4'), - KeyPress::Char('r'), - KeyPress::Char('i'), - KeyPress::Enter, - ], + &[E::ESC, E::from('4'), E::from('r'), E::from('i'), E::ENTER], ("Hiii", "i, world!"), ); } @@ -577,24 +452,13 @@ fn s() { assert_cursor( EditMode::Vi, ("Hi", ", world!"), - &[ - KeyPress::Esc, - KeyPress::Char('s'), - KeyPress::Char('o'), - KeyPress::Enter, - ], + &[E::ESC, E::from('s'), E::from('o'), E::ENTER], ("Ho", ", world!"), ); assert_cursor( EditMode::Vi, ("He", "llo, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('4'), - KeyPress::Char('s'), - KeyPress::Char('i'), - KeyPress::Enter, - ], + &[E::ESC, E::from('4'), E::from('s'), E::from('i'), E::ENTER], ("Hi", ", world!"), ); } @@ -604,7 +468,7 @@ fn uppercase_s() { assert_cursor( EditMode::Vi, ("Hello, ", "world"), - &[KeyPress::Esc, KeyPress::Char('S'), KeyPress::Enter], + &[E::ESC, E::from('S'), E::ENTER], ("", ""), ); } @@ -614,24 +478,13 @@ fn t() { assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('t'), - KeyPress::Char('r'), - KeyPress::Enter, - ], + &[E::ESC, E::from('t'), E::from('r'), E::ENTER], ("Hello, w", "orld!"), ); assert_cursor( EditMode::Vi, ("", "Hello, world!"), - &[ - KeyPress::Esc, - KeyPress::Char('3'), - KeyPress::Char('t'), - KeyPress::Char('l'), - KeyPress::Enter, - ], + &[E::ESC, E::from('3'), E::from('t'), E::from('l'), E::ENTER], ("Hello, wo", "rld!"), ); } @@ -641,24 +494,13 @@ fn uppercase_t() { assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('T'), - KeyPress::Char('r'), - KeyPress::Enter, - ], + &[E::ESC, E::from('T'), E::from('r'), E::ENTER], ("Hello, wor", "ld!"), ); assert_cursor( EditMode::Vi, ("Hello, world!", ""), - &[ - KeyPress::Esc, - KeyPress::Char('3'), - KeyPress::Char('T'), - KeyPress::Char('l'), - KeyPress::Enter, - ], + &[E::ESC, E::from('3'), E::from('T'), E::from('l'), E::ENTER], ("Hel", "lo, world!"), ); } diff --git a/src/test/vi_insert.rs b/src/test/vi_insert.rs index 608e2ac760..8a18529631 100644 --- a/src/test/vi_insert.rs +++ b/src/test/vi_insert.rs @@ -1,16 +1,11 @@ //! Vi insert mode specific key bindings use super::assert_cursor; use crate::config::EditMode; -use crate::keys::KeyPress; +use crate::keys::KeyEvent as E; #[test] fn insert_mode_by_default() { - assert_cursor( - EditMode::Vi, - ("", ""), - &[KeyPress::Char('a'), KeyPress::Enter], - ("a", ""), - ); + assert_cursor(EditMode::Vi, ("", ""), &[E::from('a'), E::ENTER], ("a", "")); } #[test] @@ -18,29 +13,24 @@ fn ctrl_h() { assert_cursor( EditMode::Vi, ("Hi", ""), - &[KeyPress::Ctrl('H'), KeyPress::Enter], + &[E::ctrl('H'), E::ENTER], ("H", ""), ); } #[test] fn backspace() { - assert_cursor( - EditMode::Vi, - ("", ""), - &[KeyPress::Backspace, KeyPress::Enter], - ("", ""), - ); + assert_cursor(EditMode::Vi, ("", ""), &[E::BACKSPACE, E::ENTER], ("", "")); assert_cursor( EditMode::Vi, ("Hi", ""), - &[KeyPress::Backspace, KeyPress::Enter], + &[E::BACKSPACE, E::ENTER], ("H", ""), ); assert_cursor( EditMode::Vi, ("", "Hi"), - &[KeyPress::Backspace, KeyPress::Enter], + &[E::BACKSPACE, E::ENTER], ("", "Hi"), ); } @@ -50,7 +40,7 @@ fn esc() { assert_cursor( EditMode::Vi, ("", ""), - &[KeyPress::Char('a'), KeyPress::Esc, KeyPress::Enter], + &[E::from('a'), E::ESC, E::ENTER], ("", "a"), ); } diff --git a/src/tty/mod.rs b/src/tty/mod.rs index cfa393274d..9f9e7280ec 100644 --- a/src/tty/mod.rs +++ b/src/tty/mod.rs @@ -4,7 +4,7 @@ use unicode_width::UnicodeWidthStr; use crate::config::{BellStyle, ColorMode, Config, OutputStreamType}; use crate::highlight::Highlighter; -use crate::keys::KeyPress; +use crate::keys::KeyEvent; use crate::layout::{Layout, Position}; use crate::line_buffer::LineBuffer; use crate::Result; @@ -18,7 +18,7 @@ pub trait RawMode: Sized { /// Translate bytes read from stdin to keys. pub trait RawReader { /// Blocking read of key pressed. - fn next_key(&mut self, single_esc_abort: bool) -> Result; + fn next_key(&mut self, single_esc_abort: bool) -> Result; /// For CTRL-V support #[cfg(unix)] fn next_char(&mut self) -> Result; diff --git a/src/tty/test.rs b/src/tty/test.rs index f8dbb02ad6..765d744520 100644 --- a/src/tty/test.rs +++ b/src/tty/test.rs @@ -7,7 +7,7 @@ use super::{RawMode, RawReader, Renderer, Term}; use crate::config::{BellStyle, ColorMode, Config, OutputStreamType}; use crate::error::ReadlineError; use crate::highlight::Highlighter; -use crate::keys::KeyPress; +use crate::keys::KeyEvent; use crate::layout::{Layout, Position}; use crate::line_buffer::LineBuffer; use crate::Result; @@ -20,8 +20,8 @@ impl RawMode for Mode { } } -impl<'a> RawReader for Iter<'a, KeyPress> { - fn next_key(&mut self, _: bool) -> Result { +impl<'a> RawReader for Iter<'a, KeyEvent> { + fn next_key(&mut self, _: bool) -> Result { match self.next() { Some(key) => Ok(*key), None => Err(ReadlineError::Eof), @@ -38,8 +38,8 @@ impl<'a> RawReader for Iter<'a, KeyPress> { } } -impl RawReader for IntoIter { - fn next_key(&mut self, _: bool) -> Result { +impl RawReader for IntoIter { + fn next_key(&mut self, _: bool) -> Result { match self.next() { Some(key) => Ok(key), None => Err(ReadlineError::Eof), @@ -48,8 +48,9 @@ impl RawReader for IntoIter { #[cfg(unix)] fn next_char(&mut self) -> Result { + use crate::keys::{KeyCode as K, KeyEvent as E, Modifiers as M}; match self.next() { - Some(KeyPress::Char(c)) => Ok(c), + Some(E(K::Char(c), M::NONE)) => Ok(c), None => Err(ReadlineError::Eof), _ => unimplemented!(), } @@ -69,7 +70,7 @@ impl Sink { } impl Renderer for Sink { - type Reader = IntoIter; + type Reader = IntoIter; fn move_cursor(&mut self, _: Position, _: Position) -> Result<()> { Ok(()) @@ -123,7 +124,7 @@ impl Renderer for Sink { false } - fn move_cursor_at_leftmost(&mut self, _: &mut IntoIter) -> Result<()> { + fn move_cursor_at_leftmost(&mut self, _: &mut IntoIter) -> Result<()> { Ok(()) } } @@ -132,7 +133,7 @@ pub type Terminal = DummyTerminal; #[derive(Clone, Debug)] pub struct DummyTerminal { - pub keys: Vec, + pub keys: Vec, pub cursor: usize, // cursor position before last command pub color_mode: ColorMode, pub bell_style: BellStyle, @@ -140,7 +141,7 @@ pub struct DummyTerminal { impl Term for DummyTerminal { type Mode = Mode; - type Reader = IntoIter; + type Reader = IntoIter; type Writer = Sink; fn new( @@ -183,7 +184,7 @@ impl Term for DummyTerminal { Ok(()) } - fn create_reader(&self, _: &Config) -> Result> { + fn create_reader(&self, _: &Config) -> Result> { Ok(self.keys.clone().into_iter()) } diff --git a/src/tty/unix.rs b/src/tty/unix.rs index e4021f33f9..bce53dce4e 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -17,7 +17,7 @@ use super::{width, RawMode, RawReader, Renderer, Term}; use crate::config::{BellStyle, ColorMode, Config, OutputStreamType}; use crate::error; use crate::highlight::Highlighter; -use crate::keys::{self, KeyPress}; +use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M}; use crate::layout::{Layout, Position}; use crate::line_buffer::LineBuffer; use crate::Result; @@ -158,6 +158,32 @@ struct Utf8 { valid: bool, } +const UP: char = 'A'; // kcuu1, kUP* +const DOWN: char = 'B'; // kcud1, kDN* +const RIGHT: char = 'C'; // kcuf1, kRIT* +const LEFT: char = 'D'; // kcub1, kLFT* +const END: char = 'F'; // kend* +const HOME: char = 'H'; // khom* +const INSERT: char = '2'; // kic* +const DELETE: char = '3'; // kdch1, kDC* +const PAGE_UP: char = '5'; // kpp, kPRV* +const PAGE_DOWN: char = '6'; // knp, kNXT* + +const RXVT_HOME: char = '7'; +const RXVT_END: char = '8'; + +const SHIFT: char = '2'; +const ALT: char = '3'; +const ALT_SHIFT: char = '4'; +const CTRL: char = '5'; +const CTRL_SHIFT: char = '6'; +const CTRL_ALT: char = '7'; +const CTRL_ALT_SHIFT: char = '8'; + +const RXVT_SHIFT: char = '$'; +const RXVT_CTRL: char = '\x1e'; +const RXVT_CTRL_SHIFT: char = '@'; + impl PosixRawReader { fn new(config: &Config) -> Result { Ok(Self { @@ -172,34 +198,39 @@ impl PosixRawReader { }) } - /// Handle ESC sequences - fn escape_sequence(&mut self) -> Result { + /// Handle \E sequences + // https://invisible-island.net/xterm/xterm-function-keys.html + fn escape_sequence(&mut self) -> Result { // Read the next byte representing the escape sequence. let seq1 = self.next_char()?; if seq1 == '[' { - // ESC [ sequences. (CSI) + // \E[ sequences. (CSI) self.escape_csi() } else if seq1 == 'O' { // xterm - // ESC O sequences. (SS3) + // \EO sequences. (SS3) self.escape_o() } else if seq1 == '\x1b' { - // ESC ESC - Ok(KeyPress::Esc) + // \E\E + // \E\E[A => Alt-Up + // \E\E[B => Alt-Down + // \E\E[C => Alt-Right + // \E\E[D => Alt-Left + // ... + Ok(E::ESC) } else { - // TODO ESC-R (r): Undo all changes made to this line. - Ok(KeyPress::Meta(seq1)) + Ok(E::alt(seq1)) } } - /// Handle ESC [ escape sequences - fn escape_csi(&mut self) -> Result { + /// Handle \E[ escape sequences + fn escape_csi(&mut self) -> Result { let seq2 = self.next_char()?; if seq2.is_digit(10) { match seq2 { '0' | '9' => { - debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2); - Ok(KeyPress::UnknownEscSeq) + debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2); + Ok(E(K::UnknownEscSeq, M::NONE)) } _ => { // Extended escape, read additional byte. @@ -210,72 +241,85 @@ impl PosixRawReader { let seq3 = self.next_char()?; // Linux console Ok(match seq3 { - 'A' => KeyPress::F(1), - 'B' => KeyPress::F(2), - 'C' => KeyPress::F(3), - 'D' => KeyPress::F(4), - 'E' => KeyPress::F(5), + 'A' => E(K::F(1), M::NONE), + 'B' => E(K::F(2), M::NONE), + 'C' => E(K::F(3), M::NONE), + 'D' => E(K::F(4), M::NONE), + 'E' => E(K::F(5), M::NONE), _ => { - debug!(target: "rustyline", "unsupported esc sequence: ESC [ [ {:?}", seq3); - KeyPress::UnknownEscSeq + debug!(target: "rustyline", "unsupported esc sequence: \\E[[{:?}", seq3); + E(K::UnknownEscSeq, M::NONE) } }) } else { // ANSI Ok(match seq2 { - 'A' => KeyPress::Up, // kcuu1 - 'B' => KeyPress::Down, // kcud1 - 'C' => KeyPress::Right, // kcuf1 - 'D' => KeyPress::Left, // kcub1 - 'F' => KeyPress::End, - 'H' => KeyPress::Home, // khome - 'Z' => KeyPress::BackTab, + UP => E(K::Up, M::NONE), + DOWN => E(K::Down, M::NONE), + RIGHT => E(K::Right, M::NONE), + LEFT => E(K::Left, M::NONE), + //'E' => E(K::, M::), // Ignore + END => E(K::End, M::NONE), + //'G' => E(K::, M::), // Ignore + HOME => E(K::Home, M::NONE), // khome + //'J' => E(K::, M::), // clr_eos + //'K' => E(K::, M::), // clr_eol + //'L' => E(K::, M::), // il1 + //'M' => E(K::, M::), // kmous + //'P' => E(K::Delete, M::NONE), // dch1 + 'Z' => E(K::BackTab, M::NONE), + 'a' => E(K::Up, M::SHIFT), // rxvt: kind or kUP + 'b' => E(K::Down, M::SHIFT), // rxvt: kri or kDN + 'c' => E(K::Right, M::SHIFT), // rxvt + 'd' => E(K::Left, M::SHIFT), // rxvt _ => { - debug!(target: "rustyline", "unsupported esc sequence: ESC [ {:?}", seq2); - KeyPress::UnknownEscSeq + debug!(target: "rustyline", "unsupported esc sequence: \\E[{:?}", seq2); + E(K::UnknownEscSeq, M::NONE) } }) } } - /// Handle ESC [ escape sequences + /// Handle \E[ escape sequences #[allow(clippy::cognitive_complexity)] - fn extended_escape(&mut self, seq2: char) -> Result { + fn extended_escape(&mut self, seq2: char) -> Result { let seq3 = self.next_char()?; if seq3 == '~' { Ok(match seq2 { - '1' | '7' => KeyPress::Home, // tmux, xrvt - '2' => KeyPress::Insert, - '3' => KeyPress::Delete, // kdch1 - '4' | '8' => KeyPress::End, // tmux, xrvt - '5' => KeyPress::PageUp, // kpp - '6' => KeyPress::PageDown, // knp + '1' | RXVT_HOME => E(K::Home, M::NONE), // tmux, xrvt + INSERT => E(K::Insert, M::NONE), + DELETE => E(K::Delete, M::NONE), + '4' | RXVT_END => E(K::End, M::NONE), // tmux, xrvt + PAGE_UP => E(K::PageUp, M::NONE), + PAGE_DOWN => E(K::PageDown, M::NONE), _ => { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {} ~", seq2); - KeyPress::UnknownEscSeq + "unsupported esc sequence: \\E[{}~", seq2); + E(K::UnknownEscSeq, M::NONE) } }) } else if seq3.is_digit(10) { let seq4 = self.next_char()?; if seq4 == '~' { Ok(match (seq2, seq3) { - ('1', '1') => KeyPress::F(1), // rxvt-unicode - ('1', '2') => KeyPress::F(2), // rxvt-unicode - ('1', '3') => KeyPress::F(3), // rxvt-unicode - ('1', '4') => KeyPress::F(4), // rxvt-unicode - ('1', '5') => KeyPress::F(5), // kf5 - ('1', '7') => KeyPress::F(6), // kf6 - ('1', '8') => KeyPress::F(7), // kf7 - ('1', '9') => KeyPress::F(8), // kf8 - ('2', '0') => KeyPress::F(9), // kf9 - ('2', '1') => KeyPress::F(10), // kf10 - ('2', '3') => KeyPress::F(11), // kf11 - ('2', '4') => KeyPress::F(12), // kf12 + ('1', '1') => E(K::F(1), M::NONE), // rxvt-unicode + ('1', '2') => E(K::F(2), M::NONE), // rxvt-unicode + ('1', '3') => E(K::F(3), M::NONE), // rxvt-unicode + ('1', '4') => E(K::F(4), M::NONE), // rxvt-unicode + ('1', '5') => E(K::F(5), M::NONE), // kf5 + ('1', '7') => E(K::F(6), M::NONE), // kf6 + ('1', '8') => E(K::F(7), M::NONE), // kf7 + ('1', '9') => E(K::F(8), M::NONE), // kf8 + ('2', '0') => E(K::F(9), M::NONE), // kf9 + ('2', '1') => E(K::F(10), M::NONE), // kf10 + ('2', '3') => E(K::F(11), M::NONE), // kf11 + ('2', '4') => E(K::F(12), M::NONE), // kf12 + //('6', '2') => KeyCode::ScrollUp, + //('6', '3') => KeyCode::ScrollDown, _ => { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{} ~", seq2, seq3); - KeyPress::UnknownEscSeq + "unsupported esc sequence: \\E[{}{}~", seq2, seq3); + E(K::UnknownEscSeq, M::NONE) } }) } else if seq4 == ';' { @@ -284,37 +328,63 @@ impl PosixRawReader { let seq6 = self.next_char()?; if seq6.is_digit(10) { self.next_char()?; // 'R' expected + Ok(E(K::UnknownEscSeq, M::NONE)) } else if seq6 == 'R' { + Ok(E(K::UnknownEscSeq, M::NONE)) + } else if seq6 == '~' { + Ok(match (seq2, seq3, seq5) { + ('1', '5', CTRL) => E(K::F(5), M::CTRL), + //('1', '5', '6') => E(K::F(17), M::CTRL), + ('1', '7', CTRL) => E(K::F(6), M::CTRL), + //('1', '7', '6') => E(K::F(18), M::CTRL), + ('1', '8', CTRL) => E(K::F(7), M::CTRL), + ('1', '9', CTRL) => E(K::F(8), M::CTRL), + //('1', '9', '6') => E(K::F(19), M::CTRL), + ('2', '0', CTRL) => E(K::F(9), M::CTRL), + //('2', '0', '6') => E(K::F(21), M::CTRL), + ('2', '1', CTRL) => E(K::F(10), M::CTRL), + //('2', '1', '6') => E(K::F(22), M::CTRL), + ('2', '3', CTRL) => E(K::F(11), M::CTRL), + //('2', '3', '6') => E(K::F(23), M::CTRL), + ('2', '4', CTRL) => E(K::F(12), M::CTRL), + //('2', '4', '6') => E(K::F(24), M::CTRL), + _ => { + debug!(target: "rustyline", + "unsupported esc sequence: \\E[{}{};{}~", seq2, seq3, seq5); + E(K::UnknownEscSeq, M::NONE) + } + }) } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{} ; {} {}", seq2, seq3, seq5, seq6); + "unsupported esc sequence: \\E[{}{};{}{}", seq2, seq3, seq5, seq6); + Ok(E(K::UnknownEscSeq, M::NONE)) } } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{} ; {:?}", seq2, seq3, seq5); + "unsupported esc sequence: \\E[{}{};{:?}", seq2, seq3, seq5); + Ok(E(K::UnknownEscSeq, M::NONE)) } - Ok(KeyPress::UnknownEscSeq) } else if seq4.is_digit(10) { let seq5 = self.next_char()?; if seq5 == '~' { Ok(match (seq2, seq3, seq4) { - ('2', '0', '0') => KeyPress::BracketedPasteStart, - ('2', '0', '1') => KeyPress::BracketedPasteEnd, + ('2', '0', '0') => E(K::BracketedPasteStart, M::NONE), + ('2', '0', '1') => E(K::BracketedPasteEnd, M::NONE), _ => { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{}{}~", seq2, seq3, seq4); - KeyPress::UnknownEscSeq + "unsupported esc sequence: \\E[{}{}{}~", seq2, seq3, seq4); + E(K::UnknownEscSeq, M::NONE) } }) } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{}{} {}", seq2, seq3, seq4, seq5); - Ok(KeyPress::UnknownEscSeq) + "unsupported esc sequence: \\E[{}{}{}{}", seq2, seq3, seq4, seq5); + Ok(E(K::UnknownEscSeq, M::NONE)) } } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {}{} {:?}", seq2, seq3, seq4); - Ok(KeyPress::UnknownEscSeq) + "unsupported esc sequence: \\E[{}{}{:?}", seq2, seq3, seq4); + Ok(E(K::UnknownEscSeq, M::NONE)) } } else if seq3 == ';' { let seq4 = self.next_char()?; @@ -322,69 +392,216 @@ impl PosixRawReader { let seq5 = self.next_char()?; if seq5.is_digit(10) { self.next_char()?; // 'R' expected - Ok(KeyPress::UnknownEscSeq) + //('1', '0', UP) => E(K::, M::), // Alt + Shift + Up + Ok(E(K::UnknownEscSeq, M::NONE)) } else if seq2 == '1' { Ok(match (seq4, seq5) { - ('5', 'A') => KeyPress::ControlUp, - ('5', 'B') => KeyPress::ControlDown, - ('5', 'C') => KeyPress::ControlRight, - ('5', 'D') => KeyPress::ControlLeft, - ('2', 'A') => KeyPress::ShiftUp, - ('2', 'B') => KeyPress::ShiftDown, - ('2', 'C') => KeyPress::ShiftRight, - ('2', 'D') => KeyPress::ShiftLeft, + (SHIFT, UP) => E(K::Up, M::SHIFT), // ~ key_sr + (SHIFT, DOWN) => E(K::Down, M::SHIFT), // ~ key_sf + (SHIFT, RIGHT) => E(K::Right, M::SHIFT), + (SHIFT, LEFT) => E(K::Left, M::SHIFT), + (SHIFT, END) => E(K::End, M::SHIFT), // kEND + (SHIFT, HOME) => E(K::Home, M::SHIFT), // kHOM + //('2', 'P') => E(K::F(13), M::NONE), + //('2', 'Q') => E(K::F(14), M::NONE), + //('2', 'S') => E(K::F(16), M::NONE), + (ALT, UP) => E(K::Up, M::ALT), + (ALT, DOWN) => E(K::Down, M::ALT), + (ALT, RIGHT) => E(K::Right, M::ALT), + (ALT, LEFT) => E(K::Left, M::ALT), + (ALT, END) => E(K::End, M::ALT), + (ALT, HOME) => E(K::Home, M::ALT), + (ALT_SHIFT, UP) => E(K::Up, M::ALT_SHIFT), + (ALT_SHIFT, DOWN) => E(K::Down, M::ALT_SHIFT), + (ALT_SHIFT, RIGHT) => E(K::Right, M::ALT_SHIFT), + (ALT_SHIFT, LEFT) => E(K::Left, M::ALT_SHIFT), + (ALT_SHIFT, END) => E(K::End, M::ALT_SHIFT), + (ALT_SHIFT, HOME) => E(K::Home, M::ALT_SHIFT), + (CTRL, UP) => E(K::Up, M::CTRL), + (CTRL, DOWN) => E(K::Down, M::CTRL), + (CTRL, RIGHT) => E(K::Right, M::CTRL), + (CTRL, LEFT) => E(K::Left, M::CTRL), + (CTRL, END) => E(K::End, M::CTRL), + (CTRL, HOME) => E(K::Home, M::CTRL), + (CTRL, 'P') => E(K::F(1), M::CTRL), + (CTRL, 'Q') => E(K::F(2), M::CTRL), + (CTRL, 'S') => E(K::F(4), M::CTRL), + (CTRL, 'p') => E(K::Char('0'), M::CTRL), + (CTRL, 'q') => E(K::Char('1'), M::CTRL), + (CTRL, 'r') => E(K::Char('2'), M::CTRL), + (CTRL, 's') => E(K::Char('3'), M::CTRL), + (CTRL, 't') => E(K::Char('4'), M::CTRL), + (CTRL, 'u') => E(K::Char('5'), M::CTRL), + (CTRL, 'v') => E(K::Char('6'), M::CTRL), + (CTRL, 'w') => E(K::Char('7'), M::CTRL), + (CTRL, 'x') => E(K::Char('8'), M::CTRL), + (CTRL, 'y') => E(K::Char('9'), M::CTRL), + (CTRL_SHIFT, UP) => E(K::Up, M::CTRL_SHIFT), + (CTRL_SHIFT, DOWN) => E(K::Down, M::CTRL_SHIFT), + (CTRL_SHIFT, RIGHT) => E(K::Right, M::CTRL_SHIFT), + (CTRL_SHIFT, LEFT) => E(K::Left, M::CTRL_SHIFT), + (CTRL_SHIFT, END) => E(K::End, M::CTRL_SHIFT), + (CTRL_SHIFT, HOME) => E(K::Home, M::CTRL_SHIFT), + //('6', 'P') => E(K::F(13), M::CTRL), + //('6', 'Q') => E(K::F(14), M::CTRL), + //('6', 'S') => E(K::F(16), M::CTRL), + (CTRL_SHIFT, 'p') => E(K::Char('0'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'q') => E(K::Char('1'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'r') => E(K::Char('2'), M::CTRL_SHIFT), + (CTRL_SHIFT, 's') => E(K::Char('3'), M::CTRL_SHIFT), + (CTRL_SHIFT, 't') => E(K::Char('4'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'u') => E(K::Char('5'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'v') => E(K::Char('6'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'w') => E(K::Char('7'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'x') => E(K::Char('8'), M::CTRL_SHIFT), + (CTRL_SHIFT, 'y') => E(K::Char('9'), M::CTRL_SHIFT), + (CTRL_ALT, UP) => E(K::Up, M::CTRL_ALT), + (CTRL_ALT, DOWN) => E(K::Down, M::CTRL_ALT), + (CTRL_ALT, RIGHT) => E(K::Right, M::CTRL_ALT), + (CTRL_ALT, LEFT) => E(K::Left, M::CTRL_ALT), + (CTRL_ALT, END) => E(K::End, M::CTRL_ALT), + (CTRL_ALT, HOME) => E(K::Home, M::CTRL_ALT), + (CTRL_ALT, 'p') => E(K::Char('0'), M::CTRL_ALT), + (CTRL_ALT, 'q') => E(K::Char('1'), M::CTRL_ALT), + (CTRL_ALT, 'r') => E(K::Char('2'), M::CTRL_ALT), + (CTRL_ALT, 's') => E(K::Char('3'), M::CTRL_ALT), + (CTRL_ALT, 't') => E(K::Char('4'), M::CTRL_ALT), + (CTRL_ALT, 'u') => E(K::Char('5'), M::CTRL_ALT), + (CTRL_ALT, 'v') => E(K::Char('6'), M::CTRL_ALT), + (CTRL_ALT, 'w') => E(K::Char('7'), M::CTRL_ALT), + (CTRL_ALT, 'x') => E(K::Char('8'), M::CTRL_ALT), + (CTRL_ALT, 'y') => E(K::Char('9'), M::CTRL_ALT), + (CTRL_ALT_SHIFT, UP) => E(K::Up, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, DOWN) => E(K::Down, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, RIGHT) => E(K::Right, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, LEFT) => E(K::Left, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, END) => E(K::End, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, HOME) => E(K::Home, M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'p') => E(K::Char('0'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'q') => E(K::Char('1'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'r') => E(K::Char('2'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 's') => E(K::Char('3'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 't') => E(K::Char('4'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'u') => E(K::Char('5'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'v') => E(K::Char('6'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'w') => E(K::Char('7'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'x') => E(K::Char('8'), M::CTRL_ALT_SHIFT), + (CTRL_ALT_SHIFT, 'y') => E(K::Char('9'), M::CTRL_ALT_SHIFT), + // Meta + arrow on (some?) Macs when using iTerm defaults + ('9', UP) => E(K::Up, M::ALT), + ('9', DOWN) => E(K::Down, M::ALT), + ('9', RIGHT) => E(K::Right, M::ALT), + ('9', LEFT) => E(K::Left, M::ALT), + _ => { + debug!(target: "rustyline", + "unsupported esc sequence: \\E[1;{}{:?}", seq4, seq5); + E(K::UnknownEscSeq, M::NONE) + } + }) + } else if seq5 == '~' { + Ok(match (seq2, seq4) { + (INSERT, SHIFT) => E(K::Insert, M::SHIFT), + (INSERT, ALT) => E(K::Insert, M::ALT), + (INSERT, ALT_SHIFT) => E(K::Insert, M::ALT_SHIFT), + (INSERT, CTRL) => E(K::Insert, M::CTRL), + (INSERT, CTRL_SHIFT) => E(K::Insert, M::CTRL_SHIFT), + (INSERT, CTRL_ALT) => E(K::Insert, M::CTRL_ALT), + (INSERT, CTRL_ALT_SHIFT) => E(K::Insert, M::CTRL_ALT_SHIFT), + (DELETE, SHIFT) => E(K::Delete, M::SHIFT), + (DELETE, ALT) => E(K::Delete, M::ALT), + (DELETE, ALT_SHIFT) => E(K::Delete, M::ALT_SHIFT), + (DELETE, CTRL) => E(K::Delete, M::CTRL), + (DELETE, CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT), + (DELETE, CTRL_ALT) => E(K::Delete, M::CTRL_ALT), + (DELETE, CTRL_ALT_SHIFT) => E(K::Delete, M::CTRL_ALT_SHIFT), + (PAGE_UP, SHIFT) => E(K::PageUp, M::SHIFT), + (PAGE_UP, ALT) => E(K::PageUp, M::ALT), + (PAGE_UP, ALT_SHIFT) => E(K::PageUp, M::ALT_SHIFT), + (PAGE_UP, CTRL) => E(K::PageUp, M::CTRL), + (PAGE_UP, CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT), + (PAGE_UP, CTRL_ALT) => E(K::PageUp, M::CTRL_ALT), + (PAGE_UP, CTRL_ALT_SHIFT) => E(K::PageUp, M::CTRL_ALT_SHIFT), + (PAGE_DOWN, SHIFT) => E(K::PageDown, M::SHIFT), + (PAGE_DOWN, ALT) => E(K::PageDown, M::ALT), + (PAGE_DOWN, ALT_SHIFT) => E(K::PageDown, M::ALT_SHIFT), + (PAGE_DOWN, CTRL) => E(K::PageDown, M::CTRL), + (PAGE_DOWN, CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT), + (PAGE_DOWN, CTRL_ALT) => E(K::PageDown, M::CTRL_ALT), + (PAGE_DOWN, CTRL_ALT_SHIFT) => E(K::PageDown, M::CTRL_ALT_SHIFT), _ => { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ 1 ; {} {:?}", seq4, seq5); - KeyPress::UnknownEscSeq + "unsupported esc sequence: \\E[{};{:?}~", seq2, seq4); + E(K::UnknownEscSeq, M::NONE) } }) } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {} ; {} {:?}", seq2, seq4, seq5); - Ok(KeyPress::UnknownEscSeq) + "unsupported esc sequence: \\E[{};{}{:?}", seq2, seq4, seq5); + Ok(E(K::UnknownEscSeq, M::NONE)) } } else { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {} ; {:?}", seq2, seq4); - Ok(KeyPress::UnknownEscSeq) + "unsupported esc sequence: \\E[{};{:?}", seq2, seq4); + Ok(E(K::UnknownEscSeq, M::NONE)) } } else { Ok(match (seq2, seq3) { - ('5', 'A') => KeyPress::ControlUp, - ('5', 'B') => KeyPress::ControlDown, - ('5', 'C') => KeyPress::ControlRight, - ('5', 'D') => KeyPress::ControlLeft, + (DELETE, RXVT_CTRL) => E(K::Delete, M::CTRL), + (DELETE, RXVT_CTRL_SHIFT) => E(K::Delete, M::CTRL_SHIFT), + (CTRL, UP) => E(K::Up, M::CTRL), + (CTRL, DOWN) => E(K::Down, M::CTRL), + (CTRL, RIGHT) => E(K::Right, M::CTRL), + (CTRL, LEFT) => E(K::Left, M::CTRL), + (PAGE_UP, RXVT_CTRL) => E(K::PageUp, M::CTRL), + (PAGE_UP, RXVT_SHIFT) => E(K::PageUp, M::SHIFT), + (PAGE_UP, RXVT_CTRL_SHIFT) => E(K::PageUp, M::CTRL_SHIFT), + (PAGE_DOWN, RXVT_CTRL) => E(K::PageDown, M::CTRL), + (PAGE_DOWN, RXVT_SHIFT) => E(K::PageDown, M::SHIFT), + (PAGE_DOWN, RXVT_CTRL_SHIFT) => E(K::PageDown, M::CTRL_SHIFT), + (RXVT_HOME, RXVT_CTRL) => E(K::Home, M::CTRL), + (RXVT_HOME, RXVT_SHIFT) => E(K::Home, M::SHIFT), + (RXVT_HOME, RXVT_CTRL_SHIFT) => E(K::Home, M::CTRL_SHIFT), + (RXVT_END, RXVT_CTRL) => E(K::End, M::CTRL), // kEND5 or kel + (RXVT_END, RXVT_SHIFT) => E(K::End, M::SHIFT), + (RXVT_END, RXVT_CTRL_SHIFT) => E(K::End, M::CTRL_SHIFT), _ => { debug!(target: "rustyline", - "unsupported esc sequence: ESC [ {} {:?}", seq2, seq3); - KeyPress::UnknownEscSeq + "unsupported esc sequence: \\E[{}{:?}", seq2, seq3); + E(K::UnknownEscSeq, M::NONE) } }) } } - /// Handle ESC O escape sequences - fn escape_o(&mut self) -> Result { + /// Handle \EO escape sequences + fn escape_o(&mut self) -> Result { let seq2 = self.next_char()?; Ok(match seq2 { - 'A' => KeyPress::Up, // kcuu1 - 'B' => KeyPress::Down, // kcud1 - 'C' => KeyPress::Right, // kcuf1 - 'D' => KeyPress::Left, // kcub1 - 'F' => KeyPress::End, // kend - 'H' => KeyPress::Home, // khome - 'P' => KeyPress::F(1), // kf1 - 'Q' => KeyPress::F(2), // kf2 - 'R' => KeyPress::F(3), // kf3 - 'S' => KeyPress::F(4), // kf4 - 'a' => KeyPress::ControlUp, - 'b' => KeyPress::ControlDown, - 'c' => KeyPress::ControlRight, // rxvt - 'd' => KeyPress::ControlLeft, // rxvt + UP => E(K::Up, M::NONE), + DOWN => E(K::Down, M::NONE), + RIGHT => E(K::Right, M::NONE), + LEFT => E(K::Left, M::NONE), + //'E' => E(K::, M::),// key_b2, kb2 + END => E(K::End, M::NONE), // kend + HOME => E(K::Home, M::NONE), // khome + 'M' => E::ENTER, // kent + 'P' => E(K::F(1), M::NONE), // kf1 + 'Q' => E(K::F(2), M::NONE), // kf2 + 'R' => E(K::F(3), M::NONE), // kf3 + 'S' => E(K::F(4), M::NONE), // kf4 + 'a' => E(K::Up, M::CTRL), + 'b' => E(K::Down, M::CTRL), + 'c' => E(K::Right, M::CTRL), // rxvt + 'd' => E(K::Left, M::CTRL), // rxvt + 'l' => E(K::F(8), M::NONE), + 't' => E(K::F(5), M::NONE), // kf5 or kb1 + 'u' => E(K::F(6), M::NONE), // kf6 or kb2 + 'v' => E(K::F(7), M::NONE), // kf7 or kb3 + 'w' => E(K::F(9), M::NONE), // kf9 or ka1 + 'x' => E(K::F(10), M::NONE), // kf10 or ka2 _ => { - debug!(target: "rustyline", "unsupported esc sequence: ESC O {:?}", seq2); - KeyPress::UnknownEscSeq + debug!(target: "rustyline", "unsupported esc sequence: \\EO{:?}", seq2); + E(K::UnknownEscSeq, M::NONE) } }) } @@ -396,11 +613,11 @@ impl PosixRawReader { } impl RawReader for PosixRawReader { - fn next_key(&mut self, single_esc_abort: bool) -> Result { + fn next_key(&mut self, single_esc_abort: bool) -> Result { let c = self.next_char()?; - let mut key = keys::char_to_key_press(c); - if key == KeyPress::Esc { + let mut key = KeyEvent::new(c, M::NONE); + if key == E::ESC { let timeout_ms = if single_esc_abort && self.timeout_ms == -1 { 0 } else { @@ -444,7 +661,7 @@ impl RawReader for PosixRawReader { match self.next_char()? { '\x1b' => { let key = self.escape_sequence()?; - if key == KeyPress::BracketedPasteEnd { + if key == E(K::BracketedPasteEnd, M::NONE) { break; } else { continue; // TODO validate diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 9fa9c6af67..dd652cc6ad 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -1,7 +1,7 @@ //! Windows specific definitions #![allow(clippy::try_err)] // suggested fix does not work (cannot infer...) -use std::io::{self, ErrorKind, Write}; +use std::io::{self, Write}; use std::mem; use std::sync::atomic; @@ -17,7 +17,7 @@ use super::{width, RawMode, RawReader, Renderer, Term}; use crate::config::{BellStyle, ColorMode, Config, OutputStreamType}; use crate::error; use crate::highlight::Highlighter; -use crate::keys::{self, KeyPress}; +use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M}; use crate::layout::{Layout, Position}; use crate::line_buffer::LineBuffer; use crate::Result; @@ -101,7 +101,7 @@ impl ConsoleRawReader { } impl RawReader for ConsoleRawReader { - fn next_key(&mut self, _: bool) -> Result { + fn next_key(&mut self, _: bool) -> Result { use std::char::decode_utf16; use winapi::um::wincon::{ LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, @@ -137,73 +137,51 @@ impl RawReader for ConsoleRawReader { let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED) == (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED); let alt = key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0; - let ctrl = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0; - let meta = alt && !alt_gr; - let shift = key_event.dwControlKeyState & SHIFT_PRESSED != 0; + let mut mods = M::NONE; + if key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0 { + mods |= M::CTRL; + } + if alt && !alt_gr { + mods |= M::ALT; + } + if key_event.dwControlKeyState & SHIFT_PRESSED != 0 { + mods |= M::SHIFT; + } let utf16 = unsafe { *key_event.uChar.UnicodeChar() }; - if utf16 == 0 { - match i32::from(key_event.wVirtualKeyCode) { - winuser::VK_LEFT => { - return Ok(if ctrl { - KeyPress::ControlLeft - } else if shift { - KeyPress::ShiftLeft - } else { - KeyPress::Left - }); - } - winuser::VK_RIGHT => { - return Ok(if ctrl { - KeyPress::ControlRight - } else if shift { - KeyPress::ShiftRight - } else { - KeyPress::Right - }); - } - winuser::VK_UP => { - return Ok(if ctrl { - KeyPress::ControlUp - } else if shift { - KeyPress::ShiftUp - } else { - KeyPress::Up - }); - } - winuser::VK_DOWN => { - return Ok(if ctrl { - KeyPress::ControlDown - } else if shift { - KeyPress::ShiftDown - } else { - KeyPress::Down - }); - } - winuser::VK_DELETE => return Ok(KeyPress::Delete), - winuser::VK_HOME => return Ok(KeyPress::Home), - winuser::VK_END => return Ok(KeyPress::End), - winuser::VK_PRIOR => return Ok(KeyPress::PageUp), - winuser::VK_NEXT => return Ok(KeyPress::PageDown), - winuser::VK_INSERT => return Ok(KeyPress::Insert), - winuser::VK_F1 => return Ok(KeyPress::F(1)), - winuser::VK_F2 => return Ok(KeyPress::F(2)), - winuser::VK_F3 => return Ok(KeyPress::F(3)), - winuser::VK_F4 => return Ok(KeyPress::F(4)), - winuser::VK_F5 => return Ok(KeyPress::F(5)), - winuser::VK_F6 => return Ok(KeyPress::F(6)), - winuser::VK_F7 => return Ok(KeyPress::F(7)), - winuser::VK_F8 => return Ok(KeyPress::F(8)), - winuser::VK_F9 => return Ok(KeyPress::F(9)), - winuser::VK_F10 => return Ok(KeyPress::F(10)), - winuser::VK_F11 => return Ok(KeyPress::F(11)), - winuser::VK_F12 => return Ok(KeyPress::F(12)), - // winuser::VK_BACK is correctly handled because the key_event.UnicodeChar is - // also set. - _ => continue, - }; + let key = if utf16 == 0 { + KeyEvent( + match i32::from(key_event.wVirtualKeyCode) { + winuser::VK_LEFT => K::Left, + winuser::VK_RIGHT => K::Right, + winuser::VK_UP => K::Up, + winuser::VK_DOWN => K::Down, + winuser::VK_DELETE => K::Delete, + winuser::VK_HOME => K::Home, + winuser::VK_END => K::End, + winuser::VK_PRIOR => K::PageUp, + winuser::VK_NEXT => K::PageDown, + winuser::VK_INSERT => K::Insert, + winuser::VK_F1 => K::F(1), + winuser::VK_F2 => K::F(2), + winuser::VK_F3 => K::F(3), + winuser::VK_F4 => K::F(4), + winuser::VK_F5 => K::F(5), + winuser::VK_F6 => K::F(6), + winuser::VK_F7 => K::F(7), + winuser::VK_F8 => K::F(8), + winuser::VK_F9 => K::F(9), + winuser::VK_F10 => K::F(10), + winuser::VK_F11 => K::F(11), + winuser::VK_F12 => K::F(12), + // winuser::VK_BACK is correctly handled because the key_event.UnicodeChar + // is also set. + _ => continue, + }, + mods, + ) } else if utf16 == 27 { - return Ok(KeyPress::Esc); + KeyEvent(K::Esc, mods) } else { if utf16 >= 0xD800 && utf16 < 0xDC00 { surrogate = utf16; @@ -220,18 +198,10 @@ impl RawReader for ConsoleRawReader { return Err(error::ReadlineError::Eof); }; let c = rc?; - if meta { - return Ok(KeyPress::Meta(c)); - } else { - let mut key = keys::char_to_key_press(c); - if key == KeyPress::Tab && shift { - key = KeyPress::BackTab; - } else if key == KeyPress::Char(' ') && ctrl { - key = KeyPress::Ctrl(' '); - } - return Ok(key); - } - } + KeyEvent::new(c, mods) + }; + debug!(target: "rustyline", "key: {:?}", key); + return Ok(key); } }