Skip to content

Commit

Permalink
Better README
Browse files Browse the repository at this point in the history
  • Loading branch information
joddie committed Sep 24, 2012
1 parent e2bf1aa commit 0fd292c
Show file tree
Hide file tree
Showing 4 changed files with 230 additions and 103 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.elc
*~
23 changes: 0 additions & 23 deletions README

This file was deleted.

132 changes: 132 additions & 0 deletions README.org
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
* macrostep: interactive macro expansion for Emacs Lisp

** Overview
This is a minor mode for interactively stepping through the
expansion of macros in Emacs Lisp source code. It lets you see
exactly what happens at each step of the expansion process by
pretty-printing the expanded forms inline in the source buffer,
which is read-only while macro expansions are visible. You can
expand and collapse macro forms one step at a time, and evaluate or
instrument them for debugging with Edebug as normal (but see "Bugs
and known limitations", below). Single-stepping through the
expansion is useful for debugging macros that expand into another
macro form, especially one like =lexical-let= that does significant
rewriting. These can be difficult to debug with Emacs' built-in
=macroexpand=, because =macroexpand= continues expansion until the
top-level form is no longer a macro call.

The mode also adds some simple additional fontification to
macro-expanded code. The heads of macro sub-forms are fontified
using =macrostep-macro-face=. Uninterned symbols (gensyms) are
fontified based on which step in the expansion created them, to
distinguish them from normal symbols and from other gensyms with
the same print name. Use =customize-group= with the =macrostep=
group to customize these faces.

** Key-bindings and usage
The standard macrostep-mode keybindings are the following:

- e, =, RET :: expand the macro form following point one step
- c, u, DEL :: collapse the form following point
- q, C-c C-c :: collapse all expanded forms and exit macrostep-mode
- n, TAB :: jump to the next macro form in the expansion
- p, M-TAB :: jump to the previous macro form in the expansion

It's not very useful to enable and disable macrostep-mode
directly. Instead, bind =macrostep-expand= to a key in
=emacs-lisp-mode-map=, for example C-c e:

#+BEGIN_SRC emacs-lisp
(add-hook
'emacs-lisp-mode-hook
(lambda ()
(define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)))
#+END_SRC

You can then enter macrostep-mode and expand a macro form
completely by typing =C-c e e e ...= as many times as necessary.

Exit macrostep-mode either with =q=, =C-c C-c=, or by successively
typing =c= to collapse all expanded forms back to their original
text.

** Expanding sub-forms
By moving point around in the macro expansion (perhaps using the
=n= and =p= keys), you can macro-expand sub-forms before fully
expanding the enclosing form. This can be useful in some cases,
but you should keep in mind that it does not correspond to the way
Emacs actually expands macro calls when evaluating or compiling
your code. Macro expansion in Emacs Lisp always proceeds by fully
expanding the outer form to a non-macro form before doing anything
with the sub-forms.

For example, with =cl= loaded, try expanding the following form:

#+BEGIN_SRC emacs-lisp
(dolist (l list-of-lists)
(incf (car l)))
#+END_SRC

to produce the following:

#+BEGIN_SRC emacs-lisp
(block nil
(let
((--cl-dolist-temp-- list-of-lists)
l)
(while --cl-dolist-temp--
(setq l
(car --cl-dolist-temp--))
(incf
(car l))
(setq --cl-dolist-temp--
(cdr --cl-dolist-temp--)))
nil))
#+END_SRC

where the forms beginning =block= and =incf= are both macro calls.

At this point, you can either continue expanding the =block= form,
which corresponds to the real order of macro expansion in
evaluation, or type =n= to move point to the unexpanded =incf= and
expand it to a =callf= form and finally to a =let*= form. If you
then move point back to the =block= and expand it, an unexpanded
=incf= form appears again in the result. This might look visually
confusing, but it does at least correspond to the way real macro
expansion works.

Why allow expanding sub-forms out of order like this at all? The
main reason is for debugging macros which expand into another
macro, like =lexical-let=, that programmatically expands its
contents in order to rewrite them. In this case, expanding the
sub-forms first allows you to see what =lexical-let= would compute
via =cl-macroexpand-all=.


** Bugs and known limitations
You can evaluate and edebug macro-expanded forms and step through
the macro-expanded version, but the form that =eval-defun= and
friends read from the buffer won't have the uninterned symbols of
the real macro expansion. This will probably work OK with CL-style
gensyms, but may cause problems with =make-symbol= symbols if they
have the same print name as another symbol in the expansion. It's
possible that using =print-circle= and =print-gensym= could get
around this.

The macro stepper doesn't bother trying to determine whether or not
a sub-form is in an evaluated position before highlighting it as a
macro. It does exclude =lambda= from treatment as a macro, since
that leads to an endless series of expansions: =(function (function
... ))=. It would be better to recognise =function=, =quote= and
other special forms using their =edebug-form-spec= property.

Macro-expanding a call to an autoloaded macro doesn't work. This
shouldn't be too hard to fix.

Please send other bug reports and feature requests to the author.

** Acknowledgements
Thanks to John Wiegley for fixing a bug with the face definitions
under Emacs 24.

#+OPTIONS: author:nil email:nil toc:nil timestamp:nil
177 changes: 97 additions & 80 deletions macrostep.el
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

;; Copyright (C) 2012 Jonathan Oddie <[email protected]>

;; Author: Jonathan Oddie <[email protected]>
;; Maintainer: Jonathan Oddie <[email protected]>
;; Author: joddie <[email protected]>
;; Maintainer: joddie <[email protected]>
;; Created: 16 January 2012
;; Updated: 02 May 2012
;; Updated: 23 September 2012
;; Version: 0.2
;; Keywords: lisp, languages, macro, debugging

Expand All @@ -26,60 +26,76 @@

;;; Commentary:

;; `macrostep-mode' is a minor mode for interactively stepping through
;; the expansion of macros in Emacs Lisp source code. It lets you see
;; exactly what happens at each step of the expansion process by
;; pretty-printing the expanded forms inline in the source buffer,
;; which is read-only while macro expansions are visible. You can
;; expand and collapse macro forms one step at a time, and evaluate
;; or instrument them for debugging with Edebug as normal (but see
;; "Bugs and known limitations", below). Single-stepping through the
;; expansion is useful for debugging macros that expand into another
;; macro form, especially one like `lexical-let' that does significant
;; rewriting. These can be difficult to debug with Emacs' built-in
;; `macroexpand' because `macroexpand' continues expansion until the
;; top-level form is no longer a macro call.
;; 1.1 Overview
;; =============
;; `macrostep-mode' is a minor mode for interactively stepping through
;; the expansion of macros in Emacs Lisp source code. It lets you see
;; exactly what happens at each step of the expansion process by
;; pretty-printing the expanded forms inline in the source buffer,
;; which is read-only while macro expansions are visible. You can
;; expand and collapse macro forms one step at a time, and evaluate or
;; instrument them for debugging with Edebug as normal (but see "Bugs
;; and known limitations", below). Single-stepping through the
;; expansion is useful for debugging macros that expand into another
;; macro form, especially one like `lexical-let' that does significant
;; rewriting. These can be difficult to debug with Emacs' built-in
;; `macroexpand', because `macroexpand' continues expansion until the
;; top-level form is no longer a macro call.
;;
;; macrostep-mode adds some simple additional fontification to
;; macro-expanded text. The heads of macro sub-forms are fontified
;; using `macrostep-macro-face'. Uninterned symbols (gensyms) are
;; fontified based on which step in the expansion created them, to
;; distinguish them from normal symbols and from other gensyms with
;; the same print name. Use `customize-group' with the "macrostep"
;; group to customize these faces.
;; `macrostep-mode' also adds some simple additional fontification to
;; macro-expanded code. The heads of macro sub-forms are fontified
;; using `macrostep-macro-face'. Uninterned symbols (gensyms) are
;; fontified based on which step in the expansion created them, to
;; distinguish them from normal symbols and from other gensyms with
;; the same print name. Use `customize-group' with the `macrostep'
;; group to customize these faces.
;;
;; The standard macrostep-mode keybindings are the following:
;; 1.2 Key-bindings and usage
;; ===========================
;; The standard macrostep-mode keybindings are the following:
;;
;; e, =, RET : expand the macro form following point one step
;; c, u, DEL : collapse the form following point
;; q, C-c C-c: collapse all expanded forms and exit macrostep-mode
;; n, TAB : jump to the next macro form in the expansion
;; p, M-TAB : jump to the previous macro form in the expansion
;;
;; e, =, RET expand the macro form following point one step
;; c, u, DEL collapse the form following point
;; q, C-c C-c collapse all expanded forms and exit macrostep-mode
;; n, TAB jump to the next macro form in the expansion
;; p, M-TAB jump to the previous macro form in the expansion
;; It's not very useful to enable and disable macrostep-mode
;; directly. Instead, bind `macrostep-expand' to a key in
;; `emacs-lisp-mode-map', for example C-c e:
;;
;; It's not very useful to enable and disable macrostep-mode
;; directly. Instead, bind `macrostep-expand' to a key in
;; `emacs-lisp-mode-map', for example C-c e:
;;
;; (add-hook
;; 'emacs-lisp-mode-hook
;; (lambda ()
;; (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)))
;;
;; You can then enter macrostep-mode and expand a macro form
;; completely by typing C-c e e e ... as many times as necessary.
;; (add-hook
;; 'emacs-lisp-mode-hook
;; (lambda ()
;; (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand)))
;;
;; Exit macrostep-mode either with 'q', C-c C-c, or by successively
;; typing 'c' to collapse all expanded forms back to their original
;; text.
;;
;; Note that by moving point around in the macro expansion, you can
;; macro-expand sub-forms before fully expanding their enclosing
;; form. For example, with `cl' loaded, try expanding
;; You can then enter macrostep-mode and expand a macro form
;; completely by typing =C-c e e e ...= as many times as necessary.
;;
;; Exit macrostep-mode either with `q', `C-c C-c', or by successively
;; typing `c' to collapse all expanded forms back to their original
;; text.
;;
;; 1.3 Expanding sub-forms
;; ========================
;; By moving point around in the macro expansion (perhaps using `n'
;; and `p'), you can macro-expand sub-forms before fully expanding
;; the enclosing form. This can be useful in some cases, but you
;; should keep in mind that it does not correspond to the way Emacs
;; actually expands macro calls when evaluating or compiling your
;; code. Macro expansion in Emacs Lisp always proceeds by fully
;; expanding the outer form to a non-macro form before doing anything
;; with the sub-forms.
;;
;; For example, with `cl' loaded, try expanding the following form:
;;
;; (dolist (l list-of-lists)
;; (incf (car l)))
;; (incf (car l)))
;;
;; which produces the following expansion:
;; to produce the following:
;;
;; (block nil
;; (let
Expand All @@ -94,47 +110,48 @@
;; (cdr --cl-dolist-temp--)))
;; nil))
;;
;; where `block' and `incf' are both macros.
;; where the forms beginning `block' and `incf' are both macro calls.
;;
;; At this point, you can either continue expanding the `block' form
;; -- which corresponds to the real order of macro expansion in
;; evaluation -- or type `n' to move point to the unexpanded `incf'
;; and expand it to a `callf' form and finally to a =let*= form. If
;; you then move point back to the `block' and expand it, an
;; unexpanded `incf' form appears again in the result. This might
;; look visually confusing, but it at least corresponds to the way
;; real macro expansion works.
;;
;; You can then either continue expanding the `block' form, which
;; corresponds to the real order of macro expansion, or type `n' to
;; move to the unexpanded `incf' and expand it to a `callf' form and
;; finally to a `let*' form. However, note that the expansion of a
;; form always works on the original, unexpanded text of its
;; sub-forms. This might look confusing: if you fully expand the
;; `incf' first in the above example, then expand the `block', the
;; result will again have an unexpanded `incf' form in it. But it
;; corresponds to the way real macro expansion works: the outer form
;; is expanded into a non-macro before any inner forms are
;; evaluated.
;; Why allow expanding sub-forms out of order like this at all? The
;; main reason is for debugging macros which expand into another
;; macro, like `lexical-let', that programmatically expands its
;; contents in order to rewrite them. In this case, expanding the
;; sub-forms first allows you to see what `lexical-let' would compute
;; via `cl-macroexpand-all'.
;;
;; Why allow expanding sub-forms out of order like this at all? The
;; main reason is for debugging macros which expand into another
;; macro, like `lexical-let', that programmatically expands its
;; contents in order to rewrite them. In this case, expanding the
;; sub-forms first allows you to see what `lexical-let' would
;; compute via `cl-macroexpand-all'.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 1.4 Bugs and known limitations:
;; ================================
;; You can evaluate and edebug macro-expanded forms and step through
;; the macro-expanded version, but the form that `eval-defun' and
;; friends read from the buffer won't have the uninterned symbols of
;; the real macro expansion. This will probably work OK with CL-style
;; `(gensym)'s, but may cause problems with =(make-symbol ...)=
;; symbols if they have the same print name as another symbol in the
;; expansion. It's possible that using `print-circle' and
;; `print-gensym' could get around this.
;;
;; Bugs and known limitations:
;; The macro stepper doesn't bother trying to determine whether or not
;; a sub-form is in an evaluated position before highlighting it as a
;; macro. It does exclude `lambda' from treatment as a macro, since
;; that leads to an endless series of expansions: =(function (function
;; ... ))=. It would be better to recognise `function', `quote' and
;; other special forms using their edebug-form-spec property.
;;
;; You can evaluate and edebug macro-expanded forms and step through
;; the macro-expanded version, but the form that `eval-defun' and
;; friends read from the buffer won't have the uninterned symbols of
;; the real macro expansion. This will probably work OK with CL-style
;; (gensym)s, but may cause problems with (make-symbol ...) symbols if
;; they have the same print name as another symbol in the expansion.
;; Macro-expanding a call to an autoloaded macro doesn't work. This
;; shouldn't be too hard to fix.
;;
;; The macro stepper doesn't bother trying to determine whether or not
;; a sub-form is in an evaluated position before highlighting it as a
;; macro. It does exclude `lambda' from treatment as a macro, since
;; that leads to an endless series of expansions: (function (function
;; ... )). It would be better to recognise `function', `quote' and
;; other special forms using their edebug-form-spec property.
;; Please send other bug reports and feature requests to the author.
;;
;; Please send other bug reports and feature requests to the author,
;; [email protected]

;;; Code:

Expand Down

0 comments on commit 0fd292c

Please sign in to comment.