diff --git a/CHANGELOG.md b/CHANGELOG.md index cd313eb8..60f96a40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## master (unreleased) +* [#933](https://github.com/clojure-emacs/cider-nrepl/pull/933): Add the `cider-inspector-print-current-value` command to print the current value of the inspector. * Bump `orchard` to [0.34.1](https://github.com/clojure-emacs/orchard/blob/master/CHANGELOG.md#0341-2025-04-23). * [#935](https://github.com/clojure-emacs/cider-nrepl/pull/935): Unify injected print-method implementations with orchard.print. diff --git a/doc/modules/ROOT/pages/nrepl-api/ops.adoc b/doc/modules/ROOT/pages/nrepl-api/ops.adoc index 67183fee..011ace11 100644 --- a/doc/modules/ROOT/pages/nrepl-api/ops.adoc +++ b/doc/modules/ROOT/pages/nrepl-api/ops.adoc @@ -576,6 +576,30 @@ Returns:: +=== `inspect-print-current-value` + +Print the current value of the inspector. + +Required parameters:: +* `:session` The current session + + +Optional parameters:: +* `:nrepl.middleware.print/buffer-size` The size of the buffer to use when streaming results. Defaults to 1024. +* `:nrepl.middleware.print/keys` A seq of the keys in the response whose values should be printed. +* `:nrepl.middleware.print/options` A map of options to pass to the printing function. Defaults to ``nil``. +* `:nrepl.middleware.print/print` A fully-qualified symbol naming a var whose function to use for printing. Must point to a function with signature [value writer options]. +* `:nrepl.middleware.print/quota` A hard limit on the number of bytes printed for each value. +* `:nrepl.middleware.print/stream?` If logical true, the result of printing each value will be streamed to the client over one or more messages. + + +Returns:: +* `:path` Printed representation of current inspector path. +* `:status` "done" +* `:value` The inspector result. Contains a specially-formatted string that can be ``read`` and then rendered client-side. + + + === `inspect-push` Inspects the inside value specified by index. diff --git a/src/cider/nrepl.clj b/src/cider/nrepl.clj index 9cfe87f6..a5de15fc 100644 --- a/src/cider/nrepl.clj +++ b/src/cider/nrepl.clj @@ -313,7 +313,12 @@ Note: Documentation may be incomplete; not all return keys are described." inspector's state in the `:value` slot." :requires #{"clone" #'wrap-caught #'wrap-print} :expects #{"eval"} - :handles {"inspect-pop" + :handles {"inspect-print-current-value" + {:doc "Print the current value of the inspector." + :requires {"session" "The current session"} + :optional wrap-print-optional-arguments + :returns inspector-returns} + "inspect-pop" {:doc "Moves one level up in the inspector stack." :requires {"session" "The current session"} :returns inspector-returns} diff --git a/src/cider/nrepl/middleware/inspect.clj b/src/cider/nrepl/middleware/inspect.clj index 25e560cf..fee99384 100644 --- a/src/cider/nrepl/middleware/inspect.clj +++ b/src/cider/nrepl/middleware/inspect.clj @@ -1,8 +1,11 @@ (ns cider.nrepl.middleware.inspect (:require + [cider.nrepl.middleware.util :refer [respond-to]] [cider.nrepl.middleware.util.cljs :as cljs] [cider.nrepl.middleware.util.error-handling :refer [eval-interceptor-transport with-safe-transport]] + [cider.nrepl.pprint :as pprint] + [nrepl.middleware.print :as print] [nrepl.misc :refer [response-for]] [nrepl.transport :as transport] [orchard.inspect :as inspect])) @@ -107,6 +110,15 @@ (defn def-current-value [msg] (inspector-response msg (swap-inspector! msg inspect/def-current-value (symbol (:ns msg)) (:var-name msg)))) +(defn print-current-value-reply [{:keys [::print/print-fn session] :as msg}] + (let [inspector (-> session meta ::inspector)] + (with-open [writer (print/replying-PrintWriter :value msg msg)] + (binding [*print-length* (or *print-length* 100) + *print-level* (or *print-level* 20)] + ((or print-fn pprint/pprint) (:value inspector) writer) + (.flush writer))) + (respond-to msg :status :done))) + (defn tap-current-value [msg] (inspector-response msg (swap-inspector! msg inspect/tap-current-value))) @@ -114,9 +126,17 @@ (inspector-response msg (swap-inspector! msg inspect/tap-indexed (:idx msg)))) (defn handle-inspect [handler {:keys [op inspect] :as msg}] - (if (and (= op "eval") inspect) + (cond + (and (= op "eval") inspect) (handle-eval-inspect handler msg) + ;; This is outside of `with-safe-transport` because it streams the pretty + ;; printed result to the client in multiple messages. It does NOT return any + ;; value, which is expected by `with-safe-transport`. + (= op "inspect-print-current-value") + (print-current-value-reply msg) + + :else (with-safe-transport handler msg "inspect-pop" pop-reply "inspect-push" push-reply diff --git a/test/clj/cider/nrepl/middleware/inspect_test.clj b/test/clj/cider/nrepl/middleware/inspect_test.clj index 943cb1d3..a9a9b5ab 100644 --- a/test/clj/cider/nrepl/middleware/inspect_test.clj +++ b/test/clj/cider/nrepl/middleware/inspect_test.clj @@ -745,6 +745,57 @@ :inspect "true" :code "(range 100)"})))))))) +(deftest inspect-print-current-value-test + (testing "inspect-print-current-value returns the currently inspected value as a printed string" + (is (= [(str/join "\n" ["{:a -1," + " :bb \"111\"," + " :ccc (1)," + " :d" + " ({:a 0, :bb \"000\", :ccc ()}" + " {:a -1, :bb \"111\", :ccc (1)}" + " {:a -2, :bb \"222\", :ccc (2 1)}" + " {:a -3, :bb \"333\", :ccc (3 2 1)}" + " {:a -4, :bb \"444\", :ccc (4 3 2 1)})}"])] + (:value (do + (session/message {:op "eval" + :code "(def test-val + (for [i (range 2)] + {:a (- i) + :bb (str i i i) + :ccc (range i 0 -1) + :d (for [i (range 5)] + {:a (- i) + :bb (str i i i) + :ccc (range i 0 -1)})}))"}) + (session/message {:op "eval" + :inspect "true" + :code "test-val"}) + (session/message {:op "inspect-push" + :idx 2}) + (session/message {:op "inspect-print-current-value" + :nrepl.middleware.print/print "cider.nrepl.pprint/pprint"}))))))) + +(deftest inspect-print-current-value-no-value-test + (testing "inspect-print-current-value returns nil if nothing has been inspected yet" + (is (= ["nil"] (:value (session/message + {:op "inspect-print-current-value" + :nrepl.middleware.print/print "cider.nrepl.pprint/pprint"})))))) + +(deftest inspect-print-current-value-default-print-fn-test + (testing "inspect-print-current-value uses a default print fn when none is provided" + (is (= ["nil"] (:value (session/message {:op "inspect-print-current-value"})))))) + +(deftest inspect-print-current-value-infinite-seq-test + (testing "inspect-print-current-value works with infinite-seqs" + (is (str/starts-with? (first (:value (do (session/message {:op "eval" + :code "(def test-val (repeat :x))"}) + (session/message {:op "eval" + :inspect "true" + :code "test-val"}) + (session/message {:op "inspect-print-current-value" + :nrepl.middleware.print/print "cider.nrepl.pprint/pprint"})))) + "(:x")))) + (deftest inspect-def-current-value-test (testing "inspect-def-current-value defines a var with the current inspector value" (is (= "{3 4}"