How can I save evil-mode (vim style) macros to my init.el?
Asked Answered
A

2

48

I'm using evil-mode, after moving over from Vim about a year ago. I've made a number of customisations, but not yet worked out how to save vim-style keyboard macros.

I can define and run them in evil-mode, using the exact same keys as in Vim.

  • qa letter to start recording a macro.
  • q to stop recording.
  • @a letter to run the macro.

I'd like to save some of these to my init.el file to use between sessions, but I've not been able to find any reference to this online, and haven't found the right syntax.

How can I save these evil-mode keyboard macros to file? Is there some reason I should be using native Emacs keyboard macros instead, and is there a way I can run these via @a key?

Another answered 2/4, 2014 at 16:7 Comment(0)
A
63

Evil-mode macros are not special, they are just ordinary Emacs macros and you save them the same way, but you'll need to do some special work to get them into the evil registers.

Let's walk through an example:

In a buffer, do qfifoobarESCq. This will save a macro into the f register that inserts foobar into the buffer.

Now run M-xname-last-kbd-macroRETmymacroRET.

Go to your init.el file and do M-xinsert-kbd-macroRETmymacroRET.

This will dump your macro out into an fset call.

(fset 'mymacro [?i ?f ?o ?o ?b ?a ?r escape])

If you place this in your init.el you will have access to the command mymacro from M-x.

But, we saved this into register f and we want it to be there at each startup. You need to extract the macro vector from the code above and save that to a register in your init.el like this:

;; make sure this is done after evil-mode has been loaded
(evil-set-register ?f [?i ?f ?o ?o ?b ?a ?r escape])

Now you will have access to it from @!

See the docs about naming and inserting macros as text

Atomics answered 2/4, 2014 at 18:45 Comment(8)
This solution works for basic movement and text deletion / insertion. However, it doesn't work for things like :s/foo/bar/g that it's possible to put into macros in Vim. Is this something that's possible to do with keyboard macros in emacs, or do I need to use lisp? Is it possible to simply record and execute such simple macros via @ in emacs evil-mode?Another
This is very weird! I believe it is a bug with evil, everytime I try recording a :s/foo/bar/g macro, the actual macro vector comes back with two f characters, it looks like this: [?: ?s ?/ ?f ?f ?o ... ]. Your macros ARE recording, it's just that they are recording wrong, I was able to name, and print my macros, manually fix the vectors and save them back into their registers for now. I will look into this bug further.Atomics
Thanks again @jordonbiondo, I see the same behaviour, and my macro works correctly once the vector is fixed.Another
Will you provide me a list of what minor modes and packages you have active? I found that when loading vanilla emacs with ONLY evil-mode the macro recording works just fine. I suspect there is another mode messing things up.Atomics
Or to be more helpful: packages = (ace-jump-mode auto-complete color-theme-solarized color-theme evil-indent-textobject evil-leader evil evil-matchit evil-nerd-commenter goto-chg goto-last-change multi-term org popup ssh undo-tree zenburn-theme)Another
@user2610437 have you found a way to fix emacs' treating these macros as more than one command? I often find I have to hit u twice if I want to reverse them.Guinness
@tcelferact Use with-undo-collapse like here emacs.stackexchange.com/questions/31454/…Gothurd
This works fine for the last macro, but how can I edit / save a macro for later use that is currently stored in a vim (evil) register, but is not the last macro, so I cannot manage it using the built in Emacs methods?Neper
J
2

I faced this problem and write a advice for evil-paste-after command in another question :

(defun evil-paste-kbd-macro-advice (&rest argv)
  "make evil paste kbd-macro if register content is a macro.
this function check whether content is a macro by:
 1. equal to `last-kbd-macro'
 2. is a vector but not string
 3. contain unprintable character"
  (if (and (>= (length argv) 2)
           (second argv))
      (let* ((register (second argv))
             (register-pair (assoc register register-alist))
             (content (if register-pair (cdr register-pair))))
        (if (and content
                 (or (eq last-kbd-macro content)
                     (vectorp content)
                     (string-match "[^\t[:print:]\n\r]" content)))
            (let ((last-kbd-macro content))
              (forward-line)
              (beginning-of-line)
              (insert-kbd-macro '##)
              (forward-line -2)
              (search-forward "setq last-kbd-macro")
              (replace-match "execute-kbd-macro")
              t)))))
(advice-add 'evil-paste-after :before-until
            'evil-paste-kbd-macro-advice)

This advice make evil p command be able to paste macro in register, even if macro contain 'return or 'backspace.

Jabe answered 2/9, 2021 at 14:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.