# HG changeset patch # User Steve Losh # Date 1460238342 0 # Node ID 7d0cd39ee703d029e54bd1f9267240e6f06757ae # Parent 3e9db801d234da2b706d4b7872f4d402200ed22a Don't shit the bed when `load`ing a file with errors Refactored the evaluator a bit so the file loading middleware can just use the same evaluation machinery and get all the error handling and stream shuttling for free. diff -r 3e9db801d234 -r 7d0cd39ee703 nrepl.asd --- a/nrepl.asd Sat Apr 09 21:13:26 2016 +0000 +++ b/nrepl.asd Sat Apr 09 21:45:42 2016 +0000 @@ -28,9 +28,9 @@ :components ((:file "core") (:file "describe") (:file "documentation") - (:file "load-file") (:file "macroexpand") (:file "eval") + (:file "load-file") (:file "session"))) (:file "server"))))) diff -r 3e9db801d234 -r 7d0cd39ee703 src/middleware/eval.lisp --- a/src/middleware/eval.lisp Sat Apr 09 21:13:26 2016 +0000 +++ b/src/middleware/eval.lisp Sat Apr 09 21:45:42 2016 +0000 @@ -14,26 +14,31 @@ (do ((data "" (flex:octets-to-string (flex:get-output-stream-sequence from-stream) :external-format :utf-8))) - ((and (not (open-stream-p from-stream)) - (equal data "")) - nil) + ((and (not (open-stream-p from-stream)) + (equal data "")) + nil) (when (not (equal data "")) (respond message (make-map "status" '("ok") stream-name data))) (sleep 0.1))) (defun get-forms (code) - "Get all lisp forms from the given hunk of code. + "Get all lisp forms from `code`. - Signal an evaluation-error if the input is mangled. + If `code` is a string, the forms will be read out of it, and an + `evaluation-error` signaled if the input is mangled. + + If `code` is anything else it will just be returned as-is. - " - (handler-case - (read-all-from-string code) - (error (e) - (error 'evaluation-error - :text "Malformed input!" - :orig e)))) + " + (if (stringp code) + (handler-case + (read-all-from-string code) + (error (e) + (error 'evaluation-error + :text "Malformed input!" + :orig e))) + code)) (defun clean-backtrace (backtrace) (format nil "~{~A~^~%~}" @@ -64,9 +69,17 @@ #-sbcl "dunno")))))) (eval form)))) -(define-middleware wrap-eval "eval" message - (let* ((code (fset:lookup message "code")) - (captured-out (flex:make-in-memory-output-stream)) +(defun evaluate-forms (message forms) + "Evaluate each form in `forms` and shuttle back the responses. + + `forms` can be a string, in which case the forms will be read out of it, or + a ready-to-go list for actual forms. + + Other middlewares (e.g. `load-file`) can use this function to evaluate things + and send the results back to the user. + + " + (let* ((captured-out (flex:make-in-memory-output-stream)) (captured-err (flex:make-in-memory-output-stream)) (*standard-output* (flex:make-flexi-stream captured-out :external-format :utf-8)) @@ -89,14 +102,17 @@ (lambda () (shuttle-stream stream desc message)) :name (format nil "NREPL ~A writer" desc)))) (unwind-protect - (progn - (make-shuttle-thread captured-out "stdout") - (make-shuttle-thread captured-err "stderr") - (handler-case - (progn - (loop for form in (get-forms code) do (eval-form form)) - (respond message (make-map "status" '("done")))) - (evaluation-error (e) (error-respond e)))) + (progn + (make-shuttle-thread captured-out "stdout") + (make-shuttle-thread captured-err "stderr") + (handler-case + (progn + (loop :for form :in (get-forms forms) :do (eval-form form)) + (respond message (make-map "status" '("done")))) + (evaluation-error (e) (error-respond e)))) (close captured-out) (close captured-err))))) +(define-middleware wrap-eval "eval" message + (evaluate-forms message (fset:lookup message "code"))) + diff -r 3e9db801d234 -r 7d0cd39ee703 src/middleware/load-file.lisp --- a/src/middleware/load-file.lisp Sat Apr 09 21:13:26 2016 +0000 +++ b/src/middleware/load-file.lisp Sat Apr 09 21:45:42 2016 +0000 @@ -2,6 +2,4 @@ (define-middleware wrap-load-file "load-file" message (let ((path (fset:lookup message "path"))) - (load path) - (respond message (make-map "status" '("done") - "value" "T")))) + (evaluate-forms message (list `(load ,path)))))