diff --git a/src/tui/light.go b/src/tui/light.go index 8d8909af..f3921c36 100644 --- a/src/tui/light.go +++ b/src/tui/light.go @@ -348,6 +348,8 @@ func getEnv(name string, defaultValue int) int { func csiContinues(b byte) bool { return b >= 0x20 && b <= 0x3f } func csiFinal(b byte) bool { return b >= 0x40 && b <= 0x7e } +func csiIntermediate(b byte) bool { return b >= 0x20 && b <= 0x2f } + // csiEnd returns the length of the CSI sequence at the start of the buffer, or // 0 if it has no final byte yet or is malformed. func csiEnd(buffer []byte) int { @@ -645,18 +647,15 @@ func (r *LightRenderer) escSequence(sz *int) Event { if end == 0 { return ev } - // The same rule as the fall-through: four bytes or fewer could be ALT-[ - // or ALT-O and typed text + // Four bytes or fewer could be ALT-[ or ALT-O and typed text, and rxvt sends + // keys of that size fzf does not know, such as \e[3^ for CTRL-DELETE if end <= 4 { - if ev.Type == Invalid { - *sz = 2 - return AltKey(rune(r.buffer[1])) - } return ev } - // A key matched on a prefix, such as Home for \e[70;5u, or a sequence - // given up on partway - if end > *sz { + // A key matched on a prefix, such as Home for \e[70;5u, or a sequence given + // up on partway. rxvt ends keys with the intermediate byte $, as in \e[7$ + // for SHIFT-HOME, so a parse ending there is complete. + if end > *sz && !csiIntermediate(r.buffer[*sz-1]) { *sz = end return Event{Invalid, 0, nil} } diff --git a/src/tui/light_csi_test.go b/src/tui/light_csi_test.go index f9e76569..3b41ed92 100644 --- a/src/tui/light_csi_test.go +++ b/src/tui/light_csi_test.go @@ -49,10 +49,16 @@ func TestUnknownEscapeSequence(t *testing.T) { {"\x1b[ x", Alt, 2}, {"\x1bOx", Alt, 2}, {"\x1bO9A", Alt, 2}, - {"\x1b[2m", Alt, 2}, // the parser gives up on these itself - {"\x1b[3x", Alt, 2}, - {"\x1b[