170 lines
3.1 KiB
Go
170 lines
3.1 KiB
Go
package cbind
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell"
|
|
)
|
|
|
|
// Modifier labels
|
|
const (
|
|
LabelCtrl = "ctrl"
|
|
LabelAlt = "alt"
|
|
LabelMeta = "meta"
|
|
LabelShift = "shift"
|
|
)
|
|
|
|
var fullKeyNames = map[string]string{
|
|
"backspace2": "Backspace",
|
|
"pgup": "PageUp",
|
|
"pgdn": "PageDown",
|
|
"esc": "Escape",
|
|
}
|
|
|
|
// Decode decodes a string as a key or combination of keys.
|
|
func Decode(s string) (mod tcell.ModMask, key tcell.Key, ch rune, err error) {
|
|
if len(s) == 0 {
|
|
return 0, 0, 0, errors.New("empty string")
|
|
}
|
|
|
|
// Special case for plus rune decoding
|
|
if s[len(s)-1:] == "+" {
|
|
key = tcell.KeyRune
|
|
ch = '+'
|
|
|
|
if len(s) == 1 {
|
|
return mod, key, ch, nil
|
|
} else if len(s) == 2 {
|
|
return 0, 0, 0, fmt.Errorf("invalid key %s", s)
|
|
} else {
|
|
s = s[:len(s)-2]
|
|
}
|
|
}
|
|
|
|
split := strings.Split(s, "+")
|
|
DECODEPIECE:
|
|
for _, piece := range split {
|
|
// Decode modifiers
|
|
pieceLower := strings.ToLower(piece)
|
|
switch pieceLower {
|
|
case LabelCtrl:
|
|
mod |= tcell.ModCtrl
|
|
continue
|
|
case LabelAlt:
|
|
mod |= tcell.ModAlt
|
|
continue
|
|
case LabelMeta:
|
|
mod |= tcell.ModMeta
|
|
continue
|
|
case LabelShift:
|
|
mod |= tcell.ModShift
|
|
continue
|
|
}
|
|
|
|
// Decode key
|
|
for shortKey, fullKey := range fullKeyNames {
|
|
if pieceLower == strings.ToLower(fullKey) {
|
|
pieceLower = shortKey
|
|
break
|
|
}
|
|
}
|
|
switch pieceLower {
|
|
case "backspace":
|
|
key = tcell.KeyBackspace2
|
|
continue
|
|
case "space", "spacebar":
|
|
key = tcell.KeyRune
|
|
ch = ' '
|
|
continue
|
|
}
|
|
for k, keyName := range tcell.KeyNames {
|
|
if pieceLower == strings.ToLower(keyName) {
|
|
key = k
|
|
continue DECODEPIECE
|
|
}
|
|
}
|
|
|
|
// Decode rune
|
|
if len(piece) > 1 {
|
|
return 0, 0, 0, fmt.Errorf("unknown key name or invalid rune: %s", piece)
|
|
}
|
|
|
|
key = tcell.KeyRune
|
|
ch = rune(piece[0])
|
|
}
|
|
|
|
return mod, key, ch, nil
|
|
}
|
|
|
|
// Encode encodes a key or combination of keys a string.
|
|
func Encode(mod tcell.ModMask, key tcell.Key, ch rune) (string, error) {
|
|
var b strings.Builder
|
|
var wrote bool
|
|
|
|
// Encode modifiers
|
|
if mod&tcell.ModCtrl != 0 {
|
|
b.WriteString(upperFirst(LabelCtrl))
|
|
wrote = true
|
|
}
|
|
if mod&tcell.ModAlt != 0 {
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteString(upperFirst(LabelAlt))
|
|
wrote = true
|
|
}
|
|
if mod&tcell.ModMeta != 0 {
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteString(upperFirst(LabelMeta))
|
|
wrote = true
|
|
}
|
|
if mod&tcell.ModShift != 0 {
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteString(upperFirst(LabelShift))
|
|
wrote = true
|
|
}
|
|
|
|
if ch == ' ' {
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteString("Space")
|
|
} else if key != tcell.KeyRune {
|
|
// Encode key
|
|
keyName := tcell.KeyNames[key]
|
|
if keyName == "" {
|
|
return "", fmt.Errorf("invalid or unknown key: %d", key)
|
|
}
|
|
fullKeyName := fullKeyNames[strings.ToLower(keyName)]
|
|
if fullKeyName != "" {
|
|
keyName = fullKeyName
|
|
}
|
|
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteString(keyName)
|
|
} else {
|
|
// Encode rune
|
|
if wrote {
|
|
b.WriteRune('+')
|
|
}
|
|
b.WriteRune(ch)
|
|
}
|
|
|
|
return b.String(), nil
|
|
}
|
|
|
|
func upperFirst(s string) string {
|
|
if len(s) <= 1 {
|
|
return strings.ToUpper(s)
|
|
}
|
|
return strings.ToUpper(s[:1]) + s[1:]
|
|
}
|