package action import ( "log" "github.com/zyedidia/tcell" ) type PaneKeyAction func(Pane) bool type PaneMouseAction func(Pane, *tcell.EventMouse) bool type PaneKeyAnyAction func(Pane, []KeyEvent) bool // A KeyTreeNode stores a single node in the KeyTree (trie). The // children are stored as a map, and any node may store a list of // actions (the list will be nil if no actions correspond to a certain // node) type KeyTreeNode struct { children map[Event]*KeyTreeNode // Only one of these actions may be active in the current // mode, and only one will be returned. If multiple actions // are active, it is undefined which one will be the one // returned. actions []TreeAction } func NewKeyTreeNode() *KeyTreeNode { n := new(KeyTreeNode) n.children = make(map[Event]*KeyTreeNode) n.actions = []TreeAction{} return n } // A TreeAction stores an action, and a set of mode constraints for // the action to be active. type TreeAction struct { // only one of these can be non-nil action PaneKeyAction any PaneKeyAnyAction mouse PaneMouseAction modes []ModeConstraint } // A KeyTree is a data structure for storing keybindings. It maps // key events to actions, and maintains a set of currently enabled // modes, which affects the action that is returned for a key event. // The tree acts like a Trie for Events to handle sequence events. type KeyTree struct { root *KeyTreeNode modes map[string]bool cursor KeyTreeCursor } // A KeyTreeCursor keeps track of the current location within the // tree, and stores any information from previous events that may // be needed to execute the action (values of wildcard events or // mouse events) type KeyTreeCursor struct { node *KeyTreeNode wildcards []KeyEvent mouseInfo *tcell.EventMouse } // MakeClosure uses the information stored in a key tree cursor to construct // a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction, // or AnyAction) func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction { if a.action != nil { return a.action } else if a.any != nil { return func(p Pane) bool { return a.any(p, k.wildcards) } } else if a.mouse != nil { return func(p Pane) bool { return a.mouse(p, k.mouseInfo) } } return nil } // NewKeyTree allocates and returns an empty key tree func NewKeyTree() *KeyTree { root := NewKeyTreeNode() tree := new(KeyTree) tree.root = root tree.modes = make(map[string]bool) tree.cursor = KeyTreeCursor{ node: root, wildcards: []KeyEvent{}, mouseInfo: nil, } return tree } // A ModeConstraint specifies that an action can only be executed // while a certain mode is enabled or disabled. type ModeConstraint struct { mode string disabled bool } // RegisterKeyBinding registers a PaneKeyAction with an Event. func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) { k.registerBinding(e, TreeAction{ action: a, any: nil, mouse: nil, modes: nil, }) } // RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event. // The event should contain an "any" event. func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) { k.registerBinding(e, TreeAction{ action: nil, any: a, mouse: nil, modes: nil, }) } // RegisterMouseBinding registers a PaneMouseAction with an Event. // The event should contain a mouse event. func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) { k.registerBinding(e, TreeAction{ action: nil, any: nil, mouse: a, modes: nil, }) } func (k *KeyTree) registerBinding(e Event, a TreeAction) { switch ev := e.(type) { case KeyEvent, MouseEvent: newNode, ok := k.root.children[e] if !ok { newNode = NewKeyTreeNode() k.root.children[e] = newNode } newNode.actions = append(newNode.actions, a) case KeySequenceEvent: n := k.root for _, key := range ev.keys { newNode, ok := n.children[key] if !ok { newNode = NewKeyTreeNode() n.children[key] = newNode } n = newNode } n.actions = append(n.actions, a) } } // NextEvent returns the action for the current sequence where e is the next // event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction, // it will be returned as a PaneKeyAction closure where the appropriate arguments // have been provided. // If no action is associated with the given Event, or mode constraints are not // met for that action, nil is returned. // A boolean is returned to indicate if there is a conflict with this action. A // conflict occurs when there is an active action for this event but there are // bindings associated with further sequences starting with this event. The // calling function can decide what to do about the conflict (e.g. use a // timeout). func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) { n := k.cursor.node c, ok := n.children[e] log.Println("NEXT EVENT", e, len(n.children), ok) if !ok { return nil, false } more := len(c.children) > 0 k.cursor.node = c switch ev := e.(type) { case KeyEvent: if ev.any { k.cursor.wildcards = append(k.cursor.wildcards, ev) } case MouseEvent: k.cursor.mouseInfo = mouse } if len(c.actions) > 0 { // check if actions are active for _, a := range c.actions { active := true for _, mc := range a.modes { // if any mode constraint is not met, the action is not active hasMode := k.modes[mc.mode] if hasMode != mc.disabled { active = false } } if active { // the first active action to be found is returned return k.cursor.MakeClosure(a), more } } } return nil, more } // ResetEvents sets the current sequence back to the initial value. func (k *KeyTree) ResetEvents() { k.cursor.node = k.root k.cursor.wildcards = []KeyEvent{} k.cursor.mouseInfo = nil } // DeleteBinding removes any currently active actions associated with the // given event. func (k *KeyTree) DeleteBinding(e Event) { } // DeleteAllBindings removes all actions associated with the given event, // regardless of whether they are active or not. func (k *KeyTree) DeleteAllBindings(e Event) { } // SetMode enables or disabled a given mode func (k *KeyTree) SetMode(mode string, en bool) { k.modes[mode] = en } // HasMode returns if the given mode is currently active func (k *KeyTree) HasMode(mode string) bool { return k.modes[mode] }