]> git.lizzy.rs Git - micro.git/blob - internal/action/keytree.go
Basic implementation of KeyTree
[micro.git] / internal / action / keytree.go
1 package action
2
3 type KeyAction func(Pane) bool
4 type MouseAction func(Pane, *MouseEvent) bool
5
6 type KeyAnyAction func(Pane, []KeyEvent) bool
7
8 // A KeyTreeNode stores a single node in the KeyTree (trie). The
9 // children are stored as a map, and any node may store a list of
10 // actions (the list will be nil if no actions correspond to a certain
11 // node)
12 type KeyTreeNode struct {
13         children map[Event]*KeyTreeNode
14
15         // Only one of these actions may be active in the current
16         // mode, and only one will be returned. If multiple actions
17         // are active, it is undefined which one will be the one
18         // returned.
19         actions []TreeAction
20 }
21
22 func NewKeyTreeNode() *KeyTreeNode {
23         n := new(KeyTreeNode)
24         n.children = make(map[Event]*KeyTreeNode)
25         n.actions = []TreeAction{}
26         return n
27 }
28
29 // A TreeAction stores an action, and a set of mode constraints for
30 // the action to be active.
31 type TreeAction struct {
32         // only one of these can be non-nil
33         action KeyAction
34         any    KeyAnyAction
35         mouse  MouseAction
36
37         modes []ModeConstraint
38 }
39
40 // A KeyTree is a data structure for storing keybindings. It maps
41 // key events to actions, and maintains a set of currently enabled
42 // modes, which affects the action that is returned for a key event.
43 // The tree acts like a Trie for Events to handle sequence events.
44 type KeyTree struct {
45         root  *KeyTreeNode
46         modes map[string]bool
47
48         cursor KeyTreeCursor
49 }
50
51 // A KeyTreeCursor keeps track of the current location within the
52 // tree, and stores any information from previous events that may
53 // be needed to execute the action (values of wildcard events or
54 // mouse events)
55 type KeyTreeCursor struct {
56         node *KeyTreeNode
57
58         wildcards []KeyEvent
59         mouseInfo *MouseEvent
60 }
61
62 // MakeClosure uses the information stored in a key tree cursor to construct
63 // a KeyAction from a TreeAction (which may have a KeyAction, MouseAction,
64 // or AnyAction)
65 func (k *KeyTreeCursor) MakeClosure(a TreeAction) KeyAction {
66         if a.action != nil {
67                 return a.action
68         } else if a.any != nil {
69                 return func(p Pane) bool {
70                         return a.any(p, k.wildcards)
71                 }
72         } else if a.mouse != nil {
73                 return func(p Pane) bool {
74                         return a.mouse(p, k.mouseInfo)
75                 }
76         }
77
78         return nil
79 }
80
81 // NewKeyTree allocates and returns an empty key tree
82 func NewKeyTree() *KeyTree {
83         root := new(KeyTreeNode)
84         tree := new(KeyTree)
85
86         tree.root = root
87         tree.modes = make(map[string]bool)
88         tree.cursor = KeyTreeCursor{
89                 node:      root,
90                 wildcards: []KeyEvent{},
91                 mouseInfo: nil,
92         }
93
94         return tree
95 }
96
97 // A ModeConstraint specifies that an action can only be executed
98 // while a certain mode is enabled or disabled.
99 type ModeConstraint struct {
100         mode     string
101         disabled bool
102 }
103
104 // RegisterKeyBinding registers a KeyAction with an Event.
105 func (k *KeyTree) RegisterKeyBinding(e Event, a KeyAction) {
106         k.registerBinding(e, TreeAction{
107                 action: a,
108                 any:    nil,
109                 mouse:  nil,
110                 modes:  nil,
111         })
112 }
113
114 // RegisterKeyAnyBinding registers a KeyAnyAction with an Event.
115 // The event should contain an "any" event.
116 func (k *KeyTree) RegisterKeyAnyBinding(e Event, a KeyAnyAction) {
117         k.registerBinding(e, TreeAction{
118                 action: nil,
119                 any:    a,
120                 mouse:  nil,
121                 modes:  nil,
122         })
123 }
124
125 // RegisterMouseBinding registers a MouseAction with an Event.
126 // The event should contain a mouse event.
127 func (k *KeyTree) RegisterMouseBinding(e Event, a MouseAction) {
128         k.registerBinding(e, TreeAction{
129                 action: nil,
130                 any:    nil,
131                 mouse:  a,
132                 modes:  nil,
133         })
134 }
135
136 func (k *KeyTree) registerBinding(e Event, a TreeAction) {
137         switch ev := e.(type) {
138         case *KeyEvent, *MouseEvent:
139                 n, ok := k.root.children[e]
140                 if !ok {
141                         newNode := NewKeyTreeNode()
142                         k.root.children[e] = newNode
143                 }
144                 n.actions = append(n.actions, a)
145         case *KeySequenceEvent:
146                 n := k.root
147                 for _, key := range ev.keys {
148                         newNode, ok := n.children[key]
149                         if !ok {
150                                 newNode := NewKeyTreeNode()
151                                 n.children[key] = newNode
152                         }
153
154                         n = newNode
155                 }
156                 n.actions = append(n.actions, a)
157         }
158 }
159
160 // NextEvent returns the action for the current sequence where e is the next
161 // event. Even if the action was registered as a KeyAnyAction or MouseAction,
162 // it will be returned as a KeyAction closure where the appropriate arguments
163 // have been provided.
164 // If no action is associated with the given Event, or mode constraints are not
165 // met for that action, nil is returned.
166 // A boolean is returned to indicate if there is a conflict with this action. A
167 // conflict occurs when there is an active action for this event but there are
168 // bindings associated with further sequences starting with this event. The
169 // calling function can decide what to do about the conflict (e.g. use a
170 // timeout).
171 func (k *KeyTree) NextEvent(e Event) (KeyAction, bool) {
172         n := k.cursor.node
173         c, ok := n.children[e]
174         if !ok {
175                 return nil, false
176         }
177
178         more := len(c.children) > 0
179
180         if len(c.actions) > 0 {
181                 // check if actions are active
182                 for _, a := range c.actions {
183                         active := true
184                         for _, mc := range a.modes {
185                                 // if any mode constraint is not met, the action is not active
186                                 hasMode := k.modes[mc.mode]
187                                 if hasMode != mc.disabled {
188                                         active = false
189                                 }
190                         }
191
192                         if active {
193                                 // the first active action to be found is returned
194                                 return k.cursor.MakeClosure(a), more
195                         }
196                 }
197         }
198
199         return nil, more
200 }
201
202 // Reset sets the current sequence back to the initial value.
203 func (k *KeyTree) Reset() {
204         k.cursor.node = k.root
205         k.cursor.wildcards = []KeyEvent{}
206         k.cursor.mouseInfo = nil
207 }
208
209 // DeleteBinding removes any currently active actions associated with the
210 // given event.
211 func (k *KeyTree) DeleteBinding(e Event) {
212
213 }
214
215 // DeleteAllBindings removes all actions associated with the given event,
216 // regardless of whether they are active or not.
217 func (k *KeyTree) DeleteAllBindings(e Event) {
218
219 }
220
221 // SetMode enables or disabled a given mode
222 func (k *KeyTree) SetMode(mode string, en bool) {
223         k.modes[mode] = en
224 }
225
226 // HasMode returns if the given mode is currently active
227 func (k *KeyTree) HasMode(mode string) bool {
228         return k.modes[mode]
229 }