310 lines
10 KiB
Go
310 lines
10 KiB
Go
package config
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/gdamore/tcell"
|
|
)
|
|
|
|
type KeyStroke struct {
|
|
Key tcell.Key
|
|
Rune rune
|
|
}
|
|
|
|
type Binding struct {
|
|
Output []KeyStroke
|
|
Input []KeyStroke
|
|
}
|
|
|
|
type KeyBindings struct {
|
|
bindings []*Binding
|
|
|
|
// If false, disable global keybindings in this context
|
|
Globals bool
|
|
// Which key opens the ex line (default is :)
|
|
ExKey KeyStroke
|
|
}
|
|
|
|
const (
|
|
BINDING_FOUND = iota
|
|
BINDING_INCOMPLETE
|
|
BINDING_NOT_FOUND
|
|
)
|
|
|
|
type BindingSearchResult int
|
|
|
|
func NewKeyBindings() *KeyBindings {
|
|
return &KeyBindings{
|
|
ExKey: KeyStroke{tcell.KeyRune, ':'},
|
|
Globals: true,
|
|
}
|
|
}
|
|
|
|
func MergeBindings(bindings ...*KeyBindings) *KeyBindings {
|
|
merged := NewKeyBindings()
|
|
for _, b := range bindings {
|
|
merged.bindings = append(merged.bindings, b.bindings...)
|
|
}
|
|
merged.ExKey = bindings[0].ExKey
|
|
merged.Globals = bindings[0].Globals
|
|
return merged
|
|
}
|
|
|
|
func (bindings *KeyBindings) Add(binding *Binding) {
|
|
// TODO: Search for conflicts?
|
|
bindings.bindings = append(bindings.bindings, binding)
|
|
}
|
|
|
|
func (bindings *KeyBindings) GetBinding(
|
|
input []KeyStroke) (BindingSearchResult, []KeyStroke) {
|
|
|
|
incomplete := false
|
|
// TODO: This could probably be a sorted list to speed things up
|
|
// TODO: Deal with bindings that share a prefix
|
|
for _, binding := range bindings.bindings {
|
|
if len(binding.Input) < len(input) {
|
|
continue
|
|
}
|
|
for i, stroke := range input {
|
|
if stroke.Key != binding.Input[i].Key {
|
|
goto next
|
|
}
|
|
if stroke.Key == tcell.KeyRune &&
|
|
stroke.Rune != binding.Input[i].Rune {
|
|
|
|
goto next
|
|
}
|
|
}
|
|
if len(binding.Input) != len(input) {
|
|
incomplete = true
|
|
} else {
|
|
return BINDING_FOUND, binding.Output
|
|
}
|
|
next:
|
|
}
|
|
if incomplete {
|
|
return BINDING_INCOMPLETE, nil
|
|
}
|
|
return BINDING_NOT_FOUND, nil
|
|
}
|
|
|
|
var (
|
|
keyNames map[string]KeyStroke
|
|
)
|
|
|
|
func ParseKeyStrokes(keystrokes string) ([]KeyStroke, error) {
|
|
var strokes []KeyStroke
|
|
buf := bytes.NewBufferString(keystrokes)
|
|
for {
|
|
tok, _, err := buf.ReadRune()
|
|
if err == io.EOF {
|
|
break
|
|
} else if err != nil {
|
|
return nil, err
|
|
}
|
|
// TODO: make it possible to bind to < or > themselves (and default to
|
|
// switching accounts)
|
|
switch tok {
|
|
case '<':
|
|
name, err := buf.ReadString(byte('>'))
|
|
if err == io.EOF {
|
|
return nil, errors.New("Expecting '>'")
|
|
} else if err != nil {
|
|
return nil, err
|
|
} else if name == ">" {
|
|
return nil, errors.New("Expected a key name")
|
|
}
|
|
name = name[:len(name)-1]
|
|
if key, ok := keyNames[strings.ToLower(name)]; ok {
|
|
strokes = append(strokes, key)
|
|
} else {
|
|
return nil, errors.New(fmt.Sprintf("Unknown key '%s'", name))
|
|
}
|
|
case '>':
|
|
return nil, errors.New("Found '>' without '<'")
|
|
default:
|
|
strokes = append(strokes, KeyStroke{
|
|
Key: tcell.KeyRune,
|
|
Rune: tok,
|
|
})
|
|
}
|
|
}
|
|
return strokes, nil
|
|
}
|
|
|
|
func ParseBinding(input, output string) (*Binding, error) {
|
|
in, err := ParseKeyStrokes(input)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
out, err := ParseKeyStrokes(output)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Binding{
|
|
Input: in,
|
|
Output: out,
|
|
}, nil
|
|
}
|
|
|
|
func init() {
|
|
keyNames = make(map[string]KeyStroke)
|
|
keyNames["space"] = KeyStroke{tcell.KeyRune, ' '}
|
|
keyNames["semicolon"] = KeyStroke{tcell.KeyRune, ';'}
|
|
keyNames["enter"] = KeyStroke{tcell.KeyEnter, 0}
|
|
keyNames["up"] = KeyStroke{tcell.KeyUp, 0}
|
|
keyNames["down"] = KeyStroke{tcell.KeyDown, 0}
|
|
keyNames["right"] = KeyStroke{tcell.KeyRight, 0}
|
|
keyNames["left"] = KeyStroke{tcell.KeyLeft, 0}
|
|
keyNames["upleft"] = KeyStroke{tcell.KeyUpLeft, 0}
|
|
keyNames["upright"] = KeyStroke{tcell.KeyUpRight, 0}
|
|
keyNames["downleft"] = KeyStroke{tcell.KeyDownLeft, 0}
|
|
keyNames["downright"] = KeyStroke{tcell.KeyDownRight, 0}
|
|
keyNames["center"] = KeyStroke{tcell.KeyCenter, 0}
|
|
keyNames["pgup"] = KeyStroke{tcell.KeyPgUp, 0}
|
|
keyNames["pgdn"] = KeyStroke{tcell.KeyPgDn, 0}
|
|
keyNames["home"] = KeyStroke{tcell.KeyHome, 0}
|
|
keyNames["end"] = KeyStroke{tcell.KeyEnd, 0}
|
|
keyNames["insert"] = KeyStroke{tcell.KeyInsert, 0}
|
|
keyNames["delete"] = KeyStroke{tcell.KeyDelete, 0}
|
|
keyNames["help"] = KeyStroke{tcell.KeyHelp, 0}
|
|
keyNames["exit"] = KeyStroke{tcell.KeyExit, 0}
|
|
keyNames["clear"] = KeyStroke{tcell.KeyClear, 0}
|
|
keyNames["cancel"] = KeyStroke{tcell.KeyCancel, 0}
|
|
keyNames["print"] = KeyStroke{tcell.KeyPrint, 0}
|
|
keyNames["pause"] = KeyStroke{tcell.KeyPause, 0}
|
|
keyNames["backtab"] = KeyStroke{tcell.KeyBacktab, 0}
|
|
keyNames["f1"] = KeyStroke{tcell.KeyF1, 0}
|
|
keyNames["f2"] = KeyStroke{tcell.KeyF2, 0}
|
|
keyNames["f3"] = KeyStroke{tcell.KeyF3, 0}
|
|
keyNames["f4"] = KeyStroke{tcell.KeyF4, 0}
|
|
keyNames["f5"] = KeyStroke{tcell.KeyF5, 0}
|
|
keyNames["f6"] = KeyStroke{tcell.KeyF6, 0}
|
|
keyNames["f7"] = KeyStroke{tcell.KeyF7, 0}
|
|
keyNames["f8"] = KeyStroke{tcell.KeyF8, 0}
|
|
keyNames["f9"] = KeyStroke{tcell.KeyF9, 0}
|
|
keyNames["f10"] = KeyStroke{tcell.KeyF10, 0}
|
|
keyNames["f11"] = KeyStroke{tcell.KeyF11, 0}
|
|
keyNames["f12"] = KeyStroke{tcell.KeyF12, 0}
|
|
keyNames["f13"] = KeyStroke{tcell.KeyF13, 0}
|
|
keyNames["f14"] = KeyStroke{tcell.KeyF14, 0}
|
|
keyNames["f15"] = KeyStroke{tcell.KeyF15, 0}
|
|
keyNames["f16"] = KeyStroke{tcell.KeyF16, 0}
|
|
keyNames["f17"] = KeyStroke{tcell.KeyF17, 0}
|
|
keyNames["f18"] = KeyStroke{tcell.KeyF18, 0}
|
|
keyNames["f19"] = KeyStroke{tcell.KeyF19, 0}
|
|
keyNames["f20"] = KeyStroke{tcell.KeyF20, 0}
|
|
keyNames["f21"] = KeyStroke{tcell.KeyF21, 0}
|
|
keyNames["f22"] = KeyStroke{tcell.KeyF22, 0}
|
|
keyNames["f23"] = KeyStroke{tcell.KeyF23, 0}
|
|
keyNames["f24"] = KeyStroke{tcell.KeyF24, 0}
|
|
keyNames["f25"] = KeyStroke{tcell.KeyF25, 0}
|
|
keyNames["f26"] = KeyStroke{tcell.KeyF26, 0}
|
|
keyNames["f27"] = KeyStroke{tcell.KeyF27, 0}
|
|
keyNames["f28"] = KeyStroke{tcell.KeyF28, 0}
|
|
keyNames["f29"] = KeyStroke{tcell.KeyF29, 0}
|
|
keyNames["f30"] = KeyStroke{tcell.KeyF30, 0}
|
|
keyNames["f31"] = KeyStroke{tcell.KeyF31, 0}
|
|
keyNames["f32"] = KeyStroke{tcell.KeyF32, 0}
|
|
keyNames["f33"] = KeyStroke{tcell.KeyF33, 0}
|
|
keyNames["f34"] = KeyStroke{tcell.KeyF34, 0}
|
|
keyNames["f35"] = KeyStroke{tcell.KeyF35, 0}
|
|
keyNames["f36"] = KeyStroke{tcell.KeyF36, 0}
|
|
keyNames["f37"] = KeyStroke{tcell.KeyF37, 0}
|
|
keyNames["f38"] = KeyStroke{tcell.KeyF38, 0}
|
|
keyNames["f39"] = KeyStroke{tcell.KeyF39, 0}
|
|
keyNames["f40"] = KeyStroke{tcell.KeyF40, 0}
|
|
keyNames["f41"] = KeyStroke{tcell.KeyF41, 0}
|
|
keyNames["f42"] = KeyStroke{tcell.KeyF42, 0}
|
|
keyNames["f43"] = KeyStroke{tcell.KeyF43, 0}
|
|
keyNames["f44"] = KeyStroke{tcell.KeyF44, 0}
|
|
keyNames["f45"] = KeyStroke{tcell.KeyF45, 0}
|
|
keyNames["f46"] = KeyStroke{tcell.KeyF46, 0}
|
|
keyNames["f47"] = KeyStroke{tcell.KeyF47, 0}
|
|
keyNames["f48"] = KeyStroke{tcell.KeyF48, 0}
|
|
keyNames["f49"] = KeyStroke{tcell.KeyF49, 0}
|
|
keyNames["f50"] = KeyStroke{tcell.KeyF50, 0}
|
|
keyNames["f51"] = KeyStroke{tcell.KeyF51, 0}
|
|
keyNames["f52"] = KeyStroke{tcell.KeyF52, 0}
|
|
keyNames["f53"] = KeyStroke{tcell.KeyF53, 0}
|
|
keyNames["f54"] = KeyStroke{tcell.KeyF54, 0}
|
|
keyNames["f55"] = KeyStroke{tcell.KeyF55, 0}
|
|
keyNames["f56"] = KeyStroke{tcell.KeyF56, 0}
|
|
keyNames["f57"] = KeyStroke{tcell.KeyF57, 0}
|
|
keyNames["f58"] = KeyStroke{tcell.KeyF58, 0}
|
|
keyNames["f59"] = KeyStroke{tcell.KeyF59, 0}
|
|
keyNames["f60"] = KeyStroke{tcell.KeyF60, 0}
|
|
keyNames["f61"] = KeyStroke{tcell.KeyF61, 0}
|
|
keyNames["f62"] = KeyStroke{tcell.KeyF62, 0}
|
|
keyNames["f63"] = KeyStroke{tcell.KeyF63, 0}
|
|
keyNames["f64"] = KeyStroke{tcell.KeyF64, 0}
|
|
keyNames["c-space"] = KeyStroke{tcell.KeyCtrlSpace, 0}
|
|
keyNames["c-a"] = KeyStroke{tcell.KeyCtrlA, 0}
|
|
keyNames["c-b"] = KeyStroke{tcell.KeyCtrlB, 0}
|
|
keyNames["c-c"] = KeyStroke{tcell.KeyCtrlC, 0}
|
|
keyNames["c-d"] = KeyStroke{tcell.KeyCtrlD, 0}
|
|
keyNames["c-e"] = KeyStroke{tcell.KeyCtrlE, 0}
|
|
keyNames["c-f"] = KeyStroke{tcell.KeyCtrlF, 0}
|
|
keyNames["c-g"] = KeyStroke{tcell.KeyCtrlG, 0}
|
|
keyNames["c-h"] = KeyStroke{tcell.KeyCtrlH, 0}
|
|
keyNames["c-i"] = KeyStroke{tcell.KeyCtrlI, 0}
|
|
keyNames["c-j"] = KeyStroke{tcell.KeyCtrlJ, 0}
|
|
keyNames["c-k"] = KeyStroke{tcell.KeyCtrlK, 0}
|
|
keyNames["c-l"] = KeyStroke{tcell.KeyCtrlL, 0}
|
|
keyNames["c-m"] = KeyStroke{tcell.KeyCtrlM, 0}
|
|
keyNames["c-n"] = KeyStroke{tcell.KeyCtrlN, 0}
|
|
keyNames["c-o"] = KeyStroke{tcell.KeyCtrlO, 0}
|
|
keyNames["c-p"] = KeyStroke{tcell.KeyCtrlP, 0}
|
|
keyNames["c-q"] = KeyStroke{tcell.KeyCtrlQ, 0}
|
|
keyNames["c-r"] = KeyStroke{tcell.KeyCtrlR, 0}
|
|
keyNames["c-s"] = KeyStroke{tcell.KeyCtrlS, 0}
|
|
keyNames["c-t"] = KeyStroke{tcell.KeyCtrlT, 0}
|
|
keyNames["c-u"] = KeyStroke{tcell.KeyCtrlU, 0}
|
|
keyNames["c-v"] = KeyStroke{tcell.KeyCtrlV, 0}
|
|
keyNames["c-w"] = KeyStroke{tcell.KeyCtrlW, 0}
|
|
keyNames["c-x"] = KeyStroke{tcell.KeyCtrlX, rune(tcell.KeyCAN)}
|
|
keyNames["c-y"] = KeyStroke{tcell.KeyCtrlY, 0} // TODO: runes for the rest
|
|
keyNames["c-z"] = KeyStroke{tcell.KeyCtrlZ, 0}
|
|
keyNames["c-]"] = KeyStroke{tcell.KeyCtrlLeftSq, 0}
|
|
keyNames["c-\\"] = KeyStroke{tcell.KeyCtrlBackslash, 0}
|
|
keyNames["c-["] = KeyStroke{tcell.KeyCtrlRightSq, 0}
|
|
keyNames["c-^"] = KeyStroke{tcell.KeyCtrlCarat, 0}
|
|
keyNames["c-_"] = KeyStroke{tcell.KeyCtrlUnderscore, 0}
|
|
keyNames["NUL"] = KeyStroke{tcell.KeyNUL, 0}
|
|
keyNames["SOH"] = KeyStroke{tcell.KeySOH, 0}
|
|
keyNames["STX"] = KeyStroke{tcell.KeySTX, 0}
|
|
keyNames["ETX"] = KeyStroke{tcell.KeyETX, 0}
|
|
keyNames["EOT"] = KeyStroke{tcell.KeyEOT, 0}
|
|
keyNames["ENQ"] = KeyStroke{tcell.KeyENQ, 0}
|
|
keyNames["ACK"] = KeyStroke{tcell.KeyACK, 0}
|
|
keyNames["BEL"] = KeyStroke{tcell.KeyBEL, 0}
|
|
keyNames["BS"] = KeyStroke{tcell.KeyBS, 0}
|
|
keyNames["tab"] = KeyStroke{tcell.KeyTAB, 0}
|
|
keyNames["LF"] = KeyStroke{tcell.KeyLF, 0}
|
|
keyNames["VT"] = KeyStroke{tcell.KeyVT, 0}
|
|
keyNames["FF"] = KeyStroke{tcell.KeyFF, 0}
|
|
keyNames["CR"] = KeyStroke{tcell.KeyCR, 0}
|
|
keyNames["SO"] = KeyStroke{tcell.KeySO, 0}
|
|
keyNames["SI"] = KeyStroke{tcell.KeySI, 0}
|
|
keyNames["DLE"] = KeyStroke{tcell.KeyDLE, 0}
|
|
keyNames["DC1"] = KeyStroke{tcell.KeyDC1, 0}
|
|
keyNames["DC2"] = KeyStroke{tcell.KeyDC2, 0}
|
|
keyNames["DC3"] = KeyStroke{tcell.KeyDC3, 0}
|
|
keyNames["DC4"] = KeyStroke{tcell.KeyDC4, 0}
|
|
keyNames["NAK"] = KeyStroke{tcell.KeyNAK, 0}
|
|
keyNames["SYN"] = KeyStroke{tcell.KeySYN, 0}
|
|
keyNames["ETB"] = KeyStroke{tcell.KeyETB, 0}
|
|
keyNames["CAN"] = KeyStroke{tcell.KeyCAN, 0}
|
|
keyNames["EM"] = KeyStroke{tcell.KeyEM, 0}
|
|
keyNames["SUB"] = KeyStroke{tcell.KeySUB, 0}
|
|
keyNames["ESC"] = KeyStroke{tcell.KeyESC, 0}
|
|
keyNames["FS"] = KeyStroke{tcell.KeyFS, 0}
|
|
keyNames["GS"] = KeyStroke{tcell.KeyGS, 0}
|
|
keyNames["RS"] = KeyStroke{tcell.KeyRS, 0}
|
|
keyNames["US"] = KeyStroke{tcell.KeyUS, 0}
|
|
keyNames["DEL"] = KeyStroke{tcell.KeyDEL, 0}
|
|
}
|