From 539495d2f740f006afd42efbce4d91050b0802f1 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Tue, 6 Sep 2016 10:43:45 -0400 Subject: [PATCH] Add support for macros Closes #270 CtrlU to toggle recording and CtrlJ to playback. You can also rebind using the "ToggleMacro" and "PlayMacro" actions. Note that recursive macros are not yet supported. --- README.md | 1 + cmd/micro/actions.go | 56 +++++++++++++++++++++++++++++++++++++ cmd/micro/bindings.go | 4 +++ cmd/micro/util.go | 6 ++++ cmd/micro/view.go | 10 +++++++ runtime/help/keybindings.md | 4 +++ 6 files changed, 81 insertions(+) diff --git a/README.md b/README.md index dffe98af..1f37c4d5 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ To see more screenshots of micro, showcasing all of the default colorschemes, se * Copy and paste with the system clipboard * Small and simple * Easily configurable +* Macros * Common editor things such as undo/redo, line numbers, unicode support... Although not yet implemented, I hope to add more features such as autocompletion ([#174](https://github.com/zyedidia/micro/issues/174)), and multiple cursors ([#5](https://github.com/zyedidia/micro/issues/5)) in the future. diff --git a/cmd/micro/actions.go b/cmd/micro/actions.go index 34fcb81b..f6597733 100644 --- a/cmd/micro/actions.go +++ b/cmd/micro/actions.go @@ -1476,6 +1476,62 @@ func (v *View) PreviousSplit(usePlugin bool) bool { return false } +var curMacro []interface{} +var recordingMacro bool + +func (v *View) ToggleMacro(usePlugin bool) bool { + if usePlugin && !PreActionCall("ToggleMacro", v) { + return false + } + + recordingMacro = !recordingMacro + + if recordingMacro { + curMacro = []interface{}{} + messenger.Message("Recording") + } else { + messenger.Message("Stopped recording") + } + + if usePlugin { + return PostActionCall("ToggleMacro", v) + } + return true +} + +func (v *View) PlayMacro(usePlugin bool) bool { + if usePlugin && !PreActionCall("PlayMacro", v) { + return false + } + + for _, action := range curMacro { + switch t := action.(type) { + case rune: + // Insert a character + if v.Cursor.HasSelection() { + v.Cursor.DeleteSelection() + v.Cursor.ResetSelection() + } + v.Buf.Insert(v.Cursor.Loc, string(t)) + v.Cursor.Right() + + for _, pl := range loadedPlugins { + _, err := Call(pl+".onRune", string(t), v) + if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { + TermMessage(err) + } + } + case func(*View, bool) bool: + t(v, true) + } + } + + if usePlugin { + return PostActionCall("PlayMacro", v) + } + return true +} + // None is no action func None() bool { return false diff --git a/cmd/micro/bindings.go b/cmd/micro/bindings.go index 67d40e0d..da6328be 100644 --- a/cmd/micro/bindings.go +++ b/cmd/micro/bindings.go @@ -79,6 +79,8 @@ var bindingActions = map[string]func(*View, bool) bool{ "NextTab": (*View).NextTab, "NextSplit": (*View).NextSplit, "PreviousSplit": (*View).PreviousSplit, + "ToggleMacro": (*View).ToggleMacro, + "PlayMacro": (*View).PlayMacro, // This was changed to InsertNewline but I don't want to break backwards compatibility "InsertEnter": (*View).InsertNewline, @@ -408,6 +410,8 @@ func DefaultBindings() map[string]string { "CtrlQ": "Quit", "CtrlE": "CommandMode", "CtrlW": "NextSplit", + "CtrlU": "ToggleMacro", + "CtrlJ": "PlayMacro", // Emacs-style keybindings "Alt-f": "WordRight", diff --git a/cmd/micro/util.go b/cmd/micro/util.go index 9bb019d1..4368a62f 100644 --- a/cmd/micro/util.go +++ b/cmd/micro/util.go @@ -3,6 +3,8 @@ package main import ( "os" "path/filepath" + "reflect" + "runtime" "strconv" "strings" "time" @@ -217,3 +219,7 @@ func Abs(n int) int { } return n } + +func FuncName(i interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name() +} diff --git a/cmd/micro/view.go b/cmd/micro/view.go index 8b6e46a1..8c1bdc52 100644 --- a/cmd/micro/view.go +++ b/cmd/micro/view.go @@ -340,6 +340,10 @@ func (v *View) HandleEvent(event tcell.Event) { TermMessage(err) } } + + if recordingMacro { + curMacro = append(curMacro, e.Rune()) + } } else { for key, actions := range bindings { if e.Key() == key.keyCode { @@ -352,6 +356,12 @@ func (v *View) HandleEvent(event tcell.Event) { relocate = false for _, action := range actions { relocate = action(v, true) || relocate + funcName := FuncName(action) + if funcName != "main.(*View).ToggleMacro" && funcName != "main.(*View).PlayMacro" { + if recordingMacro { + curMacro = append(curMacro, action) + } + } } } } diff --git a/runtime/help/keybindings.md b/runtime/help/keybindings.md index 6588ff6c..28af66e7 100644 --- a/runtime/help/keybindings.md +++ b/runtime/help/keybindings.md @@ -63,6 +63,8 @@ you can rebind them to your liking. "CtrlQ": "Quit", "CtrlE": "CommandMode", "CtrlW": "NextSplit", + "CtrlU": "ToggleMacro", + "CtrlJ": "PlayMacro", // Emacs-style keybindings "Alt-f": "WordRight", @@ -177,6 +179,8 @@ PreviousTab NextTab NextSplit PreviousSplit +ToggleMacro +PlayMacro ``` Here is the list of all possible keys you can bind: