bubbletea/key.go

133 lines
2.6 KiB
Go
Raw Normal View History

2020-01-10 16:02:04 -05:00
package tea
import (
"errors"
"io"
"unicode/utf8"
)
// KeyPressMsg contains information about a keypress
type KeyMsg Key
2020-01-10 16:02:04 -05:00
// String returns a friendly name for a key
func (k *KeyMsg) String() string {
if k.Type == KeyRune {
return string(k.Rune)
} else if s, ok := keyNames[k.Type]; ok {
return s
}
return ""
}
// IsRune returns weather or not the key is a rune
func (k *KeyMsg) IsRune() bool {
2020-01-25 00:56:53 -05:00
return k.Type == KeyRune
}
type Key struct {
Type KeyType
Rune rune
}
// KeyType indicates the key pressed
type KeyType int
// Possible keys
2020-01-10 23:46:46 -05:00
const (
KeyBreak KeyType = iota
KeyTab
KeyEnter
KeyEscape
KeyUp
KeyDown
KeyRight
KeyLeft
KeyUnitSeparator
KeyBackspace
KeyRune = -1
2020-01-10 23:46:46 -05:00
)
// Friendly key names
var keyNames = map[KeyType]string{
KeyBreak: "break",
KeyTab: "tab",
KeyEnter: "enter",
KeyEscape: "esc",
KeyUp: "up",
KeyDown: "down",
KeyRight: "right",
KeyLeft: "left",
KeyUnitSeparator: "us",
KeyBackspace: "backspace",
KeyRune: "rune",
2020-01-10 23:46:46 -05:00
}
// Control keys. I know we could do this with an iota, but the values are very
// specific, so we set the values explicitly to avoid any confusion
const (
keyETX = 3 // break, ctrl+c
keyHT = 9 // horizontal tabulation, \t
keyLF = 10 // line feed, \n
keyCR = 13 // carriage return, \r
keyESC = 27 // escape
keyUS = 31 // unit separator
keyDEL = 127 // delete. on most systems this is mapped to backspace, I hear
)
// Mapping for control keys to friendly consts
var controlKeys = map[int]KeyType{
keyETX: KeyBreak,
keyLF: KeyEnter,
keyCR: KeyEnter,
keyESC: KeyEscape,
keyUS: KeyUnitSeparator,
keyDEL: KeyBackspace,
}
// Mapping for sequences to consts
var sequences = map[string]KeyType{
"\x1b[A": KeyUp,
"\x1b[B": KeyDown,
"\x1b[C": KeyRight,
"\x1b[D": KeyLeft,
2020-01-10 23:12:25 -05:00
}
2020-01-10 16:02:04 -05:00
// ReadKey reads keypress input from a TTY and returns a string representation
// of a key
func ReadKey(r io.Reader) (Key, error) {
2020-01-10 16:02:04 -05:00
var buf [256]byte
// Read and block
2020-01-10 23:12:25 -05:00
n, err := r.Read(buf[:])
2020-01-10 16:02:04 -05:00
if err != nil {
return Key{}, err
2020-01-10 16:02:04 -05:00
}
2020-01-10 23:46:46 -05:00
// Get rune
2020-01-10 16:02:04 -05:00
c, _ := utf8.DecodeRune(buf[:])
if c == utf8.RuneError {
return Key{}, errors.New("no such rune")
2020-01-10 16:02:04 -05:00
}
2020-01-10 23:46:46 -05:00
// Is it a control character?
if n == 1 && c <= keyUS || c == keyDEL {
if k, ok := controlKeys[int(c)]; ok {
return Key{Type: k}, nil
}
}
2020-01-10 23:46:46 -05:00
if n == 1 && c <= keyUS {
if k, ok := controlKeys[int(c)]; ok {
return Key{Type: k}, nil
2020-01-10 23:46:46 -05:00
}
}
// Is it a special sequence, like an arrow key?
if k, ok := sequences[string(buf[:n])]; ok {
return Key{Type: k}, nil
2020-01-10 23:46:46 -05:00
}
// Nope, just a regular, ol' rune
return Key{Type: KeyRune, Rune: c}, nil
2020-01-10 16:02:04 -05:00
}