elvish/pkg/ui/key.go

243 lines
5.0 KiB
Go
Raw Normal View History

2017-05-22 07:33:38 +08:00
package ui
2013-09-27 20:45:45 +08:00
2016-02-12 20:31:24 +08:00
import (
2016-09-15 20:54:43 +08:00
"bytes"
"errors"
2016-02-12 20:31:24 +08:00
"fmt"
"strings"
2016-09-15 20:54:43 +08:00
2019-12-24 04:00:59 +08:00
"github.com/elves/elvish/pkg/parse"
"github.com/xiaq/persistent/hash"
2016-02-12 20:31:24 +08:00
)
var ErrKeyMustBeString = errors.New("key must be key or string value")
2016-02-12 20:31:24 +08:00
// Key represents a single keyboard input, typically assembled from a escape
// sequence.
type Key struct {
Rune rune
Mod Mod
}
// K constructs a new Key.
func K(r rune, mods ...Mod) Key {
var mod Mod
for _, m := range mods {
mod |= m
}
return Key{r, mod}
}
2016-09-15 20:54:43 +08:00
// Default is used in the key binding table to indicate default binding.
var Default = Key{DefaultBindingRune, 0}
2015-02-25 05:56:38 +08:00
// Mod represents a modifier key.
type Mod byte
2015-02-25 05:56:38 +08:00
// Values for Mod.
const (
2015-02-25 05:56:38 +08:00
// Shift is the shift modifier. It is only applied to special keys (e.g.
// Shift-F1). For instance 'A' and '@' which are typically entered with the
// shift key pressed, are not considered to be shift-modified.
Shift Mod = 1 << iota
2015-02-25 05:56:38 +08:00
// Alt is the alt modifier, traditionally known as the meta modifier.
Alt
Ctrl
)
const functionKeyOffset = 1000
2016-09-15 20:54:43 +08:00
// Special negative runes to represent function keys, used in the Rune field of
// the Key struct.
const (
// DefaultBindingRune is a special value to represent default binding.
DefaultBindingRune rune = iota - functionKeyOffset
F1
2016-09-15 20:54:43 +08:00
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
Up
Down
Right
Left
Home
Insert
Delete
End
PageUp
PageDown
// Some function key names are just aliases for their ASCII representation
Tab = '\t'
Enter = '\n'
Backspace = 0x7f
)
// functionKey stores the names of function keys, in the same order they appeared above.
2016-09-15 20:54:43 +08:00
var functionKeyNames = [...]string{
"Default",
2016-09-15 20:54:43 +08:00
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
"Up", "Down", "Right", "Left",
"Home", "Insert", "Delete", "End", "PageUp", "PageDown",
2016-09-15 20:54:43 +08:00
}
// keyNames stores the name of function keys with a positive rune.
var keyNames = map[rune]string{
Tab: "Tab", Enter: "Enter", Backspace: "Backspace",
}
func (k Key) Kind() string {
return "edit:Key"
}
func (k Key) Equal(other interface{}) bool {
return k == other
}
func (k Key) Hash() uint32 {
h := hash.DJBInit
h = hash.DJBCombine(h, uint32(k.Rune))
h = hash.DJBCombine(h, uint32(k.Mod))
return h
}
func (k Key) Repr(int) string {
return "(edit:key " + parse.Quote(k.String()) + ")"
}
2016-09-15 20:54:43 +08:00
func (k Key) String() string {
var b bytes.Buffer
2014-01-16 09:24:14 +08:00
if k.Mod&Ctrl != 0 {
2016-09-15 20:54:43 +08:00
b.WriteString("Ctrl-")
2013-09-27 20:45:45 +08:00
}
2014-01-16 09:24:14 +08:00
if k.Mod&Alt != 0 {
2016-09-15 20:54:43 +08:00
b.WriteString("Alt-")
2013-09-27 20:45:45 +08:00
}
2014-03-10 00:12:12 +08:00
if k.Mod&Shift != 0 {
2016-09-15 20:54:43 +08:00
b.WriteString("Shift-")
2014-03-10 00:12:12 +08:00
}
2014-03-10 19:06:22 +08:00
if k.Rune > 0 {
2015-02-25 05:56:38 +08:00
if name, ok := keyNames[k.Rune]; ok {
2016-09-15 20:54:43 +08:00
b.WriteString(name)
} else {
2016-09-15 20:54:43 +08:00
b.WriteRune(k.Rune)
}
2013-09-27 20:45:45 +08:00
} else {
i := int(k.Rune + functionKeyOffset)
if i >= len(functionKeyNames) {
fmt.Fprintf(&b, "(bad function key %d)", k.Rune)
} else {
b.WriteString(functionKeyNames[i])
}
2013-09-27 20:45:45 +08:00
}
2016-09-15 20:54:43 +08:00
return b.String()
2013-09-27 20:45:45 +08:00
}
2016-02-12 20:31:24 +08:00
// modifierByName maps a name to an modifier. It is used for parsing keys where
// the modifier string is first turned to lower case, so that all of C, c,
// CTRL, Ctrl and ctrl can represent the Ctrl modifier.
var modifierByName = map[string]Mod{
"s": Shift, "shift": Shift,
"a": Alt, "alt": Alt,
"m": Alt, "meta": Alt,
"c": Ctrl, "ctrl": Ctrl,
}
// parseKey parses a key. The syntax is:
//
// Key = { Mod ('+' | '-') } BareKey
//
// BareKey = FunctionKeyName | SingleRune
func parseKey(s string) (Key, error) {
var k Key
// parse modifiers
for {
i := strings.IndexAny(s, "+-")
if i == -1 {
break
}
modname := strings.ToLower(s[:i])
mod, ok := modifierByName[modname]
if !ok {
return Key{}, fmt.Errorf("bad modifier: %q", modname)
}
k.Mod |= mod
s = s[i+1:]
}
if len(s) == 1 {
k.Rune = rune(s[0])
// XXX The following assumptions about keys with Ctrl are not checked
// with all terminals.
if k.Mod&Ctrl != 0 {
// Keys with Ctrl as one of the modifiers and a single ASCII letter
// as the base rune do not distinguish between cases. So we
// normalize the base rune to upper case.
if 'a' <= k.Rune && k.Rune <= 'z' {
k.Rune += 'A' - 'a'
}
// Tab is equivalent to Ctrl-I and Ctrl-J is equivalent to Enter.
// Normalize Ctrl-I to Tab and Ctrl-J to Enter.
if k.Rune == 'I' {
k.Mod &= ^Ctrl
k.Rune = Tab
} else if k.Rune == 'J' {
k.Mod &= ^Ctrl
k.Rune = Enter
}
}
2016-02-12 20:31:24 +08:00
return k, nil
}
for r, name := range keyNames {
if s == name {
k.Rune = r
return k, nil
}
}
for i, name := range functionKeyNames {
2016-02-12 20:31:24 +08:00
if s == name {
k.Rune = rune(i - functionKeyOffset)
2016-02-12 20:31:24 +08:00
return k, nil
}
}
return Key{}, fmt.Errorf("bad key: %q", s)
}
// ToKey converts an Elvish Value to a Key. If the passed Value is not Key or
2016-09-15 20:54:43 +08:00
// String, it throws an error.
2020-01-13 06:45:42 +08:00
func ToKey(k interface{}) (Key, error) {
switch k := k.(type) {
case Key:
2020-01-13 06:45:42 +08:00
return k, nil
case string:
2020-01-13 06:45:42 +08:00
return parseKey(k)
default:
2020-01-13 06:45:42 +08:00
return Key{}, ErrKeyMustBeString
2016-09-15 20:54:43 +08:00
}
2016-02-12 20:31:24 +08:00
}
// Keys implements sort.Interface.
type Keys []Key
func (ks Keys) Len() int { return len(ks) }
func (ks Keys) Swap(i, j int) { ks[i], ks[j] = ks[j], ks[i] }
func (ks Keys) Less(i, j int) bool {
return ks[i].Mod < ks[j].Mod ||
(ks[i].Mod == ks[j].Mod && ks[i].Rune < ks[j].Rune)
}