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 {
109 // It is, we're done.
118 // See if we can find the key in bindingKeys
119 if code, ok := keyEvents[k]; ok {
131 // See if we can find the key in bindingMouse
132 if code, ok := mouseEvents[k]; ok {
139 // If we were given one character, then we've got a rune.
148 // We don't know what happened.
149 return KeyEvent{}, false
152 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
153 // Returns true if the keybinding already existed and a possible error
154 func TryBindKey(k, v string, overwrite bool) (bool, error) {
156 var parsed map[string]string
158 filename := config.ConfigDir + "/bindings.json"
159 if _, e = os.Stat(filename); e == nil {
160 input, err := ioutil.ReadFile(filename)
162 return false, errors.New("Error reading bindings.json file: " + err.Error())
165 err = json5.Unmarshal(input, &parsed)
167 return false, errors.New("Error reading bindings.json: " + err.Error())
170 key, ok := findEvent(k)
172 return false, errors.New("Invalid event " + k)
176 for ev := range parsed {
177 if e, ok := findEvent(ev); ok {
188 if found && !overwrite {
196 txt, _ := json.MarshalIndent(parsed, "", " ")
197 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
202 var mouseEvents = map[string]tcell.ButtonMask{
203 "MouseLeft": tcell.Button1,
204 "MouseMiddle": tcell.Button2,
205 "MouseRight": tcell.Button3,
206 "MouseWheelUp": tcell.WheelUp,
207 "MouseWheelDown": tcell.WheelDown,
208 "MouseWheelLeft": tcell.WheelLeft,
209 "MouseWheelRight": tcell.WheelRight,
212 var keyEvents = map[string]tcell.Key{
214 "Down": tcell.KeyDown,
215 "Right": tcell.KeyRight,
216 "Left": tcell.KeyLeft,
217 "UpLeft": tcell.KeyUpLeft,
218 "UpRight": tcell.KeyUpRight,
219 "DownLeft": tcell.KeyDownLeft,
220 "DownRight": tcell.KeyDownRight,
221 "Center": tcell.KeyCenter,
222 "PageUp": tcell.KeyPgUp,
223 "PageDown": tcell.KeyPgDn,
224 "Home": tcell.KeyHome,
226 "Insert": tcell.KeyInsert,
227 "Delete": tcell.KeyDelete,
228 "Help": tcell.KeyHelp,
229 "Exit": tcell.KeyExit,
230 "Clear": tcell.KeyClear,
231 "Cancel": tcell.KeyCancel,
232 "Print": tcell.KeyPrint,
233 "Pause": tcell.KeyPause,
234 "Backtab": tcell.KeyBacktab,
299 "CtrlSpace": tcell.KeyCtrlSpace,
300 "CtrlA": tcell.KeyCtrlA,
301 "CtrlB": tcell.KeyCtrlB,
302 "CtrlC": tcell.KeyCtrlC,
303 "CtrlD": tcell.KeyCtrlD,
304 "CtrlE": tcell.KeyCtrlE,
305 "CtrlF": tcell.KeyCtrlF,
306 "CtrlG": tcell.KeyCtrlG,
307 "CtrlH": tcell.KeyCtrlH,
308 "CtrlI": tcell.KeyCtrlI,
309 "CtrlJ": tcell.KeyCtrlJ,
310 "CtrlK": tcell.KeyCtrlK,
311 "CtrlL": tcell.KeyCtrlL,
312 "CtrlM": tcell.KeyCtrlM,
313 "CtrlN": tcell.KeyCtrlN,
314 "CtrlO": tcell.KeyCtrlO,
315 "CtrlP": tcell.KeyCtrlP,
316 "CtrlQ": tcell.KeyCtrlQ,
317 "CtrlR": tcell.KeyCtrlR,
318 "CtrlS": tcell.KeyCtrlS,
319 "CtrlT": tcell.KeyCtrlT,
320 "CtrlU": tcell.KeyCtrlU,
321 "CtrlV": tcell.KeyCtrlV,
322 "CtrlW": tcell.KeyCtrlW,
323 "CtrlX": tcell.KeyCtrlX,
324 "CtrlY": tcell.KeyCtrlY,
325 "CtrlZ": tcell.KeyCtrlZ,
326 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
327 "CtrlBackslash": tcell.KeyCtrlBackslash,
328 "CtrlRightSq": tcell.KeyCtrlRightSq,
329 "CtrlCarat": tcell.KeyCtrlCarat,
330 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
331 "CtrlPageUp": tcell.KeyCtrlPgUp,
332 "CtrlPageDown": tcell.KeyCtrlPgDn,
335 "Escape": tcell.KeyEscape,
336 "Enter": tcell.KeyEnter,
337 "Backspace": tcell.KeyBackspace2,
338 "OldBackspace": tcell.KeyBackspace,
340 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
341 "PgUp": tcell.KeyPgUp,
342 "PgDown": tcell.KeyPgDn,
345 // DefaultBindings returns a map containing micro's default keybindings
346 func DefaultBindings() map[string]string {
347 return map[string]string{
349 "Down": "CursorDown",
350 "Right": "CursorRight",
351 "Left": "CursorLeft",
352 "ShiftUp": "SelectUp",
353 "ShiftDown": "SelectDown",
354 "ShiftLeft": "SelectLeft",
355 "ShiftRight": "SelectRight",
356 "AltLeft": "WordLeft",
357 "AltRight": "WordRight",
358 "AltUp": "MoveLinesUp",
359 "AltDown": "MoveLinesDown",
360 "AltShiftRight": "SelectWordRight",
361 "AltShiftLeft": "SelectWordLeft",
362 "CtrlLeft": "StartOfLine",
363 "CtrlRight": "EndOfLine",
364 "CtrlShiftLeft": "SelectToStartOfLine",
365 "ShiftHome": "SelectToStartOfLine",
366 "CtrlShiftRight": "SelectToEndOfLine",
367 "ShiftEnd": "SelectToEndOfLine",
368 "CtrlUp": "CursorStart",
369 "CtrlDown": "CursorEnd",
370 "CtrlShiftUp": "SelectToStart",
371 "CtrlShiftDown": "SelectToEnd",
372 "Alt-{": "ParagraphPrevious",
373 "Alt-}": "ParagraphNext",
374 "Enter": "InsertNewline",
375 "CtrlH": "Backspace",
376 "Backspace": "Backspace",
377 "Alt-CtrlH": "DeleteWordLeft",
378 "Alt-Backspace": "DeleteWordLeft",
380 "Backtab": "OutdentLine",
385 "CtrlP": "FindPrevious",
391 "CtrlD": "DuplicateLine",
393 "CtrlA": "SelectAll",
395 "Alt,": "PreviousTab",
397 "Home": "StartOfLine",
399 "CtrlHome": "CursorStart",
400 "CtrlEnd": "CursorEnd",
401 "PageUp": "CursorPageUp",
402 "PageDown": "CursorPageDown",
403 "CtrlPageUp": "PreviousTab",
404 "CtrlPageDown": "NextTab",
405 "CtrlG": "ToggleHelp",
406 "Alt-g": "ToggleKeyMenu",
407 "CtrlR": "ToggleRuler",
410 "CtrlB": "ShellMode",
412 "CtrlE": "CommandMode",
413 "CtrlW": "NextSplit",
414 "CtrlU": "ToggleMacro",
415 "CtrlJ": "PlayMacro",
416 "Insert": "ToggleOverwriteMode",
418 // Emacs-style keybindings
419 "Alt-f": "WordRight",
421 "Alt-a": "StartOfLine",
422 "Alt-e": "EndOfLine",
423 // "Alt-p": "CursorUp",
424 // "Alt-n": "CursorDown",
426 // Integration with file managers
435 "MouseWheelUp": "ScrollUp",
436 "MouseWheelDown": "ScrollDown",
437 "MouseLeft": "MousePress",
438 "MouseMiddle": "PastePrimary",
439 "Ctrl-MouseLeft": "MouseMultiCursor",
441 "Alt-n": "SpawnMultiCursor",
442 "Alt-m": "SpawnMultiCursorSelect",
443 "Alt-p": "RemoveMultiCursor",
444 "Alt-c": "RemoveAllMultiCursors",
445 "Alt-x": "SkipMultiCursor",