aerc/config/bindings.go
y0ast 03650474e2 update tcell to v2 and enable TrueColor support
Also update to the tcell v2 PaletteColor api, which should keep the chosen
theme of the user intact.

Note, that if $TRUECOLOR is defined and a truecolor given, aerc will now stop
clipping the value to one of the theme colors.
Generally this is desired behaviour though.
2020-12-18 07:23:22 +01:00

311 lines
10 KiB
Go

package config
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
"github.com/gdamore/tcell/v2"
)
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, fmt.Errorf("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}
}