]> git.lizzy.rs Git - micro.git/blob - internal/action/keytree.go
More style improvements
[micro.git] / internal / action / keytree.go
1 package action
2
3 import (
4         "bytes"
5
6         "github.com/zyedidia/tcell/v2"
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         recordedEvents []Event
64         wildcards      []KeyEvent
65         mouseInfo      *tcell.EventMouse
66 }
67
68 // MakeClosure uses the information stored in a key tree cursor to construct
69 // a PaneKeyAction from a TreeAction (which may have a PaneKeyAction, PaneMouseAction,
70 // or AnyAction)
71 func (k *KeyTreeCursor) MakeClosure(a TreeAction) PaneKeyAction {
72         if a.action != nil {
73                 return a.action
74         } else if a.any != nil {
75                 return func(p Pane) bool {
76                         return a.any(p, k.wildcards)
77                 }
78         } else if a.mouse != nil {
79                 return func(p Pane) bool {
80                         return a.mouse(p, k.mouseInfo)
81                 }
82         }
83
84         return nil
85 }
86
87 // NewKeyTree allocates and returns an empty key tree
88 func NewKeyTree() *KeyTree {
89         root := NewKeyTreeNode()
90         tree := new(KeyTree)
91
92         tree.root = root
93         tree.modes = make(map[string]bool)
94         tree.cursor = KeyTreeCursor{
95                 node:      root,
96                 wildcards: []KeyEvent{},
97                 mouseInfo: nil,
98         }
99
100         return tree
101 }
102
103 // A ModeConstraint specifies that an action can only be executed
104 // while a certain mode is enabled or disabled.
105 type ModeConstraint struct {
106         mode     string
107         disabled bool
108 }
109
110 // RegisterKeyBinding registers a PaneKeyAction with an Event.
111 func (k *KeyTree) RegisterKeyBinding(e Event, a PaneKeyAction) {
112         k.registerBinding(e, TreeAction{
113                 action: a,
114                 any:    nil,
115                 mouse:  nil,
116                 modes:  nil,
117         })
118 }
119
120 // RegisterKeyAnyBinding registers a PaneKeyAnyAction with an Event.
121 // The event should contain an "any" event.
122 func (k *KeyTree) RegisterKeyAnyBinding(e Event, a PaneKeyAnyAction) {
123         k.registerBinding(e, TreeAction{
124                 action: nil,
125                 any:    a,
126                 mouse:  nil,
127                 modes:  nil,
128         })
129 }
130
131 // RegisterMouseBinding registers a PaneMouseAction with an Event.
132 // The event should contain a mouse event.
133 func (k *KeyTree) RegisterMouseBinding(e Event, a PaneMouseAction) {
134         k.registerBinding(e, TreeAction{
135                 action: nil,
136                 any:    nil,
137                 mouse:  a,
138                 modes:  nil,
139         })
140 }
141
142 func (k *KeyTree) registerBinding(e Event, a TreeAction) {
143         switch ev := e.(type) {
144         case KeyEvent, MouseEvent, RawEvent:
145                 newNode, ok := k.root.children[e]
146                 if !ok {
147                         newNode = NewKeyTreeNode()
148                         k.root.children[e] = newNode
149                 }
150                 // newNode.actions = append(newNode.actions, a)
151                 newNode.actions = []TreeAction{a}
152         case KeySequenceEvent:
153                 n := k.root
154                 for _, key := range ev.keys {
155                         newNode, ok := n.children[key]
156                         if !ok {
157                                 newNode = NewKeyTreeNode()
158                                 n.children[key] = newNode
159                         }
160
161                         n = newNode
162                 }
163                 // n.actions = append(n.actions, a)
164                 n.actions = []TreeAction{a}
165         }
166 }
167
168 // NextEvent returns the action for the current sequence where e is the next
169 // event. Even if the action was registered as a PaneKeyAnyAction or PaneMouseAction,
170 // it will be returned as a PaneKeyAction closure where the appropriate arguments
171 // have been provided.
172 // If no action is associated with the given Event, or mode constraints are not
173 // met for that action, nil is returned.
174 // A boolean is returned to indicate if there is a conflict with this action. A
175 // conflict occurs when there is an active action for this event but there are
176 // bindings associated with further sequences starting with this event. The
177 // calling function can decide what to do about the conflict (e.g. use a
178 // timeout).
179 func (k *KeyTree) NextEvent(e Event, mouse *tcell.EventMouse) (PaneKeyAction, bool) {
180         n := k.cursor.node
181         c, ok := n.children[e]
182
183         if !ok {
184                 return nil, false
185         }
186
187         more := len(c.children) > 0
188
189         k.cursor.node = c
190
191         k.cursor.recordedEvents = append(k.cursor.recordedEvents, e)
192
193         switch ev := e.(type) {
194         case KeyEvent:
195                 if ev.any {
196                         k.cursor.wildcards = append(k.cursor.wildcards, ev)
197                 }
198         case MouseEvent:
199                 k.cursor.mouseInfo = mouse
200         }
201
202         if len(c.actions) > 0 {
203                 // check if actions are active
204                 for _, a := range c.actions {
205                         active := true
206                         for _, mc := range a.modes {
207                                 // if any mode constraint is not met, the action is not active
208                                 hasMode := k.modes[mc.mode]
209                                 if hasMode != mc.disabled {
210                                         active = false
211                                 }
212                         }
213
214                         if active {
215                                 // the first active action to be found is returned
216                                 return k.cursor.MakeClosure(a), more
217                         }
218                 }
219         }
220
221         return nil, more
222 }
223
224 // ResetEvents sets the current sequence back to the initial value.
225 func (k *KeyTree) ResetEvents() {
226         k.cursor.node = k.root
227         k.cursor.wildcards = []KeyEvent{}
228         k.cursor.recordedEvents = []Event{}
229         k.cursor.mouseInfo = nil
230 }
231
232 // RecordedEventsStr returns the list of recorded events as a string
233 func (k *KeyTree) RecordedEventsStr() string {
234         buf := &bytes.Buffer{}
235         for _, e := range k.cursor.recordedEvents {
236                 buf.WriteString(e.Name())
237         }
238         return buf.String()
239 }
240
241 // DeleteBinding removes any currently active actions associated with the
242 // given event.
243 func (k *KeyTree) DeleteBinding(e Event) {
244
245 }
246
247 // DeleteAllBindings removes all actions associated with the given event,
248 // regardless of whether they are active or not.
249 func (k *KeyTree) DeleteAllBindings(e Event) {
250
251 }
252
253 // SetMode enables or disabled a given mode
254 func (k *KeyTree) SetMode(mode string, en bool) {
255         k.modes[mode] = en
256 }
257
258 // HasMode returns if the given mode is currently active
259 func (k *KeyTree) HasMode(mode string) bool {
260         return k.modes[mode]
261 }