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