From 7ad4c679b3cc3fa92b8dfe19b20e8efca1c5c9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fa=C3=AFz=20Hernawan?= <3030950+Abdillah@users.noreply.github.com> Date: Fri, 29 Sep 2023 20:52:34 +0700 Subject: [PATCH] Add kitty protocol config to nushell (#10540) # Description Support keyboard enhancement protocol as implemented by Kitty console, hence Kitty protocol. This PR enables Nushell to use keybinding that is not available before, such as Ctrl+i (that alias to Tab) or Ctrl+e (that alias to Esc, likely I mistaken). After this PR merged and you set `use_kitty_protocol` enabled, if your console app support Kitty protocol (WezTerm, Kitty, etc.) you will be able to set more fine-grained keybinding. For Colemak users, this feature is a blessing, because some Ctrl+[hjkl] that previously unmap-able to Ctlr+[hnei] now it is. # User-Facing Changes This adds `use_kitty_protocol` config which defaults to false. When set to `true`, it enables kitty protocol on the line editor when supported, or else it warns. --------- Co-authored-by: Stefan Holderbach --- .../nu-cli/src/commands/keybindings_listen.rs | 35 +++++++++++++++++++ crates/nu-cli/src/repl.rs | 8 +++++ crates/nu-protocol/src/config.rs | 6 ++++ .../src/sample_config/default_config.nu | 1 + 4 files changed, 50 insertions(+) diff --git a/crates/nu-cli/src/commands/keybindings_listen.rs b/crates/nu-cli/src/commands/keybindings_listen.rs index 96d8f4f321..6f0c780903 100644 --- a/crates/nu-cli/src/commands/keybindings_listen.rs +++ b/crates/nu-cli/src/commands/keybindings_listen.rs @@ -1,3 +1,4 @@ +use crossterm::execute; use crossterm::QueueableCommand; use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal}; use nu_protocol::ast::Call; @@ -69,6 +70,32 @@ pub fn print_events(engine_state: &EngineState) -> Result { stdout().flush()?; terminal::enable_raw_mode()?; + + if config.use_kitty_protocol { + if let Ok(false) = crossterm::terminal::supports_keyboard_enhancement() { + println!("WARN: The terminal doesn't support use_kitty_protocol config.\r"); + } + + // enable kitty protocol + // + // Note that, currently, only the following support this protocol: + // * [kitty terminal](https://sw.kovidgoyal.net/kitty/) + // * [foot terminal](https://codeberg.org/dnkl/foot/issues/319) + // * [WezTerm terminal](https://wezfurlong.org/wezterm/config/lua/config/enable_kitty_keyboard.html) + // * [notcurses library](https://github.com/dankamongmen/notcurses/issues/2131) + // * [neovim text editor](https://github.com/neovim/neovim/pull/18181) + // * [kakoune text editor](https://github.com/mawww/kakoune/issues/4103) + // * [dte text editor](https://gitlab.com/craigbarnes/dte/-/issues/138) + // + // Refer to https://sw.kovidgoyal.net/kitty/keyboard-protocol/ if you're curious. + let _ = execute!( + stdout(), + crossterm::event::PushKeyboardEnhancementFlags( + crossterm::event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES + ) + ); + } + let mut stdout = std::io::BufWriter::new(std::io::stderr()); loop { @@ -95,6 +122,14 @@ pub fn print_events(engine_state: &EngineState) -> Result { stdout.queue(crossterm::style::Print("\r\n"))?; stdout.flush()?; } + + if config.use_kitty_protocol { + let _ = execute!( + std::io::stdout(), + crossterm::event::PopKeyboardEnhancementFlags + ); + } + terminal::disable_raw_mode()?; Ok(Value::nothing(Span::unknown())) diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 908f589ddd..9f34021e00 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -180,6 +180,14 @@ pub fn evaluate_repl( ); } + if engine_state.get_config().use_kitty_protocol { + if line_editor.can_use_kitty_protocol() { + line_editor.enable_kitty_protocol(); + } else { + warn!("Terminal doesn't support use_kitty_protocol config"); + } + } + loop { let loop_start_time = std::time::Instant::now(); diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 0e00d51804..3b3f4297cc 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -115,6 +115,7 @@ pub struct Config { pub datetime_normal_format: Option, pub datetime_table_format: Option, pub error_style: String, + pub use_kitty_protocol: bool, } impl Default for Config { @@ -180,6 +181,8 @@ impl Default for Config { keybindings: Vec::new(), error_style: "fancy".into(), + + use_kitty_protocol: false, } } } @@ -1200,6 +1203,9 @@ impl Value { "bracketed_paste" => { try_bool!(cols, vals, index, span, bracketed_paste); } + "use_kitty_protocol" => { + try_bool!(cols, vals, index, span, use_kitty_protocol); + } // Menus "menus" => match create_menus(value) { Ok(map) => config.menus = map, diff --git a/crates/nu-utils/src/sample_config/default_config.nu b/crates/nu-utils/src/sample_config/default_config.nu index e7e2e4e07f..077a09a56c 100644 --- a/crates/nu-utils/src/sample_config/default_config.nu +++ b/crates/nu-utils/src/sample_config/default_config.nu @@ -239,6 +239,7 @@ $env.config = { edit_mode: emacs # emacs, vi shell_integration: false # enables terminal shell integration. Off by default, as some terminals have issues with this. render_right_prompt_on_last_line: false # true or false to enable or disable right prompt to be rendered on last line of the prompt. + use_kitty_protocol: false # enables keyboard enhancement protocol implemented by kitty console, only if your terminal support this hooks: { pre_prompt: [{ null }] # run before the prompt is shown