]> git.lizzy.rs Git - micro.git/blob - cmd/micro/bindings.go
Code optimisation (#1117)
[micro.git] / cmd / micro / bindings.go
1 package main
2
3 import (
4         "fmt"
5         "io/ioutil"
6         "os"
7         "strings"
8         "unicode"
9
10         "github.com/flynn/json5"
11         "github.com/zyedidia/tcell"
12 )
13
14 var bindingsStr map[string]string
15 var bindings map[Key][]func(*View, bool) bool
16 var mouseBindings map[Key][]func(*View, bool, *tcell.EventMouse) bool
17 var helpBinding string
18 var kmenuBinding string
19
20 var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{
21         "MousePress":       (*View).MousePress,
22         "MouseMultiCursor": (*View).MouseMultiCursor,
23 }
24
25 var bindingActions = map[string]func(*View, bool) bool{
26         "CursorUp":               (*View).CursorUp,
27         "CursorDown":             (*View).CursorDown,
28         "CursorPageUp":           (*View).CursorPageUp,
29         "CursorPageDown":         (*View).CursorPageDown,
30         "CursorLeft":             (*View).CursorLeft,
31         "CursorRight":            (*View).CursorRight,
32         "CursorStart":            (*View).CursorStart,
33         "CursorEnd":              (*View).CursorEnd,
34         "SelectToStart":          (*View).SelectToStart,
35         "SelectToEnd":            (*View).SelectToEnd,
36         "SelectUp":               (*View).SelectUp,
37         "SelectDown":             (*View).SelectDown,
38         "SelectLeft":             (*View).SelectLeft,
39         "SelectRight":            (*View).SelectRight,
40         "WordRight":              (*View).WordRight,
41         "WordLeft":               (*View).WordLeft,
42         "SelectWordRight":        (*View).SelectWordRight,
43         "SelectWordLeft":         (*View).SelectWordLeft,
44         "DeleteWordRight":        (*View).DeleteWordRight,
45         "DeleteWordLeft":         (*View).DeleteWordLeft,
46         "SelectLine":             (*View).SelectLine,
47         "SelectToStartOfLine":    (*View).SelectToStartOfLine,
48         "SelectToEndOfLine":      (*View).SelectToEndOfLine,
49         "ParagraphPrevious":      (*View).ParagraphPrevious,
50         "ParagraphNext":          (*View).ParagraphNext,
51         "InsertNewline":          (*View).InsertNewline,
52         "InsertSpace":            (*View).InsertSpace,
53         "Backspace":              (*View).Backspace,
54         "Delete":                 (*View).Delete,
55         "InsertTab":              (*View).InsertTab,
56         "Save":                   (*View).Save,
57         "SaveAll":                (*View).SaveAll,
58         "SaveAs":                 (*View).SaveAs,
59         "Find":                   (*View).Find,
60         "FindNext":               (*View).FindNext,
61         "FindPrevious":           (*View).FindPrevious,
62         "Center":                 (*View).Center,
63         "Undo":                   (*View).Undo,
64         "Redo":                   (*View).Redo,
65         "Copy":                   (*View).Copy,
66         "Cut":                    (*View).Cut,
67         "CutLine":                (*View).CutLine,
68         "DuplicateLine":          (*View).DuplicateLine,
69         "DeleteLine":             (*View).DeleteLine,
70         "MoveLinesUp":            (*View).MoveLinesUp,
71         "MoveLinesDown":          (*View).MoveLinesDown,
72         "IndentSelection":        (*View).IndentSelection,
73         "OutdentSelection":       (*View).OutdentSelection,
74         "OutdentLine":            (*View).OutdentLine,
75         "Paste":                  (*View).Paste,
76         "PastePrimary":           (*View).PastePrimary,
77         "SelectAll":              (*View).SelectAll,
78         "OpenFile":               (*View).OpenFile,
79         "Start":                  (*View).Start,
80         "End":                    (*View).End,
81         "PageUp":                 (*View).PageUp,
82         "PageDown":               (*View).PageDown,
83         "SelectPageUp":           (*View).SelectPageUp,
84         "SelectPageDown":         (*View).SelectPageDown,
85         "HalfPageUp":             (*View).HalfPageUp,
86         "HalfPageDown":           (*View).HalfPageDown,
87         "StartOfLine":            (*View).StartOfLine,
88         "EndOfLine":              (*View).EndOfLine,
89         "ToggleHelp":             (*View).ToggleHelp,
90         "ToggleKeyMenu":          (*View).ToggleKeyMenu,
91         "ToggleRuler":            (*View).ToggleRuler,
92         "JumpLine":               (*View).JumpLine,
93         "ClearStatus":            (*View).ClearStatus,
94         "ShellMode":              (*View).ShellMode,
95         "CommandMode":            (*View).CommandMode,
96         "ToggleOverwriteMode":    (*View).ToggleOverwriteMode,
97         "Escape":                 (*View).Escape,
98         "Quit":                   (*View).Quit,
99         "QuitAll":                (*View).QuitAll,
100         "AddTab":                 (*View).AddTab,
101         "PreviousTab":            (*View).PreviousTab,
102         "NextTab":                (*View).NextTab,
103         "NextSplit":              (*View).NextSplit,
104         "PreviousSplit":          (*View).PreviousSplit,
105         "Unsplit":                (*View).Unsplit,
106         "VSplit":                 (*View).VSplitBinding,
107         "HSplit":                 (*View).HSplitBinding,
108         "ToggleMacro":            (*View).ToggleMacro,
109         "PlayMacro":              (*View).PlayMacro,
110         "Suspend":                (*View).Suspend,
111         "ScrollUp":               (*View).ScrollUpAction,
112         "ScrollDown":             (*View).ScrollDownAction,
113         "SpawnMultiCursor":       (*View).SpawnMultiCursor,
114         "SpawnMultiCursorSelect": (*View).SpawnMultiCursorSelect,
115         "RemoveMultiCursor":      (*View).RemoveMultiCursor,
116         "RemoveAllMultiCursors":  (*View).RemoveAllMultiCursors,
117         "SkipMultiCursor":        (*View).SkipMultiCursor,
118         "JumpToMatchingBrace":    (*View).JumpToMatchingBrace,
119
120         // This was changed to InsertNewline but I don't want to break backwards compatibility
121         "InsertEnter": (*View).InsertNewline,
122 }
123
124 var bindingMouse = map[string]tcell.ButtonMask{
125         "MouseLeft":       tcell.Button1,
126         "MouseMiddle":     tcell.Button2,
127         "MouseRight":      tcell.Button3,
128         "MouseWheelUp":    tcell.WheelUp,
129         "MouseWheelDown":  tcell.WheelDown,
130         "MouseWheelLeft":  tcell.WheelLeft,
131         "MouseWheelRight": tcell.WheelRight,
132 }
133
134 var bindingKeys = map[string]tcell.Key{
135         "Up":             tcell.KeyUp,
136         "Down":           tcell.KeyDown,
137         "Right":          tcell.KeyRight,
138         "Left":           tcell.KeyLeft,
139         "UpLeft":         tcell.KeyUpLeft,
140         "UpRight":        tcell.KeyUpRight,
141         "DownLeft":       tcell.KeyDownLeft,
142         "DownRight":      tcell.KeyDownRight,
143         "Center":         tcell.KeyCenter,
144         "PageUp":         tcell.KeyPgUp,
145         "PageDown":       tcell.KeyPgDn,
146         "Home":           tcell.KeyHome,
147         "End":            tcell.KeyEnd,
148         "Insert":         tcell.KeyInsert,
149         "Delete":         tcell.KeyDelete,
150         "Help":           tcell.KeyHelp,
151         "Exit":           tcell.KeyExit,
152         "Clear":          tcell.KeyClear,
153         "Cancel":         tcell.KeyCancel,
154         "Print":          tcell.KeyPrint,
155         "Pause":          tcell.KeyPause,
156         "Backtab":        tcell.KeyBacktab,
157         "F1":             tcell.KeyF1,
158         "F2":             tcell.KeyF2,
159         "F3":             tcell.KeyF3,
160         "F4":             tcell.KeyF4,
161         "F5":             tcell.KeyF5,
162         "F6":             tcell.KeyF6,
163         "F7":             tcell.KeyF7,
164         "F8":             tcell.KeyF8,
165         "F9":             tcell.KeyF9,
166         "F10":            tcell.KeyF10,
167         "F11":            tcell.KeyF11,
168         "F12":            tcell.KeyF12,
169         "F13":            tcell.KeyF13,
170         "F14":            tcell.KeyF14,
171         "F15":            tcell.KeyF15,
172         "F16":            tcell.KeyF16,
173         "F17":            tcell.KeyF17,
174         "F18":            tcell.KeyF18,
175         "F19":            tcell.KeyF19,
176         "F20":            tcell.KeyF20,
177         "F21":            tcell.KeyF21,
178         "F22":            tcell.KeyF22,
179         "F23":            tcell.KeyF23,
180         "F24":            tcell.KeyF24,
181         "F25":            tcell.KeyF25,
182         "F26":            tcell.KeyF26,
183         "F27":            tcell.KeyF27,
184         "F28":            tcell.KeyF28,
185         "F29":            tcell.KeyF29,
186         "F30":            tcell.KeyF30,
187         "F31":            tcell.KeyF31,
188         "F32":            tcell.KeyF32,
189         "F33":            tcell.KeyF33,
190         "F34":            tcell.KeyF34,
191         "F35":            tcell.KeyF35,
192         "F36":            tcell.KeyF36,
193         "F37":            tcell.KeyF37,
194         "F38":            tcell.KeyF38,
195         "F39":            tcell.KeyF39,
196         "F40":            tcell.KeyF40,
197         "F41":            tcell.KeyF41,
198         "F42":            tcell.KeyF42,
199         "F43":            tcell.KeyF43,
200         "F44":            tcell.KeyF44,
201         "F45":            tcell.KeyF45,
202         "F46":            tcell.KeyF46,
203         "F47":            tcell.KeyF47,
204         "F48":            tcell.KeyF48,
205         "F49":            tcell.KeyF49,
206         "F50":            tcell.KeyF50,
207         "F51":            tcell.KeyF51,
208         "F52":            tcell.KeyF52,
209         "F53":            tcell.KeyF53,
210         "F54":            tcell.KeyF54,
211         "F55":            tcell.KeyF55,
212         "F56":            tcell.KeyF56,
213         "F57":            tcell.KeyF57,
214         "F58":            tcell.KeyF58,
215         "F59":            tcell.KeyF59,
216         "F60":            tcell.KeyF60,
217         "F61":            tcell.KeyF61,
218         "F62":            tcell.KeyF62,
219         "F63":            tcell.KeyF63,
220         "F64":            tcell.KeyF64,
221         "CtrlSpace":      tcell.KeyCtrlSpace,
222         "CtrlA":          tcell.KeyCtrlA,
223         "CtrlB":          tcell.KeyCtrlB,
224         "CtrlC":          tcell.KeyCtrlC,
225         "CtrlD":          tcell.KeyCtrlD,
226         "CtrlE":          tcell.KeyCtrlE,
227         "CtrlF":          tcell.KeyCtrlF,
228         "CtrlG":          tcell.KeyCtrlG,
229         "CtrlH":          tcell.KeyCtrlH,
230         "CtrlI":          tcell.KeyCtrlI,
231         "CtrlJ":          tcell.KeyCtrlJ,
232         "CtrlK":          tcell.KeyCtrlK,
233         "CtrlL":          tcell.KeyCtrlL,
234         "CtrlM":          tcell.KeyCtrlM,
235         "CtrlN":          tcell.KeyCtrlN,
236         "CtrlO":          tcell.KeyCtrlO,
237         "CtrlP":          tcell.KeyCtrlP,
238         "CtrlQ":          tcell.KeyCtrlQ,
239         "CtrlR":          tcell.KeyCtrlR,
240         "CtrlS":          tcell.KeyCtrlS,
241         "CtrlT":          tcell.KeyCtrlT,
242         "CtrlU":          tcell.KeyCtrlU,
243         "CtrlV":          tcell.KeyCtrlV,
244         "CtrlW":          tcell.KeyCtrlW,
245         "CtrlX":          tcell.KeyCtrlX,
246         "CtrlY":          tcell.KeyCtrlY,
247         "CtrlZ":          tcell.KeyCtrlZ,
248         "CtrlLeftSq":     tcell.KeyCtrlLeftSq,
249         "CtrlBackslash":  tcell.KeyCtrlBackslash,
250         "CtrlRightSq":    tcell.KeyCtrlRightSq,
251         "CtrlCarat":      tcell.KeyCtrlCarat,
252         "CtrlUnderscore": tcell.KeyCtrlUnderscore,
253         "CtrlPageUp":     tcell.KeyCtrlPgUp,
254         "CtrlPageDown":   tcell.KeyCtrlPgDn,
255         "Tab":            tcell.KeyTab,
256         "Esc":            tcell.KeyEsc,
257         "Escape":         tcell.KeyEscape,
258         "Enter":          tcell.KeyEnter,
259         "Backspace":      tcell.KeyBackspace2,
260         "OldBackspace":   tcell.KeyBackspace,
261
262         // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
263         "PgUp":   tcell.KeyPgUp,
264         "PgDown": tcell.KeyPgDn,
265 }
266
267 // The Key struct holds the data for a keypress (keycode + modifiers)
268 type Key struct {
269         keyCode   tcell.Key
270         modifiers tcell.ModMask
271         buttons   tcell.ButtonMask
272         r         rune
273         escape    string
274 }
275
276 // InitBindings initializes the keybindings for micro
277 func InitBindings() {
278         bindings = make(map[Key][]func(*View, bool) bool)
279         bindingsStr = make(map[string]string)
280         mouseBindings = make(map[Key][]func(*View, bool, *tcell.EventMouse) bool)
281
282         var parsed map[string]string
283         defaults := DefaultBindings()
284
285         filename := configDir + "/bindings.json"
286         if _, e := os.Stat(filename); e == nil {
287                 input, err := ioutil.ReadFile(filename)
288                 if err != nil {
289                         TermMessage("Error reading bindings.json file: " + err.Error())
290                         return
291                 }
292
293                 err = json5.Unmarshal(input, &parsed)
294                 if err != nil {
295                         TermMessage("Error reading bindings.json:", err.Error())
296                 }
297         }
298
299         parseBindings(defaults)
300         parseBindings(parsed)
301 }
302
303 func parseBindings(userBindings map[string]string) {
304         for k, v := range userBindings {
305                 BindKey(k, v)
306         }
307 }
308
309 // findKey will find binding Key 'b' using string 'k'
310 func findKey(k string) (b Key, ok bool) {
311         modifiers := tcell.ModNone
312
313         // First, we'll strip off all the modifiers in the name and add them to the
314         // ModMask
315 modSearch:
316         for {
317                 switch {
318                 case strings.HasPrefix(k, "-"):
319                         // We optionally support dashes between modifiers
320                         k = k[1:]
321                 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
322                         // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
323                         k = k[4:]
324                         modifiers |= tcell.ModCtrl
325                 case strings.HasPrefix(k, "Alt"):
326                         k = k[3:]
327                         modifiers |= tcell.ModAlt
328                 case strings.HasPrefix(k, "Shift"):
329                         k = k[5:]
330                         modifiers |= tcell.ModShift
331                 case strings.HasPrefix(k, "\x1b"):
332                         return Key{
333                                 keyCode:   -1,
334                                 modifiers: modifiers,
335                                 buttons:   -1,
336                                 r:         0,
337                                 escape:    k,
338                         }, true
339                 default:
340                         break modSearch
341                 }
342         }
343
344         if len(k) == 0 {
345                 return Key{buttons: -1}, false
346         }
347
348         // Control is handled specially, since some character codes in bindingKeys
349         // are different when Control is depressed. We should check for Control keys
350         // first.
351         if modifiers&tcell.ModCtrl != 0 {
352                 // see if the key is in bindingKeys with the Ctrl prefix.
353                 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
354                 if code, ok := bindingKeys["Ctrl"+k]; ok {
355                         // It is, we're done.
356                         return Key{
357                                 keyCode:   code,
358                                 modifiers: modifiers,
359                                 buttons:   -1,
360                                 r:         0,
361                         }, true
362                 }
363         }
364
365         // See if we can find the key in bindingKeys
366         if code, ok := bindingKeys[k]; ok {
367                 return Key{
368                         keyCode:   code,
369                         modifiers: modifiers,
370                         buttons:   -1,
371                         r:         0,
372                 }, true
373         }
374
375         // See if we can find the key in bindingMouse
376         if code, ok := bindingMouse[k]; ok {
377                 return Key{
378                         modifiers: modifiers,
379                         buttons:   code,
380                         r:         0,
381                 }, true
382         }
383
384         // If we were given one character, then we've got a rune.
385         if len(k) == 1 {
386                 return Key{
387                         keyCode:   tcell.KeyRune,
388                         modifiers: modifiers,
389                         buttons:   -1,
390                         r:         rune(k[0]),
391                 }, true
392         }
393
394         // We don't know what happened.
395         return Key{buttons: -1}, false
396 }
397
398 // findAction will find 'action' using string 'v'
399 func findAction(v string) (action func(*View, bool) bool) {
400         action, ok := bindingActions[v]
401         if !ok {
402                 // If the user seems to be binding a function that doesn't exist
403                 // We hope that it's a lua function that exists and bind it to that
404                 action = LuaFunctionBinding(v)
405         }
406         return action
407 }
408
409 func findMouseAction(v string) func(*View, bool, *tcell.EventMouse) bool {
410         action, ok := mouseBindingActions[v]
411         if !ok {
412                 // If the user seems to be binding a function that doesn't exist
413                 // We hope that it's a lua function that exists and bind it to that
414                 action = LuaFunctionMouseBinding(v)
415         }
416         return action
417 }
418
419 // TryBindKey tries to bind a key by writing to configDir/bindings.json
420 // This function is unused for now
421 func TryBindKey(k, v string) {
422         filename := configDir + "/bindings.json"
423         if _, e := os.Stat(filename); e == nil {
424                 input, err := ioutil.ReadFile(filename)
425                 if err != nil {
426                         TermMessage("Error reading bindings.json file: " + err.Error())
427                         return
428                 }
429
430                 conflict := -1
431                 lines := strings.Split(string(input), "\n")
432                 for i, l := range lines {
433                         parts := strings.Split(l, ":")
434                         if len(parts) >= 2 {
435                                 if strings.Contains(parts[0], k) {
436                                         conflict = i
437                                         TermMessage("Warning: Keybinding conflict:", k, " has been overwritten")
438                                 }
439                         }
440                 }
441
442                 binding := fmt.Sprintf("    \"%s\": \"%s\",", k, v)
443                 if conflict == -1 {
444                         lines = append([]string{lines[0], binding}, lines[conflict:]...)
445                 } else {
446                         lines = append(append(lines[:conflict], binding), lines[conflict+1:]...)
447                 }
448                 txt := strings.Join(lines, "\n")
449                 err = ioutil.WriteFile(filename, []byte(txt), 0644)
450                 if err != nil {
451                         TermMessage("Error")
452                 }
453         }
454 }
455
456 // BindKey takes a key and an action and binds the two together
457 func BindKey(k, v string) {
458         key, ok := findKey(k)
459         if !ok {
460                 TermMessage("Unknown keybinding: " + k)
461                 return
462         }
463         if v == "ToggleHelp" {
464                 helpBinding = k
465         }
466         if v == "ToggleKeyMenu" {
467                 kmenuBinding = k
468         }
469         if helpBinding == k && v != "ToggleHelp" {
470                 helpBinding = ""
471         }
472         if kmenuBinding == k && v != "ToggleKeyMenu" {
473                 kmenuBinding = ""
474         }
475
476         actionNames := strings.Split(v, ",")
477         if actionNames[0] == "UnbindKey" {
478                 delete(bindings, key)
479                 delete(mouseBindings, key)
480                 delete(bindingsStr, k)
481                 if len(actionNames) == 1 {
482                         return
483                 }
484                 actionNames = append(actionNames[:0], actionNames[1:]...)
485         }
486         actions := make([]func(*View, bool) bool, 0, len(actionNames))
487         mouseActions := make([]func(*View, bool, *tcell.EventMouse) bool, 0, len(actionNames))
488         for _, actionName := range actionNames {
489                 if strings.HasPrefix(actionName, "Mouse") {
490                         mouseActions = append(mouseActions, findMouseAction(actionName))
491                 } else if strings.HasPrefix(actionName, "command:") {
492                         cmd := strings.SplitN(actionName, ":", 2)[1]
493                         actions = append(actions, CommandAction(cmd))
494                 } else if strings.HasPrefix(actionName, "command-edit:") {
495                         cmd := strings.SplitN(actionName, ":", 2)[1]
496                         actions = append(actions, CommandEditAction(cmd))
497                 } else {
498                         actions = append(actions, findAction(actionName))
499                 }
500         }
501
502         if len(actions) > 0 {
503                 // Can't have a binding be both mouse and normal
504                 delete(mouseBindings, key)
505                 bindings[key] = actions
506                 bindingsStr[k] = v
507         } else if len(mouseActions) > 0 {
508                 // Can't have a binding be both mouse and normal
509                 delete(bindings, key)
510                 mouseBindings[key] = mouseActions
511         }
512 }
513
514 // DefaultBindings returns a map containing micro's default keybindings
515 func DefaultBindings() map[string]string {
516         return map[string]string{
517                 "Up":             "CursorUp",
518                 "Down":           "CursorDown",
519                 "Right":          "CursorRight",
520                 "Left":           "CursorLeft",
521                 "ShiftUp":        "SelectUp",
522                 "ShiftDown":      "SelectDown",
523                 "ShiftLeft":      "SelectLeft",
524                 "ShiftRight":     "SelectRight",
525                 "AltLeft":        "WordLeft",
526                 "AltRight":       "WordRight",
527                 "AltUp":          "MoveLinesUp",
528                 "AltDown":        "MoveLinesDown",
529                 "AltShiftRight":  "SelectWordRight",
530                 "AltShiftLeft":   "SelectWordLeft",
531                 "CtrlLeft":       "StartOfLine",
532                 "CtrlRight":      "EndOfLine",
533                 "CtrlShiftLeft":  "SelectToStartOfLine",
534                 "ShiftHome":      "SelectToStartOfLine",
535                 "CtrlShiftRight": "SelectToEndOfLine",
536                 "ShiftEnd":       "SelectToEndOfLine",
537                 "CtrlUp":         "CursorStart",
538                 "CtrlDown":       "CursorEnd",
539                 "CtrlShiftUp":    "SelectToStart",
540                 "CtrlShiftDown":  "SelectToEnd",
541                 "Alt-{":          "ParagraphPrevious",
542                 "Alt-}":          "ParagraphNext",
543                 "Enter":          "InsertNewline",
544                 "CtrlH":          "Backspace",
545                 "Backspace":      "Backspace",
546                 "Alt-CtrlH":      "DeleteWordLeft",
547                 "Alt-Backspace":  "DeleteWordLeft",
548                 "Tab":            "IndentSelection,InsertTab",
549                 "Backtab":        "OutdentSelection,OutdentLine",
550                 "CtrlO":          "OpenFile",
551                 "CtrlS":          "Save",
552                 "CtrlF":          "Find",
553                 "CtrlN":          "FindNext",
554                 "CtrlP":          "FindPrevious",
555                 "CtrlZ":          "Undo",
556                 "CtrlY":          "Redo",
557                 "CtrlC":          "Copy",
558                 "CtrlX":          "Cut",
559                 "CtrlK":          "CutLine",
560                 "CtrlD":          "DuplicateLine",
561                 "CtrlV":          "Paste",
562                 "CtrlA":          "SelectAll",
563                 "CtrlT":          "AddTab",
564                 "Alt,":           "PreviousTab",
565                 "Alt.":           "NextTab",
566                 "Home":           "StartOfLine",
567                 "End":            "EndOfLine",
568                 "CtrlHome":       "CursorStart",
569                 "CtrlEnd":        "CursorEnd",
570                 "PageUp":         "CursorPageUp",
571                 "PageDown":       "CursorPageDown",
572                 "CtrlPageUp":     "PreviousTab",
573                 "CtrlPageDown":   "NextTab",
574                 "CtrlG":          "ToggleHelp",
575                 "Alt-g":          "ToggleKeyMenu",
576                 "CtrlR":          "ToggleRuler",
577                 "CtrlL":          "JumpLine",
578                 "Delete":         "Delete",
579                 "CtrlB":          "ShellMode",
580                 "CtrlQ":          "Quit",
581                 "CtrlE":          "CommandMode",
582                 "CtrlW":          "NextSplit",
583                 "CtrlU":          "ToggleMacro",
584                 "CtrlJ":          "PlayMacro",
585                 "Insert":         "ToggleOverwriteMode",
586
587                 // Emacs-style keybindings
588                 "Alt-f": "WordRight",
589                 "Alt-b": "WordLeft",
590                 "Alt-a": "StartOfLine",
591                 "Alt-e": "EndOfLine",
592                 // "Alt-p": "CursorUp",
593                 // "Alt-n": "CursorDown",
594
595                 // Integration with file managers
596                 "F2":  "Save",
597                 "F3":  "Find",
598                 "F4":  "Quit",
599                 "F7":  "Find",
600                 "F10": "Quit",
601                 "Esc": "Escape",
602
603                 // Mouse bindings
604                 "MouseWheelUp":   "ScrollUp",
605                 "MouseWheelDown": "ScrollDown",
606                 "MouseLeft":      "MousePress",
607                 "MouseMiddle":    "PastePrimary",
608                 "Ctrl-MouseLeft": "MouseMultiCursor",
609
610                 "Alt-n": "SpawnMultiCursor",
611                 "Alt-m": "SpawnMultiCursorSelect",
612                 "Alt-p": "RemoveMultiCursor",
613                 "Alt-c": "RemoveAllMultiCursors",
614                 "Alt-x": "SkipMultiCursor",
615         }
616 }