]> git.lizzy.rs Git - micro.git/blob - internal/action/bindings.go
Don't overwrite user bindings
[micro.git] / internal / action / bindings.go
1 package action
2
3 import (
4         "encoding/json"
5         "errors"
6         "io/ioutil"
7         "os"
8         "path/filepath"
9         "regexp"
10         "strings"
11         "unicode"
12
13         "github.com/zyedidia/json5"
14         "github.com/zyedidia/micro/v2/internal/config"
15         "github.com/zyedidia/micro/v2/internal/screen"
16         "github.com/zyedidia/tcell"
17 )
18
19 var Binder = map[string]func(e Event, action string){
20         "info":     InfoMapEvent,
21         "buffer":   BufMapEvent,
22         "terminal": TermMapEvent,
23 }
24
25 func createBindingsIfNotExist(fname string) {
26         if _, e := os.Stat(fname); os.IsNotExist(e) {
27                 ioutil.WriteFile(fname, []byte("{}"), 0644)
28         }
29 }
30
31 // InitBindings intializes the bindings map by reading from bindings.json
32 func InitBindings() {
33         config.Bindings = DefaultBindings("buffer")
34
35         var parsed map[string]interface{}
36
37         filename := filepath.Join(config.ConfigDir, "bindings.json")
38         createBindingsIfNotExist(filename)
39
40         if _, e := os.Stat(filename); e == nil {
41                 input, err := ioutil.ReadFile(filename)
42                 if err != nil {
43                         screen.TermMessage("Error reading bindings.json file: " + err.Error())
44                         return
45                 }
46
47                 err = json5.Unmarshal(input, &parsed)
48                 if err != nil {
49                         screen.TermMessage("Error reading bindings.json:", err.Error())
50                 }
51         }
52
53         for k, v := range parsed {
54                 switch val := v.(type) {
55                 case string:
56                         BindKey(k, val, Binder["buffer"])
57                 case map[string]interface{}:
58                         bind := Binder[k]
59                         for e, a := range val {
60                                 s, ok := a.(string)
61                                 if !ok {
62                                         screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
63                                 } else {
64                                         BindKey(e, s, bind)
65                                 }
66                         }
67                 default:
68                         screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
69                 }
70         }
71
72         for p, bind := range Binder {
73                 defaults := DefaultBindings(p)
74
75                 for k, v := range defaults {
76                         BindKey(k, v, bind)
77                 }
78         }
79 }
80
81 func BindKey(k, v string, bind func(e Event, a string)) {
82         event, err := findEvent(k)
83         if err != nil {
84                 screen.TermMessage(err)
85         }
86
87         bind(event, v)
88
89         // switch e := event.(type) {
90         // case KeyEvent:
91         //      InfoMapKey(e, v)
92         // case KeySequenceEvent:
93         //      InfoMapKey(e, v)
94         // case MouseEvent:
95         //      InfoMapMouse(e, v)
96         // case RawEvent:
97         //      InfoMapKey(e, v)
98         // }
99 }
100
101 var r = regexp.MustCompile("<(.+?)>")
102
103 func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
104         var events []Event = nil
105         for len(k) > 0 {
106                 groups := r.FindStringSubmatchIndex(k)
107
108                 if len(groups) > 3 {
109                         if events == nil {
110                                 events = make([]Event, 0, 3)
111                         }
112
113                         e, ok := findSingleEvent(k[groups[2]:groups[3]])
114                         if !ok {
115                                 return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
116                         }
117
118                         events = append(events, e)
119
120                         k = k[groups[3]+1:]
121                 } else {
122                         return KeySequenceEvent{}, false, nil
123                 }
124         }
125
126         return KeySequenceEvent{events}, true, nil
127 }
128
129 // findSingleEvent will find binding Key 'b' using string 'k'
130 func findSingleEvent(k string) (b Event, ok bool) {
131         modifiers := tcell.ModNone
132
133         // First, we'll strip off all the modifiers in the name and add them to the
134         // ModMask
135 modSearch:
136         for {
137                 switch {
138                 case strings.HasPrefix(k, "-"):
139                         // We optionally support dashes between modifiers
140                         k = k[1:]
141                 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
142                         // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
143                         k = k[4:]
144                         modifiers |= tcell.ModCtrl
145                 case strings.HasPrefix(k, "Alt"):
146                         k = k[3:]
147                         modifiers |= tcell.ModAlt
148                 case strings.HasPrefix(k, "Shift"):
149                         k = k[5:]
150                         modifiers |= tcell.ModShift
151                 case strings.HasPrefix(k, "\x1b"):
152                         screen.Screen.RegisterRawSeq(k)
153                         return RawEvent{
154                                 esc: k,
155                         }, true
156                 default:
157                         break modSearch
158                 }
159         }
160
161         if len(k) == 0 {
162                 return KeyEvent{}, false
163         }
164
165         // Control is handled in a special way, since the terminal sends explicitly
166         // marked escape sequences for control keys
167         // We should check for Control keys first
168         if modifiers&tcell.ModCtrl != 0 {
169                 // see if the key is in bindingKeys with the Ctrl prefix.
170                 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
171                 if code, ok := keyEvents["Ctrl"+k]; ok {
172                         var r tcell.Key
173                         // Special case for escape, for some reason tcell doesn't send it with the esc character
174                         if code < 256 && code != 27 {
175                                 r = code
176                         }
177                         // It is, we're done.
178                         return KeyEvent{
179                                 code: code,
180                                 mod:  modifiers,
181                                 r:    rune(r),
182                         }, true
183                 }
184         }
185
186         // See if we can find the key in bindingKeys
187         if code, ok := keyEvents[k]; ok {
188                 var r tcell.Key
189                 // Special case for escape, for some reason tcell doesn't send it with the esc character
190                 if code < 256 && code != 27 {
191                         r = code
192                 }
193                 return KeyEvent{
194                         code: code,
195                         mod:  modifiers,
196                         r:    rune(r),
197                 }, true
198         }
199
200         // See if we can find the key in bindingMouse
201         if code, ok := mouseEvents[k]; ok {
202                 return MouseEvent{
203                         btn: code,
204                         mod: modifiers,
205                 }, true
206         }
207
208         // If we were given one character, then we've got a rune.
209         if len(k) == 1 {
210                 return KeyEvent{
211                         code: tcell.KeyRune,
212                         mod:  modifiers,
213                         r:    rune(k[0]),
214                 }, true
215         }
216
217         // We don't know what happened.
218         return KeyEvent{}, false
219 }
220
221 func findEvent(k string) (Event, error) {
222         var event Event
223         event, ok, err := findEvents(k)
224         if err != nil {
225                 return nil, err
226         }
227
228         if !ok {
229                 event, ok = findSingleEvent(k)
230                 if !ok {
231                         return nil, errors.New(k + " is not a bindable event")
232                 }
233         }
234
235         return event, nil
236 }
237
238 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
239 // Returns true if the keybinding already existed and a possible error
240 func TryBindKey(k, v string, overwrite bool) (bool, error) {
241         var e error
242         var parsed map[string]string
243
244         filename := filepath.Join(config.ConfigDir, "bindings.json")
245         createBindingsIfNotExist(filename)
246         if _, e = os.Stat(filename); e == nil {
247                 input, err := ioutil.ReadFile(filename)
248                 if err != nil {
249                         return false, errors.New("Error reading bindings.json file: " + err.Error())
250                 }
251
252                 err = json5.Unmarshal(input, &parsed)
253                 if err != nil {
254                         return false, errors.New("Error reading bindings.json: " + err.Error())
255                 }
256
257                 key, err := findEvent(k)
258                 if err != nil {
259                         return false, err
260                 }
261
262                 found := false
263                 for ev := range parsed {
264                         if e, err := findEvent(ev); err == nil {
265                                 if e == key {
266                                         if overwrite {
267                                                 parsed[ev] = v
268                                         }
269                                         found = true
270                                         break
271                                 }
272                         }
273                 }
274
275                 if found && !overwrite {
276                         return true, nil
277                 } else if !found {
278                         parsed[k] = v
279                 }
280
281                 BindKey(k, v, Binder["buffer"])
282
283                 txt, _ := json.MarshalIndent(parsed, "", "    ")
284                 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
285         }
286         return false, e
287 }
288
289 // UnbindKey removes the binding for a key from the bindings.json file
290 func UnbindKey(k string) error {
291         var e error
292         var parsed map[string]string
293
294         filename := filepath.Join(config.ConfigDir, "bindings.json")
295         createBindingsIfNotExist(filename)
296         if _, e = os.Stat(filename); e == nil {
297                 input, err := ioutil.ReadFile(filename)
298                 if err != nil {
299                         return errors.New("Error reading bindings.json file: " + err.Error())
300                 }
301
302                 err = json5.Unmarshal(input, &parsed)
303                 if err != nil {
304                         return errors.New("Error reading bindings.json: " + err.Error())
305                 }
306
307                 key, err := findEvent(k)
308                 if err != nil {
309                         return err
310                 }
311
312                 for ev := range parsed {
313                         if e, err := findEvent(ev); err == nil {
314                                 if e == key {
315                                         delete(parsed, ev)
316                                         break
317                                 }
318                         }
319                 }
320
321                 defaults := DefaultBindings("buffer")
322                 if a, ok := defaults[k]; ok {
323                         BindKey(k, a, Binder["buffer"])
324                 } else if _, ok := config.Bindings[k]; ok {
325                         BufUnmap(key)
326                         delete(config.Bindings, k)
327                 }
328
329                 txt, _ := json.MarshalIndent(parsed, "", "    ")
330                 return ioutil.WriteFile(filename, append(txt, '\n'), 0644)
331         }
332         return e
333 }
334
335 var mouseEvents = map[string]tcell.ButtonMask{
336         "MouseLeft":       tcell.Button1,
337         "MouseMiddle":     tcell.Button2,
338         "MouseRight":      tcell.Button3,
339         "MouseWheelUp":    tcell.WheelUp,
340         "MouseWheelDown":  tcell.WheelDown,
341         "MouseWheelLeft":  tcell.WheelLeft,
342         "MouseWheelRight": tcell.WheelRight,
343 }
344
345 var keyEvents = map[string]tcell.Key{
346         "Up":             tcell.KeyUp,
347         "Down":           tcell.KeyDown,
348         "Right":          tcell.KeyRight,
349         "Left":           tcell.KeyLeft,
350         "UpLeft":         tcell.KeyUpLeft,
351         "UpRight":        tcell.KeyUpRight,
352         "DownLeft":       tcell.KeyDownLeft,
353         "DownRight":      tcell.KeyDownRight,
354         "Center":         tcell.KeyCenter,
355         "PageUp":         tcell.KeyPgUp,
356         "PageDown":       tcell.KeyPgDn,
357         "Home":           tcell.KeyHome,
358         "End":            tcell.KeyEnd,
359         "Insert":         tcell.KeyInsert,
360         "Delete":         tcell.KeyDelete,
361         "Help":           tcell.KeyHelp,
362         "Exit":           tcell.KeyExit,
363         "Clear":          tcell.KeyClear,
364         "Cancel":         tcell.KeyCancel,
365         "Print":          tcell.KeyPrint,
366         "Pause":          tcell.KeyPause,
367         "Backtab":        tcell.KeyBacktab,
368         "F1":             tcell.KeyF1,
369         "F2":             tcell.KeyF2,
370         "F3":             tcell.KeyF3,
371         "F4":             tcell.KeyF4,
372         "F5":             tcell.KeyF5,
373         "F6":             tcell.KeyF6,
374         "F7":             tcell.KeyF7,
375         "F8":             tcell.KeyF8,
376         "F9":             tcell.KeyF9,
377         "F10":            tcell.KeyF10,
378         "F11":            tcell.KeyF11,
379         "F12":            tcell.KeyF12,
380         "F13":            tcell.KeyF13,
381         "F14":            tcell.KeyF14,
382         "F15":            tcell.KeyF15,
383         "F16":            tcell.KeyF16,
384         "F17":            tcell.KeyF17,
385         "F18":            tcell.KeyF18,
386         "F19":            tcell.KeyF19,
387         "F20":            tcell.KeyF20,
388         "F21":            tcell.KeyF21,
389         "F22":            tcell.KeyF22,
390         "F23":            tcell.KeyF23,
391         "F24":            tcell.KeyF24,
392         "F25":            tcell.KeyF25,
393         "F26":            tcell.KeyF26,
394         "F27":            tcell.KeyF27,
395         "F28":            tcell.KeyF28,
396         "F29":            tcell.KeyF29,
397         "F30":            tcell.KeyF30,
398         "F31":            tcell.KeyF31,
399         "F32":            tcell.KeyF32,
400         "F33":            tcell.KeyF33,
401         "F34":            tcell.KeyF34,
402         "F35":            tcell.KeyF35,
403         "F36":            tcell.KeyF36,
404         "F37":            tcell.KeyF37,
405         "F38":            tcell.KeyF38,
406         "F39":            tcell.KeyF39,
407         "F40":            tcell.KeyF40,
408         "F41":            tcell.KeyF41,
409         "F42":            tcell.KeyF42,
410         "F43":            tcell.KeyF43,
411         "F44":            tcell.KeyF44,
412         "F45":            tcell.KeyF45,
413         "F46":            tcell.KeyF46,
414         "F47":            tcell.KeyF47,
415         "F48":            tcell.KeyF48,
416         "F49":            tcell.KeyF49,
417         "F50":            tcell.KeyF50,
418         "F51":            tcell.KeyF51,
419         "F52":            tcell.KeyF52,
420         "F53":            tcell.KeyF53,
421         "F54":            tcell.KeyF54,
422         "F55":            tcell.KeyF55,
423         "F56":            tcell.KeyF56,
424         "F57":            tcell.KeyF57,
425         "F58":            tcell.KeyF58,
426         "F59":            tcell.KeyF59,
427         "F60":            tcell.KeyF60,
428         "F61":            tcell.KeyF61,
429         "F62":            tcell.KeyF62,
430         "F63":            tcell.KeyF63,
431         "F64":            tcell.KeyF64,
432         "CtrlSpace":      tcell.KeyCtrlSpace,
433         "CtrlA":          tcell.KeyCtrlA,
434         "CtrlB":          tcell.KeyCtrlB,
435         "CtrlC":          tcell.KeyCtrlC,
436         "CtrlD":          tcell.KeyCtrlD,
437         "CtrlE":          tcell.KeyCtrlE,
438         "CtrlF":          tcell.KeyCtrlF,
439         "CtrlG":          tcell.KeyCtrlG,
440         "CtrlH":          tcell.KeyCtrlH,
441         "CtrlI":          tcell.KeyCtrlI,
442         "CtrlJ":          tcell.KeyCtrlJ,
443         "CtrlK":          tcell.KeyCtrlK,
444         "CtrlL":          tcell.KeyCtrlL,
445         "CtrlM":          tcell.KeyCtrlM,
446         "CtrlN":          tcell.KeyCtrlN,
447         "CtrlO":          tcell.KeyCtrlO,
448         "CtrlP":          tcell.KeyCtrlP,
449         "CtrlQ":          tcell.KeyCtrlQ,
450         "CtrlR":          tcell.KeyCtrlR,
451         "CtrlS":          tcell.KeyCtrlS,
452         "CtrlT":          tcell.KeyCtrlT,
453         "CtrlU":          tcell.KeyCtrlU,
454         "CtrlV":          tcell.KeyCtrlV,
455         "CtrlW":          tcell.KeyCtrlW,
456         "CtrlX":          tcell.KeyCtrlX,
457         "CtrlY":          tcell.KeyCtrlY,
458         "CtrlZ":          tcell.KeyCtrlZ,
459         "CtrlLeftSq":     tcell.KeyCtrlLeftSq,
460         "CtrlBackslash":  tcell.KeyCtrlBackslash,
461         "CtrlRightSq":    tcell.KeyCtrlRightSq,
462         "CtrlCarat":      tcell.KeyCtrlCarat,
463         "CtrlUnderscore": tcell.KeyCtrlUnderscore,
464         "Tab":            tcell.KeyTab,
465         "Esc":            tcell.KeyEsc,
466         "Escape":         tcell.KeyEscape,
467         "Enter":          tcell.KeyEnter,
468         "Backspace":      tcell.KeyBackspace2,
469         "OldBackspace":   tcell.KeyBackspace,
470
471         // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
472         "PgUp":   tcell.KeyPgUp,
473         "PgDown": tcell.KeyPgDn,
474 }