diff --git a/crates/nu-cli/src/keybinding.rs b/crates/nu-cli/src/keybinding.rs index 4b7defa40b..50c598b839 100644 --- a/crates/nu-cli/src/keybinding.rs +++ b/crates/nu-cli/src/keybinding.rs @@ -1,63 +1,42 @@ -use rustyline::{KeyCode, Modifiers}; +use rustyline::{KeyCode as RustyKeyCode, Modifiers}; use serde::{Deserialize, Serialize}; -pub fn convert_keyevent(key_event: KeyEvent) -> rustyline::KeyEvent { +pub fn convert_keyevent(key_event: KeyCode, modifiers: Option) -> rustyline::KeyEvent { match key_event { - KeyEvent::UnknownEscSeq => convert_to_rl_keyevent(rustyline::KeyCode::UnknownEscSeq, None), - KeyEvent::Backspace => convert_to_rl_keyevent(rustyline::KeyCode::Backspace, None), - KeyEvent::BackTab => convert_to_rl_keyevent(rustyline::KeyCode::BackTab, None), - KeyEvent::BracketedPasteStart => { - convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteStart, None) + KeyCode::UnknownEscSeq => convert_to_rl_keyevent(RustyKeyCode::UnknownEscSeq, modifiers), + KeyCode::Backspace => convert_to_rl_keyevent(RustyKeyCode::Backspace, modifiers), + KeyCode::BackTab => convert_to_rl_keyevent(RustyKeyCode::BackTab, modifiers), + KeyCode::BracketedPasteStart => { + convert_to_rl_keyevent(RustyKeyCode::BracketedPasteStart, modifiers) } - KeyEvent::BracketedPasteEnd => { - convert_to_rl_keyevent(rustyline::KeyCode::BracketedPasteEnd, None) + KeyCode::BracketedPasteEnd => { + convert_to_rl_keyevent(RustyKeyCode::BracketedPasteEnd, modifiers) } - KeyEvent::Char(c) => convert_to_rl_keyevent(rustyline::KeyCode::Char(c), None), - KeyEvent::ControlDown => { - convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::CTRL)) - } - KeyEvent::ControlLeft => { - convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::CTRL)) - } - KeyEvent::ControlRight => { - convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::CTRL)) - } - KeyEvent::ControlUp => { - convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::CTRL)) - } - KeyEvent::Ctrl(c) => rustyline::KeyEvent::ctrl(c), - KeyEvent::Delete => convert_to_rl_keyevent(rustyline::KeyCode::Delete, None), - KeyEvent::Down => convert_to_rl_keyevent(rustyline::KeyCode::Down, None), - KeyEvent::End => convert_to_rl_keyevent(rustyline::KeyCode::End, None), - KeyEvent::Enter => convert_to_rl_keyevent(rustyline::KeyCode::Enter, None), - KeyEvent::Esc => convert_to_rl_keyevent(rustyline::KeyCode::Esc, None), - KeyEvent::F(u) => convert_to_rl_keyevent(rustyline::KeyCode::F(u), None), - KeyEvent::Home => convert_to_rl_keyevent(rustyline::KeyCode::Home, None), - KeyEvent::Insert => convert_to_rl_keyevent(rustyline::KeyCode::Insert, None), - KeyEvent::Left => convert_to_rl_keyevent(rustyline::KeyCode::Left, None), - KeyEvent::Meta(c) => rustyline::KeyEvent::new(c, Modifiers::NONE), - KeyEvent::Null => convert_to_rl_keyevent(rustyline::KeyCode::Null, None), - KeyEvent::PageDown => convert_to_rl_keyevent(rustyline::KeyCode::PageDown, None), - KeyEvent::PageUp => convert_to_rl_keyevent(rustyline::KeyCode::PageUp, None), - KeyEvent::Right => convert_to_rl_keyevent(rustyline::KeyCode::Right, None), - KeyEvent::ShiftDown => { - convert_to_rl_keyevent(rustyline::KeyCode::Down, Some(Modifiers::SHIFT)) - } - KeyEvent::ShiftLeft => { - convert_to_rl_keyevent(rustyline::KeyCode::Left, Some(Modifiers::SHIFT)) - } - KeyEvent::ShiftRight => { - convert_to_rl_keyevent(rustyline::KeyCode::Right, Some(Modifiers::SHIFT)) - } - KeyEvent::ShiftUp => convert_to_rl_keyevent(rustyline::KeyCode::Up, Some(Modifiers::SHIFT)), - KeyEvent::Tab => convert_to_rl_keyevent(rustyline::KeyCode::Tab, None), - KeyEvent::Up => convert_to_rl_keyevent(rustyline::KeyCode::Up, None), + KeyCode::Char(c) => convert_to_rl_keyevent(RustyKeyCode::Char(c), modifiers), + KeyCode::Delete => convert_to_rl_keyevent(RustyKeyCode::Delete, modifiers), + KeyCode::Down => convert_to_rl_keyevent(RustyKeyCode::Down, modifiers), + KeyCode::End => convert_to_rl_keyevent(RustyKeyCode::End, modifiers), + KeyCode::Enter => convert_to_rl_keyevent(RustyKeyCode::Enter, modifiers), + KeyCode::Esc => convert_to_rl_keyevent(RustyKeyCode::Esc, modifiers), + KeyCode::F(u) => convert_to_rl_keyevent(RustyKeyCode::F(u), modifiers), + KeyCode::Home => convert_to_rl_keyevent(RustyKeyCode::Home, modifiers), + KeyCode::Insert => convert_to_rl_keyevent(RustyKeyCode::Insert, modifiers), + KeyCode::Left => convert_to_rl_keyevent(RustyKeyCode::Left, modifiers), + KeyCode::Null => convert_to_rl_keyevent(RustyKeyCode::Null, modifiers), + KeyCode::PageDown => convert_to_rl_keyevent(RustyKeyCode::PageDown, modifiers), + KeyCode::PageUp => convert_to_rl_keyevent(RustyKeyCode::PageUp, modifiers), + KeyCode::Right => convert_to_rl_keyevent(RustyKeyCode::Right, modifiers), + KeyCode::Tab => convert_to_rl_keyevent(RustyKeyCode::Tab, modifiers), + KeyCode::Up => convert_to_rl_keyevent(RustyKeyCode::Up, modifiers), } } -fn convert_to_rl_keyevent(key_event: KeyCode, modifier: Option) -> rustyline::KeyEvent { +fn convert_to_rl_keyevent( + key_code: RustyKeyCode, + modifier: Option, +) -> rustyline::KeyEvent { rustyline::KeyEvent { - 0: key_event, + 0: key_code, 1: modifier.unwrap_or(Modifiers::NONE), } } @@ -132,12 +111,14 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd { Cmd::Complete => rustyline::Cmd::Complete, Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward, Cmd::CompleteHint => rustyline::Cmd::CompleteHint, + Cmd::Dedent(movement) => rustyline::Cmd::Dedent(convert_movement(movement)), Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord, Cmd::EndOfFile => rustyline::Cmd::EndOfFile, Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory, Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory, Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward, Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward, + Cmd::Indent(movement) => rustyline::Cmd::Indent(convert_movement(movement)), Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string), Cmd::Interrupt => rustyline::Cmd::Interrupt, Cmd::Kill(movement) => rustyline::Cmd::Kill(convert_movement(movement)), @@ -145,8 +126,11 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd { Cmd::LineUpOrPreviousHistory(u) => rustyline::Cmd::LineUpOrPreviousHistory(u), Cmd::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)), Cmd::NextHistory => rustyline::Cmd::NextHistory, + Cmd::Newline => rustyline::Cmd::Newline, Cmd::Noop => rustyline::Cmd::Noop, Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c), + #[cfg(windows)] + Cmd::PasteFromClipboard => rustyline::Cmd::PasteFromClipboard, Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory, Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert, Cmd::Replace { @@ -169,14 +153,28 @@ fn convert_cmd(cmd: Cmd) -> rustyline::Cmd { } fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyEvent, rustyline::Cmd) { + let rusty_modifiers = match keybinding.modifiers { + Some(mods) => match mods { + NuModifiers::CTRL => Some(Modifiers::CTRL), + NuModifiers::ALT => Some(Modifiers::ALT), + NuModifiers::SHIFT => Some(Modifiers::SHIFT), + NuModifiers::NONE => Some(Modifiers::NONE), + NuModifiers::CTRL_SHIFT => Some(Modifiers::CTRL_SHIFT), + NuModifiers::ALT_SHIFT => Some(Modifiers::ALT_SHIFT), + NuModifiers::CTRL_ALT => Some(Modifiers::CTRL_ALT), + NuModifiers::CTRL_ALT_SHIFT => Some(Modifiers::CTRL_ALT_SHIFT), + // _ => None, + }, + None => None, + }; ( - convert_keyevent(keybinding.key), + convert_keyevent(keybinding.key, rusty_modifiers), convert_cmd(keybinding.binding), ) } #[derive(Serialize, Deserialize, Debug, Clone)] -pub enum KeyEvent { +pub enum KeyCode { /// Unsupported escape sequence (on unix platform) UnknownEscSeq, /// ⌫ or `KeyEvent::Ctrl('H')` @@ -189,16 +187,6 @@ pub enum KeyEvent { BracketedPasteEnd, /// Single char Char(char), - /// Ctrl-↓ - ControlDown, - /// Ctrl-← - ControlLeft, - /// Ctrl-→ - ControlRight, - /// Ctrl-↑ - ControlUp, - /// Ctrl-char - Ctrl(char), /// ⌦ Delete, /// ↓ arrow key @@ -217,9 +205,7 @@ pub enum KeyEvent { Insert, /// ← arrow key Left, - /// Escape-char or Alt-char - Meta(char), - /// `KeyEvent::Char('\0')` + // /// `KeyEvent::Char('\0')` Null, /// ⇟ PageDown, @@ -227,14 +213,6 @@ pub enum KeyEvent { PageUp, /// → arrow key Right, - /// Shift-↓ - ShiftDown, - /// Shift-← - ShiftLeft, - /// Shift-→ - ShiftRight, - /// Shift-↑ - ShiftUp, /// ⇥ or `KeyEvent::Ctrl('I')` Tab, /// ↑ arrow key @@ -259,6 +237,8 @@ pub enum Cmd { CompleteBackward, /// complete-hint CompleteHint, + /// Dedent current line + Dedent(Movement), /// downcase-word DowncaseWord, /// vi-eof-maybe @@ -271,6 +251,8 @@ pub enum Cmd { HistorySearchBackward, /// history-search-forward HistorySearchForward, + /// Indent current line + Indent(Movement), /// Insert text Insert { repeat: RepeatCount, string: String }, /// Interrupt signal (Ctrl-C) @@ -283,12 +265,17 @@ pub enum Cmd { /// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word, /// vi-prev-word Move(Movement), + /// Inserts a newline + Newline, /// next-history NextHistory, /// No action Noop, /// vi-replace Overwrite(char), + /// Paste from the clipboard + #[cfg(windows)] + PasteFromClipboard, /// previous-history PreviousHistory, /// quoted-insert @@ -422,12 +409,36 @@ pub enum CharSearch { BackwardAfter(char), } +/// The set of modifier keys that were triggered along with a key press. +#[derive(Serialize, Deserialize)] +#[allow(non_camel_case_types)] +#[allow(clippy::clippy::upper_case_acronyms)] +pub enum NuModifiers { + /// Control modifier + CTRL = 8, + /// Escape or Alt modifier + ALT = 4, + /// Shift modifier + SHIFT = 2, + /// No modifier + NONE = 0, + /// Ctrl + Shift + CTRL_SHIFT = 10, + /// Alt + Shift + ALT_SHIFT = 6, + /// Ctrl + Alt + CTRL_ALT = 12, + /// Ctrl + Alt + Shift + CTRL_ALT_SHIFT = 14, +} + /// The number of times one command should be repeated. pub type RepeatCount = usize; -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize)] pub struct Keybinding { - key: KeyEvent, + key: KeyCode, + modifiers: Option, binding: Cmd, } @@ -442,7 +453,7 @@ pub(crate) fn load_keybindings( // Silently fail if there is no file there if let Ok(contents) = contents { let keybindings: Keybindings = serde_yaml::from_str(&contents)?; - + // eprint!("{}{}{}", keybindings.key, keybindings.mo); for keybinding in keybindings.into_iter() { let (k, b) = convert_keybinding(keybinding); diff --git a/crates/nu-cli/src/line_editor.rs b/crates/nu-cli/src/line_editor.rs index 0f5baa69dd..939ffbf082 100644 --- a/crates/nu-cli/src/line_editor.rs +++ b/crates/nu-cli/src/line_editor.rs @@ -8,7 +8,7 @@ use crate::prelude::*; use nu_engine::script::LineResult; #[cfg(feature = "rustyline-support")] -use crate::keybinding::{convert_keyevent, KeyEvent}; +use crate::keybinding::{convert_keyevent, KeyCode}; #[cfg(feature = "rustyline-support")] use crate::shell::Helper; @@ -20,7 +20,7 @@ use rustyline::{ config::{ColorMode, CompletionType, Config}, error::ReadlineError, line_buffer::LineBuffer, - At, Cmd, ConditionalEventHandler, Editor, EventHandler, Movement, Word, + At, Cmd, ConditionalEventHandler, Editor, EventHandler, Modifiers, Movement, Word, }; #[cfg(feature = "rustyline-support")] @@ -79,18 +79,20 @@ pub fn default_rustyline_editor_configuration() -> Editor { let mut rl: Editor<_> = Editor::with_config(config); // add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight + //M modifier, E KeyEvent, K KeyCode rl.bind_sequence( - convert_keyevent(KeyEvent::ControlLeft), + convert_keyevent(KeyCode::Left, Some(Modifiers::CTRL)), Cmd::Move(Movement::BackwardWord(1, Word::Vi)), ); + rl.bind_sequence( - convert_keyevent(KeyEvent::ControlRight), + convert_keyevent(KeyCode::Right, Some(Modifiers::CTRL)), EventHandler::Conditional(Box::new(PartialCompleteHintHandler)), ); // workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202) rl.bind_sequence( - convert_keyevent(KeyEvent::BracketedPasteStart), + convert_keyevent(KeyCode::BracketedPasteStart, None), rustyline::Cmd::Noop, ); // Let's set the defaults up front and then override them later if the user indicates diff --git a/docs/sample_config/keybindings.yml b/docs/sample_config/keybindings.yml index 6856e8345b..0be23545e9 100644 --- a/docs/sample_config/keybindings.yml +++ b/docs/sample_config/keybindings.yml @@ -3,13 +3,40 @@ # "key:". If you want to change the modifier you should change or add the # modifier after "key:", such as: # key: -# Ctrl: A -# Available modifiers are Ctrl, F (for function), Meta (escape-char, alt-char) +# Char: A +# modifiers: +# CTRL: +# binding: +# blah: +# Available modifiers are CTRL, ALT, SHIFT, NONE, CTRL_SHIFT, +# ALT_SHIFT, CTRL_ALT, CTRL_ALT_SHIFT ########################################################## # Common From https://github.com/kkawakam/rustyline#actions ########################################################## +# Control Backspace, delete 1 word at a time +- key: + Backspace: + modifiers: + CTRL: + binding: + Kill: + BackwardWord: + repeat: 1 + word: Big + +# Alt Backspace, delete 1 word at a time +- key: + Backspace: + modifiers: + ALT: + binding: + Kill: + BackwardWord: + repeat: 1 + word: Big + # Move cursor to the beginning of line - key: Home: @@ -38,19 +65,31 @@ # Complete Hint - key: - ShiftRight: + Right: + modifiers: + SHIFT: + binding: + CompleteHint: +- key: + Char: F + modifiers: + CTRL: binding: CompleteHint: # Interrupt/Cancel edition - key: - Ctrl: C + Char: C + modifiers: + CTRL: binding: Interrupt: # (if line is not empty) Delete character under cursor - key: - Ctrl: D + Char: D + modifiers: + CTRL: binding: EndOfFile: @@ -63,18 +102,24 @@ # Finish the line entry - key: - Ctrl: J + Char: J + modifiers: + CTRL: binding: AcceptLine: - key: - Ctrl: M - binding: - AcceptLine: -- key: - Enter: + Char: M + modifiers: + CTRL: binding: AcceptLine: +# This makes multiline editing stop working since Enter accepts a line +# - key: +# Enter: +# binding: +# AcceptLine: + # Next match from history - key: Down: #Down Arrow Key @@ -89,41 +134,55 @@ # Reverse Search history (Ctrl-S forward, Ctrl-G cancel) - key: - Ctrl: R + Char: R + modifiers: + CTRL: binding: ReverseSearchHistory: # Forward Search history (Ctrl-R backward, Ctrl-G cancel) - key: - Ctrl: S + Char: S + modifiers: + CTRL: binding: ForwardSearchHistory: # Transpose previous character with current character - key: - Ctrl: T + Char: T + modifiers: + CTRL: binding: TransposeChars: # Delete from start of line to cursor - key: - Ctrl: U + Char: U + modifiers: + CTRL: binding: Kill: BeginningOfLine # Insert any special character without performing its associated action (#65) - key: - Ctrl: Q + Char: Q + modifiers: + CTRL: binding: QuotedInsert: - key: - Ctrl: V + Char: V + modifiers: + CTRL: binding: QuotedInsert: # Delete word leading up to cursor (using white space as a word boundary) - key: - Ctrl: W + Char: W + modifiers: + CTRL: binding: Kill: BackwardWord: @@ -132,7 +191,9 @@ # Paste from Yank buffer - key: - Ctrl: Y + Char: Y + modifiers: + CTRL: binding: Yank: repeat: 1 @@ -140,22 +201,25 @@ # Suspend (Unix only) - key: - Ctrl: Z + Char: Z + modifiers: + CTRL: binding: Suspend: # Undo - key: - Ctrl: '_' + Char: "_" + modifiers: + CTRL: binding: Undo: 1 -# KeyEvent::UnknownEscSeq => Cmd::Noop, +# KeyPress::UnknownEscSeq => Cmd::Noop, - key: UnknownEscSeq: binding: Noop: - ########################################################## # Possible options for key: ########################################################## @@ -171,16 +235,6 @@ # BracketedPasteEnd, # /// Single char # Char(char), -# /// Ctrl-↓ -# ControlDown, -# /// Ctrl-← -# ControlLeft, -# /// Ctrl-→ -# ControlRight, -# /// Ctrl-↑ -# ControlUp, -# /// Ctrl-char -# Ctrl(char), # /// ⌦ # Delete, # /// ↓ arrow key @@ -199,9 +253,7 @@ # Insert, # /// ← arrow key # Left, -# /// Escape-char or Alt-char -# Meta(char), -# /// `KeyEvent::Char('\0')` +# // /// `KeyEvent::Char('\0')` # Null, # /// ⇟ # PageDown, @@ -209,14 +261,6 @@ # PageUp, # /// → arrow key # Right, -# /// Shift-↓ -# ShiftDown, -# /// Shift-← -# ShiftLeft, -# /// Shift-→ -# ShiftRight, -# /// Shift-↑ -# ShiftUp, # /// ⇥ or `KeyEvent::Ctrl('I')` # Tab, # /// ↑ arrow key @@ -241,6 +285,8 @@ # CompleteBackward, # /// complete-hint # CompleteHint, +# /// Dedent current line +# Dedent(Movement), # /// downcase-word # DowncaseWord, # /// vi-eof-maybe @@ -253,8 +299,10 @@ # HistorySearchBackward, # /// history-search-forward # HistorySearchForward, +# /// Indent current line +# Indent(Movement), # /// Insert text -# Insert(RepeatCount, String), +# Insert { repeat: RepeatCount, string: String }, # /// Interrupt signal (Ctrl-C) # Interrupt, # /// backward-delete-char, backward-kill-line, backward-kill-word @@ -265,24 +313,32 @@ # /// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word, # /// vi-prev-word # Move(Movement), +# /// Inserts a newline +# Newline, # /// next-history # NextHistory, # /// No action # Noop, # /// vi-replace # Overwrite(char), +# /// Paste from the clipboard +# #[cfg(windows)] +# PasteFromClipboard, # /// previous-history # PreviousHistory, # /// quoted-insert # QuotedInsert, # /// vi-change-char -# ReplaceChar(RepeatCount, char), +# ReplaceChar { repeat: RepeatCount, ch: char }, # /// vi-change-to, vi-substitute -# Replace(Movement, Option), +# Replace { +# movement: Movement, +# replacement: Option, +# }, # /// reverse-search-history # ReverseSearchHistory, # /// self-insert -# SelfInsert(RepeatCount, char), +# SelfInsert { repeat: RepeatCount, ch: char }, # /// Suspend signal (Ctrl-Z on unix platform) # Suspend, # /// transpose-chars @@ -298,7 +354,7 @@ # /// vi-yank-to # ViYankTo(Movement), # /// yank, vi-put -# Yank(RepeatCount, Anchor), +# Yank { repeat: RepeatCount, anchor: Anchor }, # /// yank-pop # YankPop, # /// moves cursor to the line above or switches to prev history entry if