From c2cee734f49651395e86dce8e1d12a111e90212c Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Sat, 3 Oct 2026 14:49:39 +0900 Subject: [PATCH] Wait only when the parsed sequence is the only one in the buffer The two checks could refer to different sequences. In \e[3;\a\e[ the first looked unfinished and so did the trailing \e[, though the BEL had already ended the first. The blocking read held back the CTRL-G abort. Reported by Copilot on #4926. --- src/tui/light.go | 14 +++++++------- src/tui/light_csi_test.go | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/tui/light.go b/src/tui/light.go index 32dd5a7a..8d8909af 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -444,12 +444,12 @@ func stringReply(buffer []byte) bool { return false } -// stillArriving reports whether the sequence that consumed sz bytes of the -// buffer may still be completed by more input. Both it and the buffer have to -// end unfinished: a complete sequence followed by ALT-[ is not waiting for -// anything, and an unfinished one followed by other bytes cannot complete. -func stillArriving(buffer []byte, sz int) bool { - return incompleteEscape(buffer[:min(sz, len(buffer))]) && incompleteEscape(buffer) +// stillArriving reports whether the sequence at the start of the buffer may +// still be completed by more input. It must be the only one in the buffer: +// whatever follows it, another sequence or a byte that ended it, means it is not +// waiting for anything. +func stillArriving(buffer []byte) bool { + return bytes.IndexByte(buffer[1:], Esc.Byte()) < 0 && incompleteEscape(buffer) } // incompleteEscape reports whether the buffer ends in an escape sequence that @@ -589,7 +589,7 @@ func (r *LightRenderer) GetChar(cancellable bool) Event { // Second chance, but only for an unfinished sequence. Re-reading // otherwise blocks until the next keystroke, holding back whatever // follows in the buffer. - if ev.Type == Invalid && stillArriving(r.buffer, sz) { + if ev.Type == Invalid && stillArriving(r.buffer) { r.buffer, result, err = r.getBytes(true) if err != nil { return Event{Fatal, 0, nil} diff --git a/src/tui/light_csi_test.go b/src/tui/light_csi_test.go index b7071b4b..f9e76569 100644 --- a/src/tui/light_csi_test.go +++ b/src/tui/light_csi_test.go @@ -139,19 +139,19 @@ func TestStringEnd(t *testing.T) { func TestStillArriving(t *testing.T) { for _, c := range []struct { buffer string - sz int want bool }{ - {"\x1b[97;5u\a\x1b[", 7, false}, - {"\x1b[97;5u\x1b[", 7, false}, - {"\x1b[1;\a", 4, false}, // the BEL has already ended it - {"\x1b[1;", 4, true}, - {"\x1b[", 2, true}, - {"\x1bO1", 3, true}, - {"\x1b[<0;1", 3, true}, + {"\x1b[97;5u\a\x1b[", false}, + {"\x1b[97;5u\x1b[", false}, + {"\x1b[3;\a\x1b[", false}, // the BEL ended it, the trailing \e[ is another one + {"\x1b[1;\a", false}, // the BEL has already ended it + {"\x1b[1;", true}, + {"\x1b[", true}, + {"\x1bO1", true}, + {"\x1b[<0;1", true}, } { - if got := stillArriving([]byte(c.buffer), c.sz); got != c.want { - t.Errorf("stillArriving(%q, %d) = %v, want %v", c.buffer, c.sz, got, c.want) + if got := stillArriving([]byte(c.buffer)); got != c.want { + t.Errorf("stillArriving(%q) = %v, want %v", c.buffer, got, c.want) } } }