]> git.lizzy.rs Git - micro.git/blob - internal/action/keytree.go
Allow configuration for info/term bindings
[micro.git] / internal / action / keytree.go
1 package action
2
3 import (
4         "log"
5
6         "github.com/zyedidia/tcell"
7 )
8
9 type PaneKeyAction func(Pane) bool
10 type PaneMouseAction func(Pane, *tcell.EventMouse) bool
11 type PaneKeyAnyAction func(Pane, []KeyEvent) bool
12
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
16 // node)
17 type KeyTreeNode struct {
18         children map[Event]*KeyTreeNode
19
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
23         // returned.
24         actions []TreeAction
25 }
26
27 func NewKeyTreeNode() *KeyTreeNode {
28         n := new(KeyTreeNode)
29         n.children = make(map[Event]*KeyTreeNode)
30         n.actions = []TreeAction{}
31         return n
32 }
33
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
38         action PaneKeyAction
39         any    PaneKeyAnyAction
40         mouse  PaneMouseAction
41
42         modes []ModeConstraint
43 }
44
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.
49 type KeyTree struct {
50         root  *KeyTreeNode
51         modes map[string]bool
52
53         cursor KeyTreeCursor
54 }
55
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
59 // mouse events)
60 type KeyTreeCursor struct {
61         node *KeyTreeNode
62
63         wildcards []KeyEvent
64         mouseInfo *tcell.EventMouse
65 }
66
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,
69 // or AnyAction)
70 func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction {
71         if a.action != nil {
72                 return a.action
73         } else if a.any != nil {
74                 return func(p Pane) bool {
75                         return a.any(p, k.wildcards)
76                 }
77         } else if a.mouse != nil {
78                 return func(p Pane) bool {
79                         return a.mouse(p, k.mouseInfo)
80                 }
81         }
82
83         return nil
84 }
85
86 // NewKeyTree allocates and returns an empty key tree
87 func NewKeyTree() *KeyTree {
88         root := NewKeyTreeNode()
89         tree := new(KeyTree)
90
91         tree.root = root
92         tree.modes = make(map[string]bool)
93         tree.cursor = KeyTreeCursor{
94                 node:      root,
95                 wildcards: []KeyEvent{},
96                 mouseInfo: nil,
97         }
98
99         return tree
100 }
101
102 // A ModeConstraint specifies that an action can only be executed
103 // while a certain mode is enabled or disabled.
104 type ModeConstraint struct {
105         mode     string
106         disabled bool
107 }
108
109 // RegisterKeyBinding registers a PaneKeyAction with an Event.
110 func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) {
111         k.registerBinding(e, TreeAction{
112                 action: a,
113                 any:    nil,
114                 mouse:  nil,
115                 modes:  nil,
116         })
117 }
118
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{
123                 action: nil,
124                 any:    a,
125                 mouse:  nil,
126                 modes:  nil,
127         })
128 }
129
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{
134                 action: nil,
135                 any:    nil,
136                 mouse:  a,
137                 modes:  nil,
138         })
139 }
140
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]
145                 if !ok {
146                         newNode = NewKeyTreeNode()
147                         k.root.children[e] = newNode
148                 }
149                 newNode.actions = append(newNode.actions, a)
150         case KeySequenceEvent:
151                 n := k.root
152                 for _, key := range ev.keys {
153                         newNode, ok := n.children[key]
154                         if !ok {
155                                 newNode = NewKeyTreeNode()
156                                 n.children[key] = newNode
157                         }
158
159                         n = newNode
160                 }
161                 n.actions = append(n.actions, a)
162         }
163 }
164
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
175 // timeout).
176 func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) {
177         n := k.cursor.node
178         c, ok := n.children[e]
179         log.Println("NEXT EVENT", e, len(n.children), ok)
180
181         if !ok {
182                 return nil, false
183         }
184
185         more := len(c.children) > 0
186
187         k.cursor.node = c
188
189         switch ev := e.(type) {
190         case KeyEvent:
191                 if ev.any {
192                         k.cursor.wildcards = append(k.cursor.wildcards, ev)
193                 }
194         case MouseEvent:
195                 k.cursor.mouseInfo = mouse
196         }
197
198         if len(c.actions) > 0 {
199                 // check if actions are active
200                 for _, a := range c.actions {
201                         active := true
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 {
206                                         active = false
207                                 }
208                         }
209
210                         if active {
211                                 // the first active action to be found is returned
212                                 return k.cursor.MakeClosure(a), more
213                         }
214                 }
215         }
216
217         return nil, more
218 }
219
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
225 }
226
227 // DeleteBinding removes any currently active actions associated with the
228 // given event.
229 func (k *KeyTree) DeleteBinding(e Event) {
230
231 }
232
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) {
236
237 }
238
239 // SetMode enables or disabled a given mode
240 func (k *KeyTree) SetMode(mode string, en bool) {
241         k.modes[mode] = en
242 }
243
244 // HasMode returns if the given mode is currently active
245 func (k *KeyTree) HasMode(mode string) bool {
246         return k.modes[mode]
247 }