forked from Mirrors/bubbletea
fix(key): support very long buffered input (#570)
This commit is contained in:
parent
6d07f4a410
commit
5536bca34e
36
key.go
36
key.go
|
@ -543,6 +543,8 @@ var spaceRunes = []rune{' '}
|
||||||
func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
|
func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
|
||||||
var buf [256]byte
|
var buf [256]byte
|
||||||
|
|
||||||
|
var leftOverFromPrevIteration []byte
|
||||||
|
loop:
|
||||||
for {
|
for {
|
||||||
// Read and block.
|
// Read and block.
|
||||||
numBytes, err := input.Read(buf[:])
|
numBytes, err := input.Read(buf[:])
|
||||||
|
@ -550,11 +552,31 @@ func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
|
||||||
return fmt.Errorf("error reading input: %w", err)
|
return fmt.Errorf("error reading input: %w", err)
|
||||||
}
|
}
|
||||||
b := buf[:numBytes]
|
b := buf[:numBytes]
|
||||||
|
if leftOverFromPrevIteration != nil {
|
||||||
|
b = append(leftOverFromPrevIteration, b...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we had a short read (numBytes < len(buf)), we're sure that
|
||||||
|
// the end of this read is an event boundary, so there is no doubt
|
||||||
|
// if we are encountering the end of the buffer while parsing a message.
|
||||||
|
// However, if we've succeeded in filling up the buffer, there may
|
||||||
|
// be more data in the OS buffer ready to be read in, to complete
|
||||||
|
// the last message in the input. In that case, we will retry with
|
||||||
|
// the left over data in the next iteration.
|
||||||
|
canHaveMoreData := numBytes == len(buf)
|
||||||
|
|
||||||
var i, w int
|
var i, w int
|
||||||
for i, w = 0, 0; i < len(b); i += w {
|
for i, w = 0, 0; i < len(b); i += w {
|
||||||
var msg Msg
|
var msg Msg
|
||||||
w, msg = detectOneMsg(b[i:])
|
w, msg = detectOneMsg(b[i:], canHaveMoreData)
|
||||||
|
if w == 0 {
|
||||||
|
// Expecting more bytes beyond the current buffer. Try waiting
|
||||||
|
// for more input.
|
||||||
|
leftOverFromPrevIteration = make([]byte, 0, len(b[i:])+len(buf))
|
||||||
|
leftOverFromPrevIteration = append(leftOverFromPrevIteration, b[i:]...)
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case msgs <- msg:
|
case msgs <- msg:
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
@ -565,12 +587,13 @@ func readInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
leftOverFromPrevIteration = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var unknownCSIRe = regexp.MustCompile(`^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]`)
|
var unknownCSIRe = regexp.MustCompile(`^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]`)
|
||||||
|
|
||||||
func detectOneMsg(b []byte) (w int, msg Msg) {
|
func detectOneMsg(b []byte, canHaveMoreData bool) (w int, msg Msg) {
|
||||||
// Detect mouse events.
|
// Detect mouse events.
|
||||||
const mouseEventLen = 6
|
const mouseEventLen = 6
|
||||||
if len(b) >= mouseEventLen && b[0] == '\x1b' && b[1] == '[' && b[2] == 'M' {
|
if len(b) >= mouseEventLen && b[0] == '\x1b' && b[1] == '[' && b[2] == 'M' {
|
||||||
|
@ -618,6 +641,15 @@ func detectOneMsg(b []byte) (w int, msg Msg) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if i >= len(b) && canHaveMoreData {
|
||||||
|
// We have encountered the end of the input buffer. Alas, we can't
|
||||||
|
// be sure whether the data in the remainder of the buffer is
|
||||||
|
// complete (maybe there was a short read). Instead of sending anything
|
||||||
|
// dumb to the message channel, do a short read. The outer loop will
|
||||||
|
// handle this case by extending the buffer as necessary.
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// If we found at least one rune, we report the bunch of them as
|
// If we found at least one rune, we report the bunch of them as
|
||||||
// a single KeyRunes or KeySpace event.
|
// a single KeyRunes or KeySpace event.
|
||||||
if len(runes) > 0 {
|
if len(runes) > 0 {
|
||||||
|
|
21
key_test.go
21
key_test.go
|
@ -200,7 +200,7 @@ func TestDetectOneMsg(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range td {
|
for _, tc := range td {
|
||||||
t.Run(fmt.Sprintf("%q", string(tc.seq)), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%q", string(tc.seq)), func(t *testing.T) {
|
||||||
width, msg := detectOneMsg(tc.seq)
|
width, msg := detectOneMsg(tc.seq, false /* canHaveMoreData */)
|
||||||
if width != len(tc.seq) {
|
if width != len(tc.seq) {
|
||||||
t.Errorf("parser did not consume the entire input: got %d, expected %d", width, len(tc.seq))
|
t.Errorf("parser did not consume the entire input: got %d, expected %d", width, len(tc.seq))
|
||||||
}
|
}
|
||||||
|
@ -211,6 +211,25 @@ func TestDetectOneMsg(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReadLongInput(t *testing.T) {
|
||||||
|
input := strings.Repeat("a", 1000)
|
||||||
|
msgs := testReadInputs(t, bytes.NewReader([]byte(input)))
|
||||||
|
if len(msgs) != 1 {
|
||||||
|
t.Errorf("expected 1 messages, got %d", len(msgs))
|
||||||
|
}
|
||||||
|
km := msgs[0]
|
||||||
|
k := Key(km.(KeyMsg))
|
||||||
|
if k.Type != KeyRunes {
|
||||||
|
t.Errorf("expected key runes, got %d", k.Type)
|
||||||
|
}
|
||||||
|
if len(k.Runes) != 1000 || !reflect.DeepEqual(k.Runes, []rune(input)) {
|
||||||
|
t.Errorf("unexpected runes: %+v", k)
|
||||||
|
}
|
||||||
|
if k.Alt {
|
||||||
|
t.Errorf("unexpected alt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReadInput(t *testing.T) {
|
func TestReadInput(t *testing.T) {
|
||||||
type test struct {
|
type test struct {
|
||||||
keyname string
|
keyname string
|
||||||
|
|
Loading…
Reference in New Issue