| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- package tea
- import (
- "bytes"
- "sort"
- "unicode/utf8"
- )
- // extSequences is used by the map-based algorithm below. It contains
- // the sequences plus their alternatives with an escape character
- // prefixed, plus the control chars, plus the space.
- // It does not contain the NUL character, which is handled specially
- // by detectOneMsg.
- var extSequences = func() map[string]Key {
- s := map[string]Key{}
- for seq, key := range sequences {
- key := key
- s[seq] = key
- if !key.Alt {
- key.Alt = true
- s["\x1b"+seq] = key
- }
- }
- for i := keyNUL + 1; i <= keyDEL; i++ {
- if i == keyESC {
- continue
- }
- s[string([]byte{byte(i)})] = Key{Type: i}
- s[string([]byte{'\x1b', byte(i)})] = Key{Type: i, Alt: true}
- if i == keyUS {
- i = keyDEL - 1
- }
- }
- s[" "] = Key{Type: KeySpace, Runes: spaceRunes}
- s["\x1b "] = Key{Type: KeySpace, Alt: true, Runes: spaceRunes}
- s["\x1b\x1b"] = Key{Type: KeyEscape, Alt: true}
- return s
- }()
- // seqLengths is the sizes of valid sequences, starting with the
- // largest size.
- var seqLengths = func() []int {
- sizes := map[int]struct{}{}
- for seq := range extSequences {
- sizes[len(seq)] = struct{}{}
- }
- lsizes := make([]int, 0, len(sizes))
- for sz := range sizes {
- lsizes = append(lsizes, sz)
- }
- sort.Slice(lsizes, func(i, j int) bool { return lsizes[i] > lsizes[j] })
- return lsizes
- }()
- // detectSequence uses a longest prefix match over the input
- // sequence and a hash map.
- func detectSequence(input []byte) (hasSeq bool, width int, msg Msg) {
- seqs := extSequences
- for _, sz := range seqLengths {
- if sz > len(input) {
- continue
- }
- prefix := input[:sz]
- key, ok := seqs[string(prefix)]
- if ok {
- return true, sz, KeyMsg(key)
- }
- }
- // Is this an unknown CSI sequence?
- if loc := unknownCSIRe.FindIndex(input); loc != nil {
- return true, loc[1], unknownCSISequenceMsg(input[:loc[1]])
- }
- return false, 0, nil
- }
- // detectBracketedPaste detects an input pasted while bracketed
- // paste mode was enabled.
- //
- // Note: this function is a no-op if bracketed paste was not enabled
- // on the terminal, since in that case we'd never see this
- // particular escape sequence.
- func detectBracketedPaste(input []byte) (hasBp bool, width int, msg Msg) {
- // Detect the start sequence.
- const bpStart = "\x1b[200~"
- if len(input) < len(bpStart) || string(input[:len(bpStart)]) != bpStart {
- return false, 0, nil
- }
- // Skip over the start sequence.
- input = input[len(bpStart):]
- // If we saw the start sequence, then we must have an end sequence
- // as well. Find it.
- const bpEnd = "\x1b[201~"
- idx := bytes.Index(input, []byte(bpEnd))
- inputLen := len(bpStart) + idx + len(bpEnd)
- if idx == -1 {
- // We have encountered the end of the input buffer without seeing
- // the marker for the end of the bracketed paste.
- // Tell the outer loop we have done a short read and we want more.
- return true, 0, nil
- }
- // The paste is everything in-between.
- paste := input[:idx]
- // All there is in-between is runes, not to be interpreted further.
- k := Key{Type: KeyRunes, Paste: true}
- for len(paste) > 0 {
- r, w := utf8.DecodeRune(paste)
- if r != utf8.RuneError {
- k.Runes = append(k.Runes, r)
- }
- paste = paste[w:]
- }
- return true, inputLen, KeyMsg(k)
- }
|