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 <sholderbach@users.noreply.github.com>
This commit is contained in:
Faïz Hernawan 2023-09-29 20:52:34 +07:00 committed by GitHub
parent 9a0c6f2e02
commit 7ad4c679b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 0 deletions

View File

@ -1,3 +1,4 @@
use crossterm::execute;
use crossterm::QueueableCommand; use crossterm::QueueableCommand;
use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal}; use crossterm::{event::Event, event::KeyCode, event::KeyEvent, terminal};
use nu_protocol::ast::Call; use nu_protocol::ast::Call;
@ -69,6 +70,32 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
stdout().flush()?; stdout().flush()?;
terminal::enable_raw_mode()?; 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()); let mut stdout = std::io::BufWriter::new(std::io::stderr());
loop { loop {
@ -95,6 +122,14 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
stdout.queue(crossterm::style::Print("\r\n"))?; stdout.queue(crossterm::style::Print("\r\n"))?;
stdout.flush()?; stdout.flush()?;
} }
if config.use_kitty_protocol {
let _ = execute!(
std::io::stdout(),
crossterm::event::PopKeyboardEnhancementFlags
);
}
terminal::disable_raw_mode()?; terminal::disable_raw_mode()?;
Ok(Value::nothing(Span::unknown())) Ok(Value::nothing(Span::unknown()))

View File

@ -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 { loop {
let loop_start_time = std::time::Instant::now(); let loop_start_time = std::time::Instant::now();

View File

@ -115,6 +115,7 @@ pub struct Config {
pub datetime_normal_format: Option<String>, pub datetime_normal_format: Option<String>,
pub datetime_table_format: Option<String>, pub datetime_table_format: Option<String>,
pub error_style: String, pub error_style: String,
pub use_kitty_protocol: bool,
} }
impl Default for Config { impl Default for Config {
@ -180,6 +181,8 @@ impl Default for Config {
keybindings: Vec::new(), keybindings: Vec::new(),
error_style: "fancy".into(), error_style: "fancy".into(),
use_kitty_protocol: false,
} }
} }
} }
@ -1200,6 +1203,9 @@ impl Value {
"bracketed_paste" => { "bracketed_paste" => {
try_bool!(cols, vals, index, span, bracketed_paste); try_bool!(cols, vals, index, span, bracketed_paste);
} }
"use_kitty_protocol" => {
try_bool!(cols, vals, index, span, use_kitty_protocol);
}
// Menus // Menus
"menus" => match create_menus(value) { "menus" => match create_menus(value) {
Ok(map) => config.menus = map, Ok(map) => config.menus = map,

View File

@ -239,6 +239,7 @@ $env.config = {
edit_mode: emacs # emacs, vi edit_mode: emacs # emacs, vi
shell_integration: false # enables terminal shell integration. Off by default, as some terminals have issues with this. 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. 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: { hooks: {
pre_prompt: [{ null }] # run before the prompt is shown pre_prompt: [{ null }] # run before the prompt is shown