emacsorphanage / macrostep

interactive macro-expander for Emacs
GNU General Public License v3.0
15 stars 6 forks source link

** Key-bindings and usage The standard keybindings in =macrostep-mode= 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

(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 by typing =q= or =C-c C-c=, or by successively
typing =c= to collapse all surrounding expansions.

** Customization options Type =M-x customize-group RET macrostep RET= to customize options and faces.

To display macro expansions in a separate window, instead of inline in the source buffer, customize =macrostep-expand-in-separate-buffer= to =t=. The default is =nil=. Whichever default behavior is selected, the alternative behavior can be obtained temporarily by giving a prefix argument to =macrostep-expand=.

To have =macrostep= ignore compiler macros, customize =macrostep-expand-compiler-macros= to =nil=. The default is =t=.

Customize the faces =macrostep-macro-face=, =macrostep-compiler-macro-face=, and =macrostep-gensym-1= through =macrostep-gensym-5= to alter the appearance of macro expansions.

** Locally-bound macros As of version 0.9, =macrostep= can expand calls to a locally-bound macro, whether defined by a surrounding =(cl-)macrolet= form, or by another macro-defining macro. In other words, it is possible to expand the inner =local-macro= forms in both the following examples, whether =local-macro= is defined by an enclosing =cl-macrolet= --

+BEGIN_SRC emacs-lisp

 (cl-macrolet ((local-macro (&rest args)
                 `(expansion of ,args)))
   (local-macro (do-something)))

+END_SRC

-- or by a macro which expands into =cl-macrolet=, provided that its definition of macro is evaluated prior to calling =macrostep-expand=:

+BEGIN_SRC emacs-lisp

 (defmacro with-local-macro (&rest body)
   `(cl-macrolet ((local-macro (&rest args)
                    `(expansion of ,args)))
      ,@body))

 (with-local-macro
     (local-macro (do something (else)))

+END_SRC

See the =with-js= macro in Emacs's =js.el= for a real example of the latter kind of macro.

Expansion of locally-bound macros is implemented by instrumenting Emacs Lisp's macro-expander to capture the environment at point. A similar trick is used to detect macro- and compiler-macro calls within expanded text so that they can be fontified accurately.

** Expanding sub-forms By moving point around in the macro expansion using =macrostep-next-macro= and =macrostep-prev-macro= (bound to the =n= and =p= keys), it is possible to expand other macro calls within the expansion before expanding the outermost form. This can sometimes be useful, although it does not correspond to the real order of macro expansion in Emacs Lisp, which proceeds by fully expanding the outer form to a non-macro form before expanding sub-forms.

The main reason to expand sub-forms out of order is to help with debugging macros which programmatically expand their arguments in order to rewrite them. Expanding the arguments of such a macro lets you visualise what the macro definition would compute via =macroexpand-all=.

** Extending macrostep for other languages Since version 0.9, it is possible to extend macrostep to work with other languages besides Emacs Lisp. In typical Emacs fashion, this is implemented by setting buffer-local variables to different function values. Six buffer-local variables define the language-specific part of the implementation:

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

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

** Acknowledgements Thanks to:

** Changelog

Please note that as this package will be part of Emacs soon, non-trivial code contributions require FSF copyright assignment prior to acceptance.

The copyright assignment policy is at https://www.fsf.org/licensing/contributor-faq

Any legally significant contributions can only be merged after the author has completed their paperwork. https://www.gnu.org/prep/maintain/html_node/Legally-Significant.html#Legally-Significant

+OPTIONS: author:nil email:nil toc:nil timestamp:nil