diff --git a/slime.el b/slime.el index 5a33f2192..f5d5ee3a3 100644 --- a/slime.el +++ b/slime.el @@ -4058,22 +4058,39 @@ inserted in the current buffer." (run-hooks 'slime-transcript-stop-hook) (message "Evaluation aborted on %s." condition)))) -(defun slime-eval-print (string) - "Eval STRING in Lisp; insert any output and the result at point." - (slime-eval-async `(swank:eval-and-grab-output ,string) - (lambda (result) - (cl-destructuring-bind (output value) result - (push-mark) - (insert output value))))) - -(defun slime-eval-save (string) - "Evaluate STRING in Lisp and save the result in the kill ring." - (slime-eval-async `(swank:eval-and-grab-output ,string) - (lambda (result) - (cl-destructuring-bind (output value) result - (let ((string (concat output value))) - (kill-new string) - (message "Evaluation finished; pushed result to kill ring.")))))) +(defconst slime-output-targets + '(common-lisp:values + common-lisp:*standard-output* + common-lisp:*trace-output* + common-lisp:*error-output*) + "Possible keys in the alist returned by Common Lisp function `swank:eval-and-grab-output'.") + +(cl-defun slime-eval-print + (string &optional (targets-to-print '(common-lisp:*standard-output* + common-lisp:values))) + "Eval STRING in Lisp; insert standard output and the result at point. + +Contents to be inserted may be specified via optional parameter TARGETS-TO-PRINT which should be a (multi)subset of list `slime-output-targets'." + (cl-assert (null (cl-set-difference targets-to-print slime-output-targets))) + (slime-eval-async `(swank:eval-and-grab-output ,string ',targets-to-print) + (lambda (results-alist) + (push-mark) + (dolist (target targets-to-print) + (insert (cdr (assoc target results-alist))))))) + +(cl-defun slime-eval-save + (string &optional (targets-to-save '(common-lisp:*standard-output* + common-lisp:values))) + "Evaluate STRING in Lisp and save the result in the kill ring. + +Contents to be saved may be specified via optional parameter `targets-to-save' which should be a (multi)subset of list `slime-output-targets'." + (cl-assert (null (cl-set-difference targets-to-save slime-output-targets))) + (slime-eval-async `(swank:eval-and-grab-output ,string ',targets-to-save) + (lambda (results-alist) + (kill-new + (mapconcat (lambda (target) (cdr (assoc target results-alist))) + targets-to-save "")) + (message "Evaluation finished; pushed result to kill ring.")))) (defun slime-eval-describe (form) "Evaluate FORM in Lisp and display the result in a new buffer." diff --git a/swank.lisp b/swank.lisp index 266afbb93..696043cac 100644 --- a/swank.lisp +++ b/swank.lisp @@ -1761,14 +1761,52 @@ Errors are trapped and invoke our debugger." (finish-output) (format-values-for-echo-area values))))) -(defslimefun eval-and-grab-output (string) +(defslimefun eval-and-grab-output + (string &optional (targets-to-capture '(*standard-output* values) + targets-provided-p)) + "Evaluate contents of STRING, return alist of results including various output streams. Possible keys in the returned alist should be listed in the value of `slime-output-targets' variable in `slime.el'." (with-buffer-syntax () (with-retry-restart (:msg "Retry SLIME evaluation request.") - (let* ((s (make-string-output-stream)) - (*standard-output* s) - (values (multiple-value-list (eval (from-string string))))) - (list (get-output-stream-string s) - (format nil "~{~S~^~%~}" values)))))) + (macrolet ((maybe-value-string (form) + `(if (member 'values targets-to-capture) + ,form + "")) + (maybe-output-stream-string (stream-symbol) + `(if (member ',stream-symbol targets-to-capture) + (get-output-stream-string ,stream-symbol) + "")) + (maybe-make-string-output-stream (stream-symbol) + `(if (member ',stream-symbol targets-to-capture) + (make-string-output-stream) + ,stream-symbol))) + (let* ((*trace-output* + (maybe-make-string-output-stream *trace-output*)) + (*error-output* + (maybe-make-string-output-stream *error-output*)) + (*standard-output* + (maybe-make-string-output-stream *standard-output*)) + (values (multiple-value-list (eval (from-string string))))) + (if targets-provided-p + ;; It is not clear what would be the most natural order here. + ;; We picked the reversed binding order. + (list + (cons 'values + (maybe-value-string (format nil "~{~S~^~%~}" values))) + (cons '*standard-output* + (maybe-output-stream-string *standard-output*)) + (cons '*error-output* + (maybe-output-stream-string *error-output*)) + (cons '*trace-output* + (maybe-output-stream-string *trace-output*))) + ;; targets are not provided by callers + ;; who presume older interface (slime 2.26 or earlier) + ;; to eval-and-grab-output + ;; This check (and targets-provided-p argument itself) + ;; - can be dropped when Emacs 27 becomes unsupported + ;; - can very likely be dropped when Org 9.4 becomes unsupported + (list (maybe-output-stream-string *standard-output*) + (maybe-value-string + (format nil "~{~S~^~%~}" values))))))))) (defun eval-region (string) "Evaluate STRING.