11 "github.com/zyedidia/json5"
12 "github.com/zyedidia/micro/internal/config"
13 "github.com/zyedidia/micro/internal/screen"
14 "github.com/zyedidia/tcell"
17 func createBindingsIfNotExist(fname string) {
18 if _, e := os.Stat(fname); os.IsNotExist(e) {
19 ioutil.WriteFile(fname, []byte("{}"), 0644)
23 // InitBindings intializes the bindings map by reading from bindings.json
25 config.Bindings = DefaultBindings()
27 var parsed map[string]string
28 defaults := DefaultBindings()
30 filename := config.ConfigDir + "/bindings.json"
31 createBindingsIfNotExist(filename)
33 if _, e := os.Stat(filename); e == nil {
34 input, err := ioutil.ReadFile(filename)
36 screen.TermMessage("Error reading bindings.json file: " + err.Error())
40 err = json5.Unmarshal(input, &parsed)
42 screen.TermMessage("Error reading bindings.json:", err.Error())
46 for k, v := range defaults {
49 for k, v := range parsed {
54 func BindKey(k, v string) {
55 event, ok := findEvent(k)
57 screen.TermMessage(k, "is not a bindable event")
60 switch e := event.(type) {
69 config.Bindings[k] = v
72 // findEvent will find binding Key 'b' using string 'k'
73 func findEvent(k string) (b Event, ok bool) {
74 modifiers := tcell.ModNone
76 // First, we'll strip off all the modifiers in the name and add them to the
81 case strings.HasPrefix(k, "-"):
82 // We optionally support dashes between modifiers
84 case strings.HasPrefix(k, "Ctrl") && k != "CtrlH":
85 // CtrlH technically does not have a 'Ctrl' modifier because it is really backspace
87 modifiers |= tcell.ModCtrl
88 case strings.HasPrefix(k, "Alt"):
90 modifiers |= tcell.ModAlt
91 case strings.HasPrefix(k, "Shift"):
93 modifiers |= tcell.ModShift
94 case strings.HasPrefix(k, "\x1b"):
95 screen.Screen.RegisterRawSeq(k)
105 return KeyEvent{}, false
108 // Control is handled in a special way, since the terminal sends explicitly
109 // marked escape sequences for control keys
110 // We should check for Control keys first
111 if modifiers&tcell.ModCtrl != 0 {
112 // see if the key is in bindingKeys with the Ctrl prefix.
113 k = string(unicode.ToUpper(rune(k[0]))) + k[1:]
114 if code, ok := keyEvents["Ctrl"+k]; ok {
116 // Special case for escape, for some reason tcell doesn't send it with the esc character
117 if code < 256 && code != 27 {
120 // It is, we're done.
129 // See if we can find the key in bindingKeys
130 if code, ok := keyEvents[k]; ok {
132 // Special case for escape, for some reason tcell doesn't send it with the esc character
133 if code < 256 && code != 27 {
143 // See if we can find the key in bindingMouse
144 if code, ok := mouseEvents[k]; ok {
151 // If we were given one character, then we've got a rune.
160 // We don't know what happened.
161 return KeyEvent{}, false
164 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
165 // Returns true if the keybinding already existed and a possible error
166 func TryBindKey(k, v string, overwrite bool) (bool, error) {
168 var parsed map[string]string
170 filename := config.ConfigDir + "/bindings.json"
171 createBindingsIfNotExist(filename)
172 if _, e = os.Stat(filename); e == nil {
173 input, err := ioutil.ReadFile(filename)
175 return false, errors.New("Error reading bindings.json file: " + err.Error())
178 err = json5.Unmarshal(input, &parsed)
180 return false, errors.New("Error reading bindings.json: " + err.Error())
183 key, ok := findEvent(k)
185 return false, errors.New("Invalid event " + k)
189 for ev := range parsed {
190 if e, ok := findEvent(ev); ok {
201 if found && !overwrite {
209 txt, _ := json.MarshalIndent(parsed, "", " ")
210 return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
215 // UnbindKey removes the binding for a key from the bindings.json file
216 func UnbindKey(k string) error {
218 var parsed map[string]string
220 filename := config.ConfigDir + "/bindings.json"
221 createBindingsIfNotExist(filename)
222 if _, e = os.Stat(filename); e == nil {
223 input, err := ioutil.ReadFile(filename)
225 return errors.New("Error reading bindings.json file: " + err.Error())
228 err = json5.Unmarshal(input, &parsed)
230 return errors.New("Error reading bindings.json: " + err.Error())
233 key, ok := findEvent(k)
235 return errors.New("Invalid event " + k)
238 for ev := range parsed {
239 if e, ok := findEvent(ev); ok {
247 defaults := DefaultBindings()
248 if a, ok := defaults[k]; ok {
250 } else if _, ok := config.Bindings[k]; ok {
251 delete(config.Bindings, k)
254 txt, _ := json.MarshalIndent(parsed, "", " ")
255 return ioutil.WriteFile(filename, append(txt, '\n'), 0644)
260 var mouseEvents = map[string]tcell.ButtonMask{
261 "MouseLeft": tcell.Button1,
262 "MouseMiddle": tcell.Button2,
263 "MouseRight": tcell.Button3,
264 "MouseWheelUp": tcell.WheelUp,
265 "MouseWheelDown": tcell.WheelDown,
266 "MouseWheelLeft": tcell.WheelLeft,
267 "MouseWheelRight": tcell.WheelRight,
270 var keyEvents = map[string]tcell.Key{
272 "Down": tcell.KeyDown,
273 "Right": tcell.KeyRight,
274 "Left": tcell.KeyLeft,
275 "UpLeft": tcell.KeyUpLeft,
276 "UpRight": tcell.KeyUpRight,
277 "DownLeft": tcell.KeyDownLeft,
278 "DownRight": tcell.KeyDownRight,
279 "Center": tcell.KeyCenter,
280 "PageUp": tcell.KeyPgUp,
281 "PageDown": tcell.KeyPgDn,
282 "Home": tcell.KeyHome,
284 "Insert": tcell.KeyInsert,
285 "Delete": tcell.KeyDelete,
286 "Help": tcell.KeyHelp,
287 "Exit": tcell.KeyExit,
288 "Clear": tcell.KeyClear,
289 "Cancel": tcell.KeyCancel,
290 "Print": tcell.KeyPrint,
291 "Pause": tcell.KeyPause,
292 "Backtab": tcell.KeyBacktab,
357 "CtrlSpace": tcell.KeyCtrlSpace,
358 "CtrlA": tcell.KeyCtrlA,
359 "CtrlB": tcell.KeyCtrlB,
360 "CtrlC": tcell.KeyCtrlC,
361 "CtrlD": tcell.KeyCtrlD,
362 "CtrlE": tcell.KeyCtrlE,
363 "CtrlF": tcell.KeyCtrlF,
364 "CtrlG": tcell.KeyCtrlG,
365 "CtrlH": tcell.KeyCtrlH,
366 "CtrlI": tcell.KeyCtrlI,
367 "CtrlJ": tcell.KeyCtrlJ,
368 "CtrlK": tcell.KeyCtrlK,
369 "CtrlL": tcell.KeyCtrlL,
370 "CtrlM": tcell.KeyCtrlM,
371 "CtrlN": tcell.KeyCtrlN,
372 "CtrlO": tcell.KeyCtrlO,
373 "CtrlP": tcell.KeyCtrlP,
374 "CtrlQ": tcell.KeyCtrlQ,
375 "CtrlR": tcell.KeyCtrlR,
376 "CtrlS": tcell.KeyCtrlS,
377 "CtrlT": tcell.KeyCtrlT,
378 "CtrlU": tcell.KeyCtrlU,
379 "CtrlV": tcell.KeyCtrlV,
380 "CtrlW": tcell.KeyCtrlW,
381 "CtrlX": tcell.KeyCtrlX,
382 "CtrlY": tcell.KeyCtrlY,
383 "CtrlZ": tcell.KeyCtrlZ,
384 "CtrlLeftSq": tcell.KeyCtrlLeftSq,
385 "CtrlBackslash": tcell.KeyCtrlBackslash,
386 "CtrlRightSq": tcell.KeyCtrlRightSq,
387 "CtrlCarat": tcell.KeyCtrlCarat,
388 "CtrlUnderscore": tcell.KeyCtrlUnderscore,
391 "Escape": tcell.KeyEscape,
392 "Enter": tcell.KeyEnter,
393 "Backspace": tcell.KeyBackspace2,
394 "OldBackspace": tcell.KeyBackspace,
396 // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
397 "PgUp": tcell.KeyPgUp,
398 "PgDown": tcell.KeyPgDn,