]> git.lizzy.rs Git - micro.git/blob - cmd/micro/bindings.go
128912c70204611b8610f5e8a428d2769acf9eef
[micro.git] / cmd / micro / bindings.go
1 package main
2
3 import (
4         "io/ioutil"
5         "os"
6         "strings"
7
8         "github.com/zyedidia/json5/encoding/json5"
9         "github.com/zyedidia/tcell"
10 )
11
12 var bindings map[Key][]func(*View, bool) bool
13 var helpBinding string
14
15 var bindingActions = map[string]func(*View, bool) bool{
16         "CursorUp":            (*View).CursorUp,
17         "CursorDown":          (*View).CursorDown,
18         "CursorPageUp":        (*View).CursorPageUp,
19         "CursorPageDown":      (*View).CursorPageDown,
20         "CursorLeft":          (*View).CursorLeft,
21         "CursorRight":         (*View).CursorRight,
22         "CursorStart":         (*View).CursorStart,
23         "CursorEnd":           (*View).CursorEnd,
24         "SelectToStart":       (*View).SelectToStart,
25         "SelectToEnd":         (*View).SelectToEnd,
26         "SelectUp":            (*View).SelectUp,
27         "SelectDown":          (*View).SelectDown,
28         "SelectLeft":          (*View).SelectLeft,
29         "SelectRight":         (*View).SelectRight,
30         "WordRight":           (*View).WordRight,
31         "WordLeft":            (*View).WordLeft,
32         "SelectWordRight":     (*View).SelectWordRight,
33         "SelectWordLeft":      (*View).SelectWordLeft,
34         "DeleteWordRight":     (*View).DeleteWordRight,
35         "DeleteWordLeft":      (*View).DeleteWordLeft,
36         "SelectToStartOfLine": (*View).SelectToStartOfLine,
37         "SelectToEndOfLine":   (*View).SelectToEndOfLine,
38         "InsertNewline":       (*View).InsertNewline,
39         "InsertSpace":         (*View).InsertSpace,
40         "Backspace":           (*View).Backspace,
41         "Delete":              (*View).Delete,
42         "InsertTab":           (*View).InsertTab,
43         "Save":                (*View).Save,
44         "SaveAll":             (*View).SaveAll,
45         "SaveAs":              (*View).SaveAs,
46         "Find":                (*View).Find,
47         "FindNext":            (*View).FindNext,
48         "FindPrevious":        (*View).FindPrevious,
49         "Center":              (*View).Center,
50         "Undo":                (*View).Undo,
51         "Redo":                (*View).Redo,
52         "Copy":                (*View).Copy,
53         "Cut":                 (*View).Cut,
54         "CutLine":             (*View).CutLine,
55         "DuplicateLine":       (*View).DuplicateLine,
56         "DeleteLine":          (*View).DeleteLine,
57         "MoveLinesUp":         (*View).MoveLinesUp,
58         "MoveLinesDown":       (*View).MoveLinesDown,
59         "IndentSelection":     (*View).IndentSelection,
60         "OutdentSelection":    (*View).OutdentSelection,
61         "OutdentLine":         (*View).OutdentLine,
62         "Paste":               (*View).Paste,
63         "PastePrimary":        (*View).PastePrimary,
64         "SelectAll":           (*View).SelectAll,
65         "OpenFile":            (*View).OpenFile,
66         "Start":               (*View).Start,
67         "End":                 (*View).End,
68         "PageUp":              (*View).PageUp,
69         "PageDown":            (*View).PageDown,
70         "HalfPageUp":          (*View).HalfPageUp,
71         "HalfPageDown":        (*View).HalfPageDown,
72         "StartOfLine":         (*View).StartOfLine,
73         "EndOfLine":           (*View).EndOfLine,
74         "ToggleHelp":          (*View).ToggleHelp,
75         "ToggleRuler":         (*View).ToggleRuler,
76         "JumpLine":            (*View).JumpLine,
77         "ClearStatus":         (*View).ClearStatus,
78         "ShellMode":           (*View).ShellMode,
79         "CommandMode":         (*View).CommandMode,
80         "Escape":              (*View).Escape,
81         "Quit":                (*View).Quit,
82         "QuitAll":             (*View).QuitAll,
83         "AddTab":              (*View).AddTab,
84         "PreviousTab":         (*View).PreviousTab,
85         "NextTab":             (*View).NextTab,
86         "NextSplit":           (*View).NextSplit,
87         "PreviousSplit":       (*View).PreviousSplit,
88         "Unsplit":             (*View).Unsplit,
89         "VSplit":              (*View).VSplitBinding,
90         "HSplit":              (*View).HSplitBinding,
91         "ToggleMacro":         (*View).ToggleMacro,
92         "PlayMacro":           (*View).PlayMacro,
93
94         // This was changed to InsertNewline but I don't want to break backwards compatibility
95         "InsertEnter": (*View).InsertNewline,
96 }
97
98 var bindingKeys = map[string]tcell.Key{
99         "Up":             tcell.KeyUp,
100         "Down":           tcell.KeyDown,
101         "Right":          tcell.KeyRight,
102         "Left":           tcell.KeyLeft,
103         "UpLeft":         tcell.KeyUpLeft,
104         "UpRight":        tcell.KeyUpRight,
105         "DownLeft":       tcell.KeyDownLeft,
106         "DownRight":      tcell.KeyDownRight,
107         "Center":         tcell.KeyCenter,
108         "PageUp":         tcell.KeyPgUp,
109         "PageDown":       tcell.KeyPgDn,
110         "Home":           tcell.KeyHome,
111         "End":            tcell.KeyEnd,
112         "Insert":         tcell.KeyInsert,
113         "Delete":         tcell.KeyDelete,
114         "Help":           tcell.KeyHelp,
115         "Exit":           tcell.KeyExit,
116         "Clear":          tcell.KeyClear,
117         "Cancel":         tcell.KeyCancel,
118         "Print":          tcell.KeyPrint,
119         "Pause":          tcell.KeyPause,
120         "Backtab":        tcell.KeyBacktab,
121         "F1":             tcell.KeyF1,
122         "F2":             tcell.KeyF2,
123         "F3":             tcell.KeyF3,
124         "F4":             tcell.KeyF4,
125         "F5":             tcell.KeyF5,
126         "F6":             tcell.KeyF6,
127         "F7":             tcell.KeyF7,
128         "F8":             tcell.KeyF8,
129         "F9":             tcell.KeyF9,
130         "F10":            tcell.KeyF10,
131         "F11":            tcell.KeyF11,
132         "F12":            tcell.KeyF12,
133         "F13":            tcell.KeyF13,
134         "F14":            tcell.KeyF14,
135         "F15":            tcell.KeyF15,
136         "F16":            tcell.KeyF16,
137         "F17":            tcell.KeyF17,
138         "F18":            tcell.KeyF18,
139         "F19":            tcell.KeyF19,
140         "F20":            tcell.KeyF20,
141         "F21":            tcell.KeyF21,
142         "F22":            tcell.KeyF22,
143         "F23":            tcell.KeyF23,
144         "F24":            tcell.KeyF24,
145         "F25":            tcell.KeyF25,
146         "F26":            tcell.KeyF26,
147         "F27":            tcell.KeyF27,
148         "F28":            tcell.KeyF28,
149         "F29":            tcell.KeyF29,
150         "F30":            tcell.KeyF30,
151         "F31":            tcell.KeyF31,
152         "F32":            tcell.KeyF32,
153         "F33":            tcell.KeyF33,
154         "F34":            tcell.KeyF34,
155         "F35":            tcell.KeyF35,
156         "F36":            tcell.KeyF36,
157         "F37":            tcell.KeyF37,
158         "F38":            tcell.KeyF38,
159         "F39":            tcell.KeyF39,
160         "F40":            tcell.KeyF40,
161         "F41":            tcell.KeyF41,
162         "F42":            tcell.KeyF42,
163         "F43":            tcell.KeyF43,
164         "F44":            tcell.KeyF44,
165         "F45":            tcell.KeyF45,
166         "F46":            tcell.KeyF46,
167         "F47":            tcell.KeyF47,
168         "F48":            tcell.KeyF48,
169         "F49":            tcell.KeyF49,
170         "F50":            tcell.KeyF50,
171         "F51":            tcell.KeyF51,
172         "F52":            tcell.KeyF52,
173         "F53":            tcell.KeyF53,
174         "F54":            tcell.KeyF54,
175         "F55":            tcell.KeyF55,
176         "F56":            tcell.KeyF56,
177         "F57":            tcell.KeyF57,
178         "F58":            tcell.KeyF58,
179         "F59":            tcell.KeyF59,
180         "F60":            tcell.KeyF60,
181         "F61":            tcell.KeyF61,
182         "F62":            tcell.KeyF62,
183         "F63":            tcell.KeyF63,
184         "F64":            tcell.KeyF64,
185         "CtrlSpace":      tcell.KeyCtrlSpace,
186         "CtrlA":          tcell.KeyCtrlA,
187         "CtrlB":          tcell.KeyCtrlB,
188         "CtrlC":          tcell.KeyCtrlC,
189         "CtrlD":          tcell.KeyCtrlD,
190         "CtrlE":          tcell.KeyCtrlE,
191         "CtrlF":          tcell.KeyCtrlF,
192         "CtrlG":          tcell.KeyCtrlG,
193         "CtrlH":          tcell.KeyCtrlH,
194         "CtrlI":          tcell.KeyCtrlI,
195         "CtrlJ":          tcell.KeyCtrlJ,
196         "CtrlK":          tcell.KeyCtrlK,
197         "CtrlL":          tcell.KeyCtrlL,
198         "CtrlM":          tcell.KeyCtrlM,
199         "CtrlN":          tcell.KeyCtrlN,
200         "CtrlO":          tcell.KeyCtrlO,
201         "CtrlP":          tcell.KeyCtrlP,
202         "CtrlQ":          tcell.KeyCtrlQ,
203         "CtrlR":          tcell.KeyCtrlR,
204         "CtrlS":          tcell.KeyCtrlS,
205         "CtrlT":          tcell.KeyCtrlT,
206         "CtrlU":          tcell.KeyCtrlU,
207         "CtrlV":          tcell.KeyCtrlV,
208         "CtrlW":          tcell.KeyCtrlW,
209         "CtrlX":          tcell.KeyCtrlX,
210         "CtrlY":          tcell.KeyCtrlY,
211         "CtrlZ":          tcell.KeyCtrlZ,
212         "CtrlLeftSq":     tcell.KeyCtrlLeftSq,
213         "CtrlBackslash":  tcell.KeyCtrlBackslash,
214         "CtrlRightSq":    tcell.KeyCtrlRightSq,
215         "CtrlCarat":      tcell.KeyCtrlCarat,
216         "CtrlUnderscore": tcell.KeyCtrlUnderscore,
217         "Tab":            tcell.KeyTab,
218         "Esc":            tcell.KeyEsc,
219         "Escape":         tcell.KeyEscape,
220         "Enter":          tcell.KeyEnter,
221         "Backspace":      tcell.KeyBackspace2,
222
223         // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
224         "PgUp":   tcell.KeyPgUp,
225         "PgDown": tcell.KeyPgDn,
226 }
227
228 // The Key struct holds the data for a keypress (keycode + modifiers)
229 type Key struct {
230         keyCode   tcell.Key
231         modifiers tcell.ModMask
232         r         rune
233 }
234
235 // InitBindings initializes the keybindings for micro
236 func InitBindings() {
237         bindings = make(map[Key][]func(*View, bool) bool)
238
239         var parsed map[string]string
240         defaults := DefaultBindings()
241
242         filename := configDir + "/bindings.json"
243         if _, e := os.Stat(filename); e == nil {
244                 input, err := ioutil.ReadFile(filename)
245                 if err != nil {
246                         TermMessage("Error reading bindings.json file: " + err.Error())
247                         return
248                 }
249
250                 err = json5.Unmarshal(input, &parsed)
251                 if err != nil {
252                         TermMessage("Error reading bindings.json:", err.Error())
253                 }
254         }
255
256         parseBindings(defaults)
257         parseBindings(parsed)
258 }
259
260 func parseBindings(userBindings map[string]string) {
261         for k, v := range userBindings {
262                 BindKey(k, v)
263         }
264 }
265
266 // findKey will find binding Key 'b' using string 'k'
267 func findKey(k string) (b Key, ok bool) {
268         modifiers := tcell.ModNone
269
270         // First, we'll strip off all the modifiers in the name and add them to the
271         // ModMask
272 modSearch:
273         for {
274                 switch {
275                 case strings.HasPrefix(k, "-"):
276                         // We optionally support dashes between modifiers
277                         k = k[1:]
278                 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
279                         // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
280                         k = k[4:]
281                         modifiers |= tcell.ModCtrl
282                 case strings.HasPrefix(k, "Alt"):
283                         k = k[3:]
284                         modifiers |= tcell.ModAlt
285                 case strings.HasPrefix(k, "Shift"):
286                         k = k[5:]
287                         modifiers |= tcell.ModShift
288                 default:
289                         break modSearch
290                 }
291         }
292
293         // Control is handled specially, since some character codes in bindingKeys
294         // are different when Control is depressed. We should check for Control keys
295         // first.
296         if modifiers&tcell.ModCtrl != 0 {
297                 // see if the key is in bindingKeys with the Ctrl prefix.
298                 if code, ok := bindingKeys["Ctrl"+k]; ok {
299                         // It is, we're done.
300                         return Key{
301                                 keyCode:   code,
302                                 modifiers: modifiers,
303                                 r:         0,
304                         }, true
305                 }
306         }
307
308         // See if we can find the key in bindingKeys
309         if code, ok := bindingKeys[k]; ok {
310                 return Key{
311                         keyCode:   code,
312                         modifiers: modifiers,
313                         r:         0,
314                 }, true
315         }
316
317         // If we were given one character, then we've got a rune.
318         if len(k) == 1 {
319                 return Key{
320                         keyCode:   tcell.KeyRune,
321                         modifiers: modifiers,
322                         r:         rune(k[0]),
323                 }, true
324         }
325
326         // We don't know what happened.
327         return Key{}, false
328 }
329
330 // findAction will find 'action' using string 'v'
331 func findAction(v string) (action func(*View, bool) bool) {
332         action, ok := bindingActions[v]
333         if !ok {
334                 // If the user seems to be binding a function that doesn't exist
335                 // We hope that it's a lua function that exists and bind it to that
336                 action = LuaFunctionBinding(v)
337         }
338         return action
339 }
340
341 // BindKey takes a key and an action and binds the two together
342 func BindKey(k, v string) {
343         key, ok := findKey(k)
344         if !ok {
345                 TermMessage("Unknown keybinding: " + k)
346                 return
347         }
348         if v == "ToggleHelp" {
349                 helpBinding = k
350         }
351         if helpBinding == k && v != "ToggleHelp" {
352                 helpBinding = ""
353         }
354
355         actionNames := strings.Split(v, ",")
356         if actionNames[0] == "UnbindKey" {
357                 delete(bindings, key)
358                 if len(actionNames) == 1 {
359                         actionNames = make([]string, 0, 0)
360                 } else {
361                         actionNames = append(actionNames[:0], actionNames[1:]...)
362                 }
363         }
364         actions := make([]func(*View, bool) bool, 0, len(actionNames))
365         for _, actionName := range actionNames {
366                 actions = append(actions, findAction(actionName))
367         }
368
369         bindings[key] = actions
370 }
371
372 // DefaultBindings returns a map containing micro's default keybindings
373 func DefaultBindings() map[string]string {
374         return map[string]string{
375                 "Up":             "CursorUp",
376                 "Down":           "CursorDown",
377                 "Right":          "CursorRight",
378                 "Left":           "CursorLeft",
379                 "ShiftUp":        "SelectUp",
380                 "ShiftDown":      "SelectDown",
381                 "ShiftLeft":      "SelectLeft",
382                 "ShiftRight":     "SelectRight",
383                 "AltLeft":        "WordLeft",
384                 "AltRight":       "WordRight",
385                 "AltUp":          "MoveLinesUp",
386                 "AltDown":        "MoveLinesDown",
387                 "AltShiftRight":  "SelectWordRight",
388                 "AltShiftLeft":   "SelectWordLeft",
389                 "CtrlLeft":       "StartOfLine",
390                 "CtrlRight":      "EndOfLine",
391                 "CtrlShiftLeft":  "SelectToStartOfLine",
392                 "ShiftHome":      "SelectToStartOfLine",
393                 "CtrlShiftRight": "SelectToEndOfLine",
394                 "ShiftEnd":       "SelectToEndOfLine",
395                 "CtrlUp":         "CursorStart",
396                 "CtrlDown":       "CursorEnd",
397                 "CtrlShiftUp":    "SelectToStart",
398                 "CtrlShiftDown":  "SelectToEnd",
399                 "Enter":          "InsertNewline",
400                 "CtrlH":          "Backspace",
401                 "Backspace":      "Backspace",
402                 "Alt-CtrlH":      "DeleteWordLeft",
403                 "Alt-Backspace":  "DeleteWordLeft",
404                 "Tab":            "IndentSelection,InsertTab",
405                 "Backtab":        "OutdentSelection,OutdentLine",
406                 "CtrlO":          "OpenFile",
407                 "CtrlS":          "Save",
408                 "CtrlF":          "Find",
409                 "CtrlN":          "FindNext",
410                 "CtrlP":          "FindPrevious",
411                 "CtrlZ":          "Undo",
412                 "CtrlY":          "Redo",
413                 "CtrlC":          "Copy",
414                 "CtrlX":          "Cut",
415                 "CtrlK":          "CutLine",
416                 "CtrlD":          "DuplicateLine",
417                 "CtrlV":          "Paste",
418                 "CtrlA":          "SelectAll",
419                 "CtrlT":          "AddTab",
420                 "Alt,":           "PreviousTab",
421                 "Alt.":           "NextTab",
422                 "Home":           "StartOfLine",
423                 "End":            "EndOfLine",
424                 "CtrlHome":       "CursorStart",
425                 "CtrlEnd":        "CursorEnd",
426                 "PageUp":         "CursorPageUp",
427                 "PageDown":       "CursorPageDown",
428                 "CtrlG":          "ToggleHelp",
429                 "CtrlR":          "ToggleRuler",
430                 "CtrlL":          "JumpLine",
431                 "Delete":         "Delete",
432                 "CtrlB":          "ShellMode",
433                 "CtrlQ":          "Quit",
434                 "CtrlE":          "CommandMode",
435                 "CtrlW":          "NextSplit",
436                 "CtrlU":          "ToggleMacro",
437                 "CtrlJ":          "PlayMacro",
438
439                 // Emacs-style keybindings
440                 "Alt-f": "WordRight",
441                 "Alt-b": "WordLeft",
442                 "Alt-a": "StartOfLine",
443                 "Alt-e": "EndOfLine",
444                 "Alt-p": "CursorUp",
445                 "Alt-n": "CursorDown",
446
447                 // Integration with file managers
448                 "F1":  "ToggleHelp",
449                 "F2":  "Save",
450                 "F3":  "Find",
451                 "F4":  "Quit",
452                 "F7":  "Find",
453                 "F10": "Quit",
454                 "Esc": "Escape",
455         }
456 }