6 "github.com/zyedidia/tcell"
9 type PaneKeyAction func(Pane) bool
10 type PaneMouseAction func(Pane, *tcell.EventMouse) bool
11 type PaneKeyAnyAction func(Pane, []KeyEvent) bool
13 // A KeyTreeNode stores a single node in the KeyTree (trie). The
14 // children are stored as a map, and any node may store a list of
15 // actions (the list will be nil if no actions correspond to a certain
17 type KeyTreeNode struct {
18 children map[Event]*KeyTreeNode
20 // Only one of these actions may be active in the current
21 // mode, and only one will be returned. If multiple actions
22 // are active, it is undefined which one will be the one
27 func NewKeyTreeNode() *KeyTreeNode {
29 n.children = make(map[Event]*KeyTreeNode)
30 n.actions = []TreeAction{}
34 // A TreeAction stores an action, and a set of mode constraints for
35 // the action to be active.
36 type TreeAction struct {
37 // only one of these can be non-nil
42 modes []ModeConstraint
45 // A KeyTree is a data structure for storing keybindings. It maps
46 // key events to actions, and maintains a set of currently enabled
47 // modes, which affects the action that is returned for a key event.
48 // The tree acts like a Trie for Events to handle sequence events.
56 // A KeyTreeCursor keeps track of the current location within the
57 // tree, and stores any information from previous events that may
58 // be needed to execute the action (values of wildcard events or
60 type KeyTreeCursor struct {
64 mouseInfo *tcell.EventMouse
67 // MakeClosure uses the information stored in a key tree cursor to construct
68 // a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction,
70 func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction {
73 } else if a.any != nil {
74 return func(p Pane) bool {
75 return a.any(p, k.wildcards)
77 } else if a.mouse != nil {
78 return func(p Pane) bool {
79 return a.mouse(p, k.mouseInfo)
86 // NewKeyTree allocates and returns an empty key tree
87 func NewKeyTree() *KeyTree {
88 root := NewKeyTreeNode()
92 tree.modes = make(map[string]bool)
93 tree.cursor = KeyTreeCursor{
95 wildcards: []KeyEvent{},
102 // A ModeConstraint specifies that an action can only be executed
103 // while a certain mode is enabled or disabled.
104 type ModeConstraint struct {
109 // RegisterKeyBinding registers a PaneKeyAction with an Event.
110 func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) {
111 k.registerBinding(e, TreeAction{
119 // RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event.
120 // The event should contain an "any" event.
121 func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) {
122 k.registerBinding(e, TreeAction{
130 // RegisterMouseBinding registers a PaneMouseAction with an Event.
131 // The event should contain a mouse event.
132 func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) {
133 k.registerBinding(e, TreeAction{
141 func (k *KeyTree) registerBinding(e Event, a TreeAction) {
142 switch ev := e.(type) {
143 case KeyEvent, MouseEvent:
144 newNode, ok := k.root.children[e]
146 newNode = NewKeyTreeNode()
147 k.root.children[e] = newNode
149 newNode.actions = append(newNode.actions, a)
150 case KeySequenceEvent:
152 for _, key := range ev.keys {
153 newNode, ok := n.children[key]
155 newNode = NewKeyTreeNode()
156 n.children[key] = newNode
161 n.actions = append(n.actions, a)
165 // NextEvent returns the action for the current sequence where e is the next
166 // event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction,
167 // it will be returned as a PaneKeyAction closure where the appropriate arguments
168 // have been provided.
169 // If no action is associated with the given Event, or mode constraints are not
170 // met for that action, nil is returned.
171 // A boolean is returned to indicate if there is a conflict with this action. A
172 // conflict occurs when there is an active action for this event but there are
173 // bindings associated with further sequences starting with this event. The
174 // calling function can decide what to do about the conflict (e.g. use a
176 func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) {
178 c, ok := n.children[e]
179 log.Println("NEXT EVENT", e, len(n.children), ok)
185 more := len(c.children) > 0
189 switch ev := e.(type) {
192 k.cursor.wildcards = append(k.cursor.wildcards, ev)
195 k.cursor.mouseInfo = mouse
198 if len(c.actions) > 0 {
199 // check if actions are active
200 for _, a := range c.actions {
202 for _, mc := range a.modes {
203 // if any mode constraint is not met, the action is not active
204 hasMode := k.modes[mc.mode]
205 if hasMode != mc.disabled {
211 // the first active action to be found is returned
212 return k.cursor.MakeClosure(a), more
220 // ResetEvents sets the current sequence back to the initial value.
221 func (k *KeyTree) ResetEvents() {
222 k.cursor.node = k.root
223 k.cursor.wildcards = []KeyEvent{}
224 k.cursor.mouseInfo = nil
227 // DeleteBinding removes any currently active actions associated with the
229 func (k *KeyTree) DeleteBinding(e Event) {
233 // DeleteAllBindings removes all actions associated with the given event,
234 // regardless of whether they are active or not.
235 func (k *KeyTree) DeleteAllBindings(e Event) {
239 // SetMode enables or disabled a given mode
240 func (k *KeyTree) SetMode(mode string, en bool) {
244 // HasMode returns if the given mode is currently active
245 func (k *KeyTree) HasMode(mode string) bool {