From e593da1c2e767a53bf8c20d110062d478e8cf0c5 Mon Sep 17 00:00:00 2001 From: Johannes Altmanninger Date: Thu, 24 Jul 2025 13:10:05 +0200 Subject: [PATCH] Increase timeout when reading escape sequences inside paste/kitty kbd Historically, fish has treated input bytes [0x1b, 'b'] as alt-b (rather than "escape,b") if the second byte arrives within 30ms of the first. Since we made builtin bind match key events instead of raw byte sequences, we have another place where we do similar disambiguation: when we read keys such as alt-left ("\e[1;3D"), we only consider bytes to be part of this sequence if stdin is immediately readable (actually "readable after a 1ms timeout" since e1be842 (Work around torn byte sequences in qemu kbd input with 1ms timeout, 2025-03-04)). This is technically wrong but has worked in practice (for Kakoune etc.). Issue #11668 reports two issues on some Windows terminals feeding a remote fish shell: - the "bracketed paste finished" sequence may be split into multiple packets, which causes a delay of > 1ms between individual bytes being readable. - AutoHotKey scripts simulating seven "left" keys result in sequence tearing as well. Try to fix the paste case by increasing the timeout when parsing escape sequences. Also increase the timeout for terminals that support the kitty keyboard protocol. The user should only notice this new delay after pressing one of escape,O, escape,P, escape,[, or escape,] **while the kitty keyboard protocol is disabled** (e.g. while an external command is running). In this case, the fish_escape_delay_ms is also virtually increased; hopefully this edge case is not ever relevant. Part of #11668 (cherry picked from commit 30ff3710a069a03b21025cf896f0bbb31d9fe8bd) --- src/input_common.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/input_common.rs b/src/input_common.rs index f5e5349b9..469f3784f 100644 --- a/src/input_common.rs +++ b/src/input_common.rs @@ -963,7 +963,14 @@ fn readch(&mut self) -> CharEvent { fn try_readb(&mut self, buffer: &mut Vec) -> Option { let fd = self.get_in_fd(); - if !check_fd_readable(fd, Duration::from_millis(1)) { + if !check_fd_readable( + fd, + Duration::from_millis(if self.paste_is_buffering() { 300 } else { 1 }), + ) { + FLOG!( + reader, + format!("Incomplete escape sequence: {}", DisplayBytes(buffer)) + ); return None; } let next = readb(fd)?;