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 {
105 // It is, we're done.
114 // See if we can find the key in bindingKeys
115 if code, ok := keyEvents[k]; ok {
123 // See if we can find the key in bindingMouse
124 if code, ok := mouseEvents[k]; ok {
131 // If we were given one character, then we've got a rune.
140 // We don't know what happened.
141 return KeyEvent{}, false
144 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
145 // Returns true if the keybinding already existed and a possible error
146 func TryBindKey(k, v string, overwrite bool) (bool, error) {
148 var parsed map[string]string
150 filename := config.ConfigDir + "/bindings.json"
151 if _, e = os.Stat(filename); e == nil {
152 input, err := ioutil.ReadFile(filename)
154 return false, errors.New("Error reading bindings.json file: " + err.Error())
157 err = json5.Unmarshal(input, &parsed)
159 return false, errors.New("Error reading bindings.json: " + err.Error())
162 key, ok := findEvent(k)
164 return false, errors.New("Invalid event " + k)
168 for ev := range parsed {
169 if e, ok := findEvent(ev); ok {
180 if found && !overwrite {
188 txt, _ := json.MarshalIndent(parsed, "", " ")
189 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
194 var mouseEvents = map[string]tcell.ButtonMask{
195 "MouseLeft": tcell.Button1,
196 "MouseMiddle": tcell.Button2,
197 "MouseRight": tcell.Button3,
198 "MouseWheelUp": tcell.WheelUp,
199 "MouseWheelDown": tcell.WheelDown,
200 "MouseWheelLeft": tcell.WheelLeft,
201 "MouseWheelRight": tcell.WheelRight,
204 var keyEvents = map[string]tcell.Key{
206 "Down": tcell.KeyDown,
207 "Right": tcell.KeyRight,
208 "Left": tcell.KeyLeft,
209 "UpLeft": tcell.KeyUpLeft,
210 "UpRight": tcell.KeyUpRight,
211 "DownLeft": tcell.KeyDownLeft,
212 "DownRight": tcell.KeyDownRight,
213 "Center": tcell.KeyCenter,
214 "PageUp": tcell.KeyPgUp,
215 "PageDown": tcell.KeyPgDn,
216 "Home": tcell.KeyHome,
218 "Insert": tcell.KeyInsert,
219 "Delete": tcell.KeyDelete,
220 "Help": tcell.KeyHelp,
221 "Exit": tcell.KeyExit,
222 "Clear": tcell.KeyClear,
223 "Cancel": tcell.KeyCancel,
224 "Print": tcell.KeyPrint,
225 "Pause": tcell.KeyPause,
226 "Backtab": tcell.KeyBacktab,
291 "CtrlSpace": tcell.KeyCtrlSpace,
292 "CtrlA": tcell.KeyCtrlA,
293 "CtrlB": tcell.KeyCtrlB,
294 "CtrlC": tcell.KeyCtrlC,
295 "CtrlD": tcell.KeyCtrlD,
296 "CtrlE": tcell.KeyCtrlE,
297 "CtrlF": tcell.KeyCtrlF,
298 "CtrlG": tcell.KeyCtrlG,
299 "CtrlH": tcell.KeyCtrlH,
300 "CtrlI": tcell.KeyCtrlI,
301 "CtrlJ": tcell.KeyCtrlJ,
302 "CtrlK": tcell.KeyCtrlK,
303 "CtrlL": tcell.KeyCtrlL,
304 "CtrlM": tcell.KeyCtrlM,
305 "CtrlN": tcell.KeyCtrlN,
306 "CtrlO": tcell.KeyCtrlO,
307 "CtrlP": tcell.KeyCtrlP,
308 "CtrlQ": tcell.KeyCtrlQ,
309 "CtrlR": tcell.KeyCtrlR,
310 "CtrlS": tcell.KeyCtrlS,
311 "CtrlT": tcell.KeyCtrlT,
312 "CtrlU": tcell.KeyCtrlU,
313 "CtrlV": tcell.KeyCtrlV,
314 "CtrlW": tcell.KeyCtrlW,
315 "CtrlX": tcell.KeyCtrlX,
316 "CtrlY": tcell.KeyCtrlY,
317 "CtrlZ": tcell.KeyCtrlZ,
318 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
319 "CtrlBackslash": tcell.KeyCtrlBackslash,
320 "CtrlRightSq": tcell.KeyCtrlRightSq,
321 "CtrlCarat": tcell.KeyCtrlCarat,
322 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
323 "CtrlPageUp": tcell.KeyCtrlPgUp,
324 "CtrlPageDown": tcell.KeyCtrlPgDn,
327 "Escape": tcell.KeyEscape,
328 "Enter": tcell.KeyEnter,
329 "Backspace": tcell.KeyBackspace2,
330 "OldBackspace": tcell.KeyBackspace,
332 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
333 "PgUp": tcell.KeyPgUp,
334 "PgDown": tcell.KeyPgDn,
337 // DefaultBindings returns a map containing micro's default keybindings
338 func DefaultBindings() map[string]string {
339 return map[string]string{
341 "Down": "CursorDown",
342 "Right": "CursorRight",
343 "Left": "CursorLeft",
344 "ShiftUp": "SelectUp",
345 "ShiftDown": "SelectDown",
346 "ShiftLeft": "SelectLeft",
347 "ShiftRight": "SelectRight",
348 "AltLeft": "WordLeft",
349 "AltRight": "WordRight",
350 "AltUp": "MoveLinesUp",
351 "AltDown": "MoveLinesDown",
352 "AltShiftRight": "SelectWordRight",
353 "AltShiftLeft": "SelectWordLeft",
354 "CtrlLeft": "StartOfLine",
355 "CtrlRight": "EndOfLine",
356 "CtrlShiftLeft": "SelectToStartOfLine",
357 "ShiftHome": "SelectToStartOfLine",
358 "CtrlShiftRight": "SelectToEndOfLine",
359 "ShiftEnd": "SelectToEndOfLine",
360 "CtrlUp": "CursorStart",
361 "CtrlDown": "CursorEnd",
362 "CtrlShiftUp": "SelectToStart",
363 "CtrlShiftDown": "SelectToEnd",
364 "Alt-{": "ParagraphPrevious",
365 "Alt-}": "ParagraphNext",
366 "Enter": "InsertNewline",
367 "CtrlH": "Backspace",
368 "Backspace": "Backspace",
369 "Alt-CtrlH": "DeleteWordLeft",
370 "Alt-Backspace": "DeleteWordLeft",
371 "Tab": "IndentSelection,InsertTab",
372 "Backtab": "OutdentSelection,OutdentLine",
377 "CtrlP": "FindPrevious",
383 "CtrlD": "DuplicateLine",
385 "CtrlA": "SelectAll",
387 "Alt,": "PreviousTab",
389 "Home": "StartOfLine",
391 "CtrlHome": "CursorStart",
392 "CtrlEnd": "CursorEnd",
393 "PageUp": "CursorPageUp",
394 "PageDown": "CursorPageDown",
395 "CtrlPageUp": "PreviousTab",
396 "CtrlPageDown": "NextTab",
397 "CtrlG": "ToggleHelp",
398 "Alt-g": "ToggleKeyMenu",
399 "CtrlR": "ToggleRuler",
402 "CtrlB": "ShellMode",
404 "CtrlE": "CommandMode",
405 "CtrlW": "NextSplit",
406 "CtrlU": "ToggleMacro",
407 "CtrlJ": "PlayMacro",
408 "Insert": "ToggleOverwriteMode",
410 // Emacs-style keybindings
411 "Alt-f": "WordRight",
413 "Alt-a": "StartOfLine",
414 "Alt-e": "EndOfLine",
415 // "Alt-p": "CursorUp",
416 // "Alt-n": "CursorDown",
418 // Integration with file managers
427 "MouseWheelUp": "ScrollUp",
428 "MouseWheelDown": "ScrollDown",
429 "MouseLeft": "MousePress",
430 "MouseMiddle": "PastePrimary",
431 "Ctrl-MouseLeft": "MouseMultiCursor",
433 "Alt-n": "SpawnMultiCursor",
434 "Alt-m": "SpawnMultiCursorSelect",
435 "Alt-p": "RemoveMultiCursor",
436 "Alt-c": "RemoveAllMultiCursors",
437 "Alt-x": "SkipMultiCursor",