mirror of
https://github.com/junegunn/fzf.git
synced 2026-09-28 14:42:27 +08:00
An ESC inside a DCS, OSC or APC ends the string and introduces a sequence of its own. Giving up at that point left the payload to be typed into the query, and scanning on to a later ST would swallow that following sequence. https://vt100.net/emu/dec_ansi_parser Reported by Copilot on #4926.
108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
package tui
|
|
|
|
import "testing"
|
|
|
|
// An unrecognized CSI sequence must be consumed whole. Consuming only part of
|
|
// it leaves the rest to be read as input and typed into the query.
|
|
func TestUnknownCSISequence(t *testing.T) {
|
|
for _, c := range []struct {
|
|
sequence string
|
|
event EventType
|
|
size int
|
|
}{
|
|
// Key encodings fzf does not implement
|
|
{"\x1b[97;5u", Invalid, 7},
|
|
{"\x1b[127;5u", Invalid, 8},
|
|
{"\x1b[27;5;127~", Invalid, 11},
|
|
{"\x1b[57441;1u", Invalid, 10},
|
|
{"\x1b\x1b[97;5u", Invalid, 7}, // ALT prefixed, the first ESC is dropped
|
|
|
|
// Replies to queries fzf did not send, or sent and stopped waiting for
|
|
{"\x1b[?1;2c", Invalid, 7},
|
|
{"\x1b[>0;95;0c", Invalid, 10},
|
|
|
|
// Mouse report arriving while mouse input is off
|
|
{"\x1b[<0;1;1M", Invalid, 9},
|
|
|
|
// String sequences: OSC, DCS, APC, PM, SOS
|
|
{"\x1b]11;rgb:4a4a/4a4a/4a4a\x1b\\", Invalid, 25}, // background color reply
|
|
{"\x1b]0;a title\a", Invalid, 12}, // BEL terminated
|
|
{"\x1bP>|kitty(0.48.2)\x1b\\", Invalid, 19}, // XTVERSION reply
|
|
{"\x1b_Gi=1;OK\x1b\\", Invalid, 11},
|
|
{"\x1bP\ax\x1b\\", Invalid, 6}, // BEL inside a DCS payload is not a terminator
|
|
{"\x1b]foo\x1bX\x1b\\", Invalid, 5}, // ESC aborts the string, consuming only "\e]foo" // kitty graphics reply
|
|
|
|
// Left alone: this is how ALT-[ and ALT-] arrive
|
|
{"\x1b[a", Alt, 2},
|
|
{"\x1b]abc", Alt, 2},
|
|
{"\x1b]11;rgb:", Alt, 2}, // terminator has not arrived
|
|
|
|
// SOS and PM are not framed, so ALT-X and ALT-^ are not delayed
|
|
{"\x1bXsos\x1b\\", Alt, 2},
|
|
{"\x1b^status\x1b\\", Alt, 2},
|
|
|
|
// Left alone: no final byte yet, so the sequence may still be arriving
|
|
{"\x1b[", Invalid, 2},
|
|
|
|
// Recognized sequences keep their existing parsing
|
|
{"\x1b[1;5A", CtrlUp, 6},
|
|
{"\x1b[3;5~", CtrlDelete, 6},
|
|
{"\x1b[2~", Insert, 4},
|
|
{"\x1b[200~", BracketedPasteBegin, 6},
|
|
{"\x1b[Z", ShiftTab, 3},
|
|
{"\x1bOA", Up, 3},
|
|
{"\x1b[12;34R", Invalid, 8},
|
|
{"\x1b[?2004;2$y", Invalid, 11},
|
|
} {
|
|
r := &LightRenderer{buffer: []byte(c.sequence)}
|
|
sz := 1
|
|
event := r.escSequence(&sz)
|
|
if event.Type != c.event {
|
|
t.Errorf("escSequence(%q) = %s, want %s",
|
|
c.sequence, event.Type.String(), c.event.String())
|
|
}
|
|
if sz != c.size {
|
|
t.Errorf("escSequence(%q) consumed %d bytes, want %d", c.sequence, sz, c.size)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStringEnd(t *testing.T) {
|
|
for _, c := range []struct {
|
|
buffer string
|
|
want int
|
|
}{
|
|
{"\x1b]0;t\a", 6},
|
|
{"\x1bP\ax\x1b\\", 6}, // BEL in the payload ignored, ST ends it
|
|
{"\x1bP\a", 0}, // BEL does not terminate a DCS
|
|
{"\x1b_Gi=1\a", 0}, // nor an APC
|
|
{"\x1b]0;t\x1b\\", 7},
|
|
{"\x1b_G\x1b\\", 5},
|
|
{"\x1b]0;t", 0}, // no terminator
|
|
{"\x1b]0;t\x1b", 0}, // terminator half arrived
|
|
} {
|
|
if got := stringEnd([]byte(c.buffer)); got != c.want {
|
|
t.Errorf("stringEnd(%q) = %d, want %d", c.buffer, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCsiEnd(t *testing.T) {
|
|
for _, c := range []struct {
|
|
buffer string
|
|
want int
|
|
}{
|
|
{"\x1b[97;5u", 7},
|
|
{"\x1b[A", 3},
|
|
{"\x1b[<0;1;1M", 9},
|
|
{"\x1b[?2004;2$y", 11},
|
|
{"\x1b[97;5", 0}, // no final byte
|
|
{"\x1b[", 0}, // no final byte
|
|
{"\x1b[1\x01A", 0}, // malformed, do not frame it
|
|
} {
|
|
if got := csiEnd([]byte(c.buffer)); got != c.want {
|
|
t.Errorf("csiEnd(%q) = %d, want %d", c.buffer, got, c.want)
|
|
}
|
|
}
|
|
}
|