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 []*Binding const ( BINDING_FOUND = iota BINDING_INCOMPLETE BINDING_NOT_FOUND ) type BindingSearchResult int func NewKeyBindings() *KeyBindings { return &KeyBindings{} } func (bindings *KeyBindings) Add(binding *Binding) { // TODO: Search for conflicts? *bindings = append(*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 { 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["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} keyNam
../../ranger/config/commands.py