13 "github.com/zyedidia/json5"
14 "github.com/zyedidia/micro/v2/internal/config"
15 "github.com/zyedidia/micro/v2/internal/screen"
16 "github.com/zyedidia/tcell"
19 var Binder = map[string]func(e Event, action string){
21 "buffer": BufMapEvent,
22 "terminal": TermMapEvent,
25 func createBindingsIfNotExist(fname string) {
26 if _, e := os.Stat(fname); os.IsNotExist(e) {
27 ioutil.WriteFile(fname, []byte("{}"), 0644)
31 // InitBindings intializes the bindings map by reading from bindings.json
33 var parsed map[string]interface{}
35 filename := filepath.Join(config.ConfigDir, "bindings.json")
36 createBindingsIfNotExist(filename)
38 if _, e := os.Stat(filename); e == nil {
39 input, err := ioutil.ReadFile(filename)
41 screen.TermMessage("Error reading bindings.json file: " + err.Error())
45 err = json5.Unmarshal(input, &parsed)
47 screen.TermMessage("Error reading bindings.json:", err.Error())
51 for p, bind := range Binder {
52 defaults := DefaultBindings(p)
54 for k, v := range defaults {
59 for k, v := range parsed {
60 switch val := v.(type) {
62 BindKey(k, val, Binder["buffer"])
63 case map[string]interface{}:
65 for e, a := range val {
68 screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
74 screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
79 func BindKey(k, v string, bind func(e Event, a string)) {
80 event, err := findEvent(k)
82 screen.TermMessage(err)
86 config.Bindings[event.Name()] = v
90 // switch e := event.(type) {
93 // case KeySequenceEvent:
102 var r = regexp.MustCompile("<(.+?)>")
104 func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
105 var events []Event = nil
107 groups := r.FindStringSubmatchIndex(k)
111 events = make([]Event, 0, 3)
114 e, ok := findSingleEvent(k[groups[2]:groups[3]])
116 return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
119 events = append(events, e)
123 return KeySequenceEvent{}, false, nil
127 return KeySequenceEvent{events}, true, nil
130 // findSingleEvent will find binding Key 'b' using string 'k'
131 func findSingleEvent(k string) (b Event, ok bool) {
132 modifiers := tcell.ModNone
134 // First, we'll strip off all the modifiers in the name and add them to the
139 case strings.HasPrefix(k, "-"):
140 // We optionally support dashes between modifiers
142 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
143 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
145 modifiers |= tcell.ModCtrl
146 case strings.HasPrefix(k, "Alt"):
148 modifiers |= tcell.ModAlt
149 case strings.HasPrefix(k, "Shift"):
151 modifiers |= tcell.ModShift
152 case strings.HasPrefix(k, "\x1b"):
153 screen.Screen.RegisterRawSeq(k)
163 return KeyEvent{}, false
166 // Control is handled in a special way, since the terminal sends explicitly
167 // marked escape sequences for control keys
168 // We should check for Control keys first
169 if modifiers&tcell.ModCtrl != 0 {
170 // see if the key is in bindingKeys with the Ctrl prefix.
171 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
172 if code, ok := keyEvents["Ctrl"+k]; ok {
174 // Special case for escape, for some reason tcell doesn't send it with the esc character
175 if code < 256 && code != 27 {
178 // It is, we're done.
187 // See if we can find the key in bindingKeys
188 if code, ok := keyEvents[k]; ok {
190 // Special case for escape, for some reason tcell doesn't send it with the esc character
191 if code < 256 && code != 27 {
201 // See if we can find the key in bindingMouse
202 if code, ok := mouseEvents[k]; ok {
209 // If we were given one character, then we've got a rune.
218 // We don't know what happened.
219 return KeyEvent{}, false
222 func findEvent(k string) (Event, error) {
224 event, ok, err := findEvents(k)
230 event, ok = findSingleEvent(k)
232 return nil, errors.New(k + " is not a bindable event")
239 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
240 // Returns true if the keybinding already existed and a possible error
241 func TryBindKey(k, v string, overwrite bool) (bool, error) {
243 var parsed map[string]string
245 filename := filepath.Join(config.ConfigDir, "bindings.json")
246 createBindingsIfNotExist(filename)
247 if _, e = os.Stat(filename); e == nil {
248 input, err := ioutil.ReadFile(filename)
250 return false, errors.New("Error reading bindings.json file: " + err.Error())
253 err = json5.Unmarshal(input, &parsed)
255 return false, errors.New("Error reading bindings.json: " + err.Error())
258 key, err := findEvent(k)
264 for ev := range parsed {
265 if e, err := findEvent(ev); err == nil {
276 if found && !overwrite {
282 BindKey(k, v, Binder["buffer"])
284 txt, _ := json.MarshalIndent(parsed, "", " ")
285 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
290 // UnbindKey removes the binding for a key from the bindings.json file
291 func UnbindKey(k string) error {
293 var parsed map[string]string
295 filename := filepath.Join(config.ConfigDir, "bindings.json")
296 createBindingsIfNotExist(filename)
297 if _, e = os.Stat(filename); e == nil {
298 input, err := ioutil.ReadFile(filename)
300 return errors.New("Error reading bindings.json file: " + err.Error())
303 err = json5.Unmarshal(input, &parsed)
305 return errors.New("Error reading bindings.json: " + err.Error())
308 key, err := findEvent(k)
313 for ev := range parsed {
314 if e, err := findEvent(ev); err == nil {
322 defaults := DefaultBindings("buffer")
323 if a, ok := defaults[k]; ok {
324 BindKey(k, a, Binder["buffer"])
325 } else if _, ok := config.Bindings[k]; ok {
327 delete(config.Bindings, k)
330 txt, _ := json.MarshalIndent(parsed, "", " ")
331 return ioutil.WriteFile(filename, append(txt, '\n'), 0644)
336 var mouseEvents = map[string]tcell.ButtonMask{
337 "MouseLeft": tcell.Button1,
338 "MouseMiddle": tcell.Button2,
339 "MouseRight": tcell.Button3,
340 "MouseWheelUp": tcell.WheelUp,
341 "MouseWheelDown": tcell.WheelDown,
342 "MouseWheelLeft": tcell.WheelLeft,
343 "MouseWheelRight": tcell.WheelRight,
346 var keyEvents = map[string]tcell.Key{
348 "Down": tcell.KeyDown,
349 "Right": tcell.KeyRight,
350 "Left": tcell.KeyLeft,
351 "UpLeft": tcell.KeyUpLeft,
352 "UpRight": tcell.KeyUpRight,
353 "DownLeft": tcell.KeyDownLeft,
354 "DownRight": tcell.KeyDownRight,
355 "Center": tcell.KeyCenter,
356 "PageUp": tcell.KeyPgUp,
357 "PageDown": tcell.KeyPgDn,
358 "Home": tcell.KeyHome,
360 "Insert": tcell.KeyInsert,
361 "Delete": tcell.KeyDelete,
362 "Help": tcell.KeyHelp,
363 "Exit": tcell.KeyExit,
364 "Clear": tcell.KeyClear,
365 "Cancel": tcell.KeyCancel,
366 "Print": tcell.KeyPrint,
367 "Pause": tcell.KeyPause,
368 "Backtab": tcell.KeyBacktab,
433 "CtrlSpace": tcell.KeyCtrlSpace,
434 "CtrlA": tcell.KeyCtrlA,
435 "CtrlB": tcell.KeyCtrlB,
436 "CtrlC": tcell.KeyCtrlC,
437 "CtrlD": tcell.KeyCtrlD,
438 "CtrlE": tcell.KeyCtrlE,
439 "CtrlF": tcell.KeyCtrlF,
440 "CtrlG": tcell.KeyCtrlG,
441 "CtrlH": tcell.KeyCtrlH,
442 "CtrlI": tcell.KeyCtrlI,
443 "CtrlJ": tcell.KeyCtrlJ,
444 "CtrlK": tcell.KeyCtrlK,
445 "CtrlL": tcell.KeyCtrlL,
446 "CtrlM": tcell.KeyCtrlM,
447 "CtrlN": tcell.KeyCtrlN,
448 "CtrlO": tcell.KeyCtrlO,
449 "CtrlP": tcell.KeyCtrlP,
450 "CtrlQ": tcell.KeyCtrlQ,
451 "CtrlR": tcell.KeyCtrlR,
452 "CtrlS": tcell.KeyCtrlS,
453 "CtrlT": tcell.KeyCtrlT,
454 "CtrlU": tcell.KeyCtrlU,
455 "CtrlV": tcell.KeyCtrlV,
456 "CtrlW": tcell.KeyCtrlW,
457 "CtrlX": tcell.KeyCtrlX,
458 "CtrlY": tcell.KeyCtrlY,
459 "CtrlZ": tcell.KeyCtrlZ,
460 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
461 "CtrlBackslash": tcell.KeyCtrlBackslash,
462 "CtrlRightSq": tcell.KeyCtrlRightSq,
463 "CtrlCarat": tcell.KeyCtrlCarat,
464 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
467 "Escape": tcell.KeyEscape,
468 "Enter": tcell.KeyEnter,
469 "Backspace": tcell.KeyBackspace2,
470 "OldBackspace": tcell.KeyBackspace,
472 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
473 "PgUp": tcell.KeyPgUp,
474 "PgDown": tcell.KeyPgDn,