From 8725a4077308f1721cd3fa9d11e48d1780c8a315 Mon Sep 17 00:00:00 2001 From: gwenn <45554+gwenn@users.noreply.github.com> Date: Sun, 20 Jun 2021 18:16:27 +0200 Subject: [PATCH] Misc (#538) * \0 is Ctrl-@ not Ctrl-space * Fix Ctrl-d in emacs mode * Bind Ctrl-] to character-search in emacs mode And Ctrl-Alt-] to character-search-backward * Bind Ctrl-X backspace to backward-kill-line in emacs mode --- src/command.rs | 22 +++--- src/edit.rs | 8 -- src/keymap.rs | 188 ++++++++++++++++++++++++--------------------- src/keys.rs | 10 +-- src/tty/windows.rs | 3 +- 5 files changed, 114 insertions(+), 117 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..a2eba2546 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, @@ -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)) - } else { - Cmd::Move(Movement::ForwardChar(n)) - } - } + E(K::Char('B'), M::CTRL) => Cmd::Move(if positive { + Movement::BackwardChar(n) + } else { + 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)) - } else { - Cmd::Move(Movement::BackwardChar(n)) - } - } + E(K::Char('F'), M::CTRL) => Cmd::Move(if positive { + 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 => { - if positive { - Cmd::Kill(Movement::BackwardChar(n)) - } else { - Cmd::Kill(Movement::ForwardChar(n)) - } - } + E(K::Char('H'), M::CTRL) | E::BACKSPACE => Cmd::Kill(if positive { + Movement::BackwardChar(n) + } else { + 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) - } else { - Cmd::Kill(Movement::BeginningOfLine) - } - } + E(K::Char('K'), M::CTRL) => Cmd::Kill(if positive { + 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, @@ -615,47 +607,65 @@ 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, } } } - E(K::Backspace, M::ALT) => { - if positive { - Cmd::Kill(Movement::BackwardWord(n, Word::Emacs)) - } else { - Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) + // 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) => Cmd::Kill(if positive { + 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) => { - if positive { - Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) - } else { - Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) - } - } + | E(K::Left, M::ALT) => Cmd::Move(if positive { + 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) => { - if positive { - Cmd::Kill(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) - } else { - Cmd::Kill(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) => { - if positive { - Cmd::Move(Movement::ForwardWord(n, At::AfterEnd, Word::Emacs)) - } else { - Cmd::Move(Movement::BackwardWord(n, Word::Emacs)) - } - } + | E(K::Right, M::ALT) => Cmd::Move(if positive { + 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. @@ -1022,30 +1032,34 @@ 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) => Cmd::EndOfFile, - E(K::Delete, M::NONE) => { - if positive { - Cmd::Kill(Movement::ForwardChar(n)) + 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::Kill(Movement::BackwardChar(n)) + Cmd::EndOfFile } } + 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, }, @@ -1055,26 +1069,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) 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), } 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');