From 01db48a71246ff9b68f60ff2375db090c1f30fa9 Mon Sep 17 00:00:00 2001 From: yanshay Date: Thu, 31 Aug 2023 00:12:22 +0300 Subject: [PATCH] Added fish_sequence_key_delay_ms to set time to wait between sequence key presses (#7401) (#9926) * added support for fish_sequence_key_delay_ms to set how long to wait between sequence key presses * fixed cargo fmt --- fish-rust/src/env_dispatch.rs | 10 +++++++ fish-rust/src/ffi.rs | 1 + src/fish_key_reader.cpp | 2 +- src/input.cpp | 21 ++++++++----- src/input_common.cpp | 56 +++++++++++++++++++++++++++++++++-- src/input_common.h | 8 ++++- 6 files changed, 86 insertions(+), 12 deletions(-) diff --git a/fish-rust/src/env_dispatch.rs b/fish-rust/src/env_dispatch.rs index 2a18843ef..a7b0d7528 100644 --- a/fish-rust/src/env_dispatch.rs +++ b/fish-rust/src/env_dispatch.rs @@ -61,6 +61,10 @@ static VAR_DISPATCH_TABLE: once_cell::sync::Lazy = table.add_anon(L!("fish_term256"), handle_fish_term_change); table.add_anon(L!("fish_term24bit"), handle_fish_term_change); table.add_anon(L!("fish_escape_delay_ms"), update_wait_on_escape_ms); + table.add_anon( + L!("fish_sequence_key_delay_ms"), + update_wait_on_sequence_key_ms, + ); table.add_anon(L!("fish_emoji_width"), guess_emoji_width); table.add_anon(L!("fish_ambiguous_width"), handle_change_ambiguous_width); table.add_anon(L!("LINES"), handle_term_size_change); @@ -105,6 +109,11 @@ fn update_wait_on_escape_ms(vars: &EnvStack) { let var = crate::env::environment::env_var_to_ffi(fish_escape_delay_ms); crate::ffi::update_wait_on_escape_ms_ffi(var); } +fn update_wait_on_sequence_key_ms(vars: &EnvStack) { + let fish_sequence_key_delay_ms = vars.get_unless_empty(L!("fish_sequence_key_delay_ms")); + let var = crate::env::environment::env_var_to_ffi(fish_sequence_key_delay_ms); + crate::ffi::update_wait_on_sequence_key_ms_ffi(var); +} impl VarDispatchTable { /// Add a callback for the variable `name`. We must not already be observing this variable. @@ -366,6 +375,7 @@ fn run_inits(vars: &EnvStack) { init_curses(vars); guess_emoji_width(vars); update_wait_on_escape_ms(vars); + update_wait_on_sequence_key_ms(vars); handle_read_limit_change(vars); handle_fish_use_posix_spawn_change(vars); handle_fish_trace(vars); diff --git a/fish-rust/src/ffi.rs b/fish-rust/src/ffi.rs index 55fd59dfd..142023476 100644 --- a/fish-rust/src/ffi.rs +++ b/fish-rust/src/ffi.rs @@ -147,6 +147,7 @@ include_cpp! { generate!("complete_invalidate_path") generate!("complete_add_wrapper") generate!("update_wait_on_escape_ms_ffi") + generate!("update_wait_on_sequence_key_ms_ffi") generate!("autoload_t") generate!("make_autoload_ffi") generate!("perform_autoload_ffi") diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index 50967e7f0..4fe8288c4 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -240,7 +240,7 @@ static void process_input(bool continuous_mode, bool verbose) { if (reader_test_and_clear_interrupted()) { evt = char_event_t{shell_modes.c_cc[VINTR]}; } else { - evt = queue.readch_timed(); + evt = queue.readch_timed_esc(); } if (!evt || !evt->is_char()) { output_bind_command(bind_chars); diff --git a/src/input.cpp b/src/input.cpp index dd392e11a..28aa9d647 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -468,19 +468,24 @@ class event_queue_peeker_t { /// Check if the next event is the given character. This advances the index on success only. /// If \p timed is set, then return false if this (or any other) character had a timeout. - bool next_is_char(wchar_t c, bool timed = false) { + bool next_is_char(wchar_t c, bool escaped = false) { assert(idx_ <= peeked_.size() && "Index must not be larger than dequeued event count"); // See if we had a timeout already. - if (timed && had_timeout_) { + if (escaped && had_timeout_) { return false; } // Grab a new event if we have exhausted what we have already peeked. // Use either readch or readch_timed, per our param. if (idx_ == peeked_.size()) { char_event_t newevt{L'\0'}; - if (!timed) { - newevt = event_queue_.readch(); - } else if (auto mevt = event_queue_.readch_timed()) { + if (!escaped) { + if (auto mevt = event_queue_.readch_timed_sequence_key()) { + newevt = mevt.acquire(); + } else { + had_timeout_ = true; + return false; + } + } else if (auto mevt = event_queue_.readch_timed_esc()) { newevt = mevt.acquire(); } else { had_timeout_ = true; @@ -545,7 +550,7 @@ static bool have_mouse_tracking_csi(event_queue_peeker_t *peeker) { // Maximum length of any CSI is NPAR (which is nominally 16), although this does not account for // user input intermixed with pseudo input generated by the tty emulator. // Check for the CSI first. - if (!peeker->next_is_char(L'\x1b') || !peeker->next_is_char(L'[', true /* timed */)) { + if (!peeker->next_is_char(L'\x1b') || !peeker->next_is_char(L'[', true /* escaped */)) { return false; } @@ -596,8 +601,8 @@ static bool try_peek_sequence(event_queue_peeker_t *peeker, const wcstring &str) for (wchar_t c : str) { // If we just read an escape, we need to add a timeout for the next char, // to distinguish between the actual escape key and an "alt"-modifier. - bool timed = prev == L'\x1B'; - if (!peeker->next_is_char(c, timed)) { + bool escaped = prev == L'\x1B'; + if (!peeker->next_is_char(c, escaped)) { return false; } prev = c; diff --git a/src/input_common.cpp b/src/input_common.cpp index 8087cec4d..7e2a40f59 100644 --- a/src/input_common.cpp +++ b/src/input_common.cpp @@ -35,6 +35,9 @@ #define WAIT_ON_ESCAPE_DEFAULT 30 static int wait_on_escape_ms = WAIT_ON_ESCAPE_DEFAULT; +#define WAIT_ON_SEQUENCE_KEY_INFINITE (-1) +static int wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_INFINITE; + input_event_queue_t::input_event_queue_t(int in) : in_(in) {} /// Internal function used by readch to read one byte. @@ -157,6 +160,44 @@ void update_wait_on_escape_ms_ffi(std::unique_ptr fish_escape_delay_m } } + +// Update the wait_on_sequence_key_ms value in response to the fish_sequence_key_delay_ms user variable being +// set. +void update_wait_on_sequence_key_ms(const environment_t& vars) { + auto sequence_key_time_ms = vars.get_unless_empty(L"fish_sequence_key_delay_ms"); + if (!sequence_key_time_ms) { + wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_INFINITE; + return; + } + + long tmp = fish_wcstol(sequence_key_time_ms->as_string().c_str()); + if (errno || tmp < 10 || tmp >= 5000) { + std::fwprintf(stderr, + L"ignoring fish_sequence_key_delay_ms: value '%ls' " + L"is not an integer or is < 10 or >= 5000 ms\n", + sequence_key_time_ms->as_string().c_str()); + } else { + wait_on_sequence_key_ms = static_cast(tmp); + } +} + +void update_wait_on_sequence_key_ms_ffi(std::unique_ptr fish_sequence_key_delay_ms) { + if (!fish_sequence_key_delay_ms) { + wait_on_sequence_key_ms = WAIT_ON_SEQUENCE_KEY_INFINITE; + return; + } + + long tmp = fish_wcstol(fish_sequence_key_delay_ms->as_string().c_str()); + if (errno || tmp < 10 || tmp >= 5000) { + std::fwprintf(stderr, + L"ignoring fish_sequence_key_delay_ms: value '%ls' " + L"is not an integer or is < 10 or >= 5000 ms\n", + fish_sequence_key_delay_ms->as_string().c_str()); + } else { + wait_on_sequence_key_ms = static_cast(tmp); + } +} + maybe_t input_event_queue_t::try_pop() { if (queue_.empty()) { return none(); @@ -235,7 +276,18 @@ char_event_t input_event_queue_t::readch() { } } -maybe_t input_event_queue_t::readch_timed() { +maybe_t input_event_queue_t::readch_timed_esc() { + return readch_timed(wait_on_escape_ms); +} + +maybe_t input_event_queue_t::readch_timed_sequence_key() { + if (wait_on_sequence_key_ms == WAIT_ON_SEQUENCE_KEY_INFINITE) { + return readch(); + } + return readch_timed(wait_on_sequence_key_ms); +} + +maybe_t input_event_queue_t::readch_timed(const int wait_time_ms) { if (auto evt = try_pop()) { return evt; } @@ -248,7 +300,7 @@ maybe_t input_event_queue_t::readch_timed() { // pselect expects timeouts in nanoseconds. const uint64_t nsec_per_msec = 1000 * 1000; const uint64_t nsec_per_sec = nsec_per_msec * 1000; - const uint64_t wait_nsec = wait_on_escape_ms * nsec_per_msec; + const uint64_t wait_nsec = wait_time_ms * nsec_per_msec; struct timespec timeout; timeout.tv_sec = (wait_nsec) / nsec_per_sec; timeout.tv_nsec = (wait_nsec) % nsec_per_sec; diff --git a/src/input_common.h b/src/input_common.h index 4d4b634e6..6b26171ed 100644 --- a/src/input_common.h +++ b/src/input_common.h @@ -189,6 +189,9 @@ class environment_t; void update_wait_on_escape_ms(const environment_t &vars); void update_wait_on_escape_ms_ffi(std::unique_ptr fish_escape_delay_ms); +void update_wait_on_sequence_key_ms(const environment_t &vars); +void update_wait_on_sequence_key_ms_ffi(std::unique_ptr fish_sequence_key_delay_ms); + /// A class which knows how to produce a stream of input events. /// This is a base class; you may subclass it for its override points. class input_event_queue_t { @@ -204,7 +207,10 @@ class input_event_queue_t { /// Like readch(), except it will wait at most WAIT_ON_ESCAPE milliseconds for a /// character to be available for reading. /// \return none on timeout, the event on success. - maybe_t readch_timed(); + maybe_t readch_timed(const int wait_time_ms); + + maybe_t readch_timed_esc(); + maybe_t readch_timed_sequence_key(); /// Enqueue a character or a readline function to the queue of unread characters that /// readch will return before actually reading from fd 0.