8 "github.com/zyedidia/json5/encoding/json5"
9 "github.com/zyedidia/tcell"
12 var bindings map[Key][]func(*View, bool) bool
13 var helpBinding string
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,
44 "SaveAll": (*View).SaveAll,
45 "SaveAs": (*View).SaveAs,
47 "FindNext": (*View).FindNext,
48 "FindPrevious": (*View).FindPrevious,
49 "Center": (*View).Center,
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,
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,
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,
94 // This was changed to InsertNewline but I don't want to break backwards compatibility
95 "InsertEnter": (*View).InsertNewline,
98 var bindingKeys = map[string]tcell.Key{
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,
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,
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,
219 "Escape": tcell.KeyEscape,
220 "Enter": tcell.KeyEnter,
221 "Backspace": tcell.KeyBackspace2,
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,
228 // The Key struct holds the data for a keypress (keycode + modifiers)
231 modifiers tcell.ModMask
235 // InitBindings initializes the keybindings for micro
236 func InitBindings() {
237 bindings = make(map[Key][]func(*View, bool) bool)
239 var parsed map[string]string
240 defaults := DefaultBindings()
242 filename := configDir + "/bindings.json"
243 if _, e := os.Stat(filename); e == nil {
244 input, err := ioutil.ReadFile(filename)
246 TermMessage("Error reading bindings.json file: " + err.Error())
250 err = json5.Unmarshal(input, &parsed)
252 TermMessage("Error reading bindings.json:", err.Error())
256 parseBindings(defaults)
257 parseBindings(parsed)
260 func parseBindings(userBindings map[string]string) {
261 for k, v := range userBindings {
266 // findKey will find binding Key 'b' using string 'k'
267 func findKey(k string) (b Key, ok bool) {
268 modifiers := tcell.ModNone
270 // First, we'll strip off all the modifiers in the name and add them to the
275 case strings.HasPrefix(k, "-"):
276 // We optionally support dashes between modifiers
278 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
279 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
281 modifiers |= tcell.ModCtrl
282 case strings.HasPrefix(k, "Alt"):
284 modifiers |= tcell.ModAlt
285 case strings.HasPrefix(k, "Shift"):
287 modifiers |= tcell.ModShift
293 // Control is handled specially, since some character codes in bindingKeys
294 // are different when Control is depressed. We should check for Control keys
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.
302 modifiers: modifiers,
308 // See if we can find the key in bindingKeys
309 if code, ok := bindingKeys[k]; ok {
312 modifiers: modifiers,
317 // If we were given one character, then we've got a rune.
320 keyCode: tcell.KeyRune,
321 modifiers: modifiers,
326 // We don't know what happened.
330 // findAction will find 'action' using string 'v'
331 func findAction(v string) (action func(*View, bool) bool) {
332 action, ok := bindingActions[v]
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)
341 // BindKey takes a key and an action and binds the two together
342 func BindKey(k, v string) {
343 key, ok := findKey(k)
345 TermMessage("Unknown keybinding: " + k)
348 if v == "ToggleHelp" {
351 if helpBinding == k && v != "ToggleHelp" {
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)
361 actionNames = append(actionNames[:0], actionNames[1:]...)
364 actions := make([]func(*View, bool) bool, 0, len(actionNames))
365 for _, actionName := range actionNames {
366 actions = append(actions, findAction(actionName))
369 bindings[key] = actions
372 // DefaultBindings returns a map containing micro's default keybindings
373 func DefaultBindings() map[string]string {
374 return map[string]string{
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",
410 "CtrlP": "FindPrevious",
416 "CtrlD": "DuplicateLine",
418 "CtrlA": "SelectAll",
420 "Alt,": "PreviousTab",
422 "Home": "StartOfLine",
424 "CtrlHome": "CursorStart",
425 "CtrlEnd": "CursorEnd",
426 "PageUp": "CursorPageUp",
427 "PageDown": "CursorPageDown",
428 "CtrlG": "ToggleHelp",
429 "CtrlR": "ToggleRuler",
432 "CtrlB": "ShellMode",
434 "CtrlE": "CommandMode",
435 "CtrlW": "NextSplit",
436 "CtrlU": "ToggleMacro",
437 "CtrlJ": "PlayMacro",
439 // Emacs-style keybindings
440 "Alt-f": "WordRight",
442 "Alt-a": "StartOfLine",
443 "Alt-e": "EndOfLine",
445 "Alt-n": "CursorDown",
447 // Integration with file managers