From 9c667afb73b44164e0e342674b47f9b1b04a32fa Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sun, 27 Sep 2026 21:03:07 +0900 Subject: [PATCH] Wait for a string terminator split across reads A trailing ESC was read as a lone ESC, so the read loop stopped waiting and the parser was handed an unterminated sequence, whose payload was typed into the query. With ESCDELAY=0 nothing covered it. Reported by Copilot on #4926. --- src/tui/light.go | 8 ++++++++ src/tui/light_escape_test.go | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/tui/light.go b/src/tui/light.go index f91813ef..1bedbf17 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -414,6 +414,14 @@ func incompleteEscape(buffer []byte) bool { tail = tail[len(tail)-escapeLookback:] } start := bytes.LastIndexByte(tail, Esc.Byte()) + // A trailing ESC can be the first half of a string terminator. Reading it as + // a lone ESC ends the wait and hands the parser an unterminated sequence, so + // fall back to the one this ESC would have terminated. + if start == len(tail)-1 && start > 0 { + if prev := bytes.LastIndexByte(tail[:start], Esc.Byte()); prev >= 0 { + start = prev + } + } if start < 0 || len(tail)-start < 2 { return false } diff --git a/src/tui/light_escape_test.go b/src/tui/light_escape_test.go index 0dd7c59d..70bd0a63 100644 --- a/src/tui/light_escape_test.go +++ b/src/tui/light_escape_test.go @@ -65,6 +65,18 @@ func TestIncompleteStringEscape(t *testing.T) { {"\x1b]0;title\x1b\\", false}, {"\x1bP>|kitty(0.48.2)\x1b\\", false}, {"ab\x1b]11;rgb:", true}, + + // A string terminator split across reads: the ESC has arrived, the + // backslash has not + {"\x1b]0;t\x1b", true}, + {"\x1bP>|kitty\x1b", true}, + {"\x1b_G\x1b", true}, + + // Complete sequence followed by a lone ESC, which is the ESC key + {"\x1b]0;title\a\x1b", false}, + {"\x1b\x1b", false}, + {"\x1ba\x1b", false}, + {"\x1b[A\x1b", false}, } { if got := incompleteEscape([]byte(c.buffer)); got != c.want { t.Errorf("incompleteEscape(%q) = %v, want %v", c.buffer, got, c.want)