README.org (9562B)
1 * macrostep: interactive macro-expander 2 3 =macrostep= is an Emacs minor mode for interactively stepping 4 through the expansion of macros in Emacs Lisp source code. It lets 5 you see exactly what happens at each step of the expansion process 6 by pretty-printing the expanded forms inline in the source buffer, 7 which is temporarily read-only while macro expansions are visible. 8 You can expand and collapse macro forms one step at a time, and 9 evaluate or instrument the expansions for debugging with Edebug as 10 normal (but see "Bugs and known limitations", below). 11 Single-stepping through the expansion is particularly useful for 12 debugging macros that expand into another macro form. These can be 13 difficult to debug with Emacs' built-in =macroexpand=, which 14 continues expansion until the top-level form is no longer a macro 15 call. 16 17 Both globally-visible macros as defined by =defmacro= and local 18 macros bound by =(cl-)macrolet= or another macro-defining form can 19 be expanded. Within macro expansions, calls to macros and compiler 20 macros are fontified specially: macro forms using 21 =macrostep-macro-face=, and functions with compiler macros using 22 =macrostep-compiler-macro-face=. Uninterned symbols (gensyms) are 23 fontified based on which step in the expansion created them, to 24 distinguish them both from normal symbols and from other gensyms 25 with the same print name. 26 27 As of version 0.9, it is also possible to extend =macrostep= to 28 work with other languages with macro systems in addition to Emacs 29 Lisp. An extension for Common Lisp (via SLIME) is in the works; 30 contributions for other languages are welcome. See "Extending 31 macrostep" below for details. 32 33 ** Key-bindings and usage 34 The standard keybindings in =macrostep-mode= are the following: 35 36 - e, =, RET :: expand the macro form following point one step 37 - c, u, DEL :: collapse the form following point 38 - q, C-c C-c :: collapse all expanded forms and exit macrostep-mode 39 - n, TAB :: jump to the next macro form in the expansion 40 - p, M-TAB :: jump to the previous macro form in the expansion 41 42 It's not very useful to enable and disable macrostep-mode 43 directly. Instead, bind =macrostep-expand= to a key in 44 =emacs-lisp-mode-map=, for example C-c e: 45 46 #+BEGIN_SRC emacs-lisp 47 (define-key emacs-lisp-mode-map (kbd "C-c e") 'macrostep-expand) 48 #+END_SRC 49 50 You can then enter macrostep-mode and expand a macro form 51 completely by typing =C-c e e e ...= as many times as necessary. 52 53 Exit macrostep-mode by typing =q= or =C-c C-c=, or by successively 54 typing =c= to collapse all surrounding expansions. 55 56 ** Customization options 57 Type =M-x customize-group RET macrostep RET= to customize options 58 and faces. 59 60 To display macro expansions in a separate window, instead of inline 61 in the source buffer, customize 62 =macrostep-expand-in-separate-buffer= to =t=. The default is 63 =nil=. Whichever default behavior is selected, the alternative 64 behavior can be obtained temporarily by giving a prefix argument to 65 =macrostep-expand=. 66 67 To have =macrostep= ignore compiler macros, customize 68 =macrostep-expand-compiler-macros= to =nil=. The default is =t=. 69 70 Customize the faces =macrostep-macro-face=, 71 =macrostep-compiler-macro-face=, and =macrostep-gensym-1= through 72 =macrostep-gensym-5= to alter the appearance of macro expansions. 73 74 ** Locally-bound macros 75 As of version 0.9, =macrostep= can expand calls to a locally-bound 76 macro, whether defined by a surrounding =(cl-)macrolet= form, or by 77 another macro-defining macro. In other words, it is possible to 78 expand the inner =local-macro= forms in both the following 79 examples, whether =local-macro= is defined by an enclosing 80 =cl-macrolet= -- 81 82 #+BEGIN_SRC emacs-lisp 83 (cl-macrolet ((local-macro (&rest args) 84 `(expansion of ,args))) 85 (local-macro (do-something))) 86 #+END_SRC 87 88 -- or by a macro which expands into =cl-macrolet=, provided that 89 its definition of macro is evaluated prior to calling 90 =macrostep-expand=: 91 92 #+BEGIN_SRC emacs-lisp 93 (defmacro with-local-macro (&rest body) 94 `(cl-macrolet ((local-macro (&rest args) 95 `(expansion of ,args))) 96 ,@body)) 97 98 (with-local-macro 99 (local-macro (do something (else))) 100 #+END_SRC 101 102 See the =with-js= macro in Emacs's =js.el= for a real example of 103 the latter kind of macro. 104 105 Expansion of locally-bound macros is implemented by instrumenting 106 Emacs Lisp's macro-expander to capture the environment at point. A 107 similar trick is used to detect macro- and compiler-macro calls 108 within expanded text so that they can be fontified accurately. 109 110 ** Expanding sub-forms 111 By moving point around in the macro expansion using 112 =macrostep-next-macro= and =macrostep-prev-macro= (bound to the =n= 113 and =p= keys), it is possible to expand other macro calls within 114 the expansion before expanding the outermost form. This can 115 sometimes be useful, although it does not correspond to the real 116 order of macro expansion in Emacs Lisp, which proceeds by fully 117 expanding the outer form to a non-macro form before expanding 118 sub-forms. 119 120 The main reason to expand sub-forms out of order is to help with 121 debugging macros which programmatically expand their arguments in 122 order to rewrite them. Expanding the arguments of such a macro 123 lets you visualise what the macro definition would compute via 124 =macroexpand-all=. 125 126 ** Extending macrostep for other languages 127 Since version 0.9, it is possible to extend macrostep to work with 128 other languages besides Emacs Lisp. In typical Emacs fashion, this 129 is implemented by setting buffer-local variables to different 130 function values. Six buffer-local variables define the 131 language-specific part of the implementation: 132 133 - =macrostep-sexp-bounds-function= 134 - =macrostep-sexp-at-point-function= 135 - =macrostep-environment-at-point-function= 136 - =macrostep-expand-1-function= 137 - =macrostep-print-function= 138 - =macrostep-macro-form-p-function= 139 140 Typically, an implementation for another language would set these 141 variables in a major-mode hook. See the docstrings of each 142 variable for details on how each one is called and what it should 143 return. At a minimum, another language implementation needs to 144 provide =macrostep-sexp-at-point-function=, 145 =macrostep-expand-1-function=, and =macrostep-print-function=. 146 Lisp-like languages may be able to reuse the default 147 =macrostep-sexp-bounds-function= if they provide another 148 implementation of =macrostep-macro-form-p-function=. Languages 149 which do not implement locally-defined macros can set 150 =macrostep-environment-at-point-function= to =ignore=. 151 152 Note that the core =macrostep= machinery only interprets the return 153 value of =macrostep-sexp-bounds-function=, so implementations for 154 other languages can use any internal representations of code and 155 environments which is convenient. Although the terminology is 156 Lisp-specific, there is no reason that implementations could not be 157 provided for non-Lisp languages with macro systems, provided there 158 is some way of identifying macro calls and calling the compiler / 159 preprocessor to obtain their expansions. 160 161 ** Bugs and known limitations 162 You can evaluate and edebug macro-expanded forms and step through 163 the macro-expanded version, but the form that =eval-defun= and 164 friends read from the buffer won't have the uninterned symbols of 165 the real macro expansion. This will probably work OK with CL-style 166 gensyms, but may cause problems with =make-symbol= symbols if they 167 have the same print name as another symbol in the expansion. It's 168 possible that using =print-circle= and =print-gensym= could get 169 around this. 170 171 Please send other bug reports and feature requests to the author. 172 173 ** Acknowledgements 174 Thanks to: 175 - John Wiegley for fixing a bug with the face definitions under 176 Emacs 24 & for plugging macrostep in his [[http://youtu.be/RvPFZL6NJNQ][EmacsConf presentation]]! 177 - George Kettleborough for bug reports, and patches to highlight 178 the expanded region and properly handle backquotes. 179 - Nic Ferrier for suggesting support for local definitions within 180 macrolet forms 181 - Luís Oliveira for suggesting and implementing SLIME support 182 183 =macrostep= was originally inspired by J. V. Toups's 'Deep Emacs 184 Lisp' articles ([[http://dorophone.blogspot.co.uk/2011/04/deep-emacs-part-1.html][part 1]], [[http://dorophone.blogspot.co.uk/2011/04/deep-emacs-lisp-part-2.html][part 2]], [[http://dorophone.blogspot.co.uk/2011/05/monadic-parser-combinators-in-elisp.html][screencast]]). 185 186 ** Changelog 187 - v0.9, 2015-10-01: 188 - separate into Elisp-specific and generic components 189 - highlight and expand compiler macros 190 - improve local macro expansion and macro form identification by 191 instrumenting =macroexpand(-all)= 192 - v0.8, 2014-05-29: fix a bug with printing the first element of 193 lists 194 - v0.7, 2014-05-11: expand locally-defined macros within 195 =(cl-)macrolet= forms 196 - v0.6, 2013-05-04: better handling of quote and backquote 197 - v0.5, 2013-04-16: highlight region, maintain cleaner buffer state 198 - v0.4, 2013-04-07: only enter macrostep-mode on successful 199 macro-expansion 200 - v0.3, 2012-10-30: print dotted lists correctly. autoload 201 definitions. 202 203 #+OPTIONS: author:nil email:nil toc:nil timestamp:nil