X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fbindings.go;h=e3a02841718f97ab6cfd497607c1ea85436146c5;hb=41a24e61d6b9017dbe010ae36295cb3c1dd701fc;hp=c860e69de7b64ba3c79932fb99b9677d61e7ccd2;hpb=5a2f9a374ba6c67186e7e20e4215e6c5c37634fc;p=micro.git diff --git a/cmd/micro/bindings.go b/cmd/micro/bindings.go index c860e69d..e3a02841 100644 --- a/cmd/micro/bindings.go +++ b/cmd/micro/bindings.go @@ -1,17 +1,21 @@ package main import ( + "fmt" "io/ioutil" "os" "strings" + "unicode" - "github.com/zyedidia/json5/encoding/json5" + "github.com/flynn/json5" "github.com/zyedidia/tcell" ) +var bindingsStr map[string]string var bindings map[Key][]func(*View, bool) bool var mouseBindings map[Key][]func(*View, bool, *tcell.EventMouse) bool var helpBinding string +var kmenuBinding string var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{ "MousePress": (*View).MousePress, @@ -19,90 +23,99 @@ var mouseBindingActions = map[string]func(*View, bool, *tcell.EventMouse) bool{ } var bindingActions = map[string]func(*View, bool) bool{ - "CursorUp": (*View).CursorUp, - "CursorDown": (*View).CursorDown, - "CursorPageUp": (*View).CursorPageUp, - "CursorPageDown": (*View).CursorPageDown, - "CursorLeft": (*View).CursorLeft, - "CursorRight": (*View).CursorRight, - "CursorStart": (*View).CursorStart, - "CursorEnd": (*View).CursorEnd, - "SelectToStart": (*View).SelectToStart, - "SelectToEnd": (*View).SelectToEnd, - "SelectUp": (*View).SelectUp, - "SelectDown": (*View).SelectDown, - "SelectLeft": (*View).SelectLeft, - "SelectRight": (*View).SelectRight, - "WordRight": (*View).WordRight, - "WordLeft": (*View).WordLeft, - "SelectWordRight": (*View).SelectWordRight, - "SelectWordLeft": (*View).SelectWordLeft, - "DeleteWordRight": (*View).DeleteWordRight, - "DeleteWordLeft": (*View).DeleteWordLeft, - "SelectToStartOfLine": (*View).SelectToStartOfLine, - "SelectToEndOfLine": (*View).SelectToEndOfLine, - "InsertNewline": (*View).InsertNewline, - "InsertSpace": (*View).InsertSpace, - "Backspace": (*View).Backspace, - "Delete": (*View).Delete, - "InsertTab": (*View).InsertTab, - "Save": (*View).Save, - "SaveAll": (*View).SaveAll, - "SaveAs": (*View).SaveAs, - "Find": (*View).Find, - "FindNext": (*View).FindNext, - "FindPrevious": (*View).FindPrevious, - "Center": (*View).Center, - "Undo": (*View).Undo, - "Redo": (*View).Redo, - "Copy": (*View).Copy, - "Cut": (*View).Cut, - "CutLine": (*View).CutLine, - "DuplicateLine": (*View).DuplicateLine, - "DeleteLine": (*View).DeleteLine, - "MoveLinesUp": (*View).MoveLinesUp, - "MoveLinesDown": (*View).MoveLinesDown, - "IndentSelection": (*View).IndentSelection, - "OutdentSelection": (*View).OutdentSelection, - "OutdentLine": (*View).OutdentLine, - "Paste": (*View).Paste, - "PastePrimary": (*View).PastePrimary, - "SelectAll": (*View).SelectAll, - "OpenFile": (*View).OpenFile, - "Start": (*View).Start, - "End": (*View).End, - "PageUp": (*View).PageUp, - "PageDown": (*View).PageDown, - "HalfPageUp": (*View).HalfPageUp, - "HalfPageDown": (*View).HalfPageDown, - "StartOfLine": (*View).StartOfLine, - "EndOfLine": (*View).EndOfLine, - "ToggleHelp": (*View).ToggleHelp, - "ToggleRuler": (*View).ToggleRuler, - "JumpLine": (*View).JumpLine, - "ClearStatus": (*View).ClearStatus, - "ShellMode": (*View).ShellMode, - "CommandMode": (*View).CommandMode, - "Escape": (*View).Escape, - "Quit": (*View).Quit, - "QuitAll": (*View).QuitAll, - "AddTab": (*View).AddTab, - "PreviousTab": (*View).PreviousTab, - "NextTab": (*View).NextTab, - "NextSplit": (*View).NextSplit, - "PreviousSplit": (*View).PreviousSplit, - "Unsplit": (*View).Unsplit, - "VSplit": (*View).VSplitBinding, - "HSplit": (*View).HSplitBinding, - "ToggleMacro": (*View).ToggleMacro, - "PlayMacro": (*View).PlayMacro, - "Suspend": (*View).Suspend, - "ScrollUp": (*View).ScrollUpAction, - "ScrollDown": (*View).ScrollDownAction, - "SpawnMultiCursor": (*View).SpawnMultiCursor, - "RemoveMultiCursor": (*View).RemoveMultiCursor, - "RemoveAllMultiCursors": (*View).RemoveAllMultiCursors, - "SkipMultiCursor": (*View).SkipMultiCursor, + "CursorUp": (*View).CursorUp, + "CursorDown": (*View).CursorDown, + "CursorPageUp": (*View).CursorPageUp, + "CursorPageDown": (*View).CursorPageDown, + "CursorLeft": (*View).CursorLeft, + "CursorRight": (*View).CursorRight, + "CursorStart": (*View).CursorStart, + "CursorEnd": (*View).CursorEnd, + "SelectToStart": (*View).SelectToStart, + "SelectToEnd": (*View).SelectToEnd, + "SelectUp": (*View).SelectUp, + "SelectDown": (*View).SelectDown, + "SelectLeft": (*View).SelectLeft, + "SelectRight": (*View).SelectRight, + "WordRight": (*View).WordRight, + "WordLeft": (*View).WordLeft, + "SelectWordRight": (*View).SelectWordRight, + "SelectWordLeft": (*View).SelectWordLeft, + "DeleteWordRight": (*View).DeleteWordRight, + "DeleteWordLeft": (*View).DeleteWordLeft, + "SelectLine": (*View).SelectLine, + "SelectToStartOfLine": (*View).SelectToStartOfLine, + "SelectToEndOfLine": (*View).SelectToEndOfLine, + "ParagraphPrevious": (*View).ParagraphPrevious, + "ParagraphNext": (*View).ParagraphNext, + "InsertNewline": (*View).InsertNewline, + "InsertSpace": (*View).InsertSpace, + "Backspace": (*View).Backspace, + "Delete": (*View).Delete, + "InsertTab": (*View).InsertTab, + "Save": (*View).Save, + "SaveAll": (*View).SaveAll, + "SaveAs": (*View).SaveAs, + "Find": (*View).Find, + "FindNext": (*View).FindNext, + "FindPrevious": (*View).FindPrevious, + "Center": (*View).Center, + "Undo": (*View).Undo, + "Redo": (*View).Redo, + "Copy": (*View).Copy, + "Cut": (*View).Cut, + "CutLine": (*View).CutLine, + "DuplicateLine": (*View).DuplicateLine, + "DeleteLine": (*View).DeleteLine, + "MoveLinesUp": (*View).MoveLinesUp, + "MoveLinesDown": (*View).MoveLinesDown, + "IndentSelection": (*View).IndentSelection, + "OutdentSelection": (*View).OutdentSelection, + "OutdentLine": (*View).OutdentLine, + "Paste": (*View).Paste, + "PastePrimary": (*View).PastePrimary, + "SelectAll": (*View).SelectAll, + "OpenFile": (*View).OpenFile, + "Start": (*View).Start, + "End": (*View).End, + "PageUp": (*View).PageUp, + "PageDown": (*View).PageDown, + "SelectPageUp": (*View).SelectPageUp, + "SelectPageDown": (*View).SelectPageDown, + "HalfPageUp": (*View).HalfPageUp, + "HalfPageDown": (*View).HalfPageDown, + "StartOfLine": (*View).StartOfLine, + "EndOfLine": (*View).EndOfLine, + "ToggleHelp": (*View).ToggleHelp, + "ToggleKeyMenu": (*View).ToggleKeyMenu, + "ToggleRuler": (*View).ToggleRuler, + "JumpLine": (*View).JumpLine, + "ClearStatus": (*View).ClearStatus, + "ShellMode": (*View).ShellMode, + "CommandMode": (*View).CommandMode, + "ToggleOverwriteMode": (*View).ToggleOverwriteMode, + "Escape": (*View).Escape, + "Quit": (*View).Quit, + "QuitAll": (*View).QuitAll, + "AddTab": (*View).AddTab, + "PreviousTab": (*View).PreviousTab, + "NextTab": (*View).NextTab, + "NextSplit": (*View).NextSplit, + "PreviousSplit": (*View).PreviousSplit, + "Unsplit": (*View).Unsplit, + "VSplit": (*View).VSplitBinding, + "HSplit": (*View).HSplitBinding, + "ToggleMacro": (*View).ToggleMacro, + "PlayMacro": (*View).PlayMacro, + "Suspend": (*View).Suspend, + "ScrollUp": (*View).ScrollUpAction, + "ScrollDown": (*View).ScrollDownAction, + "SpawnMultiCursor": (*View).SpawnMultiCursor, + "SpawnMultiCursorSelect": (*View).SpawnMultiCursorSelect, + "RemoveMultiCursor": (*View).RemoveMultiCursor, + "RemoveAllMultiCursors": (*View).RemoveAllMultiCursors, + "SkipMultiCursor": (*View).SkipMultiCursor, + "JumpToMatchingBrace": (*View).JumpToMatchingBrace, // This was changed to InsertNewline but I don't want to break backwards compatibility "InsertEnter": (*View).InsertNewline, @@ -244,6 +257,7 @@ var bindingKeys = map[string]tcell.Key{ "Escape": tcell.KeyEscape, "Enter": tcell.KeyEnter, "Backspace": tcell.KeyBackspace2, + "OldBackspace": tcell.KeyBackspace, // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings "PgUp": tcell.KeyPgUp, @@ -256,11 +270,13 @@ type Key struct { modifiers tcell.ModMask buttons tcell.ButtonMask r rune + escape string } // InitBindings initializes the keybindings for micro func InitBindings() { bindings = make(map[Key][]func(*View, bool) bool) + bindingsStr = make(map[string]string) mouseBindings = make(map[Key][]func(*View, bool, *tcell.EventMouse) bool) var parsed map[string]string @@ -312,16 +328,29 @@ modSearch: case strings.HasPrefix(k, "Shift"): k = k[5:] modifiers |= tcell.ModShift + case strings.HasPrefix(k, "\x1b"): + return Key{ + keyCode: -1, + modifiers: modifiers, + buttons: -1, + r: 0, + escape: k, + }, true default: break modSearch } } + if len(k) == 0 { + return Key{buttons: -1}, false + } + // Control is handled specially, since some character codes in bindingKeys // are different when Control is depressed. We should check for Control keys // first. if modifiers&tcell.ModCtrl != 0 { // see if the key is in bindingKeys with the Ctrl prefix. + k = string(unicode.ToUpper(rune(k[0]))) + k[1:] if code, ok := bindingKeys["Ctrl"+k]; ok { // It is, we're done. return Key{ @@ -387,6 +416,43 @@ func findMouseAction(v string) func(*View, bool, *tcell.EventMouse) bool { return action } +// TryBindKey tries to bind a key by writing to configDir/bindings.json +// This function is unused for now +func TryBindKey(k, v string) { + filename := configDir + "/bindings.json" + if _, e := os.Stat(filename); e == nil { + input, err := ioutil.ReadFile(filename) + if err != nil { + TermMessage("Error reading bindings.json file: " + err.Error()) + return + } + + conflict := -1 + lines := strings.Split(string(input), "\n") + for i, l := range lines { + parts := strings.Split(l, ":") + if len(parts) >= 2 { + if strings.Contains(parts[0], k) { + conflict = i + TermMessage("Warning: Keybinding conflict:", k, " has been overwritten") + } + } + } + + binding := fmt.Sprintf(" \"%s\": \"%s\",", k, v) + if conflict == -1 { + lines = append([]string{lines[0], binding}, lines[conflict:]...) + } else { + lines = append(append(lines[:conflict], binding), lines[conflict+1:]...) + } + txt := strings.Join(lines, "\n") + err = ioutil.WriteFile(filename, []byte(txt), 0644) + if err != nil { + TermMessage("Error") + } + } +} + // BindKey takes a key and an action and binds the two together func BindKey(k, v string) { key, ok := findKey(k) @@ -397,14 +463,21 @@ func BindKey(k, v string) { if v == "ToggleHelp" { helpBinding = k } + if v == "ToggleKeyMenu" { + kmenuBinding = k + } if helpBinding == k && v != "ToggleHelp" { helpBinding = "" } + if kmenuBinding == k && v != "ToggleKeyMenu" { + kmenuBinding = "" + } actionNames := strings.Split(v, ",") if actionNames[0] == "UnbindKey" { delete(bindings, key) delete(mouseBindings, key) + delete(bindingsStr, k) if len(actionNames) == 1 { return } @@ -415,6 +488,12 @@ func BindKey(k, v string) { for _, actionName := range actionNames { if strings.HasPrefix(actionName, "Mouse") { mouseActions = append(mouseActions, findMouseAction(actionName)) + } else if strings.HasPrefix(actionName, "command:") { + cmd := strings.SplitN(actionName, ":", 2)[1] + actions = append(actions, CommandAction(cmd)) + } else if strings.HasPrefix(actionName, "command-edit:") { + cmd := strings.SplitN(actionName, ":", 2)[1] + actions = append(actions, CommandEditAction(cmd)) } else { actions = append(actions, findAction(actionName)) } @@ -424,6 +503,7 @@ func BindKey(k, v string) { // Can't have a binding be both mouse and normal delete(mouseBindings, key) bindings[key] = actions + bindingsStr[k] = v } else if len(mouseActions) > 0 { // Can't have a binding be both mouse and normal delete(bindings, key) @@ -458,6 +538,8 @@ func DefaultBindings() map[string]string { "CtrlDown": "CursorEnd", "CtrlShiftUp": "SelectToStart", "CtrlShiftDown": "SelectToEnd", + "Alt-{": "ParagraphPrevious", + "Alt-}": "ParagraphNext", "Enter": "InsertNewline", "CtrlH": "Backspace", "Backspace": "Backspace", @@ -490,6 +572,7 @@ func DefaultBindings() map[string]string { "CtrlPageUp": "PreviousTab", "CtrlPageDown": "NextTab", "CtrlG": "ToggleHelp", + "Alt-g": "ToggleKeyMenu", "CtrlR": "ToggleRuler", "CtrlL": "JumpLine", "Delete": "Delete", @@ -499,6 +582,7 @@ func DefaultBindings() map[string]string { "CtrlW": "NextSplit", "CtrlU": "ToggleMacro", "CtrlJ": "PlayMacro", + "Insert": "ToggleOverwriteMode", // Emacs-style keybindings "Alt-f": "WordRight", @@ -509,7 +593,6 @@ func DefaultBindings() map[string]string { // "Alt-n": "CursorDown", // Integration with file managers - "F1": "ToggleHelp", "F2": "Save", "F3": "Find", "F4": "Quit", @@ -525,6 +608,7 @@ func DefaultBindings() map[string]string { "Ctrl-MouseLeft": "MouseMultiCursor", "Alt-n": "SpawnMultiCursor", + "Alt-m": "SpawnMultiCursorSelect", "Alt-p": "RemoveMultiCursor", "Alt-c": "RemoveAllMultiCursors", "Alt-x": "SkipMultiCursor",