Macroexpansion

Typing C-c C-m after some form in a source buffer or the REPL will show you the macroexpand-1 expansion of the form in a new buffer.

C-c M-m is a prefix map (cider-macroexpand-map) gathering all of CIDER’s form-expanding commands:

Keyboard shortcut Description

C-c M-m 1

macroexpand-1 into a macroexpansion buffer (the same as C-c C-m).

C-c M-m a

macroexpand-all into a macroexpansion buffer.

C-c M-m e

Expand the macro at point one step, inline in the source buffer (see inline macro stepping below).

C-c M-m E

Fully expand the macro at point, inline in the source buffer.

C-c M-m b

Run an inline-style stepping session in a dedicated popup buffer.

Prefer C-c C-m as the prefix instead? Point it at the same map:

(define-key cider-mode-map (kbd "C-c C-m") 'cider-macroexpand-map)

You’ll then reach plain macroexpand-1 at C-c C-m 1.

When you expand into a buffer you’ll have access to additional keybindings there (the buffer uses cider-macroexpansion-mode):

Keyboard shortcut Description

m

Invoke macroexpand-1 on the form before point and replace the original form with its expansion. If invoked with a prefix argument, macroexpand is used instead of macroexpand-1.

a

Invoke clojure.walk/macroexpand-all on the form before point and replace the original form with its expansion.

g

Re-expand the original form. This picks up the latest definition of the macro as well as any change to the display options below.

n

Cycle cider-macroexpansion-display-namespaces between tidy, qualified and none, then re-expand.

t

Toggle cider-macroexpansion-print-metadata, then re-expand.

C-/
u

Undo the last in-place expansion performed in the macroexpansion buffer.

.

Jump to the definition of the symbol at point.

d

Display documentation for the symbol at point.

q

Quit the macroexpansion buffer.

Macroexpansion resolves the macro in the running REPL, so its namespace has to be loaded. If it isn’t (or the form’s head isn’t actually a macro), CIDER tells you why - pointing you at C-c C-k when the namespace simply hasn’t been evaluated yet - instead of silently returning the form unchanged.

Expansion levels

CIDER exposes three expanders, which map one-to-one onto the underlying Clojure functions. They differ in how deep they go:

  • macroexpand-1 expands the form one step, and only when its head is a macro. It doesn’t re-expand the result or descend into sub-forms. This is what C-c C-m uses.

  • macroexpand keeps applying macroexpand-1 to the head position until the head is no longer a macro. It still leaves nested macro calls in argument positions untouched. Reach for it via the prefixed C-u C-c C-m (or m with a prefix in the expansion buffer) - there’s no separate binding.

  • macroexpand-all (really clojure.walk/macroexpand-all) walks the whole form recursively, expanding every macro it finds, nested ones included. This is what C-c M-m uses.

macroexpand-1 is literally a single step, so it can leave a macro behind - here cond expands one clause-pair at a time:

(macroexpand-1 '(cond a 1 :else 2))
;; => (if a 1 (clojure.core/cond :else 2))   ; note the leftover cond

macroexpand chases the head all the way down. Given a macro that expands into another macro, the difference from macroexpand-1 shows:

(defmacro unless [test & body]
  `(when (not ~test) ~@body))

(macroexpand-1 '(unless x a b))
;; => (clojure.core/when (clojure.core/not x) a b)   ; head is still `when', a macro

(macroexpand '(unless x a b))
;; => (if (clojure.core/not x) (do a b))             ; kept going: unless -> when -> if

But macroexpand only follows the head. A macro sitting in an argument position is left alone, whereas macroexpand-all reaches it:

(macroexpand '(when a (when b c)))
;; => (if a (do (when b c)))            ; inner when untouched

(clojure.walk/macroexpand-all '(when a (when b c)))
;; => (if a (do (if b (do c))))         ; inner when expanded too
clojure.walk/macroexpand-all is a plain code walk - it doesn’t track local bindings or fully honor quote, so it can occasionally expand something the real compiler wouldn’t. It’s an excellent inspection tool, not a semantics-preserving transformation.

Configuration

The option cider-macroexpansion-display-namespaces controls whether to display namespaces in the macroexpansion buffer. It can be set to one of the following:

  • qualified - Vars are fully-qualified in the expansion.

  • none- Vars are displayed without namespace qualification.

  • tidy(default) - Vars that are :refer-ed or defined in the current namespace are displayed with their simple name, non-referred vars from other namespaces are referred using the alias for that namespace (if defined), other vars are displayed fully qualified.

The option cider-macroexpansion-print-metadata controls whether to print the var metadata in the macroexpansion buffer. It’s set to nil by default.

Both options can also be overridden for a single expansion, without touching your configuration: the macroexpand menu that pops up when you pause after C-c M-m has a -n (--ns=) argument to pick the namespace-display style (tidy, qualified or none) and a -m (--meta) toggle for the metadata, honored by the popup expansion commands (1 and a) invoked from the menu.

Inline macroexpansion

As an alternative to the separate buffer, cider-macrostep-expand expands macros in place in the source buffer, in the spirit of the macrostep package. Place point right after a form (as with C-x C-e) and run M-x cider-macrostep-expand: the form is replaced by its one-step expansion and you enter a transient, read-only cider-macrostep-mode with these keybindings:

Keyboard shortcut Description

e
=
RET

Expand the form before point one step further. Inside an existing expansion, put point after a nested form to step into it.

a

Fully expand the form before point in one step (macroexpand-all), instead of stepping level by level.

c
u
DEL

Collapse the innermost expansion at point, restoring the original form.

n
p

Move to the next/previous further-expandable sub-form.

q

Collapse every expansion and leave cider-macrostep-mode.

Each expansion remembers the exact text it replaced, so collapsing restores the original verbatim and nested expansions peel back in order. The operators of sub-forms that can be expanded further (those that resolve to a macro) are underlined so you can see what’s left to expand, and n/p jump between them. The gensyms a macro introduces (e.g. x42auto__) are each given their own color, so a binding can be tracked through the expansion.

The expandable-head highlighting and gensym coloring rely on the cider/classify-symbols nREPL op, available in cider-nrepl 0.60 and newer. Without it the stepping still works, just without those visual aids.

If you’d rather not touch the source buffer, cider-macrostep-expand-in-buffer runs the same stepping session in a dedicated *cider-macrostep* popup: it copies the form before point there and you step through it exactly as above, with the source left untouched. In the popup q dismisses the whole buffer in one step.

Configuration

  • cider-macroexpansion-display-namespaces - how namespaces are displayed in the expansion (tidy, qualified or none); shared with the separate-buffer flow.

  • cider-macroexpansion-highlight-expansion - briefly pulse a freshly inserted expansion (on by default); shared with the separate-buffer flow.

  • cider-macrostep-highlight-expandable - underline the operators of further-expandable sub-forms (on by default).

  • cider-macrostep-color-gensyms - colorize the gensyms introduced by an expansion (on by default).

  • cider-macrostep-gensym-faces - the face palette cycled through when coloring gensyms (theme-aware, inheriting from font-lock faces).