main
1package tea
2
3import (
4 "context"
5 "fmt"
6 "io"
7 "regexp"
8 "strings"
9 "unicode/utf8"
10)
11
12// KeyMsg contains information about a keypress. KeyMsgs are always sent to
13// the program's update function. There are a couple general patterns you could
14// use to check for keypresses:
15//
16// // Switch on the string representation of the key (shorter)
17// switch msg := msg.(type) {
18// case KeyMsg:
19// switch msg.String() {
20// case "enter":
21// fmt.Println("you pressed enter!")
22// case "a":
23// fmt.Println("you pressed a!")
24// }
25// }
26//
27// // Switch on the key type (more foolproof)
28// switch msg := msg.(type) {
29// case KeyMsg:
30// switch msg.Type {
31// case KeyEnter:
32// fmt.Println("you pressed enter!")
33// case KeyRunes:
34// switch string(msg.Runes) {
35// case "a":
36// fmt.Println("you pressed a!")
37// }
38// }
39// }
40//
41// Note that Key.Runes will always contain at least one character, so you can
42// always safely call Key.Runes[0]. In most cases Key.Runes will only contain
43// one character, though certain input method editors (most notably Chinese
44// IMEs) can input multiple runes at once.
45type KeyMsg Key
46
47// String returns a string representation for a key message. It's safe (and
48// encouraged) for use in key comparison.
49func (k KeyMsg) String() (str string) {
50 return Key(k).String()
51}
52
53// Key contains information about a keypress.
54type Key struct {
55 Type KeyType
56 Runes []rune
57 Alt bool
58 Paste bool
59}
60
61// String returns a friendly string representation for a key. It's safe (and
62// encouraged) for use in key comparison.
63//
64// k := Key{Type: KeyEnter}
65// fmt.Println(k)
66// // Output: enter
67func (k Key) String() (str string) {
68 var buf strings.Builder
69 if k.Alt {
70 buf.WriteString("alt+")
71 }
72 if k.Type == KeyRunes {
73 if k.Paste {
74 // Note: bubbles/keys bindings currently do string compares to
75 // recognize shortcuts. Since pasted text should never activate
76 // shortcuts, we need to ensure that the binding code doesn't
77 // match Key events that result from pastes. We achieve this
78 // here by enclosing pastes in '[...]' so that the string
79 // comparison in Matches() fails in that case.
80 buf.WriteByte('[')
81 }
82 buf.WriteString(string(k.Runes))
83 if k.Paste {
84 buf.WriteByte(']')
85 }
86 return buf.String()
87 } else if s, ok := keyNames[k.Type]; ok {
88 buf.WriteString(s)
89 return buf.String()
90 }
91 return ""
92}
93
94// KeyType indicates the key pressed, such as KeyEnter or KeyBreak or KeyCtrlC.
95// All other keys will be type KeyRunes. To get the rune value, check the Rune
96// method on a Key struct, or use the Key.String() method:
97//
98// k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true}
99// if k.Type == KeyRunes {
100//
101// fmt.Println(k.Runes)
102// // Output: a
103//
104// fmt.Println(k.String())
105// // Output: alt+a
106//
107// }
108type KeyType int
109
110func (k KeyType) String() (str string) {
111 if s, ok := keyNames[k]; ok {
112 return s
113 }
114 return ""
115}
116
117// Control keys. We could do this with an iota, but the values are very
118// specific, so we set the values explicitly to avoid any confusion.
119//
120// See also:
121// https://en.wikipedia.org/wiki/C0_and_C1_control_codes
122const (
123 keyNUL KeyType = 0 // null, \0
124 keySOH KeyType = 1 // start of heading
125 keySTX KeyType = 2 // start of text
126 keyETX KeyType = 3 // break, ctrl+c
127 keyEOT KeyType = 4 // end of transmission
128 keyENQ KeyType = 5 // enquiry
129 keyACK KeyType = 6 // acknowledge
130 keyBEL KeyType = 7 // bell, \a
131 keyBS KeyType = 8 // backspace
132 keyHT KeyType = 9 // horizontal tabulation, \t
133 keyLF KeyType = 10 // line feed, \n
134 keyVT KeyType = 11 // vertical tabulation \v
135 keyFF KeyType = 12 // form feed \f
136 keyCR KeyType = 13 // carriage return, \r
137 keySO KeyType = 14 // shift out
138 keySI KeyType = 15 // shift in
139 keyDLE KeyType = 16 // data link escape
140 keyDC1 KeyType = 17 // device control one
141 keyDC2 KeyType = 18 // device control two
142 keyDC3 KeyType = 19 // device control three
143 keyDC4 KeyType = 20 // device control four
144 keyNAK KeyType = 21 // negative acknowledge
145 keySYN KeyType = 22 // synchronous idle
146 keyETB KeyType = 23 // end of transmission block
147 keyCAN KeyType = 24 // cancel
148 keyEM KeyType = 25 // end of medium
149 keySUB KeyType = 26 // substitution
150 keyESC KeyType = 27 // escape, \e
151 keyFS KeyType = 28 // file separator
152 keyGS KeyType = 29 // group separator
153 keyRS KeyType = 30 // record separator
154 keyUS KeyType = 31 // unit separator
155 keyDEL KeyType = 127 // delete. on most systems this is mapped to backspace, I hear
156)
157
158// Control key aliases.
159const (
160 KeyNull KeyType = keyNUL
161 KeyBreak KeyType = keyETX
162 KeyEnter KeyType = keyCR
163 KeyBackspace KeyType = keyDEL
164 KeyTab KeyType = keyHT
165 KeyEsc KeyType = keyESC
166 KeyEscape KeyType = keyESC
167
168 KeyCtrlAt KeyType = keyNUL // ctrl+@
169 KeyCtrlA KeyType = keySOH
170 KeyCtrlB KeyType = keySTX
171 KeyCtrlC KeyType = keyETX
172 KeyCtrlD KeyType = keyEOT
173 KeyCtrlE KeyType = keyENQ
174 KeyCtrlF KeyType = keyACK
175 KeyCtrlG KeyType = keyBEL
176 KeyCtrlH KeyType = keyBS
177 KeyCtrlI KeyType = keyHT
178 KeyCtrlJ KeyType = keyLF
179 KeyCtrlK KeyType = keyVT
180 KeyCtrlL KeyType = keyFF
181 KeyCtrlM KeyType = keyCR
182 KeyCtrlN KeyType = keySO
183 KeyCtrlO KeyType = keySI
184 KeyCtrlP KeyType = keyDLE
185 KeyCtrlQ KeyType = keyDC1
186 KeyCtrlR KeyType = keyDC2
187 KeyCtrlS KeyType = keyDC3
188 KeyCtrlT KeyType = keyDC4
189 KeyCtrlU KeyType = keyNAK
190 KeyCtrlV KeyType = keySYN
191 KeyCtrlW KeyType = keyETB
192 KeyCtrlX KeyType = keyCAN
193 KeyCtrlY KeyType = keyEM
194 KeyCtrlZ KeyType = keySUB
195 KeyCtrlOpenBracket KeyType = keyESC // ctrl+[
196 KeyCtrlBackslash KeyType = keyFS // ctrl+\
197 KeyCtrlCloseBracket KeyType = keyGS // ctrl+]
198 KeyCtrlCaret KeyType = keyRS // ctrl+^
199 KeyCtrlUnderscore KeyType = keyUS // ctrl+_
200 KeyCtrlQuestionMark KeyType = keyDEL // ctrl+?
201)
202
203// Other keys.
204const (
205 KeyRunes KeyType = -(iota + 1)
206 KeyUp
207 KeyDown
208 KeyRight
209 KeyLeft
210 KeyShiftTab
211 KeyHome
212 KeyEnd
213 KeyPgUp
214 KeyPgDown
215 KeyCtrlPgUp
216 KeyCtrlPgDown
217 KeyDelete
218 KeyInsert
219 KeySpace
220 KeyCtrlUp
221 KeyCtrlDown
222 KeyCtrlRight
223 KeyCtrlLeft
224 KeyCtrlHome
225 KeyCtrlEnd
226 KeyShiftUp
227 KeyShiftDown
228 KeyShiftRight
229 KeyShiftLeft
230 KeyShiftHome
231 KeyShiftEnd
232 KeyCtrlShiftUp
233 KeyCtrlShiftDown
234 KeyCtrlShiftLeft
235 KeyCtrlShiftRight
236 KeyCtrlShiftHome
237 KeyCtrlShiftEnd
238 KeyF1
239 KeyF2
240 KeyF3
241 KeyF4
242 KeyF5
243 KeyF6
244 KeyF7
245 KeyF8
246 KeyF9
247 KeyF10
248 KeyF11
249 KeyF12
250 KeyF13
251 KeyF14
252 KeyF15
253 KeyF16
254 KeyF17
255 KeyF18
256 KeyF19
257 KeyF20
258)
259
260// Mappings for control keys and other special keys to friendly consts.
261var keyNames = map[KeyType]string{
262 // Control keys.
263 keyNUL: "ctrl+@", // also ctrl+` (that's ctrl+backtick)
264 keySOH: "ctrl+a",
265 keySTX: "ctrl+b",
266 keyETX: "ctrl+c",
267 keyEOT: "ctrl+d",
268 keyENQ: "ctrl+e",
269 keyACK: "ctrl+f",
270 keyBEL: "ctrl+g",
271 keyBS: "ctrl+h",
272 keyHT: "tab", // also ctrl+i
273 keyLF: "ctrl+j",
274 keyVT: "ctrl+k",
275 keyFF: "ctrl+l",
276 keyCR: "enter",
277 keySO: "ctrl+n",
278 keySI: "ctrl+o",
279 keyDLE: "ctrl+p",
280 keyDC1: "ctrl+q",
281 keyDC2: "ctrl+r",
282 keyDC3: "ctrl+s",
283 keyDC4: "ctrl+t",
284 keyNAK: "ctrl+u",
285 keySYN: "ctrl+v",
286 keyETB: "ctrl+w",
287 keyCAN: "ctrl+x",
288 keyEM: "ctrl+y",
289 keySUB: "ctrl+z",
290 keyESC: "esc",
291 keyFS: "ctrl+\\",
292 keyGS: "ctrl+]",
293 keyRS: "ctrl+^",
294 keyUS: "ctrl+_",
295 keyDEL: "backspace",
296
297 // Other keys.
298 KeyRunes: "runes",
299 KeyUp: "up",
300 KeyDown: "down",
301 KeyRight: "right",
302 KeySpace: " ", // for backwards compatibility
303 KeyLeft: "left",
304 KeyShiftTab: "shift+tab",
305 KeyHome: "home",
306 KeyEnd: "end",
307 KeyCtrlHome: "ctrl+home",
308 KeyCtrlEnd: "ctrl+end",
309 KeyShiftHome: "shift+home",
310 KeyShiftEnd: "shift+end",
311 KeyCtrlShiftHome: "ctrl+shift+home",
312 KeyCtrlShiftEnd: "ctrl+shift+end",
313 KeyPgUp: "pgup",
314 KeyPgDown: "pgdown",
315 KeyCtrlPgUp: "ctrl+pgup",
316 KeyCtrlPgDown: "ctrl+pgdown",
317 KeyDelete: "delete",
318 KeyInsert: "insert",
319 KeyCtrlUp: "ctrl+up",
320 KeyCtrlDown: "ctrl+down",
321 KeyCtrlRight: "ctrl+right",
322 KeyCtrlLeft: "ctrl+left",
323 KeyShiftUp: "shift+up",
324 KeyShiftDown: "shift+down",
325 KeyShiftRight: "shift+right",
326 KeyShiftLeft: "shift+left",
327 KeyCtrlShiftUp: "ctrl+shift+up",
328 KeyCtrlShiftDown: "ctrl+shift+down",
329 KeyCtrlShiftLeft: "ctrl+shift+left",
330 KeyCtrlShiftRight: "ctrl+shift+right",
331 KeyF1: "f1",
332 KeyF2: "f2",
333 KeyF3: "f3",
334 KeyF4: "f4",
335 KeyF5: "f5",
336 KeyF6: "f6",
337 KeyF7: "f7",
338 KeyF8: "f8",
339 KeyF9: "f9",
340 KeyF10: "f10",
341 KeyF11: "f11",
342 KeyF12: "f12",
343 KeyF13: "f13",
344 KeyF14: "f14",
345 KeyF15: "f15",
346 KeyF16: "f16",
347 KeyF17: "f17",
348 KeyF18: "f18",
349 KeyF19: "f19",
350 KeyF20: "f20",
351}
352
353// Sequence mappings.
354var sequences = map[string]Key{
355 // Arrow keys
356 "\x1b[A": {Type: KeyUp},
357 "\x1b[B": {Type: KeyDown},
358 "\x1b[C": {Type: KeyRight},
359 "\x1b[D": {Type: KeyLeft},
360 "\x1b[1;2A": {Type: KeyShiftUp},
361 "\x1b[1;2B": {Type: KeyShiftDown},
362 "\x1b[1;2C": {Type: KeyShiftRight},
363 "\x1b[1;2D": {Type: KeyShiftLeft},
364 "\x1b[OA": {Type: KeyShiftUp}, // DECCKM
365 "\x1b[OB": {Type: KeyShiftDown}, // DECCKM
366 "\x1b[OC": {Type: KeyShiftRight}, // DECCKM
367 "\x1b[OD": {Type: KeyShiftLeft}, // DECCKM
368 "\x1b[a": {Type: KeyShiftUp}, // urxvt
369 "\x1b[b": {Type: KeyShiftDown}, // urxvt
370 "\x1b[c": {Type: KeyShiftRight}, // urxvt
371 "\x1b[d": {Type: KeyShiftLeft}, // urxvt
372 "\x1b[1;3A": {Type: KeyUp, Alt: true},
373 "\x1b[1;3B": {Type: KeyDown, Alt: true},
374 "\x1b[1;3C": {Type: KeyRight, Alt: true},
375 "\x1b[1;3D": {Type: KeyLeft, Alt: true},
376
377 "\x1b[1;4A": {Type: KeyShiftUp, Alt: true},
378 "\x1b[1;4B": {Type: KeyShiftDown, Alt: true},
379 "\x1b[1;4C": {Type: KeyShiftRight, Alt: true},
380 "\x1b[1;4D": {Type: KeyShiftLeft, Alt: true},
381
382 "\x1b[1;5A": {Type: KeyCtrlUp},
383 "\x1b[1;5B": {Type: KeyCtrlDown},
384 "\x1b[1;5C": {Type: KeyCtrlRight},
385 "\x1b[1;5D": {Type: KeyCtrlLeft},
386 "\x1b[Oa": {Type: KeyCtrlUp, Alt: true}, // urxvt
387 "\x1b[Ob": {Type: KeyCtrlDown, Alt: true}, // urxvt
388 "\x1b[Oc": {Type: KeyCtrlRight, Alt: true}, // urxvt
389 "\x1b[Od": {Type: KeyCtrlLeft, Alt: true}, // urxvt
390 "\x1b[1;6A": {Type: KeyCtrlShiftUp},
391 "\x1b[1;6B": {Type: KeyCtrlShiftDown},
392 "\x1b[1;6C": {Type: KeyCtrlShiftRight},
393 "\x1b[1;6D": {Type: KeyCtrlShiftLeft},
394 "\x1b[1;7A": {Type: KeyCtrlUp, Alt: true},
395 "\x1b[1;7B": {Type: KeyCtrlDown, Alt: true},
396 "\x1b[1;7C": {Type: KeyCtrlRight, Alt: true},
397 "\x1b[1;7D": {Type: KeyCtrlLeft, Alt: true},
398 "\x1b[1;8A": {Type: KeyCtrlShiftUp, Alt: true},
399 "\x1b[1;8B": {Type: KeyCtrlShiftDown, Alt: true},
400 "\x1b[1;8C": {Type: KeyCtrlShiftRight, Alt: true},
401 "\x1b[1;8D": {Type: KeyCtrlShiftLeft, Alt: true},
402
403 // Miscellaneous keys
404 "\x1b[Z": {Type: KeyShiftTab},
405
406 "\x1b[2~": {Type: KeyInsert},
407 "\x1b[3;2~": {Type: KeyInsert, Alt: true},
408
409 "\x1b[3~": {Type: KeyDelete},
410 "\x1b[3;3~": {Type: KeyDelete, Alt: true},
411
412 "\x1b[5~": {Type: KeyPgUp},
413 "\x1b[5;3~": {Type: KeyPgUp, Alt: true},
414 "\x1b[5;5~": {Type: KeyCtrlPgUp},
415 "\x1b[5^": {Type: KeyCtrlPgUp}, // urxvt
416 "\x1b[5;7~": {Type: KeyCtrlPgUp, Alt: true},
417
418 "\x1b[6~": {Type: KeyPgDown},
419 "\x1b[6;3~": {Type: KeyPgDown, Alt: true},
420 "\x1b[6;5~": {Type: KeyCtrlPgDown},
421 "\x1b[6^": {Type: KeyCtrlPgDown}, // urxvt
422 "\x1b[6;7~": {Type: KeyCtrlPgDown, Alt: true},
423
424 "\x1b[1~": {Type: KeyHome},
425 "\x1b[H": {Type: KeyHome}, // xterm, lxterm
426 "\x1b[1;3H": {Type: KeyHome, Alt: true}, // xterm, lxterm
427 "\x1b[1;5H": {Type: KeyCtrlHome}, // xterm, lxterm
428 "\x1b[1;7H": {Type: KeyCtrlHome, Alt: true}, // xterm, lxterm
429 "\x1b[1;2H": {Type: KeyShiftHome}, // xterm, lxterm
430 "\x1b[1;4H": {Type: KeyShiftHome, Alt: true}, // xterm, lxterm
431 "\x1b[1;6H": {Type: KeyCtrlShiftHome}, // xterm, lxterm
432 "\x1b[1;8H": {Type: KeyCtrlShiftHome, Alt: true}, // xterm, lxterm
433
434 "\x1b[4~": {Type: KeyEnd},
435 "\x1b[F": {Type: KeyEnd}, // xterm, lxterm
436 "\x1b[1;3F": {Type: KeyEnd, Alt: true}, // xterm, lxterm
437 "\x1b[1;5F": {Type: KeyCtrlEnd}, // xterm, lxterm
438 "\x1b[1;7F": {Type: KeyCtrlEnd, Alt: true}, // xterm, lxterm
439 "\x1b[1;2F": {Type: KeyShiftEnd}, // xterm, lxterm
440 "\x1b[1;4F": {Type: KeyShiftEnd, Alt: true}, // xterm, lxterm
441 "\x1b[1;6F": {Type: KeyCtrlShiftEnd}, // xterm, lxterm
442 "\x1b[1;8F": {Type: KeyCtrlShiftEnd, Alt: true}, // xterm, lxterm
443
444 "\x1b[7~": {Type: KeyHome}, // urxvt
445 "\x1b[7^": {Type: KeyCtrlHome}, // urxvt
446 "\x1b[7$": {Type: KeyShiftHome}, // urxvt
447 "\x1b[7@": {Type: KeyCtrlShiftHome}, // urxvt
448
449 "\x1b[8~": {Type: KeyEnd}, // urxvt
450 "\x1b[8^": {Type: KeyCtrlEnd}, // urxvt
451 "\x1b[8$": {Type: KeyShiftEnd}, // urxvt
452 "\x1b[8@": {Type: KeyCtrlShiftEnd}, // urxvt
453
454 // Function keys, Linux console
455 "\x1b[[A": {Type: KeyF1}, // linux console
456 "\x1b[[B": {Type: KeyF2}, // linux console
457 "\x1b[[C": {Type: KeyF3}, // linux console
458 "\x1b[[D": {Type: KeyF4}, // linux console
459 "\x1b[[E": {Type: KeyF5}, // linux console
460
461 // Function keys, X11
462 "\x1bOP": {Type: KeyF1}, // vt100, xterm
463 "\x1bOQ": {Type: KeyF2}, // vt100, xterm
464 "\x1bOR": {Type: KeyF3}, // vt100, xterm
465 "\x1bOS": {Type: KeyF4}, // vt100, xterm
466
467 "\x1b[1;3P": {Type: KeyF1, Alt: true}, // vt100, xterm
468 "\x1b[1;3Q": {Type: KeyF2, Alt: true}, // vt100, xterm
469 "\x1b[1;3R": {Type: KeyF3, Alt: true}, // vt100, xterm
470 "\x1b[1;3S": {Type: KeyF4, Alt: true}, // vt100, xterm
471
472 "\x1b[11~": {Type: KeyF1}, // urxvt
473 "\x1b[12~": {Type: KeyF2}, // urxvt
474 "\x1b[13~": {Type: KeyF3}, // urxvt
475 "\x1b[14~": {Type: KeyF4}, // urxvt
476
477 "\x1b[15~": {Type: KeyF5}, // vt100, xterm, also urxvt
478
479 "\x1b[15;3~": {Type: KeyF5, Alt: true}, // vt100, xterm, also urxvt
480
481 "\x1b[17~": {Type: KeyF6}, // vt100, xterm, also urxvt
482 "\x1b[18~": {Type: KeyF7}, // vt100, xterm, also urxvt
483 "\x1b[19~": {Type: KeyF8}, // vt100, xterm, also urxvt
484 "\x1b[20~": {Type: KeyF9}, // vt100, xterm, also urxvt
485 "\x1b[21~": {Type: KeyF10}, // vt100, xterm, also urxvt
486
487 "\x1b[17;3~": {Type: KeyF6, Alt: true}, // vt100, xterm
488 "\x1b[18;3~": {Type: KeyF7, Alt: true}, // vt100, xterm
489 "\x1b[19;3~": {Type: KeyF8, Alt: true}, // vt100, xterm
490 "\x1b[20;3~": {Type: KeyF9, Alt: true}, // vt100, xterm
491 "\x1b[21;3~": {Type: KeyF10, Alt: true}, // vt100, xterm
492
493 "\x1b[23~": {Type: KeyF11}, // vt100, xterm, also urxvt
494 "\x1b[24~": {Type: KeyF12}, // vt100, xterm, also urxvt
495
496 "\x1b[23;3~": {Type: KeyF11, Alt: true}, // vt100, xterm
497 "\x1b[24;3~": {Type: KeyF12, Alt: true}, // vt100, xterm
498
499 "\x1b[1;2P": {Type: KeyF13},
500 "\x1b[1;2Q": {Type: KeyF14},
501
502 "\x1b[25~": {Type: KeyF13}, // vt100, xterm, also urxvt
503 "\x1b[26~": {Type: KeyF14}, // vt100, xterm, also urxvt
504
505 "\x1b[25;3~": {Type: KeyF13, Alt: true}, // vt100, xterm
506 "\x1b[26;3~": {Type: KeyF14, Alt: true}, // vt100, xterm
507
508 "\x1b[1;2R": {Type: KeyF15},
509 "\x1b[1;2S": {Type: KeyF16},
510
511 "\x1b[28~": {Type: KeyF15}, // vt100, xterm, also urxvt
512 "\x1b[29~": {Type: KeyF16}, // vt100, xterm, also urxvt
513
514 "\x1b[28;3~": {Type: KeyF15, Alt: true}, // vt100, xterm
515 "\x1b[29;3~": {Type: KeyF16, Alt: true}, // vt100, xterm
516
517 "\x1b[15;2~": {Type: KeyF17},
518 "\x1b[17;2~": {Type: KeyF18},
519 "\x1b[18;2~": {Type: KeyF19},
520 "\x1b[19;2~": {Type: KeyF20},
521
522 "\x1b[31~": {Type: KeyF17},
523 "\x1b[32~": {Type: KeyF18},
524 "\x1b[33~": {Type: KeyF19},
525 "\x1b[34~": {Type: KeyF20},
526
527 // Powershell sequences.
528 "\x1bOA": {Type: KeyUp, Alt: false},
529 "\x1bOB": {Type: KeyDown, Alt: false},
530 "\x1bOC": {Type: KeyRight, Alt: false},
531 "\x1bOD": {Type: KeyLeft, Alt: false},
532}
533
534// unknownInputByteMsg is reported by the input reader when an invalid
535// utf-8 byte is detected on the input. Currently, it is not handled
536// further by bubbletea. However, having this event makes it possible
537// to troubleshoot invalid inputs.
538type unknownInputByteMsg byte
539
540func (u unknownInputByteMsg) String() string {
541 return fmt.Sprintf("?%#02x?", int(u))
542}
543
544// unknownCSISequenceMsg is reported by the input reader when an
545// unrecognized CSI sequence is detected on the input. Currently, it
546// is not handled further by bubbletea. However, having this event
547// makes it possible to troubleshoot invalid inputs.
548type unknownCSISequenceMsg []byte
549
550func (u unknownCSISequenceMsg) String() string {
551 return fmt.Sprintf("?CSI%+v?", []byte(u)[2:])
552}
553
554var spaceRunes = []rune{' '}
555
556// readAnsiInputs reads keypress and mouse inputs from a TTY and produces messages
557// containing information about the key or mouse events accordingly.
558func readAnsiInputs(ctx context.Context, msgs chan<- Msg, input io.Reader) error {
559 var buf [256]byte
560
561 var leftOverFromPrevIteration []byte
562loop:
563 for {
564 // Read and block.
565 numBytes, err := input.Read(buf[:])
566 if err != nil {
567 return fmt.Errorf("error reading input: %w", err)
568 }
569 b := buf[:numBytes]
570 if leftOverFromPrevIteration != nil {
571 b = append(leftOverFromPrevIteration, b...)
572 }
573
574 // If we had a short read (numBytes < len(buf)), we're sure that
575 // the end of this read is an event boundary, so there is no doubt
576 // if we are encountering the end of the buffer while parsing a message.
577 // However, if we've succeeded in filling up the buffer, there may
578 // be more data in the OS buffer ready to be read in, to complete
579 // the last message in the input. In that case, we will retry with
580 // the left over data in the next iteration.
581 canHaveMoreData := numBytes == len(buf)
582
583 var i, w int
584 for i, w = 0, 0; i < len(b); i += w {
585 var msg Msg
586 w, msg = detectOneMsg(b[i:], canHaveMoreData)
587 if w == 0 {
588 // Expecting more bytes beyond the current buffer. Try waiting
589 // for more input.
590 leftOverFromPrevIteration = make([]byte, 0, len(b[i:])+len(buf))
591 leftOverFromPrevIteration = append(leftOverFromPrevIteration, b[i:]...)
592 continue loop
593 }
594
595 select {
596 case msgs <- msg:
597 case <-ctx.Done():
598 err := ctx.Err()
599 if err != nil {
600 err = fmt.Errorf("found context error while reading input: %w", err)
601 }
602 return err
603 }
604 }
605 leftOverFromPrevIteration = nil
606 }
607}
608
609var (
610 unknownCSIRe = regexp.MustCompile(`^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]`)
611 mouseSGRRegex = regexp.MustCompile(`(\d+);(\d+);(\d+)([Mm])`)
612)
613
614func detectOneMsg(b []byte, canHaveMoreData bool) (w int, msg Msg) {
615 // Detect mouse events.
616 // X10 mouse events have a length of 6 bytes
617 const mouseEventX10Len = 6
618 if len(b) >= mouseEventX10Len && b[0] == '\x1b' && b[1] == '[' {
619 switch b[2] {
620 case 'M':
621 return mouseEventX10Len, MouseMsg(parseX10MouseEvent(b))
622 case '<':
623 if matchIndices := mouseSGRRegex.FindSubmatchIndex(b[3:]); matchIndices != nil {
624 // SGR mouse events length is the length of the match plus the length of the escape sequence
625 mouseEventSGRLen := matchIndices[1] + 3 //nolint:gomnd
626 return mouseEventSGRLen, MouseMsg(parseSGRMouseEvent(b))
627 }
628 }
629 }
630
631 // Detect focus events.
632 var foundRF bool
633 foundRF, w, msg = detectReportFocus(b)
634 if foundRF {
635 return w, msg
636 }
637
638 // Detect bracketed paste.
639 var foundbp bool
640 foundbp, w, msg = detectBracketedPaste(b)
641 if foundbp {
642 return w, msg
643 }
644
645 // Detect escape sequence and control characters other than NUL,
646 // possibly with an escape character in front to mark the Alt
647 // modifier.
648 var foundSeq bool
649 foundSeq, w, msg = detectSequence(b)
650 if foundSeq {
651 return w, msg
652 }
653
654 // No non-NUL control character or escape sequence.
655 // If we are seeing at least an escape character, remember it for later below.
656 alt := false
657 i := 0
658 if b[0] == '\x1b' {
659 alt = true
660 i++
661 }
662
663 // Are we seeing a standalone NUL? This is not handled by detectSequence().
664 if i < len(b) && b[i] == 0 {
665 return i + 1, KeyMsg{Type: keyNUL, Alt: alt}
666 }
667
668 // Find the longest sequence of runes that are not control
669 // characters from this point.
670 var runes []rune
671 for rw := 0; i < len(b); i += rw {
672 var r rune
673 r, rw = utf8.DecodeRune(b[i:])
674 if r == utf8.RuneError || r <= rune(keyUS) || r == rune(keyDEL) || r == ' ' {
675 // Rune errors are handled below; control characters and spaces will
676 // be handled by detectSequence in the next call to detectOneMsg.
677 break
678 }
679 runes = append(runes, r)
680 if alt {
681 // We only support a single rune after an escape alt modifier.
682 i += rw
683 break
684 }
685 }
686 if i >= len(b) && canHaveMoreData {
687 // We have encountered the end of the input buffer. Alas, we can't
688 // be sure whether the data in the remainder of the buffer is
689 // complete (maybe there was a short read). Instead of sending anything
690 // dumb to the message channel, do a short read. The outer loop will
691 // handle this case by extending the buffer as necessary.
692 return 0, nil
693 }
694
695 // If we found at least one rune, we report the bunch of them as
696 // a single KeyRunes or KeySpace event.
697 if len(runes) > 0 {
698 k := Key{Type: KeyRunes, Runes: runes, Alt: alt}
699 if len(runes) == 1 && runes[0] == ' ' {
700 k.Type = KeySpace
701 }
702 return i, KeyMsg(k)
703 }
704
705 // We didn't find an escape sequence, nor a valid rune. Was this a
706 // lone escape character at the end of the input?
707 if alt && len(b) == 1 {
708 return 1, KeyMsg(Key{Type: KeyEscape})
709 }
710
711 // The character at the current position is neither an escape
712 // sequence, a valid rune start or a sole escape character. Report
713 // it as an invalid byte.
714 return 1, unknownInputByteMsg(b[0])
715}