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.
This commit is contained in:
Zachary Yedidia 2016-09-06 10:43:45 -04:00
parent 966dac97f8
commit 539495d2f7
6 changed files with 81 additions and 0 deletions

View file

@ -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.

View file

@ -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

View file

@ -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",

View file

@ -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()
}

View file

@ -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)
}
}
}
}
}

View file

@ -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: