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.
This commit is contained in:
Junegunn Choi committed 2026-10-03 14:49:39 +09:00
1 parent 8bd6ba1830
commit c2cee734f4
2 files changed
+17 -17

No files matched your search

+7 -7
View File
@@ -444,12 +444,12 @@ func stringReply(buffer []byte) bool {
return false return false
} }
// stillArriving reports whether the sequence that consumed sz bytes of the // stillArriving reports whether the sequence at the start of the buffer may
// buffer may still be completed by more input. Both it and the buffer have to // still be completed by more input. It must be the only one in the buffer:
// end unfinished: a complete sequence followed by ALT-[ is not waiting for // whatever follows it, another sequence or a byte that ended it, means it is not
// anything, and an unfinished one followed by other bytes cannot complete. // waiting for anything.
func stillArriving(buffer []byte, sz int) bool { func stillArriving(buffer []byte) bool {
return incompleteEscape(buffer[:min(sz, len(buffer))]) && incompleteEscape(buffer) return bytes.IndexByte(buffer[1:], Esc.Byte()) < 0 && incompleteEscape(buffer)
} }
// incompleteEscape reports whether the buffer ends in an escape sequence that // 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 // Second chance, but only for an unfinished sequence. Re-reading
// otherwise blocks until the next keystroke, holding back whatever // otherwise blocks until the next keystroke, holding back whatever
// follows in the buffer. // 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) r.buffer, result, err = r.getBytes(true)
if err != nil { if err != nil {
return Event{Fatal, 0, nil} return Event{Fatal, 0, nil}
+10 -10
View File
@@ -139,19 +139,19 @@ func TestStringEnd(t *testing.T) {
func TestStillArriving(t *testing.T) { func TestStillArriving(t *testing.T) {
for _, c := range []struct { for _, c := range []struct {
buffer string buffer string
sz int
want bool want bool
}{ }{
{"\x1b[97;5u\a\x1b[", 7, false}, {"\x1b[97;5u\a\x1b[", false},
{"\x1b[97;5u\x1b[", 7, false}, {"\x1b[97;5u\x1b[", false},
{"\x1b[1;\a", 4, false}, // the BEL has already ended it {"\x1b[3;\a\x1b[", false}, // the BEL ended it, the trailing \e[ is another one
{"\x1b[1;", 4, true}, {"\x1b[1;\a", false}, // the BEL has already ended it
{"\x1b[", 2, true}, {"\x1b[1;", true},
{"\x1bO1", 3, true}, {"\x1b[", true},
{"\x1b[<0;1", 3, true}, {"\x1bO1", true},
{"\x1b[<0;1", true},
} { } {
if got := stillArriving([]byte(c.buffer), c.sz); got != c.want { if got := stillArriving([]byte(c.buffer)); got != c.want {
t.Errorf("stillArriving(%q, %d) = %v, want %v", c.buffer, c.sz, got, c.want) t.Errorf("stillArriving(%q) = %v, want %v", c.buffer, got, c.want)
} }
} }
} }