SLIME Tips and Techniques - Part 6 (Send SEXPs to REPL)
Tuesday, April 24, 2007
I sometimes do
lispvan (or other) presentations where I want to single-step
through code
that is in a
lisp file while discussing the code with the
audience. Basically, I have the lisp code in one window while, in a
second window, I evaluate
the code and look at the output in a SLIME REPL. A typical
presentation setup would
be something similar to this:

I have one window open on the left-hand side with the source code in
it. In the right-hand window, I have a SLIME REPL. I move the cursor
through the source code (in the left window) and press a function key to copy a specific
s-expression to the SLIME REPL and evaluate it. This is more effective than
just pressing "C-x C-e" as it isn't always apparent to the audience
where the cursor is at in the source window. By copying the code over,
there is the visual reinforcement as to what is happening and what
code is being evaluated. In the
past, I've used a simple keyboard macro to do this; however, it wasn't
really flexible enough. So, I wrote an elisp function to do the
job:
(defun slime-send-dwim (arg)
"Send the appropriate forms to CL to be evaluated."
(interactive "P")
(save-excursion
(cond
;;Region selected - evaluate region
((not (equal mark-active nil))
(copy-region-as-kill-nomark (mark) (point)))
;; At/before sexp - evaluate next sexp
((or (looking-at "\s(")
(save-excursion
(ignore-errors (forward-char 1))
(looking-at "\s(")))
(forward-list 1)
(let ((end (point))
(beg (save-excursion
(backward-list 1)
(point))))
(copy-region-as-kill-nomark beg end)))
;; At/after sexp - evaluate last sexp
((or (looking-at "\s)")
(save-excursion
(backward-char 1)
(looking-at "\s)")))
(if (looking-at "\s)")
(forward-char 1))
(let ((end (point))
(beg (save-excursion
(backward-list 1)
(point))))
(copy-region-as-kill-nomark beg end)))
;; Default - evaluate enclosing top-level sexp
(t (progn
(while (ignore-errors (progn
(backward-up-list)
t)))
(forward-list 1)
(let ((end (point))
(beg (save-excursion
(backward-list 1)
(point))))
(copy-region-as-kill-nomark beg end)))))
(set-buffer (slime-output-buffer))
(unless (eq (current-buffer) (window-buffer))
(pop-to-buffer (current-buffer) t))
(goto-char (point-max))
(yank)
(if arg (progn
(slime-repl-return)
(other-window 1)))))
The function actually does a few things:- If a region has been selected, the region will be copied to the SLIME REPL.
- If the point is either at an open paren or before an open paren, the following s-expression will be copied to the SLIME REPL.
- If the point is either at a close paren or after a close paren, the preceeding s-expression will be copied to the SLIME REPL.
- If none of the above conditions has been met, the top-level s-expression enclosing the point will be copied to the SLIME REPL.
(define-key lisp-mode-map [f7] 'slime-send-dwim) (define-key lisp-mode-map [f8] '(lambda () (interactive) (slime-send-dwim 1)))Although I use
slime-send-dwim primarily for
presentations, some people may also find it useful for just sending
forms to the REPL (which has the added advantage over using normal
SLIME eval/compile commands in that it will update the history
and */**/*** values in the REPL).Update-2007-04-28: Vilson Vieira da Silva pointed out to me in an email that the
copy-region-as-kill-nomark function
that I use in slime-send-dwim is in the pc-select package
and that a "(require 'pc-select)" is needed. Alternatively, if you
don't want to use pc-select, you can just
substitute copy-region-as-kill for copy-region-as-kill-nomark.

