-
Notifications
You must be signed in to change notification settings - Fork 88
/
Copy pathcommandhandler.go
221 lines (208 loc) · 6.09 KB
/
commandhandler.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
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
// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.
package backend
import (
"fmt"
"reflect"
"strings"
"time"
"github.com/limetext/backend/log"
"github.com/limetext/util"
)
type (
CommandHandler interface {
Unregister(string) error
RegisterWithDefault(cmd interface{}) error
Register(name string, cmd interface{}) error
// TODO(q): Do the commands need to be split in separate lists?
RunWindowCommand(*Window, string, Args) error
RunTextCommand(*View, string, Args) error
RunApplicationCommand(string, Args) error
}
appcmd map[string]Command
textcmd map[string]Command
wndcmd map[string]Command
commandHandler struct {
ApplicationCommands appcmd
TextCommands textcmd
WindowCommands wndcmd
log bool
verbose bool
}
)
func DefaultName(cmd interface{}) string {
name := reflect.TypeOf(cmd).Elem().Name()
return util.PascalCaseToSnakeCase(strings.TrimSuffix(name, "Command"))
}
// If the cmd implements the CustomInit interface, its Init function
// is called, otherwise the fields of the cmd's underlying struct type
// will be enumerated and match against the dictionary keys in args,
// or if the key isn't provided in args, the Zero value will be used.
func (ch *commandHandler) init(cmd interface{}, args Args) error {
if in, ok := cmd.(CustomInit); ok {
return in.Init(args)
}
v := reflect.ValueOf(cmd).Elem()
t := v.Type()
for i := 0; i < v.NumField(); i++ {
ft := t.Field(i)
f := v.Field(i)
if ft.Anonymous || !f.CanSet() {
continue
}
key := util.PascalCaseToSnakeCase(ft.Name)
fv, ok := args[key]
if !ok {
fv = reflect.Zero(ft.Type).Interface()
if def, ok := cmd.(CustomDefault); ok {
if val := def.Default(key); val != nil {
fv = val
}
}
}
if f.CanAddr() {
if f2, ok := f.Addr().Interface().(CustomSet); ok {
if err := f2.Set(fv); err != nil {
return err
}
continue
}
}
rv := reflect.ValueOf(fv)
rvtype := rv.Type()
ftype := f.Type()
if !rvtype.AssignableTo(ftype) {
if rvtype.ConvertibleTo(ftype) {
rv = rv.Convert(ftype)
} else {
return fmt.Errorf("Command %v arg %v of type %v not assignable or convertable to %v of type %v", t, rv, rvtype, ft.Name, ftype)
}
}
f.Set(rv)
}
return nil
}
func (ch *commandHandler) RunWindowCommand(wnd *Window, name string, args Args) error {
lvl := log.FINE
p := util.Prof.Enter("wc")
defer p.Exit()
if ch.log {
lvl = log.DEBUG
}
log.Logf(lvl, "Running window command: %s %v", name, args)
t := time.Now()
if c, ok := ch.WindowCommands[name].(WindowCommand); c != nil && ok {
if err := ch.init(c, args); err != nil && ch.verbose {
log.Debug("Command initialization failed: %s", err)
return err
} else if err := wnd.runCommand(c, name); err != nil {
log.Logf(lvl+1, "Command execution failed: %s", err)
return err
} else {
log.Logf(lvl, "Ran Window command: %s %s", name, time.Since(t))
}
} else {
log.Logf(lvl, "No such window command: %s", name)
}
return nil
}
func (ch *commandHandler) RunTextCommand(view *View, name string, args Args) error {
defer func() {
if r := recover(); r != nil {
log.Error("Panic in RunTextCommand: %v\n%v %#v %#v", r, view, name, args)
panic(r)
}
}()
lvl := log.FINE
p := util.Prof.Enter("tc")
defer p.Exit()
t := time.Now()
if ch.log {
lvl = log.DEBUG
}
log.Logf(lvl, "Running text command: %s %v", name, args)
if c, ok := ch.TextCommands[name].(TextCommand); c != nil && ok {
if err := ch.init(c, args); err != nil && ch.verbose {
log.Debug("Command initialization failed: %s", err)
return err
} else if err := view.runCommand(c, name); err != nil {
log.Logf(lvl, "Command execution failed: %s", err)
return err
}
} else if w := view.Window(); w != nil {
if c, ok := ch.WindowCommands[name].(WindowCommand); c != nil && ok {
if err := w.runCommand(c, name); err != nil {
log.Logf(lvl, "Command execution failed: %s", err)
return err
}
}
}
log.Logf(lvl, "Ran text command: %s %s", name, time.Since(t))
return nil
}
func (ch *commandHandler) RunApplicationCommand(name string, args Args) error {
p := util.Prof.Enter("ac")
defer p.Exit()
if ch.log {
log.Info("Running application command: %s %v", name, args)
} else {
log.Fine("Running application command: %s %v", name, args)
}
if c, ok := ch.ApplicationCommands[name].(ApplicationCommand); c != nil && ok {
if err := ch.init(c, args); err != nil && ch.verbose {
log.Debug("Command initialization failed: %s", err)
return err
} else if err := c.Run(); err != nil && ch.verbose {
log.Debug("Command execution failed: %s", err)
return err
}
}
return nil
}
func (ch *commandHandler) Unregister(name string) error {
if _, ok := ch.ApplicationCommands[name]; ok {
ch.ApplicationCommands[name] = nil
} else if _, ok := ch.WindowCommands[name]; ok {
ch.WindowCommands[name] = nil
} else if _, ok := ch.TextCommands[name]; ok {
ch.TextCommands[name] = nil
} else {
return fmt.Errorf("%s wasn't a registered command", name)
}
return nil
}
func (ch *commandHandler) RegisterWithDefault(cmd interface{}) error {
return ch.Register(DefaultName(cmd), cmd)
}
func (ch *commandHandler) Register(name string, cmd interface{}) error {
var r = false
log.Finest("Want to register %s", name)
if ac, ok := cmd.(ApplicationCommand); ok {
if _, ok := ch.ApplicationCommands[name]; ok {
return fmt.Errorf("%s is already a registered command", name)
}
r = true
ch.ApplicationCommands[name] = ac
}
if wc, ok := cmd.(WindowCommand); ok {
if _, ok := ch.WindowCommands[name]; ok {
return fmt.Errorf("%s is already a registered command", name)
}
r = true
ch.WindowCommands[name] = wc
}
if tc, ok := cmd.(TextCommand); ok {
if _, ok := ch.TextCommands[name]; ok {
return fmt.Errorf("%s is already a registered command", name)
}
r = true
ch.TextCommands[name] = tc
}
if !r {
return fmt.Errorf("Command wasn't registered in any list: %s", name)
} else if ch.verbose {
log.Finest("Successfully registered command %s", name)
}
return nil
}