From fb2d427a45071cd1542a1fed722918392db00bc6 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Mon, 31 Mar 2025 20:30:09 +0200 Subject: [PATCH] Remove "bind -k" terminfo key names, update "bind --key-names" I don't think there's a relevant terminal where the "bind -k" notation is still needed. The remaining reason to keep it is backwards compatibility. But "bind -k" is already subtly broken on terminals that implement either of modifyOtherKeys, application keypad mode or the kitty keyboard protocol, since those alter the byte sequences (see #11278). Having it randomly not work might do more harm than good. Remove it. This is meant go into 4.1, which means that users who switch back and forth between 4.1 and 4.0 can already use the new notation. If someone wants to use the bind config for a wider range of versions they could use "bind -k 2>/dev/null" etc. While at it, use the new key names in "bind --key-names", and sort it like we do in "bind --function-names". Closes #11342 --- CHANGELOG.rst | 1 + doc_src/cmds/bind.rst | 10 +- share/completions/bind.fish | 5 +- .../functions/__fish_shared_key_bindings.fish | 17 +- .../functions/fish_default_key_bindings.fish | 7 +- share/functions/fish_vi_key_bindings.fish | 10 - src/builtins/bind.rs | 100 +++----- src/builtins/fish_key_reader.rs | 30 +-- src/input.rs | 144 +---------- src/key.rs | 4 +- src/terminal.rs | 229 ------------------ tests/checks/bind.fish | 3 + tests/checks/tmux-abbr.fish | 2 +- tests/pexpects/bind.py | 2 +- tests/pexpects/cursor_selection.py | 2 +- 15 files changed, 55 insertions(+), 511 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 44cd42335..46c41f581 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,6 +9,7 @@ Notable improvements and fixes Deprecations and removed features --------------------------------- - Tokens like `{ echo, echo }`` in command position are no longer interpreted as brace expansion but as compound command. +- Terminfo-style key names (``bind -k``) are no longer supported. They had been superseded by the native notation. Scripting improvements ---------------------- diff --git a/doc_src/cmds/bind.rst b/doc_src/cmds/bind.rst index 964c2552f..088890ce4 100644 --- a/doc_src/cmds/bind.rst +++ b/doc_src/cmds/bind.rst @@ -11,6 +11,7 @@ Synopsis bind [(-M | --mode) MODE] [--preset] [--user] [KEYS] bind [-a | --all] [--preset] [--user] bind (-f | --function-names) + bind (-K | --key-names) bind (-L | --list-modes) bind (-e | --erase) [(-M | --mode) MODE] [--preset] [--user] [-a | --all] | KEYS ... @@ -72,6 +73,9 @@ The following options are available: **-f** or **--function-names** Display a list of available input functions +-**-K** or **--key-names** + Display a list of available named keys such as ``backspace``. + **-L** or **--list-modes** Display a list of defined bind modes @@ -102,12 +106,6 @@ The following options are available: **-s** or **--silent** Silences some of the error messages, including for unknown key names and unbound sequences. -**-k KEY_NAME** or **--key KEY_NAME** - This looks up KEY_NAME in terminfo and binds that sequence instead of a key that fish would decode. - To view a list of the terminfo keys fish knows about, use ``bind --key-names`` or ``bind -K``. - This is deprecated and provided for compatibility with older fish versions. You should bind the keys directly. - Instead of ``bind -k sright`` use ``bind shift-right``, instead of ``bind -k nul`` use ``bind ctrl-space`` and so on. - **-h** or **--help** Displays help about using this command. diff --git a/share/completions/bind.fish b/share/completions/bind.fish index 3ca324f9b..411e291d8 100644 --- a/share/completions/bind.fish +++ b/share/completions/bind.fish @@ -70,10 +70,7 @@ function __fish_bind_complete printf '%salt-\tAlt modifier…\n' $prefix printf '%sa-\tAlt modifier…\n' $prefix printf '%sshift-\tShift modifier…\n' $prefix - set -l key_names minus comma backspace delete escape \ - enter up down left right pageup pagedown home end insert tab \ - space menu printscreen f(seq 12) - printf '%s\tNamed key\n' $prefix$key_names + printf '%s\tNamed key\n' $prefix(bind --key-names) end end complete -c bind -k -a '(__fish_bind_complete)' -f diff --git a/share/functions/__fish_shared_key_bindings.fish b/share/functions/__fish_shared_key_bindings.fish index 2cb856f1d..a5155c8d0 100644 --- a/share/functions/__fish_shared_key_bindings.fish +++ b/share/functions/__fish_shared_key_bindings.fish @@ -1,5 +1,4 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mode" - set -l legacy_bind bind # These are some bindings that are supposed to be shared between vi mode and default mode. # They are supposed to be unrelated to text-editing (or movement). # This takes $argv so the vi-bindings can pass the mode they are valid in. @@ -17,8 +16,6 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod # Left/Right arrow bind --preset $argv right forward-char bind --preset $argv left backward-char - $legacy_bind --preset $argv -k right forward-char - $legacy_bind --preset $argv -k left backward-char # Ctrl-left/right - these also work in vim. if test (__fish_uname) = Darwin @@ -31,8 +28,6 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv pageup beginning-of-history bind --preset $argv pagedown end-of-history - $legacy_bind --preset $argv -k ppage beginning-of-history - $legacy_bind --preset $argv -k npage end-of-history # Interaction with the system clipboard. bind --preset $argv ctrl-x fish_clipboard_copy @@ -45,19 +40,13 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv ctrl-s pager-toggle-search # shift-tab does a tab complete followed by a search. bind --preset $argv shift-tab complete-and-search - $legacy_bind --preset $argv -k btab complete-and-search bind --preset $argv shift-delete history-delete or backward-delete-char - $legacy_bind --preset $argv -k sdc history-delete or backward-delete-char bind --preset $argv down down-or-search - $legacy_bind --preset $argv -k down down-or-search bind --preset $argv up up-or-search - $legacy_bind --preset $argv -k up up-or-search bind --preset $argv shift-right forward-bigword bind --preset $argv shift-left backward-bigword - $legacy_bind --preset $argv -k sright forward-bigword - $legacy_bind --preset $argv -k sleft backward-bigword set -l alt_right_aliases alt-right \e\[1\;9C # iTerm2 < 3.5.12 set -l alt_left_aliases alt-left \e\[1\;9D # iTerm2 < 3.5.12 @@ -79,8 +68,8 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv alt-up history-token-search-backward bind --preset $argv alt-down history-token-search-forward - $legacy_bind --preset $argv \e\[1\;9A history-token-search-backward # iTerm2 < 3.5.12 - $legacy_bind --preset $argv \e\[1\;9B history-token-search-forward # iTerm2 < 3.5.12 + bind --preset $argv \e\[1\;9A history-token-search-backward # iTerm2 < 3.5.12 + bind --preset $argv \e\[1\;9B history-token-search-forward # iTerm2 < 3.5.12 # Bash compatibility # https://github.com/fish-shell/fish-shell/issues/89 bind --preset $argv alt-. history-token-search-backward @@ -103,7 +92,6 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod # Allow reading manpages by pressing f1 (many GUI applications) or Alt+h (like in zsh). bind --preset $argv f1 __fish_man_page - $legacy_bind --preset $argv -k f1 __fish_man_page bind --preset $argv alt-h __fish_man_page # This will make sure the output of the current command is paged using the default pager when @@ -139,7 +127,6 @@ function __fish_shared_key_bindings -d "Bindings shared between emacs and vi mod bind --preset $argv alt-enter "commandline -i \n $maybe_search_field" expand-abbr bind --preset $argv ")" self-insert expand-abbr # Closing a command substitution. bind --preset $argv ctrl-space 'test -n "$(commandline)" && commandline -i " " '$maybe_search_field - $legacy_bind --preset $argv -k nul 'test -n "$(commandline)" && commandline -i " " '$maybe_search_field # Shift-space behaves like space because it's easy to mistype. bind --preset $argv shift-space 'commandline -i " " '$maybe_search_field expand-abbr diff --git a/share/functions/fish_default_key_bindings.fish b/share/functions/fish_default_key_bindings.fish index dc36354c1..70e2b4b6d 100644 --- a/share/functions/fish_default_key_bindings.fish +++ b/share/functions/fish_default_key_bindings.fish @@ -1,5 +1,4 @@ function fish_default_key_bindings -d "emacs-like key binds" - set -l legacy_bind bind if contains -- -h $argv or contains -- --help $argv echo "Sorry but this function doesn't support -h or --help" @@ -25,17 +24,13 @@ function fish_default_key_bindings -d "emacs-like key binds" bind --preset $argv right forward-char bind --preset $argv left backward-char - $legacy_bind --preset $argv -k right forward-char - $legacy_bind --preset $argv -k left backward-char bind --preset $argv delete delete-char bind --preset $argv backspace backward-delete-char bind --preset $argv shift-backspace backward-delete-char bind --preset $argv home beginning-of-line - $legacy_bind --preset $argv -k home beginning-of-line bind --preset $argv end end-of-line - $legacy_bind --preset $argv -k end end-of-line bind --preset $argv ctrl-a beginning-of-line bind --preset $argv ctrl-e end-of-line @@ -79,6 +74,6 @@ function fish_default_key_bindings -d "emacs-like key binds" case xterm-256color # Microsoft's conemu uses xterm-256color plus # the following to tell a console to paste: - $legacy_bind --preset $argv \e\x20ep fish_clipboard_paste + bind --preset $argv \e\x20ep fish_clipboard_paste end end diff --git a/share/functions/fish_vi_key_bindings.fish b/share/functions/fish_vi_key_bindings.fish index 73690f27e..edde7b30f 100644 --- a/share/functions/fish_vi_key_bindings.fish +++ b/share/functions/fish_vi_key_bindings.fish @@ -1,5 +1,4 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' - set -l legacy_bind bind if contains -- -h $argv or contains -- --help $argv echo "Sorry but this function doesn't support -h or --help" >&2 @@ -110,13 +109,9 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' # Vi/Vim doesn't support these keys in insert mode but that seems silly so we do so anyway. bind -s --preset -M insert home beginning-of-line - $legacy_bind -s --preset -M insert -k home beginning-of-line bind -s --preset -M default home beginning-of-line - $legacy_bind -s --preset -M default -k home beginning-of-line bind -s --preset -M insert end end-of-line - $legacy_bind -s --preset -M insert -k end end-of-line bind -s --preset -M default end end-of-line - $legacy_bind -s --preset -M default -k end end-of-line # Vi moves the cursor back if, after deleting, it is at EOL. # To emulate that, move forward, then backward, which will be a NOP @@ -124,16 +119,12 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' bind -s --preset -M default x delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive' bind -s --preset -M default X backward-delete-char bind -s --preset -M insert delete delete-char forward-single-char backward-char - $legacy_bind -s --preset -M insert -k dc delete-char forward-single-char backward-char bind -s --preset -M default delete delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive' - $legacy_bind -s --preset -M default -k dc delete-char 'set fish_cursor_end_mode exclusive' forward-single-char backward-char 'set fish_cursor_end_mode inclusive' # Backspace deletes a char in insert mode, but not in normal/default mode. bind -s --preset -M insert backspace backward-delete-char bind -s --preset -M insert shift-backspace backward-delete-char - $legacy_bind -s --preset -M insert -k backspace backward-delete-char bind -s --preset -M default backspace backward-char - $legacy_bind -s --preset -M default -k backspace backward-char bind -s --preset -M insert ctrl-h backward-delete-char bind -s --preset -M default ctrl-h backward-char @@ -282,7 +273,6 @@ function fish_vi_key_bindings --description 'vi-like key bindings for fish' # but this binding just move cursor backward, not delete the changes bind -s --preset -M replace backspace backward-char bind -s --preset -M replace shift-backspace backward-char - $legacy_bind -s --preset -M replace -k backspace backward-char # # visual mode diff --git a/src/builtins/bind.rs b/src/builtins/bind.rs index ac89b65f8..7d2664603 100644 --- a/src/builtins/bind.rs +++ b/src/builtins/bind.rs @@ -5,11 +5,10 @@ escape, escape_string, str2wcstring, valid_var_name, EscapeFlags, EscapeStringStyle, }; use crate::highlight::{colorize, highlight_shell}; -use crate::input::{ - input_function_get_names, input_mappings, input_terminfo_get_names, - input_terminfo_get_sequence, GetSequenceError, InputMappingSet, KeyNameStyle, +use crate::input::{input_function_get_names, input_mappings, InputMappingSet, KeyNameStyle}; +use crate::key::{ + self, char_to_symbol, function_key, parse_keys, Key, Modifiers, KEY_NAMES, MAX_FUNCTION_KEY, }; -use crate::key::{self, canonicalize_raw_escapes, char_to_symbol, parse_keys, Key, Modifiers}; use crate::nix::isatty; use std::sync::MutexGuard; @@ -26,7 +25,6 @@ struct Options { list_modes: bool, print_help: bool, silent: bool, - use_terminfo: bool, have_user: bool, user: bool, have_preset: bool, @@ -44,7 +42,6 @@ fn new() -> Options { list_modes: false, print_help: false, silent: false, - use_terminfo: false, have_user: false, user: false, have_preset: false, @@ -144,11 +141,6 @@ fn list_one( } } } - KeyNameStyle::Terminfo(tname) => { - // Note that we show -k here because we have an input key name. - out.push_str("-k "); - out.push_utfstr(&tname); - } } // Now show the list of commands. @@ -203,13 +195,18 @@ fn list(&self, bind_mode: Option<&wstr>, user: bool, parser: &Parser, streams: & } } - /// Print terminfo key binding names to string buffer used for standard output. - /// - /// \param all if set, all terminfo key binding names will be printed. If not set, only ones that - /// are defined for this terminal are printed. - fn key_names(&self, all: bool, streams: &mut IoStreams) { - let names = input_terminfo_get_names(!all); - for name in names { + /// Print all named keys to the string buffer used for standard output. + fn key_names(&self, streams: &mut IoStreams) { + let function_keys: Vec = (1..=MAX_FUNCTION_KEY) + .map(|i| WString::from(Key::from_raw(function_key(i)))) + .collect(); + let mut keys: Vec<&wstr> = KEY_NAMES + .iter() + .map(|(_encoding, name)| *name) + .chain(function_keys.iter().map(|s| s.as_utfstr())) + .collect(); + keys.sort_unstable(); + for name in keys { streams.out.appendln(name); } } @@ -222,31 +219,6 @@ fn function_names(&self, streams: &mut IoStreams) { } } - /// Wraps input_terminfo_get_sequence(), appending the correct error messages as needed. - fn get_terminfo_sequence(&self, seq: &wstr, streams: &mut IoStreams) -> Option { - match input_terminfo_get_sequence(seq) { - Ok(tseq) => Some(tseq), - Err(err) if !self.opts.silent => { - let eseq = - escape_string(seq, EscapeStringStyle::Script(EscapeFlags::NO_PRINTABLES)); - match err { - GetSequenceError::NotFound => streams.err.append(wgettext_fmt!( - "%ls: No key with name '%ls' found\n", - "bind", - eseq - )), - GetSequenceError::NoSeq => streams.err.append(wgettext_fmt!( - "%ls: Key with name '%ls' does not have any mapping\n", - "bind", - eseq - )), - }; - None - } - Err(_) => None, - } - } - /// Add specified key binding. fn add( &mut self, @@ -262,9 +234,7 @@ fn add( let Some(key_seq) = self.compute_seq(streams, seq) else { return true; }; - let key_name_style = if self.opts.use_terminfo { - KeyNameStyle::Terminfo(seq.to_owned()) - } else if is_raw_escape_sequence { + let key_name_style = if is_raw_escape_sequence { KeyNameStyle::RawEscapeSequence } else { KeyNameStyle::Plain @@ -275,21 +245,11 @@ fn add( } fn compute_seq(&self, streams: &mut IoStreams, seq: &wstr) -> Option> { - if self.opts.use_terminfo { - let Some(tinfo_seq) = self.get_terminfo_sequence(seq, streams) else { - // get_terminfo_sequence already printed the error. - return None; - }; - Some(canonicalize_raw_escapes( - tinfo_seq.chars().map(Key::from_single_char).collect(), - )) - } else { - match parse_keys(seq) { - Ok(keys) => Some(keys), - Err(err) => { - streams.err.append(sprintf!("bind: %s\n", err)); - None - } + match parse_keys(seq) { + Ok(keys) => Some(keys), + Err(err) => { + streams.err.append(sprintf!("bind: %s\n", err)); + None } } } @@ -385,13 +345,7 @@ fn insert( EscapeStringStyle::Script(EscapeFlags::NO_PRINTABLES), ); if !self.opts.silent { - if self.opts.use_terminfo { - streams.err.append(wgettext_fmt!( - "%ls: No binding found for key '%ls'\n", - cmd, - eseq - )); - } else if seq.len() == 1 { + if seq.len() == 1 { streams.err.append(wgettext_fmt!( "%ls: No binding found for key '%ls'\n", cmd, @@ -473,7 +427,13 @@ fn parse_cmd_opts( 'e' => opts.mode = BIND_ERASE, 'f' => opts.mode = BIND_FUNCTION_NAMES, 'h' => opts.print_help = true, - 'k' => opts.use_terminfo = true, + 'k' => { + streams.err.append(wgettext_fmt!( + "%ls: the -k/--key syntax is no longer supported. See `bind --help` and `bind --key-names`\n", + cmd, + )); + return Err(STATUS_INVALID_ARGS); + } 'K' => opts.mode = BIND_KEY_NAMES, 'L' => { opts.list_modes = true; @@ -583,7 +543,7 @@ pub fn bind( return Err(STATUS_CMD_ERROR); } } - BIND_KEY_NAMES => self.key_names(self.opts.all, streams), + BIND_KEY_NAMES => self.key_names(streams), BIND_FUNCTION_NAMES => self.function_names(streams), _ => { streams diff --git a/src/builtins/fish_key_reader.rs b/src/builtins/fish_key_reader.rs index 9ccfff3c6..2881bfc1c 100644 --- a/src/builtins/fish_key_reader.rs +++ b/src/builtins/fish_key_reader.rs @@ -17,7 +17,6 @@ builtins::shared::BUILTIN_ERR_UNKNOWN, common::{shell_modes, str2wcstring, PROGRAM_NAME}, env::env_init, - input::input_terminfo_get_name, input_common::{ kitty_progressive_enhancements_query, terminal_protocol_hacks, terminal_protocols_enable_ifn, Capability, CharEvent, ImplicitEvent, InputEventQueue, @@ -69,30 +68,11 @@ fn should_exit(streams: &mut IoStreams, recent_keys: &mut Vec, key: Key) -> tail.clone().eq("exit".chars()) || tail.eq("quit".chars()) } -/// Return the name if the recent sequence of characters matches a known terminfo sequence. -fn sequence_name(recent_chars: &mut Vec, c: char) -> Option { - if c >= '\u{80}' { - // Terminfo sequences are always ASCII. - recent_chars.clear(); - return None; - } - - let c = c as u8; - recent_chars.push(c); - while recent_chars.len() > 8 { - recent_chars.remove(0); - } - - // The entire sequence needs to match the sequence, or else we would output substrings. - input_terminfo_get_name(&str2wcstring(recent_chars)) -} - /// Process the characters we receive as the user presses keys. fn process_input(streams: &mut IoStreams, continuous_mode: bool, verbose: bool) -> BuiltinResult { let mut first_char_seen = false; let mut queue = InputEventQueue::new(STDIN_FILENO); - let mut recent_chars1 = vec![]; - let mut recent_chars2 = vec![]; + let mut recent_chars = vec![]; streams.err.appendln("Press a key:\n"); while (!first_char_seen || continuous_mode) && !check_exit_loop_maybe_warning(None) { @@ -110,7 +90,6 @@ fn process_input(streams: &mut IoStreams, continuous_mode: bool, verbose: bool) } CharEvent::Implicit(_) => continue, }; - let c = kevt.key.codepoint; if verbose { streams.out.append(L!("# decoded from: ")); for byte in kevt.seq.chars() { @@ -121,13 +100,8 @@ fn process_input(streams: &mut IoStreams, continuous_mode: bool, verbose: bool) streams .out .append(sprintf!("bind %s 'do something'\n", kevt.key)); - if let Some(name) = sequence_name(&mut recent_chars1, c) { - streams - .out - .append(sprintf!("bind -k %ls 'do something'\n", name)); - } - if continuous_mode && should_exit(streams, &mut recent_chars2, kevt.key) { + if continuous_mode && should_exit(streams, &mut recent_chars, kevt.key) { streams.err.appendln("\nExiting at your request."); break; } diff --git a/src/input.rs b/src/input.rs index f4a4650bd..a66d7ed15 100644 --- a/src/input.rs +++ b/src/input.rs @@ -5,6 +5,7 @@ // Polyfill for Option::is_none_or(), stabilized in 1.82.0 #[allow(unused_imports)] use crate::future::IsSomeAnd; +use crate::global_safety::RelaxedAtomicBool; use crate::input_common::{ BlockingWait, CharEvent, CharInputStyle, CursorPositionWait, ImplicitEvent, InputData, InputEventQueuer, ReadlineCmd, R_END_INPUT_FUNCTIONS, @@ -17,11 +18,9 @@ reader_reading_interrupted, reader_reset_interrupted, reader_schedule_prompt_repaint, Reader, }; use crate::signal::signal_clear_cancel; -use crate::terminal; use crate::threads::{assert_is_main_thread, iothread_service_main}; use crate::wchar::prelude::*; -use once_cell::sync::{Lazy, OnceCell}; -use std::ffi::CString; +use once_cell::sync::Lazy; use std::sync::{ atomic::{AtomicU32, Ordering}, Mutex, MutexGuard, @@ -43,7 +42,6 @@ pub struct InputMappingName { pub enum KeyNameStyle { Plain, RawEscapeSequence, - Terminfo(WString), } /// Struct representing a keybinding. Returned by input_get_mappings. @@ -59,7 +57,7 @@ pub struct InputMapping { mode: WString, /// New mode that should be switched to after command evaluation, or None to leave the mode unchanged. sets_mode: Option, - /// Perhaps this binding was created using a raw escape sequence or terminfo. + /// Perhaps this binding was created using a raw escape sequence. key_name_style: KeyNameStyle, } @@ -94,16 +92,6 @@ fn is_generic(&self) -> bool { } } -/// A struct representing the mapping from a terminfo key name to a terminfo character sequence. -#[derive(Debug)] -struct TerminfoMapping { - // name of key - name: &'static wstr, - - // character sequence generated on keypress, or none if there was no mapping. - seq: Option>, -} - /// Input function metadata. This list should be kept in sync with the key code list in /// input_common.rs. struct InputFunctionMetadata { @@ -259,9 +247,6 @@ pub fn input_mappings() -> MutexGuard<'static, InputMappingSet> { INPUT_MAPPINGS.lock().unwrap() } -/// Terminfo map list. -static TERMINFO_MAPPINGS: OnceCell> = OnceCell::new(); - /// Return the current bind mode. fn input_get_bind_mode(vars: &dyn Environment) -> WString { if let Some(mode) = vars.get(FISH_BIND_MODE_VAR) { @@ -348,10 +333,11 @@ pub fn add1( /// initializations for our input subsystem. pub fn init_input() { assert_is_main_thread(); - if TERMINFO_MAPPINGS.get().is_some() { + + static DONE: RelaxedAtomicBool = RelaxedAtomicBool::new(false); + if DONE.swap(true) { return; } - TERMINFO_MAPPINGS.set(create_input_terminfo()).unwrap(); let mut input_mapping = input_mappings(); @@ -1022,124 +1008,6 @@ pub fn get<'a>( } } -/// Create a list of terminfo mappings. -fn create_input_terminfo() -> Box<[TerminfoMapping]> { - let Some(term) = terminal::term() else { - // loading terminfo failed so we can't reference any key definitions. - return Box::new([]); - }; - - // Helper to convert an Option to an Option>. - // The nul-terminator is NOT included. - fn opt_cstr_to_bytes(opt: &Option) -> Option> { - opt.clone().map(|s| s.into_bytes().into()) - } - - macro_rules! terminfo_add { - ($key:ident) => { - TerminfoMapping { - name: &L!(stringify!($key))[4..], - seq: opt_cstr_to_bytes(&term.$key), - } - }; - } - #[rustfmt::skip] - return Box::new([ - terminfo_add!(key_a1), terminfo_add!(key_a3), terminfo_add!(key_b2), - terminfo_add!(key_backspace), terminfo_add!(key_beg), terminfo_add!(key_btab), - terminfo_add!(key_c1), terminfo_add!(key_c3), terminfo_add!(key_cancel), - terminfo_add!(key_catab), terminfo_add!(key_clear), terminfo_add!(key_close), - terminfo_add!(key_command), terminfo_add!(key_copy), terminfo_add!(key_create), - terminfo_add!(key_ctab), terminfo_add!(key_dc), terminfo_add!(key_dl), terminfo_add!(key_down), - terminfo_add!(key_eic), terminfo_add!(key_end), terminfo_add!(key_enter), - terminfo_add!(key_eol), terminfo_add!(key_eos), terminfo_add!(key_exit), terminfo_add!(key_f0), - terminfo_add!(key_f1), terminfo_add!(key_f2), terminfo_add!(key_f3), terminfo_add!(key_f4), - terminfo_add!(key_f5), terminfo_add!(key_f6), terminfo_add!(key_f7), terminfo_add!(key_f8), - terminfo_add!(key_f9), terminfo_add!(key_f10), terminfo_add!(key_f11), terminfo_add!(key_f12), - terminfo_add!(key_find), terminfo_add!(key_help), terminfo_add!(key_home), - terminfo_add!(key_ic), terminfo_add!(key_il), terminfo_add!(key_left), terminfo_add!(key_ll), - terminfo_add!(key_mark), terminfo_add!(key_message), terminfo_add!(key_move), - terminfo_add!(key_next), terminfo_add!(key_npage), terminfo_add!(key_open), - terminfo_add!(key_options), terminfo_add!(key_ppage), terminfo_add!(key_previous), - terminfo_add!(key_print), terminfo_add!(key_redo), terminfo_add!(key_reference), - terminfo_add!(key_refresh), terminfo_add!(key_replace), terminfo_add!(key_restart), - terminfo_add!(key_resume), terminfo_add!(key_right), terminfo_add!(key_save), - terminfo_add!(key_sbeg), terminfo_add!(key_scancel), terminfo_add!(key_scommand), - terminfo_add!(key_scopy), terminfo_add!(key_screate), terminfo_add!(key_sdc), - terminfo_add!(key_sdl), terminfo_add!(key_select), terminfo_add!(key_send), - terminfo_add!(key_seol), terminfo_add!(key_sexit), terminfo_add!(key_sf), - terminfo_add!(key_sfind), terminfo_add!(key_shelp), terminfo_add!(key_shome), - terminfo_add!(key_sic), terminfo_add!(key_sleft), terminfo_add!(key_smessage), - terminfo_add!(key_smove), terminfo_add!(key_snext), terminfo_add!(key_soptions), - terminfo_add!(key_sprevious), terminfo_add!(key_sprint), terminfo_add!(key_sr), - terminfo_add!(key_sredo), terminfo_add!(key_sreplace), terminfo_add!(key_sright), - terminfo_add!(key_srsume), terminfo_add!(key_ssave), terminfo_add!(key_ssuspend), - terminfo_add!(key_stab), terminfo_add!(key_sundo), terminfo_add!(key_suspend), - terminfo_add!(key_undo), terminfo_add!(key_up), - - // We introduce our own name for the string containing only the nul character - see - // #3189. This can typically be generated via control-space. - TerminfoMapping { name: NUL_MAPPING_NAME, seq: Some(Box::new([0])) }, - ]); -} - -/// Possible errors from from input_terminfo_get_sequence. -pub enum GetSequenceError { - /// The mapping was not found. - NotFound, - /// The terminfo variable does not have a value. - NoSeq, -} - -/// Return the sequence for the terminfo variable of the specified name. -/// -/// If no terminfo variable of the specified name could be found, return false and set errno to -/// ENOENT. If the terminfo variable does not have a value, return false and set errno to EILSEQ. -pub fn input_terminfo_get_sequence(name: &wstr) -> Result { - let mappings = TERMINFO_MAPPINGS - .get() - .expect("TERMINFO_MAPPINGS not initialized"); - for m in mappings.iter() { - if name == m.name { - // Found the mapping. - return if let Some(seq) = &m.seq { - Ok(str2wcstring(seq)) - } else { - Err(GetSequenceError::NoSeq) - }; - } - } - Err(GetSequenceError::NotFound) -} - -/// Return the name of the terminfo variable with the specified sequence. -pub fn input_terminfo_get_name(seq: &wstr) -> Option { - let mappings = TERMINFO_MAPPINGS - .get() - .expect("TERMINFO_MAPPINGS not initialized"); - for m in mappings.iter() { - if m.seq.is_some() && seq == str2wcstring(m.seq.as_ref().unwrap()) { - return Some(m.name.to_owned()); - } - } - None -} - -/// Return a list of all known terminfo names. -pub fn input_terminfo_get_names(skip_null: bool) -> Vec { - let mappings = TERMINFO_MAPPINGS - .get() - .expect("TERMINFO_MAPPINGS not initialized"); - let mut result = Vec::with_capacity(mappings.len()); - for m in mappings.iter() { - if skip_null && m.seq.is_none() { - continue; - } - result.push(m.name.to_owned()); - } - result -} - /// Returns a list of all existing input function names. pub fn input_function_get_names() -> Vec<&'static wstr> { // Note: the C++ cached this, but we don't to save memory. diff --git a/src/key.rs b/src/key.rs index 93e4479bc..8568b18a8 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,7 +6,7 @@ flog::FloggableDebug, reader::TERMINAL_MODE_ON_STARTUP, wchar::{decode_byte_from_char, prelude::*}, - wutil::{fish_is_pua, fish_wcstoi}, + wutil::{fish_is_pua, fish_wcstoul}, }; pub(crate) const Backspace: char = '\u{F500}'; // below ENCODE_DIRECT_BASE @@ -33,7 +33,7 @@ pub(crate) fn function_key(n: u32) -> char { } pub(crate) const Invalid: char = '\u{F5FF}'; -const KEY_NAMES: &[(char, &wstr)] = &[ +pub(crate) const KEY_NAMES: &[(char, &wstr)] = &[ ('-', L!("minus")), (',', L!("comma")), (Backspace, L!("backspace")), diff --git a/src/terminal.rs b/src/terminal.rs index 95ab30f96..4942ba056 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -78,108 +78,6 @@ pub struct Term { // Flag/boolean capabilities pub eat_newline_glitch: bool, pub auto_right_margin: bool, - - // Keys. - pub key_a1: Option, - pub key_a3: Option, - pub key_b2: Option, - pub key_backspace: Option, - pub key_beg: Option, - pub key_btab: Option, - pub key_c1: Option, - pub key_c3: Option, - pub key_cancel: Option, - pub key_catab: Option, - pub key_clear: Option, - pub key_close: Option, - pub key_command: Option, - pub key_copy: Option, - pub key_create: Option, - pub key_ctab: Option, - pub key_dc: Option, - pub key_dl: Option, - pub key_down: Option, - pub key_eic: Option, - pub key_end: Option, - pub key_enter: Option, - pub key_eol: Option, - pub key_eos: Option, - pub key_exit: Option, - pub key_f0: Option, - pub key_f1: Option, - pub key_f2: Option, - pub key_f3: Option, - pub key_f4: Option, - pub key_f5: Option, - pub key_f6: Option, - pub key_f7: Option, - pub key_f8: Option, - pub key_f9: Option, - pub key_f10: Option, - pub key_f11: Option, - pub key_f12: Option, - // Note key_f21 through key_f63 are available but no actual keyboard supports them. - // key_f13 and up are also basically unused and not supported by key.rs - pub key_find: Option, - pub key_help: Option, - pub key_home: Option, - pub key_ic: Option, - pub key_il: Option, - pub key_left: Option, - pub key_ll: Option, - pub key_mark: Option, - pub key_message: Option, - pub key_move: Option, - pub key_next: Option, - pub key_npage: Option, - pub key_open: Option, - pub key_options: Option, - pub key_ppage: Option, - pub key_previous: Option, - pub key_print: Option, - pub key_redo: Option, - pub key_reference: Option, - pub key_refresh: Option, - pub key_replace: Option, - pub key_restart: Option, - pub key_resume: Option, - pub key_right: Option, - pub key_save: Option, - pub key_sbeg: Option, - pub key_scancel: Option, - pub key_scommand: Option, - pub key_scopy: Option, - pub key_screate: Option, - pub key_sdc: Option, - pub key_sdl: Option, - pub key_select: Option, - pub key_send: Option, - pub key_seol: Option, - pub key_sexit: Option, - pub key_sf: Option, - pub key_sfind: Option, - pub key_shelp: Option, - pub key_shome: Option, - pub key_sic: Option, - pub key_sleft: Option, - pub key_smessage: Option, - pub key_smove: Option, - pub key_snext: Option, - pub key_soptions: Option, - pub key_sprevious: Option, - pub key_sprint: Option, - pub key_sr: Option, - pub key_sredo: Option, - pub key_sreplace: Option, - pub key_sright: Option, - pub key_srsume: Option, - pub key_ssave: Option, - pub key_ssuspend: Option, - pub key_stab: Option, - pub key_sundo: Option, - pub key_suspend: Option, - pub key_undo: Option, - pub key_up: Option, } impl Term { @@ -227,106 +125,6 @@ fn new(db: terminfo::Database) -> Self { // Flag/boolean capabilities eat_newline_glitch: get_flag_cap(&db, "xn"), auto_right_margin: get_flag_cap(&db, "am"), - - // Keys. See `man terminfo` for these strings. - key_a1: get_str_cap(&db, "K1"), - key_a3: get_str_cap(&db, "K3"), - key_b2: get_str_cap(&db, "K2"), - key_backspace: get_str_cap(&db, "kb"), - key_beg: get_str_cap(&db, "@1"), - key_btab: get_str_cap(&db, "kB"), - key_c1: get_str_cap(&db, "K4"), - key_c3: get_str_cap(&db, "K5"), - key_cancel: get_str_cap(&db, "@2"), - key_catab: get_str_cap(&db, "ka"), - key_clear: get_str_cap(&db, "kC"), - key_close: get_str_cap(&db, "@3"), - key_command: get_str_cap(&db, "@4"), - key_copy: get_str_cap(&db, "@5"), - key_create: get_str_cap(&db, "@6"), - key_ctab: get_str_cap(&db, "kt"), - key_dc: get_str_cap(&db, "kD"), - key_dl: get_str_cap(&db, "kL"), - key_down: get_str_cap(&db, "kd"), - key_eic: get_str_cap(&db, "kM"), - key_end: get_str_cap(&db, "@7"), - key_enter: get_str_cap(&db, "@8"), - key_eol: get_str_cap(&db, "kE"), - key_eos: get_str_cap(&db, "kS"), - key_exit: get_str_cap(&db, "@9"), - key_f0: get_str_cap(&db, "k0"), - key_f1: get_str_cap(&db, "k1"), - key_f10: get_str_cap(&db, "k;"), - key_f11: get_str_cap(&db, "F1"), - key_f12: get_str_cap(&db, "F2"), - key_f2: get_str_cap(&db, "k2"), - key_f3: get_str_cap(&db, "k3"), - key_f4: get_str_cap(&db, "k4"), - key_f5: get_str_cap(&db, "k5"), - key_f6: get_str_cap(&db, "k6"), - key_f7: get_str_cap(&db, "k7"), - key_f8: get_str_cap(&db, "k8"), - key_f9: get_str_cap(&db, "k9"), - key_find: get_str_cap(&db, "@0"), - key_help: get_str_cap(&db, "%1"), - key_home: get_str_cap(&db, "kh"), - key_ic: get_str_cap(&db, "kI"), - key_il: get_str_cap(&db, "kA"), - key_left: get_str_cap(&db, "kl"), - key_ll: get_str_cap(&db, "kH"), - key_mark: get_str_cap(&db, "%2"), - key_message: get_str_cap(&db, "%3"), - key_move: get_str_cap(&db, "%4"), - key_next: get_str_cap(&db, "%5"), - key_npage: get_str_cap(&db, "kN"), - key_open: get_str_cap(&db, "%6"), - key_options: get_str_cap(&db, "%7"), - key_ppage: get_str_cap(&db, "kP"), - key_previous: get_str_cap(&db, "%8"), - key_print: get_str_cap(&db, "%9"), - key_redo: get_str_cap(&db, "%0"), - key_reference: get_str_cap(&db, "&1"), - key_refresh: get_str_cap(&db, "&2"), - key_replace: get_str_cap(&db, "&3"), - key_restart: get_str_cap(&db, "&4"), - key_resume: get_str_cap(&db, "&5"), - key_right: get_str_cap(&db, "kr"), - key_save: get_str_cap(&db, "&6"), - key_sbeg: get_str_cap(&db, "&9"), - key_scancel: get_str_cap(&db, "&0"), - key_scommand: get_str_cap(&db, "*1"), - key_scopy: get_str_cap(&db, "*2"), - key_screate: get_str_cap(&db, "*3"), - key_sdc: get_str_cap(&db, "*4"), - key_sdl: get_str_cap(&db, "*5"), - key_select: get_str_cap(&db, "*6"), - key_send: get_str_cap(&db, "*7"), - key_seol: get_str_cap(&db, "*8"), - key_sexit: get_str_cap(&db, "*9"), - key_sf: get_str_cap(&db, "kF"), - key_sfind: get_str_cap(&db, "*0"), - key_shelp: get_str_cap(&db, "#1"), - key_shome: get_str_cap(&db, "#2"), - key_sic: get_str_cap(&db, "#3"), - key_sleft: get_str_cap(&db, "#4"), - key_smessage: get_str_cap(&db, "%a"), - key_smove: get_str_cap(&db, "%b"), - key_snext: get_str_cap(&db, "%c"), - key_soptions: get_str_cap(&db, "%d"), - key_sprevious: get_str_cap(&db, "%e"), - key_sprint: get_str_cap(&db, "%f"), - key_sr: get_str_cap(&db, "kR"), - key_sredo: get_str_cap(&db, "%g"), - key_sreplace: get_str_cap(&db, "%h"), - key_sright: get_str_cap(&db, "%i"), - key_srsume: get_str_cap(&db, "%j"), - key_ssave: get_str_cap(&db, "!1"), - key_ssuspend: get_str_cap(&db, "!2"), - key_stab: get_str_cap(&db, "kT"), - key_sundo: get_str_cap(&db, "!3"), - key_suspend: get_str_cap(&db, "&7"), - key_undo: get_str_cap(&db, "&8"), - key_up: get_str_cap(&db, "ku"), } } } @@ -431,33 +229,6 @@ pub fn setup_fallback_term() -> Arc { init_tabs: Some(8), eat_newline_glitch: true, auto_right_margin: true, - key_a1: Some(CString::new("\x1bOw").unwrap()), - key_a3: Some(CString::new("\x1bOy").unwrap()), - key_b2: Some(CString::new("\x1bOu").unwrap()), - key_backspace: Some(CString::new("\x7f").unwrap()), - key_btab: Some(CString::new("\x1b[Z").unwrap()), - key_c1: Some(CString::new("\x1bOq").unwrap()), - key_c3: Some(CString::new("\x1bOs").unwrap()), - key_dc: Some(CString::new("\x1b[3~").unwrap()), - key_down: Some(CString::new("\x1bOB").unwrap()), - key_f1: Some(CString::new("\x1bOP").unwrap()), - key_home: Some(CString::new("\x1bOH").unwrap()), - key_ic: Some(CString::new("\x1b[2~").unwrap()), - key_left: Some(CString::new("\x1bOD").unwrap()), - key_npage: Some(CString::new("\x1b[6~").unwrap()), - key_ppage: Some(CString::new("\x1b[5~").unwrap()), - key_right: Some(CString::new("\x1bOC").unwrap()), - key_sdc: Some(CString::new("\x1b[3;2~").unwrap()), - key_send: Some(CString::new("\x1b[1;2F").unwrap()), - key_sf: Some(CString::new("\x1b[1;2B").unwrap()), - key_shome: Some(CString::new("\x1b[1;2H").unwrap()), - key_sic: Some(CString::new("\x1b[2;2~").unwrap()), - key_sleft: Some(CString::new("\x1b[1;2D").unwrap()), - key_snext: Some(CString::new("\x1b[6;2~").unwrap()), - key_sprevious: Some(CString::new("\x1b[5;2~").unwrap()), - key_sr: Some(CString::new("\x1b[1;2A").unwrap()), - key_sright: Some(CString::new("\x1b[1;2C").unwrap()), - key_up: Some(CString::new("\x1bOA").unwrap()), ..Default::default() }; let term = Arc::new(term); diff --git a/tests/checks/bind.fish b/tests/checks/bind.fish index baf7dc683..3a5241392 100644 --- a/tests/checks/bind.fish +++ b/tests/checks/bind.fish @@ -141,6 +141,9 @@ bind tab bind ctrl-\b # CHECKERR: bind: Cannot add control modifier to control character 'ctrl-h' +bind -k nul 'echo foo' +# CHECKERR: bind: the -k/--key syntax is no longer supported. See `bind --help` and `bind --key-names` + # Either Return or ctrl-m. bind \r # CHECK: bind --preset enter execute diff --git a/tests/checks/tmux-abbr.fish b/tests/checks/tmux-abbr.fish index 8c265960e..1d713c842 100644 --- a/tests/checks/tmux-abbr.fish +++ b/tests/checks/tmux-abbr.fish @@ -29,7 +29,7 @@ isolated-tmux send-keys C-u abbr-test Space C-z Space arg2 Enter tmux-sleep # CHECK: prompt {{\d+}}> abbr-test arg2 -# Or use Control+Space ("bind -k nul") to the same effect. +# Or use Control+Space to the same effect. isolated-tmux send-keys abbr-test C-Space arg3 Enter tmux-sleep # CHECK: prompt {{\d+}}> abbr-test arg3 diff --git a/tests/pexpects/bind.py b/tests/pexpects/bind.py index d065ab8f6..a39690b43 100644 --- a/tests/pexpects/bind.py +++ b/tests/pexpects/bind.py @@ -319,7 +319,7 @@ expect_str(" banana\r") expect_prompt("foonanana banana") # Ensure that nul can be bound properly (#3189). -send("bind -k nul 'echo nul seen'\r") +send("bind ctrl-space 'echo nul seen'\r") expect_prompt() send("\0" * 3) # We need to sleep briefly before emitting a newline, because when we execute the diff --git a/tests/pexpects/cursor_selection.py b/tests/pexpects/cursor_selection.py index 54aa1ecd4..8e3124586 100644 --- a/tests/pexpects/cursor_selection.py +++ b/tests/pexpects/cursor_selection.py @@ -14,7 +14,7 @@ left, right = "\x02", "\x06" # Ctrl-B, Ctrl-F # Additional keys to start selecting and dump the current selection select, dump = "\x00", "!" # Ctrl-Space, "!" -sendline("bind -k nul begin-selection") +sendline("bind ctrl-space begin-selection") expect_prompt() sendline("bind ! 'echo -n \"<$(commandline --current-selection)>\"'") expect_prompt()