12 "github.com/zyedidia/json5"
13 "github.com/zyedidia/micro/v2/internal/config"
14 "github.com/zyedidia/micro/v2/internal/screen"
15 "github.com/zyedidia/tcell"
18 func createBindingsIfNotExist(fname string) {
19 if _, e := os.Stat(fname); os.IsNotExist(e) {
20 ioutil.WriteFile(fname, []byte("{}"), 0644)
24 // InitBindings intializes the bindings map by reading from bindings.json
26 config.Bindings = DefaultBindings()
28 var parsed map[string]string
29 defaults := DefaultBindings()
31 filename := filepath.Join(config.ConfigDir, "bindings.json")
32 createBindingsIfNotExist(filename)
34 if _, e := os.Stat(filename); e == nil {
35 input, err := ioutil.ReadFile(filename)
37 screen.TermMessage("Error reading bindings.json file: " + err.Error())
41 err = json5.Unmarshal(input, &parsed)
43 screen.TermMessage("Error reading bindings.json:", err.Error())
47 for k, v := range defaults {
50 for k, v := range parsed {
55 func BindKey(k, v string) {
56 event, ok := findEvent(k)
58 screen.TermMessage(k, "is not a bindable event")
61 switch e := event.(type) {
70 config.Bindings[k] = v
73 // findEvent will find binding Key 'b' using string 'k'
74 func findEvent(k string) (b Event, ok bool) {
75 modifiers := tcell.ModNone
77 // First, we'll strip off all the modifiers in the name and add them to the
82 case strings.HasPrefix(k, "-"):
83 // We optionally support dashes between modifiers
85 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
86 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
88 modifiers |= tcell.ModCtrl
89 case strings.HasPrefix(k, "Alt"):
91 modifiers |= tcell.ModAlt
92 case strings.HasPrefix(k, "Shift"):
94 modifiers |= tcell.ModShift
95 case strings.HasPrefix(k, "\x1b"):
96 screen.Screen.RegisterRawSeq(k)
106 return KeyEvent{}, false
109 // Control is handled in a special way, since the terminal sends explicitly
110 // marked escape sequences for control keys
111 // We should check for Control keys first
112 if modifiers&tcell.ModCtrl != 0 {
113 // see if the key is in bindingKeys with the Ctrl prefix.
114 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
115 if code, ok := keyEvents["Ctrl"+k]; ok {
117 // Special case for escape, for some reason tcell doesn't send it with the esc character
118 if code < 256 && code != 27 {
121 // It is, we're done.
130 // See if we can find the key in bindingKeys
131 if code, ok := keyEvents[k]; ok {
133 // Special case for escape, for some reason tcell doesn't send it with the esc character
134 if code < 256 && code != 27 {
144 // See if we can find the key in bindingMouse
145 if code, ok := mouseEvents[k]; ok {
152 // If we were given one character, then we've got a rune.
161 // We don't know what happened.
162 return KeyEvent{}, false
165 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
166 // Returns true if the keybinding already existed and a possible error
167 func TryBindKey(k, v string, overwrite bool) (bool, error) {
169 var parsed map[string]string
171 filename := filepath.Join(config.ConfigDir, "bindings.json")
172 createBindingsIfNotExist(filename)
173 if _, e = os.Stat(filename); e == nil {
174 input, err := ioutil.ReadFile(filename)
176 return false, errors.New("Error reading bindings.json file: " + err.Error())
179 err = json5.Unmarshal(input, &parsed)
181 return false, errors.New("Error reading bindings.json: " + err.Error())
184 key, ok := findEvent(k)
186 return false, errors.New("Invalid event " + k)
190 for ev := range parsed {
191 if e, ok := findEvent(ev); ok {
202 if found && !overwrite {
210 txt, _ := json.MarshalIndent(parsed, "", " ")
211 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
216 // UnbindKey removes the binding for a key from the bindings.json file
217 func UnbindKey(k string) error {
219 var parsed map[string]string
221 filename := filepath.Join(config.ConfigDir, "bindings.json")
222 createBindingsIfNotExist(filename)
223 if _, e = os.Stat(filename); e == nil {
224 input, err := ioutil.ReadFile(filename)
226 return errors.New("Error reading bindings.json file: " + err.Error())
229 err = json5.Unmarshal(input, &parsed)
231 return errors.New("Error reading bindings.json: " + err.Error())
234 key, ok := findEvent(k)
236 return errors.New("Invalid event " + k)
239 for ev := range parsed {
240 if e, ok := findEvent(ev); ok {
248 defaults := DefaultBindings()
249 if a, ok := defaults[k]; ok {
251 } else if _, ok := config.Bindings[k]; ok {
253 delete(config.Bindings, k)
256 txt, _ := json.MarshalIndent(parsed, "", " ")
257 return ioutil.WriteFile(filename, append(txt, '\n'), 0644)
262 var mouseEvents = map[string]tcell.ButtonMask{
263 "MouseLeft": tcell.Button1,
264 "MouseMiddle": tcell.Button2,
265 "MouseRight": tcell.Button3,
266 "MouseWheelUp": tcell.WheelUp,
267 "MouseWheelDown": tcell.WheelDown,
268 "MouseWheelLeft": tcell.WheelLeft,
269 "MouseWheelRight": tcell.WheelRight,
272 var keyEvents = map[string]tcell.Key{
274 "Down": tcell.KeyDown,
275 "Right": tcell.KeyRight,
276 "Left": tcell.KeyLeft,
277 "UpLeft": tcell.KeyUpLeft,
278 "UpRight": tcell.KeyUpRight,
279 "DownLeft": tcell.KeyDownLeft,
280 "DownRight": tcell.KeyDownRight,
281 "Center": tcell.KeyCenter,
282 "PageUp": tcell.KeyPgUp,
283 "PageDown": tcell.KeyPgDn,
284 "Home": tcell.KeyHome,
286 "Insert": tcell.KeyInsert,
287 "Delete": tcell.KeyDelete,
288 "Help": tcell.KeyHelp,
289 "Exit": tcell.KeyExit,
290 "Clear": tcell.KeyClear,
291 "Cancel": tcell.KeyCancel,
292 "Print": tcell.KeyPrint,
293 "Pause": tcell.KeyPause,
294 "Backtab": tcell.KeyBacktab,
359 "CtrlSpace": tcell.KeyCtrlSpace,
360 "CtrlA": tcell.KeyCtrlA,
361 "CtrlB": tcell.KeyCtrlB,
362 "CtrlC": tcell.KeyCtrlC,
363 "CtrlD": tcell.KeyCtrlD,
364 "CtrlE": tcell.KeyCtrlE,
365 "CtrlF": tcell.KeyCtrlF,
366 "CtrlG": tcell.KeyCtrlG,
367 "CtrlH": tcell.KeyCtrlH,
368 "CtrlI": tcell.KeyCtrlI,
369 "CtrlJ": tcell.KeyCtrlJ,
370 "CtrlK": tcell.KeyCtrlK,
371 "CtrlL": tcell.KeyCtrlL,
372 "CtrlM": tcell.KeyCtrlM,
373 "CtrlN": tcell.KeyCtrlN,
374 "CtrlO": tcell.KeyCtrlO,
375 "CtrlP": tcell.KeyCtrlP,
376 "CtrlQ": tcell.KeyCtrlQ,
377 "CtrlR": tcell.KeyCtrlR,
378 "CtrlS": tcell.KeyCtrlS,
379 "CtrlT": tcell.KeyCtrlT,
380 "CtrlU": tcell.KeyCtrlU,
381 "CtrlV": tcell.KeyCtrlV,
382 "CtrlW": tcell.KeyCtrlW,
383 "CtrlX": tcell.KeyCtrlX,
384 "CtrlY": tcell.KeyCtrlY,
385 "CtrlZ": tcell.KeyCtrlZ,
386 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
387 "CtrlBackslash": tcell.KeyCtrlBackslash,
388 "CtrlRightSq": tcell.KeyCtrlRightSq,
389 "CtrlCarat": tcell.KeyCtrlCarat,
390 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
393 "Escape": tcell.KeyEscape,
394 "Enter": tcell.KeyEnter,
395 "Backspace": tcell.KeyBackspace2,
396 "OldBackspace": tcell.KeyBackspace,
398 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
399 "PgUp": tcell.KeyPgUp,
400 "PgDown": tcell.KeyPgDn,