11 "github.com/flynn/json5"
12 "github.com/zyedidia/micro/cmd/micro/config"
13 "github.com/zyedidia/micro/cmd/micro/util"
14 "github.com/zyedidia/tcell"
17 var Bindings = DefaultBindings()
20 var parsed map[string]string
21 defaults := DefaultBindings()
23 filename := config.ConfigDir + "/bindings.json"
24 if _, e := os.Stat(filename); e == nil {
25 input, err := ioutil.ReadFile(filename)
27 util.TermMessage("Error reading bindings.json file: " + err.Error())
31 err = json5.Unmarshal(input, &parsed)
33 util.TermMessage("Error reading bindings.json:", err.Error())
37 for k, v := range defaults {
40 for k, v := range parsed {
45 func BindKey(k, v string) {
46 event, ok := findEvent(k)
48 util.TermMessage(k, "is not a bindable event")
51 switch e := event.(type) {
57 util.TermMessage("Raw events not supported yet")
63 // findKeyEvent will find binding Key 'b' using string 'k'
64 func findEvent(k string) (b Event, ok bool) {
65 modifiers := tcell.ModNone
67 // First, we'll strip off all the modifiers in the name and add them to the
72 case strings.HasPrefix(k, "-"):
73 // We optionally support dashes between modifiers
75 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
76 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
78 modifiers |= tcell.ModCtrl
79 case strings.HasPrefix(k, "Alt"):
81 modifiers |= tcell.ModAlt
82 case strings.HasPrefix(k, "Shift"):
84 modifiers |= tcell.ModShift
85 case strings.HasPrefix(k, "\x1b"):
95 return KeyEvent{}, false
98 // Control is handled in a special way, since the terminal sends explicitly
99 // marked escape sequences for control keys
100 // We should check for Control keys first
101 if modifiers&tcell.ModCtrl != 0 {
102 // see if the key is in bindingKeys with the Ctrl prefix.
103 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
104 if code, ok := keyEvents["Ctrl"+k]; ok {
106 // Special case for escape, for some reason tcell doesn't send it with the esc character
107 if code < 256 && code != 27 {
110 // It is, we're done.
119 // See if we can find the key in bindingKeys
120 if code, ok := keyEvents[k]; ok {
122 // Special case for escape, for some reason tcell doesn't send it with the esc character
123 if code < 256 && code != 27 {
133 // See if we can find the key in bindingMouse
134 if code, ok := mouseEvents[k]; ok {
141 // If we were given one character, then we've got a rune.
150 // We don't know what happened.
151 return KeyEvent{}, false
154 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
155 // Returns true if the keybinding already existed and a possible error
156 func TryBindKey(k, v string, overwrite bool) (bool, error) {
158 var parsed map[string]string
160 filename := config.ConfigDir + "/bindings.json"
161 if _, e = os.Stat(filename); e == nil {
162 input, err := ioutil.ReadFile(filename)
164 return false, errors.New("Error reading bindings.json file: " + err.Error())
167 err = json5.Unmarshal(input, &parsed)
169 return false, errors.New("Error reading bindings.json: " + err.Error())
172 key, ok := findEvent(k)
174 return false, errors.New("Invalid event " + k)
178 for ev := range parsed {
179 if e, ok := findEvent(ev); ok {
190 if found && !overwrite {
198 txt, _ := json.MarshalIndent(parsed, "", " ")
199 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
204 var mouseEvents = map[string]tcell.ButtonMask{
205 "MouseLeft": tcell.Button1,
206 "MouseMiddle": tcell.Button2,
207 "MouseRight": tcell.Button3,
208 "MouseWheelUp": tcell.WheelUp,
209 "MouseWheelDown": tcell.WheelDown,
210 "MouseWheelLeft": tcell.WheelLeft,
211 "MouseWheelRight": tcell.WheelRight,
214 var keyEvents = map[string]tcell.Key{
216 "Down": tcell.KeyDown,
217 "Right": tcell.KeyRight,
218 "Left": tcell.KeyLeft,
219 "UpLeft": tcell.KeyUpLeft,
220 "UpRight": tcell.KeyUpRight,
221 "DownLeft": tcell.KeyDownLeft,
222 "DownRight": tcell.KeyDownRight,
223 "Center": tcell.KeyCenter,
224 "PageUp": tcell.KeyPgUp,
225 "PageDown": tcell.KeyPgDn,
226 "Home": tcell.KeyHome,
228 "Insert": tcell.KeyInsert,
229 "Delete": tcell.KeyDelete,
230 "Help": tcell.KeyHelp,
231 "Exit": tcell.KeyExit,
232 "Clear": tcell.KeyClear,
233 "Cancel": tcell.KeyCancel,
234 "Print": tcell.KeyPrint,
235 "Pause": tcell.KeyPause,
236 "Backtab": tcell.KeyBacktab,
301 "CtrlSpace": tcell.KeyCtrlSpace,
302 "CtrlA": tcell.KeyCtrlA,
303 "CtrlB": tcell.KeyCtrlB,
304 "CtrlC": tcell.KeyCtrlC,
305 "CtrlD": tcell.KeyCtrlD,
306 "CtrlE": tcell.KeyCtrlE,
307 "CtrlF": tcell.KeyCtrlF,
308 "CtrlG": tcell.KeyCtrlG,
309 "CtrlH": tcell.KeyCtrlH,
310 "CtrlI": tcell.KeyCtrlI,
311 "CtrlJ": tcell.KeyCtrlJ,
312 "CtrlK": tcell.KeyCtrlK,
313 "CtrlL": tcell.KeyCtrlL,
314 "CtrlM": tcell.KeyCtrlM,
315 "CtrlN": tcell.KeyCtrlN,
316 "CtrlO": tcell.KeyCtrlO,
317 "CtrlP": tcell.KeyCtrlP,
318 "CtrlQ": tcell.KeyCtrlQ,
319 "CtrlR": tcell.KeyCtrlR,
320 "CtrlS": tcell.KeyCtrlS,
321 "CtrlT": tcell.KeyCtrlT,
322 "CtrlU": tcell.KeyCtrlU,
323 "CtrlV": tcell.KeyCtrlV,
324 "CtrlW": tcell.KeyCtrlW,
325 "CtrlX": tcell.KeyCtrlX,
326 "CtrlY": tcell.KeyCtrlY,
327 "CtrlZ": tcell.KeyCtrlZ,
328 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
329 "CtrlBackslash": tcell.KeyCtrlBackslash,
330 "CtrlRightSq": tcell.KeyCtrlRightSq,
331 "CtrlCarat": tcell.KeyCtrlCarat,
332 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
333 "CtrlPageUp": tcell.KeyCtrlPgUp,
334 "CtrlPageDown": tcell.KeyCtrlPgDn,
337 "Escape": tcell.KeyEscape,
338 "Enter": tcell.KeyEnter,
339 "Backspace": tcell.KeyBackspace2,
340 "OldBackspace": tcell.KeyBackspace,
342 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
343 "PgUp": tcell.KeyPgUp,
344 "PgDown": tcell.KeyPgDn,
347 // DefaultBindings returns a map containing micro's default keybindings
348 func DefaultBindings() map[string]string {
349 return map[string]string{
351 "Down": "CursorDown",
352 "Right": "CursorRight",
353 "Left": "CursorLeft",
354 "ShiftUp": "SelectUp",
355 "ShiftDown": "SelectDown",
356 "ShiftLeft": "SelectLeft",
357 "ShiftRight": "SelectRight",
358 "AltLeft": "WordLeft",
359 "AltRight": "WordRight",
360 "AltUp": "MoveLinesUp",
361 "AltDown": "MoveLinesDown",
362 "AltShiftRight": "SelectWordRight",
363 "AltShiftLeft": "SelectWordLeft",
364 "CtrlLeft": "StartOfLine",
365 "CtrlRight": "EndOfLine",
366 "CtrlShiftLeft": "SelectToStartOfLine",
367 "ShiftHome": "SelectToStartOfLine",
368 "CtrlShiftRight": "SelectToEndOfLine",
369 "ShiftEnd": "SelectToEndOfLine",
370 "CtrlUp": "CursorStart",
371 "CtrlDown": "CursorEnd",
372 "CtrlShiftUp": "SelectToStart",
373 "CtrlShiftDown": "SelectToEnd",
374 "Alt-{": "ParagraphPrevious",
375 "Alt-}": "ParagraphNext",
376 "Enter": "InsertNewline",
377 "CtrlH": "Backspace",
378 "Backspace": "Backspace",
379 "Alt-CtrlH": "DeleteWordLeft",
380 "Alt-Backspace": "DeleteWordLeft",
382 "Backtab": "OutdentLine",
387 "CtrlP": "FindPrevious",
393 "CtrlD": "DuplicateLine",
395 "CtrlA": "SelectAll",
397 "Alt,": "PreviousTab",
399 "Home": "StartOfLine",
401 "CtrlHome": "CursorStart",
402 "CtrlEnd": "CursorEnd",
403 "PageUp": "CursorPageUp",
404 "PageDown": "CursorPageDown",
405 "CtrlPageUp": "PreviousTab",
406 "CtrlPageDown": "NextTab",
407 "CtrlG": "ToggleHelp",
408 "Alt-g": "ToggleKeyMenu",
409 "CtrlR": "ToggleRuler",
412 "CtrlB": "ShellMode",
414 "CtrlE": "CommandMode",
415 "CtrlW": "NextSplit",
416 "CtrlU": "ToggleMacro",
417 "CtrlJ": "PlayMacro",
418 "Insert": "ToggleOverwriteMode",
420 // Emacs-style keybindings
421 "Alt-f": "WordRight",
423 "Alt-a": "StartOfLine",
424 "Alt-e": "EndOfLine",
425 // "Alt-p": "CursorUp",
426 // "Alt-n": "CursorDown",
428 // Integration with file managers
437 "MouseWheelUp": "ScrollUp",
438 "MouseWheelDown": "ScrollDown",
439 "MouseLeft": "MousePress",
440 "MouseMiddle": "PastePrimary",
441 "Ctrl-MouseLeft": "MouseMultiCursor",
443 "Alt-n": "SpawnMultiCursor",
444 "Alt-m": "SpawnMultiCursorSelect",
445 "Alt-p": "RemoveMultiCursor",
446 "Alt-c": "RemoveAllMultiCursors",
447 "Alt-x": "SkipMultiCursor",