]> git.lizzy.rs Git - micro.git/blobdiff - internal/action/bindings.go
Update keybinding docs
[micro.git] / internal / action / bindings.go
index 8984c043b17802f37d6570f907cd52125d7ecd16..80a7acffec75f75a76ef1ee92715470e8684bd57 100644 (file)
@@ -5,22 +5,36 @@ import (
        "errors"
        "io/ioutil"
        "os"
+       "path/filepath"
+       "regexp"
        "strings"
        "unicode"
 
        "github.com/zyedidia/json5"
-       "github.com/zyedidia/micro/internal/config"
-       "github.com/zyedidia/micro/internal/screen"
-       "github.com/zyedidia/tcell"
+       "github.com/zyedidia/micro/v2/internal/config"
+       "github.com/zyedidia/micro/v2/internal/screen"
+       "github.com/zyedidia/tcell/v2"
 )
 
+var Binder = map[string]func(e Event, action string){
+       "command":  InfoMapEvent,
+       "buffer":   BufMapEvent,
+       "terminal": TermMapEvent,
+}
+
+func createBindingsIfNotExist(fname string) {
+       if _, e := os.Stat(fname); os.IsNotExist(e) {
+               ioutil.WriteFile(fname, []byte("{}"), 0644)
+       }
+}
+
+// InitBindings intializes the bindings map by reading from bindings.json
 func InitBindings() {
-       config.Bindings = DefaultBindings()
+       var parsed map[string]interface{}
 
-       var parsed map[string]string
-       defaults := DefaultBindings()
+       filename := filepath.Join(config.ConfigDir, "bindings.json")
+       createBindingsIfNotExist(filename)
 
-       filename := config.ConfigDir + "/bindings.json"
        if _, e := os.Stat(filename); e == nil {
                input, err := ioutil.ReadFile(filename)
                if err != nil {
@@ -34,34 +48,87 @@ func InitBindings() {
                }
        }
 
-       for k, v := range defaults {
-               BindKey(k, v)
+       for p, bind := range Binder {
+               defaults := DefaultBindings(p)
+
+               for k, v := range defaults {
+                       BindKey(k, v, bind)
+               }
        }
+
        for k, v := range parsed {
-               BindKey(k, v)
+               switch val := v.(type) {
+               case string:
+                       BindKey(k, val, Binder["buffer"])
+               case map[string]interface{}:
+                       bind := Binder[k]
+                       for e, a := range val {
+                               s, ok := a.(string)
+                               if !ok {
+                                       screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
+                               } else {
+                                       BindKey(e, s, bind)
+                               }
+                       }
+               default:
+                       screen.TermMessage("Error reading bindings.json: non-string and non-map entry", k)
+               }
        }
 }
 
-func BindKey(k, v string) {
-       event, ok := findEvent(k)
-       if !ok {
-               screen.TermMessage(k, "is not a bindable event")
+func BindKey(k, v string, bind func(e Event, a string)) {
+       event, err := findEvent(k)
+       if err != nil {
+               screen.TermMessage(err)
+               return
        }
 
-       switch e := event.(type) {
-       case KeyEvent:
-               BufMapKey(e, v)
-       case MouseEvent:
-               BufMapMouse(e, v)
-       case RawEvent:
-               BufMapKey(e, v)
+       config.Bindings[event.Name()] = v
+
+       bind(event, v)
+
+       // switch e := event.(type) {
+       // case KeyEvent:
+       //      InfoMapKey(e, v)
+       // case KeySequenceEvent:
+       //      InfoMapKey(e, v)
+       // case MouseEvent:
+       //      InfoMapMouse(e, v)
+       // case RawEvent:
+       //      InfoMapKey(e, v)
+       // }
+}
+
+var r = regexp.MustCompile("<(.+?)>")
+
+func findEvents(k string) (b KeySequenceEvent, ok bool, err error) {
+       var events []Event = nil
+       for len(k) > 0 {
+               groups := r.FindStringSubmatchIndex(k)
+
+               if len(groups) > 3 {
+                       if events == nil {
+                               events = make([]Event, 0, 3)
+                       }
+
+                       e, ok := findSingleEvent(k[groups[2]:groups[3]])
+                       if !ok {
+                               return KeySequenceEvent{}, false, errors.New("Invalid event " + k[groups[2]:groups[3]])
+                       }
+
+                       events = append(events, e)
+
+                       k = k[groups[3]+1:]
+               } else {
+                       return KeySequenceEvent{}, false, nil
+               }
        }
 
-       config.Bindings[k] = v
+       return KeySequenceEvent{events}, true, nil
 }
 
-// findEvent will find binding Key 'b' using string 'k'
-func findEvent(k string) (b Event, ok bool) {
+// findSingleEvent will find binding Key 'b' using string 'k'
+func findSingleEvent(k string) (b Event, ok bool) {
        modifiers := tcell.ModNone
 
        // First, we'll strip off all the modifiers in the name and add them to the
@@ -152,13 +219,31 @@ modSearch:
        return KeyEvent{}, false
 }
 
+func findEvent(k string) (Event, error) {
+       var event Event
+       event, ok, err := findEvents(k)
+       if err != nil {
+               return nil, err
+       }
+
+       if !ok {
+               event, ok = findSingleEvent(k)
+               if !ok {
+                       return nil, errors.New(k + " is not a bindable event")
+               }
+       }
+
+       return event, nil
+}
+
 // TryBindKey tries to bind a key by writing to config.ConfigDir/bindings.json
 // Returns true if the keybinding already existed and a possible error
 func TryBindKey(k, v string, overwrite bool) (bool, error) {
        var e error
        var parsed map[string]string
 
-       filename := config.ConfigDir + "/bindings.json"
+       filename := filepath.Join(config.ConfigDir, "bindings.json")
+       createBindingsIfNotExist(filename)
        if _, e = os.Stat(filename); e == nil {
                input, err := ioutil.ReadFile(filename)
                if err != nil {
@@ -170,14 +255,14 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
                        return false, errors.New("Error reading bindings.json: " + err.Error())
                }
 
-               key, ok := findEvent(k)
-               if !ok {
-                       return false, errors.New("Invalid event " + k)
+               key, err := findEvent(k)
+               if err != nil {
+                       return false, err
                }
 
                found := false
                for ev := range parsed {
-                       if e, ok := findEvent(ev); ok {
+                       if e, err := findEvent(ev); err == nil {
                                if e == key {
                                        if overwrite {
                                                parsed[ev] = v
@@ -194,7 +279,7 @@ func TryBindKey(k, v string, overwrite bool) (bool, error) {
                        parsed[k] = v
                }
 
-               BindKey(k, v)
+               BindKey(k, v, Binder["buffer"])
 
                txt, _ := json.MarshalIndent(parsed, "", "    ")
                return true, ioutil.WriteFile(filename, append(txt, '\n'), 0644)
@@ -207,7 +292,8 @@ func UnbindKey(k string) error {
        var e error
        var parsed map[string]string
 
-       filename := config.ConfigDir + "/bindings.json"
+       filename := filepath.Join(config.ConfigDir, "bindings.json")
+       createBindingsIfNotExist(filename)
        if _, e = os.Stat(filename); e == nil {
                input, err := ioutil.ReadFile(filename)
                if err != nil {
@@ -219,13 +305,13 @@ func UnbindKey(k string) error {
                        return errors.New("Error reading bindings.json: " + err.Error())
                }
 
-               key, ok := findEvent(k)
-               if !ok {
-                       return errors.New("Invalid event " + k)
+               key, err := findEvent(k)
+               if err != nil {
+                       return err
                }
 
                for ev := range parsed {
-                       if e, ok := findEvent(ev); ok {
+                       if e, err := findEvent(ev); err == nil {
                                if e == key {
                                        delete(parsed, ev)
                                        break
@@ -233,10 +319,11 @@ func UnbindKey(k string) error {
                        }
                }
 
-               defaults := DefaultBindings()
+               defaults := DefaultBindings("buffer")
                if a, ok := defaults[k]; ok {
-                       BindKey(k, a)
+                       BindKey(k, a, Binder["buffer"])
                } else if _, ok := config.Bindings[k]; ok {
+                       BufUnmap(key)
                        delete(config.Bindings, k)
                }
 
@@ -247,9 +334,9 @@ func UnbindKey(k string) error {
 }
 
 var mouseEvents = map[string]tcell.ButtonMask{
-       "MouseLeft":       tcell.Button1,
-       "MouseMiddle":     tcell.Button2,
-       "MouseRight":      tcell.Button3,
+       "MouseLeft":       tcell.ButtonPrimary,
+       "MouseMiddle":     tcell.ButtonMiddle,
+       "MouseRight":      tcell.ButtonSecondary,
        "MouseWheelUp":    tcell.WheelUp,
        "MouseWheelDown":  tcell.WheelDown,
        "MouseWheelLeft":  tcell.WheelLeft,
@@ -386,107 +473,3 @@ var keyEvents = map[string]tcell.Key{
        "PgUp":   tcell.KeyPgUp,
        "PgDown": tcell.KeyPgDn,
 }
-
-// DefaultBindings returns a map containing micro's default keybindings
-func DefaultBindings() map[string]string {
-       return map[string]string{
-               "Up":             "CursorUp",
-               "Down":           "CursorDown",
-               "Right":          "CursorRight",
-               "Left":           "CursorLeft",
-               "ShiftUp":        "SelectUp",
-               "ShiftDown":      "SelectDown",
-               "ShiftLeft":      "SelectLeft",
-               "ShiftRight":     "SelectRight",
-               "AltLeft":        "WordLeft",
-               "AltRight":       "WordRight",
-               "AltUp":          "MoveLinesUp",
-               "AltDown":        "MoveLinesDown",
-               "AltShiftRight":  "SelectWordRight",
-               "AltShiftLeft":   "SelectWordLeft",
-               "CtrlLeft":       "StartOfLine",
-               "CtrlRight":      "EndOfLine",
-               "CtrlShiftLeft":  "SelectToStartOfLine",
-               "ShiftHome":      "SelectToStartOfLine",
-               "CtrlShiftRight": "SelectToEndOfLine",
-               "ShiftEnd":       "SelectToEndOfLine",
-               "CtrlUp":         "CursorStart",
-               "CtrlDown":       "CursorEnd",
-               "CtrlShiftUp":    "SelectToStart",
-               "CtrlShiftDown":  "SelectToEnd",
-               "Alt-{":          "ParagraphPrevious",
-               "Alt-}":          "ParagraphNext",
-               "Enter":          "InsertNewline",
-               "CtrlH":          "Backspace",
-               "Backspace":      "Backspace",
-               "Alt-CtrlH":      "DeleteWordLeft",
-               "Alt-Backspace":  "DeleteWordLeft",
-               "Tab":            "Autocomplete|IndentSelection|InsertTab",
-               "Backtab":        "CycleAutocompleteBack|OutdentSelection|OutdentLine",
-               "CtrlO":          "OpenFile",
-               "CtrlS":          "Save",
-               "CtrlF":          "Find",
-               "CtrlN":          "FindNext",
-               "CtrlP":          "FindPrevious",
-               "CtrlZ":          "Undo",
-               "CtrlY":          "Redo",
-               "CtrlC":          "Copy",
-               "CtrlX":          "Cut",
-               "CtrlK":          "CutLine",
-               "CtrlD":          "DuplicateLine",
-               "CtrlV":          "Paste",
-               "CtrlA":          "SelectAll",
-               "CtrlT":          "AddTab",
-               "Alt,":           "PreviousTab",
-               "Alt.":           "NextTab",
-               "Home":           "StartOfLine",
-               "End":            "EndOfLine",
-               "CtrlHome":       "CursorStart",
-               "CtrlEnd":        "CursorEnd",
-               "PageUp":         "CursorPageUp",
-               "PageDown":       "CursorPageDown",
-               "CtrlPageUp":     "PreviousTab",
-               "CtrlPageDown":   "NextTab",
-               "CtrlG":          "ToggleHelp",
-               "Alt-g":          "ToggleKeyMenu",
-               "CtrlR":          "ToggleRuler",
-               "CtrlL":          "command-edit:goto ",
-               "Delete":         "Delete",
-               "CtrlB":          "ShellMode",
-               "CtrlQ":          "Quit",
-               "CtrlE":          "CommandMode",
-               "CtrlW":          "NextSplit",
-               "CtrlU":          "ToggleMacro",
-               "CtrlJ":          "PlayMacro",
-               "Insert":         "ToggleOverwriteMode",
-
-               // Emacs-style keybindings
-               "Alt-f": "WordRight",
-               "Alt-b": "WordLeft",
-               "Alt-a": "StartOfLine",
-               "Alt-e": "EndOfLine",
-               // "Alt-p": "CursorUp",
-               // "Alt-n": "CursorDown",
-
-               // Integration with file managers
-               "F2":  "Save",
-               "F3":  "Find",
-               "F4":  "Quit",
-               "F7":  "Find",
-               "F10": "Quit",
-               "Esc": "Escape",
-
-               // Mouse bindings
-               "MouseWheelUp":   "ScrollUp",
-               "MouseWheelDown": "ScrollDown",
-               "MouseLeft":      "MousePress",
-               "MouseMiddle":    "PastePrimary",
-               "Ctrl-MouseLeft": "MouseMultiCursor",
-
-               "Alt-n": "SpawnMultiCursor",
-               "Alt-m": "SpawnMultiCursorSelect",
-               "Alt-p": "RemoveMultiCursor",
-               "Alt-c": "RemoveAllMultiCursors",
-               "Alt-x": "SkipMultiCursor",
-       }
-}