Skip to content

Commit e6dabb3

Browse files
committed
fix: handle batched key msgs
1 parent 958dc20 commit e6dabb3

2 files changed

Lines changed: 156 additions & 68 deletions

File tree

key.go

Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -492,35 +492,7 @@ func readInputs(input io.Reader) ([]Msg, error) {
492492
return m, nil
493493
}
494494

495-
// Is it a sequence, like an arrow key?
496-
if k, ok := sequences[string(buf[:numBytes])]; ok {
497-
return []Msg{
498-
KeyMsg(k),
499-
}, nil
500-
}
501-
502-
// Some of these need special handling.
503-
hex := fmt.Sprintf("%x", buf[:numBytes])
504-
if k, ok := hexes[hex]; ok {
505-
return []Msg{
506-
KeyMsg(k),
507-
}, nil
508-
}
509-
510-
// Is the alt key pressed? If so, the buffer will be prefixed with an
511-
// escape.
512-
if numBytes > 1 && buf[0] == 0x1b {
513-
// Now remove the initial escape sequence and re-process to get the
514-
// character being pressed in combination with alt.
515-
c, _ := utf8.DecodeRune(buf[1:])
516-
if c == utf8.RuneError {
517-
return nil, errors.New("could not decode rune after removing initial escape")
518-
}
519-
return []Msg{
520-
KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: []rune{c}}),
521-
}, nil
522-
}
523-
495+
var runeSets [][]rune
524496
var runes []rune
525497
b := buf[:numBytes]
526498

@@ -532,38 +504,64 @@ func readInputs(input io.Reader) ([]Msg, error) {
532504
if r == utf8.RuneError {
533505
return nil, errors.New("could not decode rune")
534506
}
507+
508+
if r == '\x1b' && len(runes) > 1 {
509+
// a new key sequence has started
510+
runeSets = append(runeSets, runes)
511+
runes = []rune{}
512+
}
513+
535514
runes = append(runes, r)
536515
w = width
537516
}
517+
// add the final set of runes we decoded
518+
runeSets = append(runeSets, runes)
538519

539-
if len(runes) == 0 {
520+
if len(runeSets) == 0 {
540521
return nil, errors.New("received 0 runes from input")
541-
} else if len(runes) > 1 {
542-
// We received multiple runes, so we know this isn't a control
543-
// character, sequence, and so on.
544-
return []Msg{
545-
KeyMsg(Key{Type: KeyRunes, Runes: runes}),
546-
}, nil
547522
}
548523

549-
// Is the first rune a control character?
550-
r := KeyType(runes[0])
551-
if numBytes == 1 && r <= keyUS || r == keyDEL {
552-
return []Msg{
553-
KeyMsg(Key{Type: r}),
554-
}, nil
555-
}
524+
var msgs []Msg
525+
for _, runes := range runeSets {
526+
// Is it a sequence, like an arrow key?
527+
if k, ok := sequences[string(runes)]; ok {
528+
msgs = append(msgs, KeyMsg(k))
529+
continue
530+
}
531+
532+
// Some of these need special handling.
533+
hex := fmt.Sprintf("%x", runes)
534+
if k, ok := hexes[hex]; ok {
535+
msgs = append(msgs, KeyMsg(k))
536+
continue
537+
}
538+
539+
// Is the alt key pressed? If so, the buffer will be prefixed with an
540+
// escape.
541+
if len(runes) > 1 && runes[0] == 0x1b {
542+
msgs = append(msgs, KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: runes[1:]}))
543+
continue
544+
}
556545

557-
// If it's a space, override the type with KeySpace (but still include the
558-
// rune).
559-
if runes[0] == ' ' {
560-
return []Msg{
561-
KeyMsg(Key{Type: KeySpace, Runes: runes}),
562-
}, nil
546+
for _, v := range runes {
547+
// Is the first rune a control character?
548+
r := KeyType(v)
549+
if r <= keyUS || r == keyDEL {
550+
msgs = append(msgs, KeyMsg(Key{Type: r}))
551+
continue
552+
}
553+
554+
// If it's a space, override the type with KeySpace (but still include
555+
// the rune).
556+
if r == ' ' {
557+
msgs = append(msgs, KeyMsg(Key{Type: KeySpace, Runes: []rune{v}}))
558+
continue
559+
}
560+
561+
// Welp, just regular, ol' runes.
562+
msgs = append(msgs, KeyMsg(Key{Type: KeyRunes, Runes: []rune{v}}))
563+
}
563564
}
564565

565-
// Welp, it's just a regular, ol' single rune.
566-
return []Msg{
567-
KeyMsg(Key{Type: KeyRunes, Runes: runes}),
568-
}, nil
566+
return msgs, nil
569567
}

key_test.go

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,29 +48,119 @@ func TestKeyTypeString(t *testing.T) {
4848
}
4949

5050
func TestReadInput(t *testing.T) {
51-
for out, in := range map[string][]byte{
52-
"a": {'a'},
53-
"ctrl+a": {byte(keySOH)},
54-
"alt+a": {0x1b, 'a'},
55-
"abcd": {'a', 'b', 'c', 'd'},
56-
"up": []byte("\x1b[A"),
57-
"wheel up": {'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
58-
"shift+tab": {'\x1b', '[', 'Z'},
51+
type test struct {
52+
in []byte
53+
out []Msg
54+
}
55+
for out, td := range map[string]test{
56+
"a": {
57+
[]byte{'a'},
58+
[]Msg{
59+
KeyMsg{
60+
Type: KeyRunes,
61+
Runes: []rune{'a'},
62+
},
63+
},
64+
},
65+
" ": {
66+
[]byte{' '},
67+
[]Msg{
68+
KeyMsg{
69+
Type: KeySpace,
70+
Runes: []rune{' '},
71+
},
72+
},
73+
},
74+
"ctrl+a": {
75+
[]byte{byte(keySOH)},
76+
[]Msg{
77+
KeyMsg{
78+
Type: KeyCtrlA,
79+
},
80+
},
81+
},
82+
"alt+a": {
83+
[]byte{byte(0x1b), 'a'},
84+
[]Msg{
85+
KeyMsg{
86+
Type: KeyRunes,
87+
Alt: true,
88+
Runes: []rune{'a'},
89+
},
90+
},
91+
},
92+
"abcd": {
93+
[]byte{'a', 'b', 'c', 'd'},
94+
[]Msg{
95+
KeyMsg{
96+
Type: KeyRunes,
97+
Runes: []rune{'a'},
98+
},
99+
KeyMsg{
100+
Type: KeyRunes,
101+
Runes: []rune{'b'},
102+
},
103+
KeyMsg{
104+
Type: KeyRunes,
105+
Runes: []rune{'c'},
106+
},
107+
KeyMsg{
108+
Type: KeyRunes,
109+
Runes: []rune{'d'},
110+
},
111+
},
112+
},
113+
"up": {
114+
[]byte("\x1b[A"),
115+
[]Msg{
116+
KeyMsg{
117+
Type: KeyUp,
118+
},
119+
},
120+
},
121+
"wheel up": {
122+
[]byte{'\x1b', '[', 'M', byte(32) + 0b0100_0000, byte(65), byte(49)},
123+
[]Msg{
124+
MouseMsg{
125+
Type: MouseWheelUp,
126+
},
127+
},
128+
},
129+
"shift+tab": {
130+
[]byte{'\x1b', '[', 'Z'},
131+
[]Msg{
132+
KeyMsg{
133+
Type: KeyShiftTab,
134+
},
135+
},
136+
},
59137
} {
60138
t.Run(out, func(t *testing.T) {
61-
msgs, err := readInputs(bytes.NewReader(in))
139+
msgs, err := readInputs(bytes.NewReader(td.in))
62140
if err != nil {
63141
t.Fatalf("unexpected error: %v", err)
64142
}
65-
if len(msgs) == 0 {
66-
t.Fatalf("unexpected empty message list")
143+
if len(msgs) != len(td.out) {
144+
t.Fatalf("unexpected message list length")
67145
}
68146

69-
if m, ok := msgs[0].(KeyMsg); ok && m.String() != out {
70-
t.Fatalf(`expected a keymsg %q, got %q`, out, m)
147+
if len(msgs) == 1 {
148+
if m, ok := msgs[0].(KeyMsg); ok && m.String() != out {
149+
t.Fatalf(`expected a keymsg %q, got %q`, out, m)
150+
}
71151
}
72-
if m, ok := msgs[0].(MouseMsg); ok && mouseEventTypes[m.Type] != out {
73-
t.Fatalf(`expected a mousemsg %q, got %q`, out, mouseEventTypes[m.Type])
152+
153+
for i, v := range msgs {
154+
if m, ok := v.(KeyMsg); ok &&
155+
m.String() != td.out[i].(KeyMsg).String() {
156+
t.Fatalf(`expected a keymsg %q, got %q`, td.out[i].(KeyMsg), m)
157+
}
158+
if m, ok := v.(MouseMsg); ok &&
159+
(mouseEventTypes[m.Type] != out || m.Type != td.out[i].(MouseMsg).Type) {
160+
t.Fatalf(`expected a mousemsg %q, got %q`,
161+
out,
162+
mouseEventTypes[td.out[i].(MouseMsg).Type])
163+
}
74164
}
75165
})
76166
}

0 commit comments

Comments
 (0)