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
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-dwimprimarily 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-nomarkfunction that I use in
slime-send-dwimis 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