File: keytree.go

package info (click to toggle)
micro 2.0.15-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,128 kB
  • sloc: sh: 265; makefile: 77; xml: 53
file content (261 lines) | stat: -rw-r--r-- 6,793 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
package action

import (
	"bytes"

	"github.com/micro-editor/tcell/v2"
)

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

	recordedEvents []Event
	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, RawEvent:
		newNode, ok := k.root.children[e]
		if !ok {
			newNode = NewKeyTreeNode()
			k.root.children[e] = newNode
		}
		// newNode.actions = append(newNode.actions, a)
		newNode.actions = []TreeAction{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)
		n.actions = []TreeAction{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]

	if !ok {
		return nil, false
	}

	more := len(c.children) > 0

	k.cursor.node = c

	k.cursor.recordedEvents = append(k.cursor.recordedEvents, e)

	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.recordedEvents = []Event{}
	k.cursor.mouseInfo = nil
}

// RecordedEventsStr returns the list of recorded events as a string
func (k *KeyTree) RecordedEventsStr() string {
	buf := &bytes.Buffer{}
	for _, e := range k.cursor.recordedEvents {
		buf.WriteString(e.Name())
	}
	return buf.String()
}

// 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]
}