For some work I am doing I wanted to customize the behavior of the repl so that some forms wouldn't be evaluated, but instead handled in a different way. Below is a proposed hook for doing so. Unless someone can think of why this is a bad idea, or finds a bug, I'll update the cvs sources. This would be a change to swank.lisp.
It also fixes a behavior I found objectionable, namely that when a form caused an error, and you aborted, *, **, *** and friends were changed.
(in-package :swank)
(defvar *slime-repl-advance-history* nil "In the dynamic scope of a single form typed at the repl, set to nil to prevent the repl from advancing the history - * ** *** etc.")
(defvar *slime-repl-suppress-output* nil "In the dynamic scope of a single form typed at the repl, set to nil to prevent the repl from printing the result of the evalation.")
(defvar *slime-repl-eval-hook-pass* (gensym "PASS") "Token to indicate that a repl hook declines to evaluate the form")
(defvar *slime-repl-eval-hooks* nil "A list of functions. When the repl is about to eval a form, first try running each of these hooks. The first hook which returns a value which is not *slime-repl-eval-hook-pass* is considered a replacement for calling eval. If there are no hooks, or all pass, then eval is used.")
(defslimefun listener-eval (string) (clear-user-input) (with-buffer-syntax () (let ((*slime-repl-suppress-output* :unset) (*slime-repl-advance-history* :unset)) (multiple-value-bind (values last-form) (eval-region string t) (unless (or (and (eq values nil) (eq last-form nil)) (eq *slime-repl-advance-history* nil)) (setq *** ** ** * * (car values) /// // // / / values)) (setq +++ ++ ++ + + last-form) (if (eq *slime-repl-suppress-output* t) "" (cond ((null values) "; No value") (t (format nil "~{~S~^~%~}" values))))))))
(defun eval-region (string &optional package-update-p) "Evaluate STRING and return the result. If PACKAGE-UPDATE-P is non-nil, and evaluation causes a package change, then send Emacs an update." (unwind-protect (with-input-from-string (stream string) (let (- values) (loop (let ((form (read stream nil stream))) (when (eq form stream) (fresh-line) (force-output) (return (values values -))) (setq - form) (if *slime-repl-eval-hooks* (loop for hook in *slime-repl-eval-hooks* for res = (multiple-value-list (funcall hook form)) until (not (eq (car res) *slime-repl-eval-hook-pass*)) finally (when (eq (car res) *slime-repl-eval-hook-pass*) (setq values (multiple-value-list (eval form))))) (setq values (multiple-value-list (eval form)))) (force-output))))) (when (and package-update-p (not (eq *package* *buffer-package*))) (send-to-emacs (list :new-package (package-name *package*) (package-string-for-prompt *package*))))))
;; example hook - if you type (setq foo *) then nothing is printed, and ;; the history is not advanced.
(defun setq-no-print-repl-hook(form) (if (and (listp form) (listp (cdr form)) (eq (car form) 'setq) (eq (third form) '*)) (progn (setq *slime-repl-suppress-output* t) (setq *slime-repl-advance-history* nil) (eval form)) *slime-repl-eval-hook-pass*))