/
keymap.go
146 lines (122 loc) · 3.18 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
package peco
import (
"fmt"
"os"
"strings"
"time"
"github.com/nsf/termbox-go"
"github.com/peco/peco/internal/keyseq"
)
// NewKeymap creates a new Keymap struct
func NewKeymap(config map[string]string, actions map[string][]string) Keymap {
return Keymap{config, actions, keyseq.New()}
}
// 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, ev.Key, ev.Ch}
action, err := km.seq.AcceptKey(key)
switch err {
case nil:
// Found an action!
trace("Keymap.Handler: Fetched action")
return wrapClearSequence(action.(Action))
case keyseq.ErrInSequence:
trace("Keymap.Handler: Waiting for more commands...")
return wrapRememberSequence(ActionFunc(doNothing))
default:
trace("Keymap.Handler: Defaulting to doAcceptChar")
return wrapClearSequence(ActionFunc(doAcceptChar))
}
}
func wrapRememberSequence(a Action) Action {
return ActionFunc(func(i *Input, ev termbox.Event) {
s, err := keyseq.EventToString(ev)
if err == nil {
i.currentKeySeq = append(i.currentKeySeq, s)
i.SendStatusMsg(strings.Join(i.currentKeySeq, " "))
}
a.Execute(i, ev)
})
}
func wrapClearSequence(a Action) Action {
return ActionFunc(func(i *Input, ev termbox.Event) {
s, err := keyseq.EventToString(ev)
if err == nil {
i.currentKeySeq = append(i.currentKeySeq, s)
}
if len(i.currentKeySeq) > 0 {
i.SendStatusMsgAndClear(strings.Join(i.currentKeySeq, " "), 500*time.Millisecond)
i.currentKeySeq = []string{}
}
a.Execute(i, ev)
})
}
const maxResolveActionDepth = 100
func (km Keymap) resolveActionName(name string, depth int) (Action, error) {
if depth >= maxResolveActionDepth {
return nil, fmt.Errorf("error: 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, fmt.Errorf("error: 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() {
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 {
fmt.Fprintln(os.Stderr, err)
continue
}
kb[s] = v
}
// now compile using kb
for s, a := range kb {
list, err := keyseq.ToKeyList(s)
if err != nil {
fmt.Fprintf(os.Stderr, "Unknown key %s: %s", s, err)
continue
}
k.Add(list, a)
}
k.Compile()
}
// TODO: this needs to be fixed.
func (km Keymap) hasModifierMaps() bool {
return false
}