X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=internal%2Faction%2Fbindings.go;h=80a7acffec75f75a76ef1ee92715470e8684bd57;hb=5044ccf6bb4ea93348577e51ebcc16a6c0e6ec71;hp=5d469c76499149dcd145b45aa6054fdd19ba5631;hpb=e98be1a1e57b283587f830232e344bb04853d4a7;p=micro.git diff --git a/internal/action/bindings.go b/internal/action/bindings.go index 5d469c76..80a7acff 100644 --- a/internal/action/bindings.go +++ b/internal/action/bindings.go @@ -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": "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", - } -}