From d53e2c258de85ec906c856a16eebb26c40e50f83 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 10:35:57 +0200 Subject: [PATCH 1/9] \0 is Ctrl-@ not Ctrl-space --- src/keys.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index 6a250aeae..c9dfa6662 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -25,15 +25,15 @@ impl KeyEvent { } #[allow(clippy::match_same_arms)] match c { - '\x00' => E(K::Char(' '), mods | M::CTRL), + '\x00' => E(K::Char('@'), mods | M::CTRL), // '\0' '\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' + '\x07' => E(K::Char('G'), mods | M::CTRL), // '\a' + '\x08' => E(K::Backspace, mods), // '\b' '\x09' => { // '\t' if mods.contains(M::SHIFT) { @@ -60,12 +60,12 @@ impl KeyEvent { '\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-[ + '\x1b' => E(K::Esc, mods), // Ctrl-[, '\e' '\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 + '\x7f' => E(K::Backspace, mods), // Rubout, Ctrl-? '\u{9b}' => E(K::Esc, mods | M::SHIFT), _ => E(K::Null, mods), } From c3a948d1422b14ec5c8c5d5c7f3224f9258d3700 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 10:38:29 +0200 Subject: [PATCH 2/9] Fix Ctrl-d in emacs mode --- src/command.rs | 22 +++++++++------------- src/edit.rs | 8 -------- src/keymap.rs | 12 +++++++++++- 3 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/command.rs b/src/command.rs index a20d76f3a..4a20272cf 100644 --- a/src/command.rs +++ b/src/command.rs @@ -58,19 +58,15 @@ pub fn execute( s.edit_overwrite_char(c)?; } Cmd::EndOfFile => { - if input_state.is_emacs_mode() && !s.line.is_empty() { - s.edit_delete(1)? - } else { - if s.has_hint() || !s.is_default_prompt() { - // Force a refresh without hints to leave the previous - // line as the user typed it after a newline. - s.refresh_line_with_msg(None)?; - } - if s.line.is_empty() { - return Err(error::ReadlineError::Eof); - } else if !input_state.is_emacs_mode() { - return Ok(Submit); - } + if s.has_hint() || !s.is_default_prompt() { + // Force a refresh without hints to leave the previous + // line as the user typed it after a newline. + s.refresh_line_with_msg(None)?; + } + if s.line.is_empty() { + return Err(error::ReadlineError::Eof); + } else if !input_state.is_emacs_mode() { + return Ok(Submit); } } Cmd::Move(Movement::EndOfLine) => { diff --git a/src/edit.rs b/src/edit.rs index 5f2d0e94e..d3dbdad83 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -482,14 +482,6 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> { self.refresh_line() } - pub fn edit_delete(&mut self, n: RepeatCount) -> Result<()> { - if self.line.delete(n).is_some() { - self.refresh_line() - } else { - Ok(()) - } - } - /// Exchange the char before cursor with the character at cursor. pub fn edit_transpose_chars(&mut self) -> Result<()> { self.changes.borrow_mut().begin(); diff --git a/src/keymap.rs b/src/keymap.rs index bbaccdf27..ca22e91f1 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -1030,7 +1030,17 @@ impl InputState { } } E(K::Char('C'), M::CTRL) => Cmd::Interrupt, - E(K::Char('D'), M::CTRL) => Cmd::EndOfFile, + E(K::Char('D'), M::CTRL) => { + if self.is_emacs_mode() && !wrt.line().is_empty() { + Cmd::Kill(if positive { + Movement::ForwardChar(n) + } else { + Movement::BackwardChar(n) + }) + } else { + Cmd::EndOfFile + } + } E(K::Delete, M::NONE) => { if positive { Cmd::Kill(Movement::ForwardChar(n)) From 0b670c6ac7a5d4f137b29798da82dad3c49b2539 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 10:46:17 +0200 Subject: [PATCH 3/9] Bind Ctrl-] to character-search in emacs mode And Ctrl-Alt-] to character-search-backward --- src/keymap.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index ca22e91f1..6b1c22631 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -245,7 +245,7 @@ pub enum Anchor { Before, } -/// Vi character search +/// character search #[derive(Debug, Clone, PartialEq, Copy)] pub enum CharSearch { /// Forward search @@ -283,7 +283,7 @@ pub enum Movement { BackwardWord(RepeatCount, Word), // Backward until start of word /// forward-word, vi-end-word, vi-next-word ForwardWord(RepeatCount, At, Word), // Forward until start/end of word - /// vi-char-search + /// character-search, character-search-backward, vi-char-search ViCharSearch(RepeatCount, CharSearch), /// vi-first-print ViFirstPrint, @@ -619,6 +619,26 @@ impl InputState { } } } + // character-search, character-search-backward + E(K::Char(']'), m @ M::CTRL) | E(K::Char(']'), m @ M::CTRL_ALT) => { + let ch = rdr.next_key(false)?; + match ch { + E(K::Char(ch), M::NONE) => { + Cmd::Move(Movement::ViCharSearch(n, if positive { + if m.contains(M::ALT) { + CharSearch::Backward(ch) + } else { + CharSearch::ForwardBefore(ch) + } + } else if m.contains(M::ALT) { + CharSearch::ForwardBefore(ch) + } else { + CharSearch::Backward(ch) + })) + } + _ => Cmd::Unknown, + } + } E(K::Backspace, M::ALT) => { if positive { Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) From 124452e16b01f12d46834017aa55eca7a85ecbb8 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 11:21:04 +0200 Subject: [PATCH 4/9] Use a more compact syntax --- src/keymap.rs | 139 +++++++++++++++++++++----------------------------- 1 file changed, 57 insertions(+), 82 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index 6b1c22631..c5be04b67 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -560,29 +560,23 @@ impl InputState { } } E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine), - E(K::Char('B'), M::CTRL) => { - if positive { - Cmd::Move(Movement::BackwardChar(n)) + E(K::Char('B'), M::CTRL) => Cmd::Move(if positive { + Movement::BackwardChar(n) } else { - Cmd::Move(Movement::ForwardChar(n)) - } - } + Movement::ForwardChar(n) + }), E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine), - E(K::Char('F'), M::CTRL) => { - if positive { - Cmd::Move(Movement::ForwardChar(n)) + E(K::Char('F'), M::CTRL) => Cmd::Move(if positive { + Movement::ForwardChar(n) } else { - Cmd::Move(Movement::BackwardChar(n)) - } - } + Movement::BackwardChar(n) + }), 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)) + E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive { + Movement::BackwardChar(n) } else { - Cmd::Kill(Movement::ForwardChar(n)) - } - } + Movement::ForwardChar(n) + }), E(K::BackTab, M::NONE) => Cmd::CompleteBackward, E(K::Tab, M::NONE) => { if positive { @@ -593,13 +587,11 @@ impl InputState { } // Don't complete hints when the cursor is not at the end of a line 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) + E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive { + Movement::EndOfLine } else { - Cmd::Kill(Movement::BeginningOfLine) - } - } + Movement::BeginningOfLine + }), E(K::Char('L'), M::CTRL) => Cmd::ClearScreen, E(K::Char('N'), M::CTRL) => Cmd::NextHistory, E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory, @@ -639,43 +631,36 @@ impl InputState { _ => Cmd::Unknown, } } - E(K::Backspace, M::ALT) => { - if positive { - Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) + E(K::Backspace, M::ALT) => Cmd::Kill(if positive { + Movement::BackwardWord(n, Word::Emacs) } else { - Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) - } - } + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + }), 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) | E(K::Left, M::CTRL) - | E(K::Left, M::ALT) => { - if positive { - Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) + | E(K::Left, M::ALT) => Cmd::Move(if positive { + Movement::BackwardWord(n, Word::Emacs) } else { - Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) - } - } + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + }), 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) => { + E(K::Char('D'), M::ALT) | E(K::Char('d'), M::ALT) => Cmd::Kill( if positive { - Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) } else { - Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) - } - } + Movement::BackwardWord(n, Word::Emacs) + }), E(K::Char('F'), M::ALT) | E(K::Char('f'), M::ALT) | E(K::Right, M::CTRL) - | E(K::Right, M::ALT) => { - if positive { - Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) + | E(K::Right, M::ALT) => Cmd::Move(if positive { + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) } else { - Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) - } - } + Movement::BackwardWord(n, Word::Emacs) + }), 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. @@ -1042,13 +1027,11 @@ impl InputState { ) -> Result { Ok(match key { 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)) - } - } + E(K::Left, M::NONE) => Cmd::Move(if positive { + Movement::BackwardChar(n) + } else { + Movement::ForwardChar(n) + }), E(K::Char('C'), M::CTRL) => Cmd::Interrupt, E(K::Char('D'), M::CTRL) => { if self.is_emacs_mode() && !wrt.line().is_empty() { @@ -1061,21 +1044,17 @@ impl InputState { Cmd::EndOfFile } } - E(K::Delete, M::NONE) => { - if positive { - Cmd::Kill(Movement::ForwardChar(n)) - } else { - Cmd::Kill(Movement::BackwardChar(n)) - } - } + E(K::Delete, M::NONE) => Cmd::Kill(if positive { + Movement::ForwardChar(n) + } else { + Movement::BackwardChar(n) + }), 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)) - } - } + E(K::Right, M::NONE) => Cmd::Move(if positive { + Movement::ForwardChar(n) + } else { + Movement::BackwardChar(n) + }), E(K::Char('J'), M::CTRL) | E::ENTER => Cmd::AcceptOrInsertLine { accept_in_the_middle: true, }, @@ -1085,26 +1064,22 @@ impl InputState { // most terminals override Ctrl+S to suspend execution E(K::Char('S'), M::CTRL) => Cmd::ForwardSearchHistory, 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) - } - } + E(K::Char('U'), M::CTRL) => Cmd::Kill(if positive { + Movement::BeginningOfLine + } else { + Movement::EndOfLine + }), // most terminals override Ctrl+Q to resume execution E(K::Char('Q'), M::CTRL) => Cmd::QuotedInsert, #[cfg(not(windows))] E(K::Char('V'), M::CTRL) => Cmd::QuotedInsert, #[cfg(windows)] E(K::Char('V'), M::CTRL) => Cmd::PasteFromClipboard, - 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)) - } - } + E(K::Char('W'), M::CTRL) => Cmd::Kill(if positive { + Movement::BackwardWord(n, Word::Big) + } else { + Movement::ForwardWord(n, At::AfterEnd, Word::Big) + }), E(K::Char('Y'), M::CTRL) => { if positive { Cmd::Yank(n, Anchor::Before) From 1dff3385fd7f9c2f915d6b6105119da24cd54f29 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 11:22:08 +0200 Subject: [PATCH 5/9] Bind Ctrl-X backspace to backward-kill-line in emacs mode --- src/keymap.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/keymap.rs b/src/keymap.rs index c5be04b67..c6c9aad1e 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -607,6 +607,11 @@ impl InputState { match snd_key { E(K::Char('G'), M::CTRL) | E::ESC => Cmd::Abort, E(K::Char('U'), M::CTRL) => Cmd::Undo(n), + E(K::Backspace, M::NONE) => Cmd::Kill(if positive { + Movement::BeginningOfLine + } else { + Movement::EndOfLine + }), _ => Cmd::Unknown, } } From 9c082c846bd85a0d0df974b4832afb3ec5ceaa8c Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 17:40:02 +0200 Subject: [PATCH 6/9] Clippy --- src/tty/windows.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/tty/windows.rs b/src/tty/windows.rs index 067a2b1d9..16d78b5da 100644 --- a/src/tty/windows.rs +++ b/src/tty/windows.rs @@ -272,7 +272,6 @@ impl ConsoleRenderer { for c in s.graphemes(true) { if c == "\n" { col = 0; - self.buffer.push_str(c); } else { let cw = width(c, &mut esc_seq); col += cw; @@ -280,8 +279,8 @@ impl ConsoleRenderer { self.buffer.push('\n'); col = cw; } - self.buffer.push_str(c); } + self.buffer.push_str(c); } if col == self.cols { self.buffer.push('\n'); From 40900d3d345498daba818fb99c908b7314bf8969 Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 18:01:08 +0200 Subject: [PATCH 7/9] Rustfmt --- src/keymap.rs | 67 +++++++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index c6c9aad1e..6460a50bc 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -561,22 +561,22 @@ impl InputState { } E(K::Char('A'), M::CTRL) => Cmd::Move(Movement::BeginningOfLine), E(K::Char('B'), M::CTRL) => Cmd::Move(if positive { - Movement::BackwardChar(n) - } else { - Movement::ForwardChar(n) - }), + Movement::BackwardChar(n) + } else { + Movement::ForwardChar(n) + }), E(K::Char('E'), M::CTRL) => Cmd::Move(Movement::EndOfLine), E(K::Char('F'), M::CTRL) => Cmd::Move(if positive { - Movement::ForwardChar(n) - } else { - Movement::BackwardChar(n) - }), + Movement::ForwardChar(n) + } else { + Movement::BackwardChar(n) + }), 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 => Cmd::Kill(if positive { - Movement::BackwardChar(n) - } else { - Movement::ForwardChar(n) - }), + Movement::BackwardChar(n) + } else { + Movement::ForwardChar(n) + }), E(K::BackTab, M::NONE) => Cmd::CompleteBackward, E(K::Tab, M::NONE) => { if positive { @@ -588,10 +588,10 @@ impl InputState { // Don't complete hints when the cursor is not at the end of a line E(K::Right, M::NONE) if wrt.has_hint() && wrt.is_cursor_at_end() => Cmd::CompleteHint, E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive { - Movement::EndOfLine - } else { - Movement::BeginningOfLine - }), + Movement::EndOfLine + } else { + Movement::BeginningOfLine + }), E(K::Char('L'), M::CTRL) => Cmd::ClearScreen, E(K::Char('N'), M::CTRL) => Cmd::NextHistory, E(K::Char('P'), M::CTRL) => Cmd::PreviousHistory, @@ -637,35 +637,34 @@ impl InputState { } } E(K::Backspace, M::ALT) => Cmd::Kill(if positive { - Movement::BackwardWord(n, Word::Emacs) - } else { - Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) - }), + Movement::BackwardWord(n, Word::Emacs) + } else { + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + }), 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) | E(K::Left, M::CTRL) | E(K::Left, M::ALT) => Cmd::Move(if positive { - Movement::BackwardWord(n, Word::Emacs) - } else { - Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) - }), + Movement::BackwardWord(n, Word::Emacs) + } else { + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + }), 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) => Cmd::Kill( - if positive { - Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) - } else { - Movement::BackwardWord(n, Word::Emacs) - }), + E(K::Char('D'), M::ALT) | E(K::Char('d'), M::ALT) => Cmd::Kill(if positive { + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + } else { + Movement::BackwardWord(n, Word::Emacs) + }), E(K::Char('F'), M::ALT) | E(K::Char('f'), M::ALT) | E(K::Right, M::CTRL) | E(K::Right, M::ALT) => Cmd::Move(if positive { - Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) - } else { - Movement::BackwardWord(n, Word::Emacs) - }), + Movement::ForwardWord(n, At::AfterEnd, Word::Emacs) + } else { + Movement::BackwardWord(n, Word::Emacs) + }), 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. From 98341b82eb8de8667749231d07e8af236aeeb6fd Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 18:09:46 +0200 Subject: [PATCH 8/9] Rustfmt --- src/keymap.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/keymap.rs b/src/keymap.rs index 6460a50bc..1bef48515 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -620,8 +620,9 @@ impl InputState { E(K::Char(']'), m @ M::CTRL) | E(K::Char(']'), m @ M::CTRL_ALT) => { let ch = rdr.next_key(false)?; match ch { - E(K::Char(ch), M::NONE) => { - Cmd::Move(Movement::ViCharSearch(n, if positive { + E(K::Char(ch), M::NONE) => Cmd::Move(Movement::ViCharSearch( + n, + if positive { if m.contains(M::ALT) { CharSearch::Backward(ch) } else { @@ -631,8 +632,8 @@ impl InputState { CharSearch::ForwardBefore(ch) } else { CharSearch::Backward(ch) - })) - } + } + )), _ => Cmd::Unknown, } } From b6639875be8bb35493be84ff27e9b3539dac258a Mon Sep 17 00:00:00 2001 From: gwenn Date: Sun, 20 Jun 2021 18:12:16 +0200 Subject: [PATCH 9/9] Rustfmt --- src/keymap.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/keymap.rs b/src/keymap.rs index 1bef48515..a2eba2546 100644 --- a/src/keymap.rs +++ b/src/keymap.rs @@ -632,7 +632,7 @@ impl InputState { CharSearch::ForwardBefore(ch) } else { CharSearch::Backward(ch) - } + }, )), _ => Cmd::Unknown, }