Drop unrecognized escape sequences instead of typing them

fzf consumed only the part of a sequence it recognized, and the rest was
typed into the query. CTRL-A sent as \e[97;5u became "97;5u". A
BEL-terminated OSC reply also aborted fzf, because the BEL that followed
the typed payload was read as CTRL-G.

Frame CSI by its parameter and final byte ranges, OSC, DCS and APC by
their terminator, then drop the whole sequence when nothing matches it.

- Wait for a string terminator in the read loop, as already done for CSI
- Take the second-chance read only while a sequence is unfinished. After
  a complete one it blocked until the next keystroke
- Leave unterminated sequences alone. ALT-[ and ALT-] arrive that way
- Skip SOS and PM. Nothing sends them, so ALT-X and ALT-^ do not wait
This commit is contained in:
Junegunn Choi committed 2026-09-27 20:53:13 +09:00
1 parent b1be3a8be1
commit 993abd8a80
3 files changed
+208 -2

No files matched your search

+20
View File
@@ -51,3 +51,23 @@ func TestIncompleteEscape(t *testing.T) {
}
}
}
// String sequences must be waited for until their terminator arrives
func TestIncompleteStringEscape(t *testing.T) {
for _, c := range []struct {
buffer string
want bool
}{
{"\x1b]11;rgb:", true},
{"\x1bP>|kitty", true},
{"\x1b_Gi=1", true},
{"\x1b]0;title\a", false},
{"\x1b]0;title\x1b\\", false},
{"\x1bP>|kitty(0.48.2)\x1b\\", false},
{"ab\x1b]11;rgb:", true},
} {
if got := incompleteEscape([]byte(c.buffer)); got != c.want {
t.Errorf("incompleteEscape(%q) = %v, want %v", c.buffer, got, c.want)
}
}
}