Package to display keyboard macros or latest interactive commands as emacs lisp.
Switch branches/tags
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.



Shows keyboard macros or latest interactive commands as emacs lisp.



Say you have the following text:

violets are blue
roses are red

With the cursor somewhere on the first line. Press the following keys:

F3 C-e M-b M-u C-a C-n F4

Then doing M-x elmacro-show-last-macro upcase-last-word RET produces a buffer with:

(defun upcase-last-word ()
  (move-end-of-line 1)
  (backward-word 1)
  (upcase-word 1)
  (move-beginning-of-line 1)
  (next-line 1 1))

You can now do M-x eval-buffer followed by M-x upcase-last-word or call it from your emacs lisp code.

Table of Contents


The recommended way to install elmacro is through MELPA.

Otherwise, simply add elmacro.el to your load-path and then (require 'elmacro).

To enable elmacro, do M-x elmacro-mode or enable it from your config file like this:




M-x elmacro-show-last-macro shows your latest macro as emacs lisp.

In order to use this, you must firt record a keyboard macro. Then, when you do M-x elmacro-show-last-macro it will ask you for a defun name and show the latest macro as emacs lisp.


M-x elmacro-show-last-commands shows your latest emacs activity as emacs lisp.

This is basically a better version of kmacro-edit-lossage.

The default number of commands shown is modifiable in variable elmacro-show-last-commands-default.

You can also modify this number by using a numeric prefix argument or by using the universal argument, in which case it’ll ask for how many in the minibuffer.


Clears the list of recorded commands.



Default value: (elmacro-processor-filter-unwanted elmacro-processor-prettify-inserts elmacro-processor-concatenate-inserts elmacro-processor-handle-special-objects)

List of processors functions used to improve code listing.

Each function is passed the list of commands meant to be displayed and is expected to return a modified list of commands.


Default value: 30

Number of commands shown by default in elmacro-show-last-commands.


Default value: (copy-file copy-directory rename-file delete-file make-directory)

List of non-interactive functions that you also want to be recorded.

For example, dired-copy-file (C key in dired) doesn't reads its arguments as an interactive specification, and thus the file name is never stored.


Default value: ("^(ido.*)$" "^(smex)$")

Regexps used to filter unwanted commands.


Default value:

'(("#<frame [^0]+\\(0x[0-9a-f]+\\)>" ",(elmacro-get-frame \"\\1\")")
  ("#<window \\([0-9]+\\)[^>]+>"     ",(elmacro-get-window \\1)")
  ("#<buffer \\([^>]+\\)>"           ",(get-buffer \"\\1\")"))

List of (regexp replacement) for special objects.

This will be used as arguments for replace-regexp-in-string.


Default value: nil

Set to true to turn debugging in buffer * elmacro debug *.


The way elmacro processes commands can be modified using processors.

A processor is an emacs lisp function that takes a list the commands meant to be displayed and is expected to return a modified list of commands.

For example, a simple processor that filters anything you insert in a buffer:

(defun filter-insert-processor (commands)
  (--remove (eq 'insert (car it)) commands))


Remove unwanted commands using elmacro-unwanted-commands-regexps.


Transform all occurences of self-insert-command into insert. This filter should be not be enabled with packages that advice self-insert-command, see the FAQ for more information.


(setq last-command-event 97)
(self-insert-command 1)
(setq last-command-event 98)
(self-insert-command 1)
(setq last-command-event 99)
(self-insert-command 3)


(insert "a")
(insert "b")
(insert "ccc")


Concatenate multiple text insertion together.


(insert "a")
(insert "b")
(insert "c")


(insert "abc")


Turn special objects into usable objects using elmacro-special-objects.


org-mode, smartparens, etc

Normally elmacro works reasonably well with these, but if you want to ensure the most accurate experience you should disable the elmacro-processor-prettify-inserts processor (see elmacro-processors).

This is necessary because these packages usually advice self-insert-command, and by transforming it into an insert the advice does not run and we miss functionnality.

Mouse events

A nice addition to normal macros is that mouse events (clicks / scroll) are also recorded and elmacro can figure which emacs window / frame was the target.

For example, by default clicking in a window will generate code like:

(mouse-set-point '(mouse-1 (#<window 75 on foo.el> 913 (90 . 286) 185432429 nil 913 (10 . 15) nil (90 . 1) (9 . 19))))

We see that the <#window 75 on foo.el> part is not very useful. Thanks to the processor elmacro-processor-handle-special-objects, the following code is generated instead (elmacro-get-window is a helper that returns the correct emacs window object):

(mouse-set-point `(mouse-1 (,(elmacro-get-window 75) 913 (90 . 286) 185432429 nil 913 (10 . 15) nil (90 . 1) (9 . 19))))

Contributions welcome!

Either as suggestions or as pull requests by opening tickets on the issue tracker.