(macros)
Customization

Anvil REPL

Anvil is an interactive Steel REPL — the SLIME-style way to develop Steel. It runs your code in a second, fully independent Steel interpreter, separate from the editor's own config/scripting engine. Forms you evaluate in Anvil live in their own image: experiments never leak into the running editor, the image can be restarted from scratch, and a runaway loop can be interrupted instead of freezing the editor.

The name: you hammer Steel into shape on an anvil.

Anvil vs. eval-buffer

Macros already has in-editor evaluation: M-: (eval-expression), C-c C-e (eval-last-sexp), and C-c C-b (eval-buffer). Those run against the editor's own engine — that's exactly what you want for live-editing your config, because a define immediately changes the running editor.

Anvil is the opposite trade-off, for when you want a clean room:

In-editor eval Anvil REPL
Engine The editor's config engine A separate, dedicated interpreter
State Shared with the live editor Isolated; restartable
Runaway loop Wedges editor scripting Interruptible (C-c C-c)
Editor primitives Available (insert, define-key, …) Not registered — pure values
Printed output Goes to the editor Captured and shown with the result
Introspection Static (LSP, compile-only) Live image (apropos, doc, macroexpand)

Use in-editor eval to hack on your config; use Anvil to test pure Steel code in isolation without risking the running editor.

Opening the REPL

M-x anvil-repl opens the *anvil-repl* buffer. Type a form on a line and press RET to evaluate it; the result is appended below as a ;; => … comment line.

(+ 1 2)
;; => 3
(map (lambda (x) (* x x)) '(1 2 3 4))
;; => (1 4 9 16)

From a Steel source buffer you can pop straight to the REPL with C-c C-z.

Interrupting a runaway evaluation

If you evaluate something that never returns — an infinite loop or unbounded recursion — press C-c C-c to stop it. The evaluation unwinds with an error and the REPL stays alive for the next form:

(define (spin) (spin))
(spin)            ; ... hangs ... press C-c C-c
;; error: Interrupted by user

This works for any Steel loop, including raw tail recursion like the above — Steel's VM checks an interrupt flag on every instruction, so there's nowhere for a loop to hide. (The one thing it can't interrupt is code blocked inside a native call, but Anvil registers no such primitives.)

Restarting the image

C-c C-k (anvil-repl-reset) throws away the interpreter and builds a fresh one — every define you made is gone, as if you'd just started Anvil. Handy when the image has accumulated state you want to clear, or to confirm code works from a clean slate.

Evaluating from a source buffer

While editing a .scm file (steel or macros-steel mode), you can send code into the Anvil image and see the result echoed in the minibuffer, without leaving your buffer:

  • C-c C-nanvil-eval-region: evaluate the active region.
  • C-M-xanvil-eval-defun: evaluate the top-level form at point (no need to select it).
  • C-c C-lanvil-load-file: (load …) the file you're visiting into the image — the SLIME "compile/load file" feel. Because it loads by path, relative load/require inside the file resolve against the file's directory.
  • M-x anvil-eval-buffer — evaluate the whole buffer's text (works even on an unsaved buffer).
;; Point anywhere inside this form, then C-M-x:
(define (square x) (* x x))
;; minibuffer: anvil => #<function:square>

Captured output

(display …), (displayln …), and friends are captured and shown with the result, instead of vanishing to the process's stdout. So print-debugging works as you'd expect:

(begin (displayln "computing…") (+ 2 3))
;; anvil:
;; computing…
;; 5

Each evaluation starts with a fresh output buffer, so you only ever see this form's output.

Introspecting the live image

These commands answer against the actual running image — its real bindings, docstrings, and macros — not a static analysis of your source. (That distinction matters: the LSP completion/diagnostics you get while editing come from a separate compile-only engine; these come from Anvil itself.)

  • C-c C-danvil-doc: show the docstring for the symbol at point.
  • C-c C-xanvil-macroexpand: macroexpand the region (or the form at point) and show the expansion. Uses the un-optimized expansion, so you see the real macro shape — (when #t 1) reveals its if, rather than being folded to 1.
  • C-c C-aanvil-apropos: prompt for a pattern and list every live binding whose name contains it. Because it queries the image, anything you've just defined shows up immediately.

Results that don't fit the minibuffer (doc, macroexpand, apropos) are rendered into an *anvil* buffer.

(define my-helper 42)
;; C-c C-a, type "my-hel" → *anvil* lists:  my-helper

Completion

Inside the *anvil-repl* buffer, M-i (completion-at-point) completes the symbol at point against the live image — not a static analysis. So it offers every built-in and everything you've defined this session, the moment you define it:

(define (frobnicate x) (* x 2))
;; later, type  (frob  then M-i  →  frobnicate

This is the SLIME model: completion asks the running interpreter, the same way anvil-apropos does, rather than the editor's compile-only LSP. Completion is symbol-aware, so hyphenated/punctuated names like string->number and set! complete as one token. The popup narrows as you keep typing.

Not here: an interactive debugger with restarts. Steel unwinds on error rather than offering a condition/restart system, so a runaway or failing form surfaces as an error string (and stays interruptible), not a debugger stack.

Command & key reference

Key Command Action
M-x anvil-repl anvil-repl Open / switch to the *anvil-repl* buffer
RET (in REPL) anvil-send-input Evaluate the current line
C-c C-c (in REPL) anvil-repl-interrupt Interrupt the current evaluation
C-c C-k (in REPL) anvil-repl-reset Restart the image (fresh interpreter)
M-i (in REPL) completion-at-point Complete the symbol at point against the live image
C-c C-z (in steel) anvil-repl Pop to the REPL from a source buffer
C-c C-n (in steel) anvil-eval-region Evaluate the region in the Anvil image
C-M-x (in steel) anvil-eval-defun Evaluate the top-level form at point
C-c C-l (in steel) anvil-load-file (load …) the visited file into the image
C-c C-d (in steel) anvil-doc Docstring for the symbol at point
C-c C-x (in steel) anvil-macroexpand Macroexpand the region / form at point
C-c C-a (in steel) anvil-apropos List live image bindings matching a pattern
M-x anvil-eval-buffer anvil-eval-buffer Evaluate the whole buffer in the Anvil image

The source-buffer commands are bound in both the steel and macros-steel modes. All of these are ordinary Steel commands defined in the bundled runtime — see Scripting with Steel to rebind them or build on anvil-eval.