Skip to content

Commit

Permalink
Support for grabbing multiple outputs in swank:eval-and-grab-output
Browse files Browse the repository at this point in the history
It is often desirable to get output from time and trace in Emacs,
particularly in org-babel.  This patch extends
swank:eval-and-grab-output to support emitting *trace-output*; it also
introduces *error-output*, for good measure.

We check whether eval-and-grab-output caller presumes older interface
to it and return a legacy list of two elements instead of alist in
those cases.

This check can very likely be dropped when either Emacs 27 or Org 9.4
become unsupported.
  • Loading branch information
akater committed Oct 4, 2021
1 parent b6d5b82 commit ead2e2e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 22 deletions.
49 changes: 33 additions & 16 deletions slime.el
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
50 changes: 44 additions & 6 deletions swank.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit ead2e2e

Please sign in to comment.