|
4 | 4 | calls `print-method`, which includes return values, `pr`, `print`
|
5 | 5 | and the likes."
|
6 | 6 | (:require
|
7 |
| - [clojure.main :as main]) |
| 7 | + [orchard.print :as print]) |
8 | 8 | (:import
|
9 |
| - [clojure.lang AFunction Atom MultiFn Namespace] |
10 |
| - [java.io Writer])) |
| 9 | + (clojure.lang AFunction Atom IDeref MultiFn Namespace))) |
11 | 10 |
|
12 | 11 | (def ^:dynamic *pretty-objects*
|
13 | 12 | "If true, cider prettifies some object descriptions.
|
|
20 | 19 | (alter-var-root #'cider.nrepl.print-method/*pretty-objects* not)"
|
21 | 20 | true)
|
22 | 21 |
|
23 |
| -(defmacro def-print-method [dispatch-val arg & strings] |
24 |
| - `(defmethod print-method ~dispatch-val [~arg ~'^Writer w] |
25 |
| - (if *pretty-objects* |
26 |
| - (do ~@(map #(list '.write |
27 |
| - (with-meta 'w {:tag `Writer}) |
28 |
| - %) |
29 |
| - strings)) |
30 |
| - (#'clojure.core/print-object ~arg ~'w)))) |
31 |
| - |
32 |
| -(defn- translate-class-name ^String [c] |
33 |
| - (main/demunge (.getName (class c)))) |
34 |
| - |
35 |
| -;;; Atoms |
36 |
| -;; Ex: #atom[{:foo :bar} 0x54274a2b] |
37 |
| -(def-print-method Atom c |
38 |
| - "#atom[" |
39 |
| - (pr-str @c) |
40 |
| - (format " 0x%x]" (System/identityHashCode c))) |
41 |
| - |
42 |
| -;;; Function objects |
43 |
| -;; Ex: #function[cider.nrepl.print-method/multifn-name] |
44 |
| -(def-print-method AFunction c |
45 |
| - "#function[" |
46 |
| - (translate-class-name c) |
47 |
| - "]") |
48 |
| - |
49 |
| -;;; Multimethods |
50 |
| -;; Ex: #multifn[print-method 0x3f0cd5b4] |
51 |
| -(defn multifn-name [^MultiFn mfn] |
52 |
| - (let [field (.getDeclaredField MultiFn "name") |
53 |
| - private (not (.isAccessible field))] |
54 |
| - (when private |
55 |
| - (.setAccessible field true)) |
56 |
| - (let [name (.get field mfn)] |
57 |
| - (when private |
58 |
| - (.setAccessible field false)) |
59 |
| - name))) |
60 |
| - |
61 |
| -(defn multifn-name-or-translated-name ^String [c] |
62 |
| - (try (multifn-name c) |
63 |
| - (catch SecurityException _ |
64 |
| - (translate-class-name c)))) |
65 |
| - |
66 |
| -(def-print-method MultiFn c |
67 |
| - "#multifn[" |
68 |
| - (multifn-name-or-translated-name c) |
69 |
| - ;; MultiFn names are not unique so we keep the identity HashCode to |
70 |
| - ;; make sure it's unique. |
71 |
| - (format " 0x%x]" (System/identityHashCode c))) |
72 |
| - |
73 |
| -;;; Namespaces |
74 |
| -;; Ex: #namespace[clojure.core] |
75 |
| -(def-print-method Namespace c |
76 |
| - "#namespace[" |
77 |
| - (format "%s" (ns-name c)) |
78 |
| - "]") |
79 |
| - |
80 |
| -;;; Agents, futures, delays, promises, etc |
81 |
| -(defn- deref-name ^String [c] |
82 |
| - (let [class-name (translate-class-name c)] |
83 |
| - (if-let [[_ ^String short-name] (re-find #"^clojure\.lang\.([^.]+)" class-name)] |
84 |
| - (.toLowerCase short-name) |
85 |
| - (case (second (re-find #"^clojure\.core/(.+)/reify" class-name)) |
86 |
| - "future-call" "future" |
87 |
| - "promise" "promise" |
88 |
| - nil class-name)))) |
89 |
| - |
90 |
| -;; `deref-as-map` is a private function, so let's be careful. |
91 |
| -(when-let [f (resolve 'clojure.core/deref-as-map)] |
92 |
| - (def-print-method clojure.lang.IDeref c |
93 |
| - "#" (deref-name c) "[" |
94 |
| - (pr-str (f c)) |
95 |
| - (format " 0x%x]" (System/identityHashCode c)))) |
| 22 | +(defn- replace-with-orchard-print |
| 23 | + "Replace `clojure.core/print-method` for the given class with |
| 24 | + `orchard.print/print` when `*pretty-objects*` is true, otherwise call the |
| 25 | + default Clojure implementation." |
| 26 | + [klass] |
| 27 | + (defmethod print-method klass [x writer] |
| 28 | + (if *pretty-objects* |
| 29 | + (print/print x writer) |
| 30 | + (#'clojure.core/print-object writer)))) |
| 31 | + |
| 32 | +;; NB: we don't replace all clojure.core/print-method implementations with |
| 33 | +;; orchard.print/print because they arguably have different purpose. Orchard |
| 34 | +;; printer is more human-oriented whereas print-method is a bit more |
| 35 | +;; machine-oriented. So, we only replace it for those types where the visual |
| 36 | +;; improvement is substantial yet we are confident it won't break something. |
| 37 | + |
| 38 | +;;; Atoms: #atom[{:foo :bar}] |
| 39 | +(replace-with-orchard-print Atom) |
| 40 | + |
| 41 | +;;; Function objects: #function[clojure.core/str] |
| 42 | +(replace-with-orchard-print AFunction) |
| 43 | + |
| 44 | +;;; Multimethods: #multifn[print-method 0x3f0cd5b4] |
| 45 | +(replace-with-orchard-print MultiFn) |
| 46 | + |
| 47 | +;;; Namespaces: #namespace[clojure.core] |
| 48 | +(replace-with-orchard-print Namespace) |
| 49 | + |
| 50 | +;;; Various derefables |
| 51 | +;; #agent[1], #agent[<failed> #error[...]] |
| 52 | +;; #delay[<pending>], #delay[1], #delay[<failed> #error[...]] |
| 53 | +;; #future[<pending>], #future[1], #future[<failed> #error[...]] |
| 54 | +;; #promise[<pending>], #promise[1] |
| 55 | +(replace-with-orchard-print IDeref) |
0 commit comments