-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeymap.go
191 lines (162 loc) · 4.25 KB
/
keymap.go
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
package peco
import (
"sort"
"strings"
"time"
"github.com/lestrrat/go-pdebug"
"github.com/nsf/termbox-go"
"github.com/peco/peco/internal/keyseq"
"github.com/pkg/errors"
"golang.org/x/net/context"
)
// NewKeymap creates a new Keymap struct
func NewKeymap(config map[string]string, actions map[string][]string) Keymap {
return Keymap{
Config: config,
Action: actions,
seq: keyseq.New(),
}
}
func (km Keymap) Sequence() Keyseq {
return km.seq
}
const isTopLevelActionCall = "peco.isTopLevelActionCall"
func (km Keymap) ExecuteAction(ctx context.Context, state *Peco, ev termbox.Event) (err error) {
if pdebug.Enabled {
g := pdebug.Marker("Keymap.ExecuteAction %v", ev).BindError(&err)
defer g.End()
}
a := km.LookupAction(ev)
if a == nil {
return errors.New("action not found")
}
ctx = context.WithValue(ctx, isTopLevelActionCall, true)
a.Execute(ctx, state, ev)
return nil
}
// LookupAction returns the appropriate action for the given termbox event
func (km Keymap) LookupAction(ev termbox.Event) Action {
modifier := keyseq.ModNone
if (ev.Mod & termbox.ModAlt) != 0 {
modifier = keyseq.ModAlt
}
key := keyseq.Key{
Modifier: modifier,
Key: ev.Key,
Ch: ev.Ch,
}
action, err := km.seq.AcceptKey(key)
switch err {
case nil:
// Found an action!
if pdebug.Enabled {
pdebug.Printf("Keymap.Handler: Fetched action")
}
return wrapClearSequence(action.(Action))
case keyseq.ErrInSequence:
if pdebug.Enabled {
pdebug.Printf("Keymap.Handler: Waiting for more commands...")
}
return wrapRememberSequence(ActionFunc(doNothing))
default:
if pdebug.Enabled {
pdebug.Printf("Keymap.Handler: Defaulting to doAcceptChar")
}
return wrapClearSequence(ActionFunc(doAcceptChar))
}
}
func wrapRememberSequence(a Action) Action {
return ActionFunc(func(ctx context.Context, state *Peco, ev termbox.Event) {
if s, err := keyseq.EventToString(ev); err == nil {
seq := state.Inputseq()
seq.Add(s)
state.Hub().SendStatusMsg(strings.Join(seq.KeyNames(), " "))
}
a.Execute(ctx, state, ev)
})
}
func wrapClearSequence(a Action) Action {
return ActionFunc(func(ctx context.Context, state *Peco, ev termbox.Event) {
seq := state.Inputseq()
if s, err := keyseq.EventToString(ev); err == nil {
seq.Add(s)
}
if seq.Len() > 0 {
msg := strings.Join(seq.KeyNames(), " ")
state.Hub().SendStatusMsgAndClear(msg, 500*time.Millisecond)
seq.Reset()
}
a.Execute(ctx, state, ev)
})
}
const maxResolveActionDepth = 100
func (km Keymap) resolveActionName(name string, depth int) (Action, error) {
if depth >= maxResolveActionDepth {
return nil, errors.Errorf("could not resolve %s: deep recursion", name)
}
// Can it be resolved via regular nameToActions ?
v, ok := nameToActions[name]
if ok {
return v, nil
}
// Can it be resolved via combined actions?
l, ok := km.Action[name]
if ok {
actions := []Action{}
for _, actionName := range l {
child, err := km.resolveActionName(actionName, depth+1)
if err != nil {
return nil, err
}
actions = append(actions, child)
}
v = makeCombinedAction(actions...)
nameToActions[name] = v
return v, nil
}
return nil, errors.Errorf("could not resolve %s: no such action", name)
}
// ApplyKeybinding applies all of the custom key bindings on top of
// the default key bindings
func (km *Keymap) ApplyKeybinding() error {
k := km.seq
k.Clear()
// Copy the map
kb := map[string]Action{}
for s, a := range defaultKeyBinding {
kb[s] = a
}
// munge the map using config
for s, as := range km.Config {
if as == "-" {
delete(kb, s)
continue
}
v, err := km.resolveActionName(as, 0)
if err != nil {
return errors.Wrapf(err, "failed to resolve action name %s", as)
}
kb[s] = v
}
// now compile using kb
// there's no need to do this, but we sort keys here just to make
// debugging easier
keys := make([]string, 0, len(kb))
for s := range kb {
keys = append(keys, s)
}
sort.Strings(keys)
for _, s := range keys {
a := kb[s]
list, err := keyseq.ToKeyList(s)
if err != nil {
return errors.Wrapf(err, "urnknown key %s: %s", s, err)
}
k.Add(list, a)
}
return errors.Wrap(k.Compile(), "failed to compile key binding patterns")
}
// TODO: this needs to be fixed.
func (km Keymap) hasModifierMaps() bool {
return false
}