From fc37167e160f1aefc24b5650de13d33529f5c8ac Mon Sep 17 00:00:00 2001 From: Christian Rocha Date: Fri, 30 Oct 2020 10:37:51 -0400 Subject: [PATCH] Support multi-character input (i.e. Chinese and Japanese IME) --- key.go | 92 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 35 deletions(-) diff --git a/key.go b/key.go index 17e5da0..8865d3c 100644 --- a/key.go +++ b/key.go @@ -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 }