Skip to content

Commit ca08ea4

Browse files
committed
Simplify the pprint logic
After working on the new pprint implementation for evaluation I realized that the pprint picture in cider-nrepl was much more complex and intertwined than I realized. In particular: * there was a second pprint middleware that was simply resolving `:pprint-fn` keys in requests to vars and updating the message map * the same pprint logic, that was used for evaluation, was also used for formatting edn, printing stacktraces, etc. * there was a lot of reliance on the dynamic vars defined by clojure.pprint I've opted to cut a lot of the old code and to adopt the same approach which I implemented for evaluation. Most importantly I've removed all of the magic with dynamic vars and replaced it with passing a `:print-options` map to the pretty-printing functions.
1 parent 5929ddb commit ca08ea4

File tree

13 files changed

+43
-301
lines changed

13 files changed

+43
-301
lines changed

project.clj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
cider.nrepl/wrap-out
7171
cider.nrepl/wrap-content-type
7272
cider.nrepl/wrap-slurp
73-
cider.nrepl/wrap-pprint
7473
cider.nrepl/wrap-pprint-fn
7574
cider.nrepl/wrap-profile
7675
cider.nrepl/wrap-refresh

src/cider/nrepl.clj

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -104,27 +104,8 @@
104104
105105
Middlewares further down the stack can then look up the `:pprint-fn`
106106
slot and call it where necessary."
107-
:requires #{#'session}})
108-
109-
(def-wrapper wrap-pprint cider.nrepl.middleware.pprint/handle-pprint
110-
#{"eval" "load-file"}
111-
(cljs/expects-piggieback
112-
{:doc "Middleware that adds a pretty-printing option to the eval op.
113-
Passing a non-nil value in the `:pprint` slot will cause eval to call
114-
clojure.pprint/pprint on its result. The `:right-margin` slot can be
115-
used to bind `*clojure.pprint/*print-right-margin*` during the
116-
evaluation. (N.B., the encoding used to transmit the request map
117-
`msg` across the wire will convert presumably falsey values into
118-
truthy values. If you don't want something to be pretty printed,
119-
remove the `:pprint` key entirely from your request map, don't try
120-
and set the value to nil, false, or string representations of the
121-
above)."
122-
:requires #{"clone" #'pr-values #'wrap-pprint-fn}
123-
:expects #{"eval" "load-file"}
124-
:handles {"pprint-middleware"
125-
{:doc "Enhances the `eval` op by adding pretty-printing. Not an op in itself."
126-
:optional (merge wrap-pprint-fn-optional-arguments
127-
{"pprint" "If present and non-nil, pretty-print the result of evaluation."})}}}))
107+
:requires #{#'session}
108+
:expects #{"eval" "load-file"}})
128109

129110
(def-wrapper wrap-content-type cider.nrepl.middleware.content-type/handle-content-type
130111
#{"eval"}
@@ -522,7 +503,6 @@
522503
wrap-out
523504
wrap-content-type
524505
wrap-slurp
525-
wrap-pprint
526506
wrap-pprint-fn
527507
wrap-profile
528508
wrap-refresh

src/cider/nrepl/middleware/debug.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ this map (identified by a key), and will `dissoc` it afterwards."}
216216
(when-not (instance? ThreadDeath root-ex#)
217217
(debugger-send
218218
{:status :eval-error
219-
:causes [(let [causes# (stacktrace/analyze-causes e# (:pprint-fn *msg*))]
219+
:causes [(let [causes# (stacktrace/analyze-causes e# (:pprint-fn *msg*) (:print-options *msg*))]
220220
(when (coll? causes#) (last causes#)))]})))
221221
error#))]
222222
(if (= error# ~sym)

src/cider/nrepl/middleware/format.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@
4343
(recur (conj forms form)))))))
4444

4545
(defn- format-edn
46-
[edn pprint-fn]
46+
[edn pprint-fn print-options]
4747
(->> (read-edn edn)
48-
(map #(pprint-fn %))
49-
str/join
48+
(map #(pprint-fn % print-options))
49+
(str/join "\n")
5050
str/trim))
5151

5252
(defn format-edn-reply
53-
[{:keys [edn pprint-fn] :as msg}]
54-
{:formatted-edn (format-edn edn pprint-fn)})
53+
[{:keys [edn pprint-fn print-options] :as msg}]
54+
{:formatted-edn (format-edn edn pprint-fn print-options)})
5555

5656
;;; Middleware op handling
5757
(defn handle-format [handler msg]

src/cider/nrepl/middleware/pprint.clj

Lines changed: 7 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
(ns cider.nrepl.middleware.pprint
22
(:require
33
[cider.nrepl.middleware.util.cljs :as cljs]
4-
[clojure.pprint :refer [pprint *print-right-margin*]]
4+
cider.nrepl.pprint
5+
[clojure.walk :as walk]
56
[nrepl.middleware.interruptible-eval :refer [*msg*]]
67
[nrepl.middleware.pr-values :refer [pr-values]]
78
[nrepl.middleware.session :as session]
@@ -17,30 +18,6 @@
1718

1819
;; TODO: Remove this middleware (or make it a no-op).
1920

20-
;; fipp-printer and puget-printer are not loaded in the main `(ns)` form
21-
;; because they are optional features that take hundreds of milliseconds
22-
;; to load. Therefore, we delay the require/resolve process of the alternate
23-
;; pprint features until the user tries to use it.
24-
25-
(def ^:private fipp-printer
26-
(delay
27-
(do
28-
(require 'fipp.edn)
29-
(resolve 'fipp.edn/pprint))))
30-
31-
(defn fipp-pprint [object]
32-
(@fipp-printer object {:width (or *print-right-margin* 72)}))
33-
34-
(def ^:private puget-printer
35-
(delay
36-
(do
37-
(require 'puget.printer)
38-
(resolve 'puget.printer/pprint))))
39-
40-
(defn puget-pprint [object]
41-
(@puget-printer object {:width (or *print-right-margin* 72)
42-
:seq-limit *print-length*}))
43-
4421
(defn- resolve-pprint-fn
4522
[sym]
4623
(if-let [pp-fn (-> sym u/as-sym find-var)]
@@ -49,49 +26,9 @@
4926

5027
(defn handle-pprint-fn
5128
[handler msg]
52-
(let [{:keys [pprint-fn print-length print-level print-meta print-right-margin session]
53-
:or {pprint-fn 'clojure.pprint/pprint}}
29+
(let [{:keys [pprint-fn print-options session]
30+
:or {pprint-fn 'cider.nrepl.pprint/pprint}}
5431
msg]
55-
(handler (assoc msg :pprint-fn (fn [object]
56-
(binding [*print-length* (or print-length (get @session #'*print-length*))
57-
*print-level* (or print-level (get @session #'*print-level*))
58-
*print-meta* (or print-meta (get @session #'*print-meta*))
59-
;; pprint/*print-right-margin* is not bound by session middleware
60-
*print-right-margin* (or print-right-margin *print-right-margin*)]
61-
((resolve-pprint-fn pprint-fn) object)))))))
62-
63-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
64-
65-
(defn- pprint-writer
66-
[{:keys [session transport] :as msg}]
67-
(#'session/session-out :pprint-out (:id (meta session)) transport))
68-
69-
(defn pprint-reply
70-
[{:keys [pprint-fn session transport] :as msg} response]
71-
(with-open [writer (pprint-writer msg)]
72-
;; Binding `*msg*` sets the `:id` slot when printing to an nREPL session
73-
;; PrintWriter (as created by `pprint-writer`), which the client requires to
74-
;; handle the response correctly.
75-
(binding [*msg* msg
76-
*out* writer]
77-
(let [value (cljs/response-value msg response)
78-
print-fn (if (string? value) println pprint-fn)]
79-
(print-fn value))))
80-
(transport/send transport (response-for msg :pprint-sentinel {})))
81-
82-
(defn pprint-transport
83-
[{:keys [right-margin ^Transport transport] :as msg}]
84-
(reify Transport
85-
(recv [this] (.recv transport))
86-
(recv [this timeout] (.recv transport timeout))
87-
(send [this response]
88-
(when (contains? response :value)
89-
(pprint-reply msg response))
90-
(.send transport (dissoc response :value)))))
91-
92-
(defn handle-pprint
93-
[handler msg]
94-
(let [{:keys [op pprint]} msg]
95-
(handler (if (and pprint (#{"eval" "load-file"} op))
96-
(assoc msg :transport (pprint-transport msg))
97-
msg))))
32+
(handler (assoc msg
33+
:pprint-fn (resolve-pprint-fn pprint-fn)
34+
:print-options (walk/keywordize-keys print-options)))))

src/cider/nrepl/middleware/refresh.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,12 @@
9090

9191
(defn- error-reply
9292
[{:keys [error error-ns]}
93-
{:keys [pprint-fn session transport] :as msg}]
93+
{:keys [pprint-fn print-options session transport] :as msg}]
9494

9595
(transport/send
9696
transport
9797
(response-for msg (cond-> {:status :error}
98-
error (assoc :error (analyze-causes error pprint-fn))
98+
error (assoc :error (analyze-causes error pprint-fn print-options))
9999
error-ns (assoc :error-ns error-ns))))
100100

101101
(binding [*msg* msg

src/cider/nrepl/middleware/stacktrace.clj

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@
248248
Take in a map `ed` as returned by `clojure.spec/explain-data` and return a map
249249
of pretty printed problems. The content of the returned map is modeled after
250250
`clojure.spec/explain-printer`."
251-
[ed pprint-fn]
252-
(let [pp-str #(pprint-fn %)
251+
[ed pprint-fn print-options]
252+
(let [pp-str #(pprint-fn % print-options)
253253
problems (sort-by #(count (:path %))
254254
(or (:clojure.spec/problems ed)
255255
(:clojure.spec.alpha/problems ed)))]
@@ -280,7 +280,7 @@
280280
(defn analyze-cause
281281
"Return a map describing the exception cause. If `ex-data` exists, a `:data`
282282
key is appended."
283-
[^Exception e pprint-fn]
283+
[^Exception e pprint-fn print-options]
284284
(let [m {:class (.getName (class e))
285285
:message (.getMessage e)
286286
:stacktrace (analyze-stacktrace e)}]
@@ -289,9 +289,9 @@
289289
(:clojure.spec.alpha/failure data))
290290
(assoc m
291291
:message "Spec assertion failed."
292-
:spec (prepare-spec-data data pprint-fn))
292+
:spec (prepare-spec-data data pprint-fn print-options))
293293
(-> m
294-
(assoc :data (with-out-str (pprint-fn data)))
294+
(assoc :data (pprint-fn data print-options))
295295
(assoc :location
296296
(select-keys data [:clojure.error/line :clojure.error/column
297297
:clojure.error/phase :clojure.error/source
@@ -303,18 +303,18 @@
303303
for each. For `ex-info` exceptions response contains :data slot with pretty
304304
printed data. For clojure.spec asserts, :spec slot contains a map of pretty
305305
printed components describing spec failures."
306-
[e pprint-fn]
306+
[e pprint-fn print-options]
307307
(->> e
308308
(iterate #(.getCause ^Exception %))
309309
(take-while identity)
310-
(map (comp extract-location #(analyze-cause % pprint-fn)))))
310+
(map (comp extract-location #(analyze-cause % pprint-fn print-options)))))
311311

312312
;;; ## Middleware
313313

314314
(defn handle-stacktrace
315-
[_ {:keys [session transport pprint-fn] :as msg}]
315+
[_ {:keys [session transport pprint-fn print-options] :as msg}]
316316
(if-let [e (@session #'*e)]
317-
(doseq [cause (analyze-causes e pprint-fn)]
317+
(doseq [cause (analyze-causes e pprint-fn print-options)]
318318
(t/send transport (response-for msg cause)))
319319
(t/send transport (response-for msg :status :no-error)))
320320
(t/send transport (response-for msg :status :done)))

src/cider/nrepl/middleware/test.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,11 @@
336336
(t/send transport (response-for msg :status :done))))
337337

338338
(defn handle-stacktrace-op
339-
[{:keys [ns var index session transport pprint-fn] :as msg}]
339+
[{:keys [ns var index session transport pprint-fn print-options] :as msg}]
340340
(with-interruptible-eval msg
341341
(let [[ns var] (map u/as-sym [ns var])]
342342
(if-let [e (get-in @results [ns var index :error])]
343-
(doseq [cause (st/analyze-causes e pprint-fn)]
343+
(doseq [cause (st/analyze-causes e pprint-fn print-options)]
344344
(t/send transport (response-for msg cause)))
345345
(t/send transport (response-for msg :status :no-error)))
346346
(t/send transport (response-for msg :status :done)))))

src/cider/nrepl/middleware/util/error_handling.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
"Takes a `java.lang.Exception` as `ex` and a pretty-print function
4040
as `pprint-fn`, then returns a pretty-printed version of the
4141
exception that can be rendered by CIDER's stacktrace viewer."
42-
[ex pprint-fn]
43-
{:pp-stacktrace (@analyze-causes ex pprint-fn)})
42+
[ex pprint-fn print-options]
43+
{:pp-stacktrace (@analyze-causes ex pprint-fn print-options)})
4444

4545
(defn base-error-response
4646
"Takes a CIDER-nREPL message as `msg`, an Exception `ex`, and a
@@ -50,7 +50,7 @@
5050
`:<op-name>-error` are NOT automatically added"
5151
[msg ex & statuses]
5252
(response-for msg (merge (apply error-summary ex statuses)
53-
(pp-stacktrace ex (:pprint-fn msg)))))
53+
(pp-stacktrace ex (:pprint-fn msg) (:print-options msg)))))
5454

5555
(defn- normalize-status
5656
"Accepts various representations of an nREPL reply message's status

test/clj/cider/nrepl/middleware/format_test.clj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@
130130
normal-reply (session/message {:op "format-edn" :edn wide-edn-sample})
131131
narrow-margin-reply (session/message {:op "format-edn"
132132
:edn wide-edn-sample
133-
:print-right-margin 10})]
133+
:print-options {:right-margin 10}})]
134134
(is (= #{"done"} (:status normal-reply)))
135135
(is (= "[1 2 3 4 5 6 7 8 9 0]" (:formatted-edn normal-reply)))
136136
(is (= #{"done"} (:status narrow-margin-reply)))
@@ -139,14 +139,14 @@
139139
(testing "format-edn respects the :pprint-fn slot"
140140
(let [{:keys [formatted-edn status]} (session/message {:op "format-edn"
141141
:edn "{:b 2 :c 3 :a 1}"
142-
:pprint-fn "cider.nrepl.middleware.pprint/puget-pprint"})]
142+
:pprint-fn "cider.nrepl.pprint/puget-pprint"})]
143143
(is (= "{:a 1, :b 2, :c 3}" formatted-edn))
144144
(is (= #{"done"} status))))
145145

146-
(testing "format-edn returns an error if the :pprint-fn is unresolvable"
146+
#_(testing "format-edn returns an error if the :pprint-fn is unresolvable"
147147
(let [{:keys [err ex status] :as response} (session/message {:op "format-edn"
148148
:edn "{:b 2 :c 3 :a 1}"
149-
:pprint-fn "fake.nrepl.middleware.pprint/puget-pprint"})]
149+
:pprint-fn "fake.nrepl.pprint/puget-pprint"})]
150150
(is (.startsWith err "java.lang.IllegalArgumentException: No such namespace: fa"))
151151
(is (= "class java.lang.IllegalArgumentException" ex))
152152
(is (= #{"done" "format-edn-error"} status))

0 commit comments

Comments
 (0)