Support multi-character input (i.e. Chinese and Japanese IME)

This commit is contained in:
Christian Rocha
2020-10-30 10:37:51 -04:00
committed by Christian Rocha
parent eae4d66917
commit fc37167e16

92
key.go
View File

@@ -35,6 +35,11 @@ import (
// fmt.Println("you pressed a!")
// }
// }
//
// Note that Key.Runes will always contain at least one character, so you can
// always safely call Key.Runes[0]. In most cases Key.Runes will only contain
// one character, though certain input method editors (most notably Chinese
// IMEs) can input multiple runes at once.
type KeyMsg Key
// String returns a friendly name for a key.
@@ -46,8 +51,8 @@ func (k *KeyMsg) String() (str string) {
if k.Alt {
str += "alt+"
}
if k.Type == KeyRune {
str += string(k.Rune)
if k.Type == KeyRunes {
str += string(k.Runes)
return str
} else if s, ok := keyNames[int(k.Type)]; ok {
str += s
@@ -58,24 +63,24 @@ func (k *KeyMsg) String() (str string) {
// IsRune returns whether or not the key is a rune.
func (k *KeyMsg) IsRune() bool {
return k.Type == KeyRune
return k.Type == KeyRunes
}
// Key contains information about a keypress.
type Key struct {
Type KeyType
Rune rune
Alt bool
Type KeyType
Runes []rune
Alt bool
}
// KeyType indicates the key pressed, such as KeyEnter or KeyBreak or
// KeyCtrlC. All other keys will be type KeyRune. To get the rune value, check
// KeyCtrlC. All other keys will be type KeyRunes. To get the rune value, check
// the Rune method on a Key struct, or use the Key.String() method:
//
// k := Key{Type: KeyRune, Rune: 'a', Alt: true}
// if k.Type == KeyRune {
// k := Key{Type: KeyRunes, Runes: []rune{'a'}, Alt: true}
// if k.Type == KeyRunes {
//
// fmt.Println(k.Rune)
// fmt.Println(k.Runes)
// // Output: a
//
// fmt.Println(k.String())
@@ -174,7 +179,7 @@ const (
// Other keys.
const (
KeyRune = -(iota + 1)
KeyRunes = -(iota + 1)
KeyUp
KeyDown
KeyRight
@@ -224,7 +229,7 @@ var keyNames = map[int]string{
keySP: "space",
keyDEL: "backspace",
KeyRune: "rune",
KeyRunes: "runes",
KeyUp: "up",
KeyDown: "down",
KeyRight: "right",
@@ -279,11 +284,11 @@ var hexes = map[string]Key{
// readInput reads keypress and mouse input from a TTY and returns a message
// containing information about the key or mouse event accordingly.
func readInput(r io.Reader) (Msg, error) {
func readInput(input io.Reader) (Msg, error) {
var buf [256]byte
// Read and block
numBytes, err := r.Read(buf[:])
numBytes, err := input.Read(buf[:])
if err != nil {
return nil, err
}
@@ -295,31 +300,48 @@ func readInput(r io.Reader) (Msg, error) {
return MouseMsg(mouseEvent), nil
}
hex := fmt.Sprintf("%x", buf[:numBytes])
// Some of these need special handling
if k, ok := hexes[hex]; ok {
return KeyMsg(k), nil
}
// Get unicode value
char, _ := utf8.DecodeRune(buf[:])
if char == utf8.RuneError {
return nil, errors.New("could not decode rune")
}
// Is it a control character?
if numBytes == 1 && char <= keyUS || char == keyDEL {
return KeyMsg(Key{Type: KeyType(char)}), nil
}
// Is it a special sequence, like an arrow key?
if k, ok := sequences[string(buf[:numBytes])]; ok {
return KeyMsg(Key{Type: k}), nil
}
// Some of these need special handling
hex := fmt.Sprintf("%x", buf[:numBytes])
if k, ok := hexes[hex]; ok {
return KeyMsg(k), nil
}
var runes []rune
b := buf[:numBytes]
// Translate input into runes. In most cases we'll receive exactly one
// rune, but there are cases, particularly when an input method editor is
// used, where we can receive multiple runes at once.
for i, w := 0, 0; i < len(b); i += w {
r, width := utf8.DecodeRune(b[i:])
if r == utf8.RuneError {
return nil, errors.New("could not decode rune")
}
runes = append(runes, r)
w = width
}
if len(runes) == 0 {
return nil, errors.New("receied 0 runes from input")
} else if len(runes) > 1 {
// We received multiple runes, so we know this isn't a control
// character, sequence, and so on.
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
}
// Is it a control character?
char := runes[0]
if numBytes == 1 && char <= keyUS || char == keyDEL {
return KeyMsg(Key{Type: KeyType(char)}), nil
}
// Is the alt key pressed? The buffer will be prefixed with an escape
// sequence if so
// sequence if so.
if numBytes > 1 && buf[0] == 0x1b {
// Now remove the initial escape sequence and re-process to get the
// character.
@@ -327,9 +349,9 @@ func readInput(r io.Reader) (Msg, error) {
if c == utf8.RuneError {
return nil, errors.New("could not decode rune after removing initial escape")
}
return KeyMsg(Key{Alt: true, Type: KeyRune, Rune: c}), nil
return KeyMsg(Key{Alt: true, Type: KeyRunes, Runes: runes}), nil
}
// Just a regular, ol' rune
return KeyMsg(Key{Type: KeyRune, Rune: char}), nil
return KeyMsg(Key{Type: KeyRunes, Runes: runes}), nil
}