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 func createBindingsIfNotExist(fname string) {
20 if _, e := os.Stat(fname); os.IsNotExist(e) {
21 ioutil.WriteFile(fname, []byte("{}"), 0644)
25 // InitBindings intializes the bindings map by reading from bindings.json
27 config.Bindings = DefaultBindings()
29 var parsed map[string]string
30 defaults := DefaultBindings()
32 filename := filepath.Join(config.ConfigDir, "bindings.json")
33 createBindingsIfNotExist(filename)
35 if _, e := os.Stat(filename); e == nil {
36 input, err := ioutil.ReadFile(filename)
38 screen.TermMessage("Error reading bindings.json file: " + err.Error())
42 err = json5.Unmarshal(input, &parsed)
44 screen.TermMessage("Error reading bindings.json:", err.Error())
48 for k, v := range defaults {
51 for k, v := range parsed {
55 defaultInfos := DefaultInfoBindings()
56 for k, v := range defaultInfos {
61 func BindInfoKey(k, v string) {
62 event, err := findEvent(k)
64 screen.TermMessage(err)
67 switch e := event.(type) {
70 case KeySequenceEvent:
79 func BindKey(k, v string) {
80 event, err := findEvent(k)
82 screen.TermMessage(err)
85 switch e := event.(type) {
88 case KeySequenceEvent:
96 config.Bindings[k] = v
99 var r = regexp.MustCompile("<(.+?)>")
101 func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
102 var events []Event = nil
104 groups := r.FindStringSubmatchIndex(k)
108 events = make([]Event, 0, 3)
111 e, ok := findSingleEvent(k[groups[2]:groups[3]])
113 return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
116 events = append(events, e)
120 return KeySequenceEvent{}, false, nil
124 return KeySequenceEvent{events}, true, nil
127 // findSingleEvent will find binding Key 'b' using string 'k'
128 func findSingleEvent(k string) (b Event, ok bool) {
129 modifiers := tcell.ModNone
131 // First, we'll strip off all the modifiers in the name and add them to the
136 case strings.HasPrefix(k, "-"):
137 // We optionally support dashes between modifiers
139 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
140 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
142 modifiers |= tcell.ModCtrl
143 case strings.HasPrefix(k, "Alt"):
145 modifiers |= tcell.ModAlt
146 case strings.HasPrefix(k, "Shift"):
148 modifiers |= tcell.ModShift
149 case strings.HasPrefix(k, "\x1b"):
150 screen.Screen.RegisterRawSeq(k)
160 return KeyEvent{}, false
163 // Control is handled in a special way, since the terminal sends explicitly
164 // marked escape sequences for control keys
165 // We should check for Control keys first
166 if modifiers&tcell.ModCtrl != 0 {
167 // see if the key is in bindingKeys with the Ctrl prefix.
168 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
169 if code, ok := keyEvents["Ctrl"+k]; ok {
171 // Special case for escape, for some reason tcell doesn't send it with the esc character
172 if code < 256 && code != 27 {
175 // It is, we're done.
184 // See if we can find the key in bindingKeys
185 if code, ok := keyEvents[k]; ok {
187 // Special case for escape, for some reason tcell doesn't send it with the esc character
188 if code < 256 && code != 27 {
198 // See if we can find the key in bindingMouse
199 if code, ok := mouseEvents[k]; ok {
206 // If we were given one character, then we've got a rune.
215 // We don't know what happened.
216 return KeyEvent{}, false
219 func findEvent(k string) (Event, error) {
221 event, ok, err := findEvents(k)
227 event, ok = findSingleEvent(k)
229 return nil, errors.New(k + " is not a bindable event")
236 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
237 // Returns true if the keybinding already existed and a possible error
238 func TryBindKey(k, v string, overwrite bool) (bool, error) {
240 var parsed map[string]string
242 filename := filepath.Join(config.ConfigDir, "bindings.json")
243 createBindingsIfNotExist(filename)
244 if _, e = os.Stat(filename); e == nil {
245 input, err := ioutil.ReadFile(filename)
247 return false, errors.New("Error reading bindings.json file: " + err.Error())
250 err = json5.Unmarshal(input, &parsed)
252 return false, errors.New("Error reading bindings.json: " + err.Error())
255 key, err := findEvent(k)
261 for ev := range parsed {
262 if e, err := findEvent(ev); err == nil {
273 if found && !overwrite {
281 txt, _ := json.MarshalIndent(parsed, "", " ")
282 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
287 // UnbindKey removes the binding for a key from the bindings.json file
288 func UnbindKey(k string) error {
290 var parsed map[string]string
292 filename := filepath.Join(config.ConfigDir, "bindings.json")
293 createBindingsIfNotExist(filename)
294 if _, e = os.Stat(filename); e == nil {
295 input, err := ioutil.ReadFile(filename)
297 return errors.New("Error reading bindings.json file: " + err.Error())
300 err = json5.Unmarshal(input, &parsed)
302 return errors.New("Error reading bindings.json: " + err.Error())
305 key, err := findEvent(k)
310 for ev := range parsed {
311 if e, err := findEvent(ev); err == nil {
319 defaults := DefaultBindings()
320 if a, ok := defaults[k]; ok {
322 } else if _, ok := config.Bindings[k]; ok {
324 delete(config.Bindings, k)
327 txt, _ := json.MarshalIndent(parsed, "", " ")
328 return ioutil.WriteFile(filename, append(txt, '\n'), 0644)
333 var mouseEvents = map[string]tcell.ButtonMask{
334 "MouseLeft": tcell.Button1,
335 "MouseMiddle": tcell.Button2,
336 "MouseRight": tcell.Button3,
337 "MouseWheelUp": tcell.WheelUp,
338 "MouseWheelDown": tcell.WheelDown,
339 "MouseWheelLeft": tcell.WheelLeft,
340 "MouseWheelRight": tcell.WheelRight,
343 var keyEvents = map[string]tcell.Key{
345 "Down": tcell.KeyDown,
346 "Right": tcell.KeyRight,
347 "Left": tcell.KeyLeft,
348 "UpLeft": tcell.KeyUpLeft,
349 "UpRight": tcell.KeyUpRight,
350 "DownLeft": tcell.KeyDownLeft,
351 "DownRight": tcell.KeyDownRight,
352 "Center": tcell.KeyCenter,
353 "PageUp": tcell.KeyPgUp,
354 "PageDown": tcell.KeyPgDn,
355 "Home": tcell.KeyHome,
357 "Insert": tcell.KeyInsert,
358 "Delete": tcell.KeyDelete,
359 "Help": tcell.KeyHelp,
360 "Exit": tcell.KeyExit,
361 "Clear": tcell.KeyClear,
362 "Cancel": tcell.KeyCancel,
363 "Print": tcell.KeyPrint,
364 "Pause": tcell.KeyPause,
365 "Backtab": tcell.KeyBacktab,
430 "CtrlSpace": tcell.KeyCtrlSpace,
431 "CtrlA": tcell.KeyCtrlA,
432 "CtrlB": tcell.KeyCtrlB,
433 "CtrlC": tcell.KeyCtrlC,
434 "CtrlD": tcell.KeyCtrlD,
435 "CtrlE": tcell.KeyCtrlE,
436 "CtrlF": tcell.KeyCtrlF,
437 "CtrlG": tcell.KeyCtrlG,
438 "CtrlH": tcell.KeyCtrlH,
439 "CtrlI": tcell.KeyCtrlI,
440 "CtrlJ": tcell.KeyCtrlJ,
441 "CtrlK": tcell.KeyCtrlK,
442 "CtrlL": tcell.KeyCtrlL,
443 "CtrlM": tcell.KeyCtrlM,
444 "CtrlN": tcell.KeyCtrlN,
445 "CtrlO": tcell.KeyCtrlO,
446 "CtrlP": tcell.KeyCtrlP,
447 "CtrlQ": tcell.KeyCtrlQ,
448 "CtrlR": tcell.KeyCtrlR,
449 "CtrlS": tcell.KeyCtrlS,
450 "CtrlT": tcell.KeyCtrlT,
451 "CtrlU": tcell.KeyCtrlU,
452 "CtrlV": tcell.KeyCtrlV,
453 "CtrlW": tcell.KeyCtrlW,
454 "CtrlX": tcell.KeyCtrlX,
455 "CtrlY": tcell.KeyCtrlY,
456 "CtrlZ": tcell.KeyCtrlZ,
457 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
458 "CtrlBackslash": tcell.KeyCtrlBackslash,
459 "CtrlRightSq": tcell.KeyCtrlRightSq,
460 "CtrlCarat": tcell.KeyCtrlCarat,
461 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
464 "Escape": tcell.KeyEscape,
465 "Enter": tcell.KeyEnter,
466 "Backspace": tcell.KeyBackspace2,
467 "OldBackspace": tcell.KeyBackspace,
469 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
470 "PgUp": tcell.KeyPgUp,
471 "PgDown": tcell.KeyPgDn,