Catch the alt key modifier

This commit is contained in:
Christian Rocha 2020-02-19 18:35:34 -05:00
parent f88269fb22
commit eefa9c7aa3
No known key found for this signature in database
GPG Key ID: D6CC7A16E5878018
1 changed files with 35 additions and 27 deletions

62
key.go
View File

@ -11,11 +11,16 @@ import (
type KeyMsg Key type KeyMsg Key
// String returns a friendly name for a key // String returns a friendly name for a key
func (k *KeyMsg) String() string { func (k *KeyMsg) String() (str string) {
if k.Alt {
str += "alt+"
}
if k.Type == KeyRune { if k.Type == KeyRune {
return string(k.Rune) str += string(k.Rune)
return str
} else if s, ok := keyNames[int(k.Type)]; ok { } else if s, ok := keyNames[int(k.Type)]; ok {
return s str += s
return str
} }
return "" return ""
} }
@ -29,6 +34,7 @@ func (k *KeyMsg) IsRune() bool {
type Key struct { type Key struct {
Type KeyType Type KeyType
Rune rune Rune rune
Alt bool
} }
// KeyType indicates the key pressed // KeyType indicates the key pressed
@ -179,11 +185,10 @@ var keyNames = map[int]string{
// Mapping for sequences to consts // Mapping for sequences to consts
var sequences = map[string]KeyType{ var sequences = map[string]KeyType{
"\x1b[A": KeyUp, "\x1b[A": KeyUp,
"\x1b[B": KeyDown, "\x1b[B": KeyDown,
"\x1b[C": KeyRight, "\x1b[C": KeyRight,
"\x1b[D": KeyLeft, "\x1b[D": KeyLeft,
"\x1b5B5A": KeyShiftTab,
} }
// ReadKey reads keypress input from a TTY and returns a string representation // ReadKey reads keypress input from a TTY and returns a string representation
@ -192,41 +197,44 @@ func ReadKey(r io.Reader) (Key, error) {
var buf [256]byte var buf [256]byte
// Read and block // Read and block
n, err := r.Read(buf[:]) numBytes, err := r.Read(buf[:])
if err != nil { if err != nil {
return Key{}, err return Key{}, err
} }
// Hex representation of input sequence // Shift+tab needs some very special handling
hex := fmt.Sprintf("%x", buf[:n]) if "1b5b5a" == fmt.Sprintf("%x", buf[:numBytes]) {
switch hex {
case "1b5b5a":
return Key{Type: KeyShiftTab}, nil return Key{Type: KeyShiftTab}, nil
} }
// Get rune // Get unicode value
c, _ := utf8.DecodeRune(buf[:]) char, _ := utf8.DecodeRune(buf[:])
if c == utf8.RuneError { if char == utf8.RuneError {
return Key{}, errors.New("no such rune") return Key{}, errors.New("could not decode rune")
} }
// Is it a control character? // Is it a control character?
if n == 1 && c <= keyUS || c == keyDEL { if numBytes == 1 && char <= keyUS || char == keyDEL {
return Key{Type: KeyType(c)}, nil return Key{Type: KeyType(char)}, nil
} }
// Is it a special sequence, like an arrow key? // Is it a special sequence, like an arrow key?
if k, ok := sequences[string(buf[:n])]; ok { if k, ok := sequences[string(buf[:numBytes])]; ok {
return Key{Type: k}, nil return Key{Type: k}, nil
} }
// If the first byte is an escape sequence, and we're still here, just // Is the alt key pressed? The buffer will be prefixed with an escape
// send a null to avoid sending bizarre escape sequences down the line // sequence if so
if n > 0 && buf[0] == 0x1b { if numBytes > 1 && buf[0] == 0x1b {
return Key{Type: KeyNull}, nil // Now remove the initial escape sequence and re-process to get the
// character.
c, _ := utf8.DecodeRune(buf[1:])
if c == utf8.RuneError {
return Key{}, errors.New("could not decode rune after removing initial escape")
}
return Key{Alt: true, Type: KeyRune, Rune: c}, nil
} }
// Nope, just a regular, ol' rune // Just a regular, ol' rune
return Key{Type: KeyRune, Rune: c}, nil return Key{Type: KeyRune, Rune: char}, nil
} }