diff --git a/CHANGELOG.md b/CHANGELOG.md index cfe4a077..f489546c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [#348](https://github.com/clojure-emacs/orchard/pull/348): Inspector: display length of inspected strings. - [#348](https://github.com/clojure-emacs/orchard/pull/348): Inspector: display class flags. - [#349](https://github.com/clojure-emacs/orchard/pull/349): Inspector: add ability to sort maps by key. +- [#350](https://github.com/clojure-emacs/orchard/pull/350): Inspector: add diff mode and `orchard.inspect/diff`. ## 0.35.0 (2025-05-28) diff --git a/src/orchard/inspect.clj b/src/orchard/inspect.clj index 87e26a12..ce19dfa6 100644 --- a/src/orchard/inspect.clj +++ b/src/orchard/inspect.clj @@ -18,7 +18,8 @@ [orchard.print :as print]) (:import (java.lang.reflect Constructor Field Method Modifier) - (java.util Arrays List Map))) + (java.util Arrays List Map) + (orchard.print Diff DiffColl))) ;; ;; Navigating Inspector State @@ -47,6 +48,7 @@ :display-analytics-hint nil :analytics-size-cutoff 100000 :sort-maps false + :only-diff false :pretty-print false}) (defn- reset-render-state [inspector] @@ -1038,6 +1040,27 @@ (unindent) (unindent)))) +(defmethod inspect DiffColl [{:keys [only-diff] :as inspector} ^DiffColl obj] + (let [val (cond-> (.coll obj) + only-diff print/diff-coll-hide-equal-items)] + (-> inspector + (render-class-name val) + (render-counted-length val) + (render-section-header "Diff contents") + (indent) + (render-value-maybe-expand val) + (unindent)))) + +(defmethod inspect Diff [inspector ^Diff obj] + (let [d1 (.d1 obj), d2 (.d2 obj)] + (-> inspector + (render-class-name obj) + (render-section-header "Diff") + (indent) + (render-labeled-value " Left" d1) + (render-labeled-value "Right" d2) + (unindent)))) + (defn ns-refers-by-ns [^clojure.lang.Namespace ns] (group-by (fn [^clojure.lang.Var v] (.ns v)) (map val (ns-refers ns)))) @@ -1086,15 +1109,21 @@ (unindent)) inspector))) -(defn render-view-mode [{:keys [value view-mode pretty-print] :as inspector}] +(defn render-view-mode [{:keys [value view-mode pretty-print sort-maps only-diff] :as inspector}] (if (some? value) (let [supported (filter #(view-mode-supported? inspector %) view-mode-order) add-circle #(if %2 (str "●" %1) %1) + diff? (print/diff-result? value) view-mode-str (str (->> supported (map #(add-circle (name %) (= % view-mode))) (str/join " ")) - " " (add-circle "pretty" pretty-print))] - (-> (render-section-header inspector "View mode (press 'v' to cycle, 'P' to pretty-print)") + " " (add-circle "pretty" pretty-print) + " " (add-circle "sort-maps" sort-maps) + (when diff? + (str " " (add-circle "only-diff" only-diff)))) + caption (format "View mode (press 'v' to cycle, 'P' to pretty-print, 'S' to sort maps%s)" + (if diff? ", 'D' to show only diffs" ""))] + (-> (render-section-header inspector caption) (indent) (render-indent view-mode-str) (unindent))) @@ -1104,10 +1133,12 @@ (seq (persistent! rendered))) (defn inspect-render - ([{:keys [max-atom-length max-value-length max-coll-size max-nested-depth value pretty-print] + ([{:keys [max-atom-length max-value-length max-coll-size max-nested-depth value + pretty-print only-diff] :as inspector}] (binding [print/*max-atom-length* max-atom-length print/*max-total-length* max-value-length + print/*coll-show-only-diff* (boolean only-diff) *print-length* max-coll-size *print-level* (cond-> max-nested-depth ;; In pretty mode a higher *print-level* @@ -1141,12 +1172,25 @@ (assoc :view-mode (first (supported-view-modes inspector))) inspect-render)))) -(defn ^:deprecated clear - "If necessary, use `(start inspector nil) instead.`" - [inspector] - (start inspector nil)) - -(defn ^:deprecated fresh - "If necessary, use `(start nil)` instead." - [] - (start nil)) +(defn diff + "Perform a recursive diff on two values and return a structure suitable to be + viewed with the inspector." + [d1 d2] + (cond (= d1 d2) d1 + (not= (class d1) (class d2)) (print/->Diff d1 d2) + + (and (sequential? d1) (sequential? d2)) + (let [n (max (count d1) (count d2))] + (->> (mapv #(diff (nth d1 % print/nothing) (nth d2 % print/nothing)) + (range n)) + print/->DiffColl)) + + (and (map? d1) (map? d2)) + (print/->DiffColl + (->> (concat (keys d1) (keys d2)) + distinct + (mapv (fn [k] + [k (diff (get d1 k print/nothing) (get d2 k print/nothing))])) + (into {}))) + + :else (print/->Diff d1 d2))) diff --git a/src/orchard/pp.clj b/src/orchard/pp.clj index adaa9abe..db3d1a20 100644 --- a/src/orchard/pp.clj +++ b/src/orchard/pp.clj @@ -14,7 +14,8 @@ (:require [clojure.string :as str] [orchard.print :as print]) (:import (mx.cider.orchard TruncatingStringWriter - TruncatingStringWriter$TotalLimitExceeded))) + TruncatingStringWriter$TotalLimitExceeded) + (orchard.print DiffColl))) (defn ^:private strip-ns "Given a (presumably qualified) ident, return an unqualified version @@ -317,10 +318,18 @@ (do (write writer reader-macro) (-pprint (second this) writer - (update opts :indentation - (fn [indentation] (str indentation " ")))))) + (update opts :indentation str " ")))) (-pprint-coll this writer opts))) +(defn ^:private -pprint-diff-coll + [^DiffColl this writer opts] + (if (meets-print-level? (:level opts)) + (write writer "#") + (let [coll (cond-> (.coll this) + print/*coll-show-only-diff* print/diff-coll-hide-equal-items)] + (write writer "#≠") + (-pprint coll writer (update opts :indentation str " "))))) + (extend-protocol PrettyPrintable nil (-pprint [_ writer _] @@ -350,6 +359,10 @@ (-pprint [this writer opts] (-pprint-coll (or (seq this) ()) writer opts)) + DiffColl + (-pprint [this writer opts] + (-pprint-diff-coll this writer opts)) + Object (-pprint [this writer opts] (if (array? this) diff --git a/src/orchard/print.clj b/src/orchard/print.clj index 74b97afd..c4ebc900 100644 --- a/src/orchard/print.clj +++ b/src/orchard/print.clj @@ -48,6 +48,10 @@ "Maximum total size of the resulting string." Integer/MAX_VALUE) +(def ^:dynamic *coll-show-only-diff* + "When displaying collection diffs, whether to hide matching values." + false) + (defn- print-coll-item "Print an item in the context of a collection. When printing a map, don't print `[]` characters around map entries." @@ -218,6 +222,41 @@ (print (str first-frame) w)) (.write w "]")) +;;;; Diffing support. Used for orchard.inspect/diff. + +(deftype Diff [d1 d2]) +(deftype DiffColl [coll]) ;; For collections that contain diff elements. +(deftype Nothing []) ;; To represent absent value. +(def nothing (->Nothing)) + +(defn diff-result? + "Return true if the object represents a diff result." + [x] + (or (instance? Diff x) (instance? DiffColl x))) + +(defn diff-coll-hide-equal-items [coll] + (cond (map? coll) (into {} (filter (fn [[_ v]] (diff-result? v)) + coll)) + (sequential? coll) (mapv #(if (diff-result? %) % nothing) + coll) + :else coll)) + +(defmethod print DiffColl [^DiffColl x, ^Writer w] + (let [coll (cond-> (.coll x) + *coll-show-only-diff* diff-coll-hide-equal-items)] + (.write w "#≠") + (print coll w))) + +(defmethod print Diff [^Diff x, ^Writer w] + (let [d1 (.d1 x), d2 (.d2 x)] + (.write w "#±[") + (print d1 w) + (.write w " ~~ ") + (print d2 w) + (.write w "]"))) + +(defmethod print Nothing [_ _]) + (defmethod print :default [^Object x, ^Writer w] (print-method x w)) diff --git a/test/orchard/inspect_test.clj b/test/orchard/inspect_test.clj index ccb4ed8b..f644ebbf 100644 --- a/test/orchard/inspect_test.clj +++ b/test/orchard/inspect_test.clj @@ -51,7 +51,7 @@ [:newline] " " [:value ":f" 7] " = " [:value "[2 3]" 8] [:newline] [:newline] - #"--- View mode" [:newline] " ●normal object pretty"]) + #"--- View mode" [:newline] " ●normal object pretty sort-maps"]) (def long-sequence (range 70)) (def long-vector (vec (range 70))) @@ -101,7 +101,9 @@ (defn- extend-nav-vector [m] (vary-meta m assoc 'clojure.core.protocols/nav (fn [coll k v] [k (get coll k v)]))) -(def inspect inspect/start) +(defn inspect + [value & [config]] + (inspect/start config value)) (defn render [inspector] @@ -784,9 +786,8 @@ [:newline] " 0. " [:value "[111111 2222 333 ...]" 1] [:newline]]) - (-> (inspect/start {:max-atom-length 20 - :max-coll-size 3} - [[111111 2222 333 44 5]]) + (-> [[111111 2222 333 44 5]] + (inspect {:max-atom-length 20, :max-coll-size 3}) render))) (testing "inspect respects :max-value-length configuration" (is+ (matchers/prefix @@ -800,7 +801,8 @@ [:newline] " 0. " [:value "(\"long value\" \"long value\" \"long value\" \"long valu..." 1] [:newline]]) - (-> (inspect/start {:max-value-length 50} [(repeat "long value")]) + (-> [(repeat "long value")] + (inspect {:max-value-length 50}) render))) (testing "inspect respects :max-value-depth configuration" @@ -815,7 +817,8 @@ [:newline] " 0. " [:value "[[[[[[...]]]]]]" 1] [:newline]]) - (-> (inspect/start {:max-nested-depth 5} [[[[[[[[[[1]]]]]]]]]]) + (-> [[[[[[[[[[1]]]]]]]]]] + (inspect {:max-nested-depth 5}) render)))) (deftest inspect-java-hashmap-test @@ -1363,7 +1366,7 @@ (deftest object-view-mode-test (testing "in :object view-mode recognized objects are rendered as :default" (let [rendered (-> (list 1 2 3) - (inspect/start) + inspect (inspect/set-view-mode :object) render)] (is+ (matchers/prefix @@ -1373,11 +1376,11 @@ " " [:value "_first" pos?] " = " [:value "1" pos?] [:newline] " " [:value "_hash" pos?] " = " [:value "0" pos?] [:newline]]) (section rendered "Instance fields")) - (is+ [#"--- View mode" [:newline] " normal ●object pretty"] + (is+ [#"--- View mode" [:newline] " normal ●object pretty sort-maps"] (section rendered "View mode"))) (let [rendered (-> (atom "foo") - (inspect/start) + inspect (inspect/set-view-mode :object) render)] (is+ (matchers/prefix @@ -1388,7 +1391,7 @@ " " [:value "validator" pos?] " = " [:value "nil" pos?] [:newline] " " [:value "watches" pos?] " = " [:value "{}" pos?]]) (section rendered "Instance fields")) - (is+ [#"--- View mode" [:newline] " normal ●object pretty"] + (is+ [#"--- View mode" [:newline] " normal ●object pretty sort-maps"] (section rendered "View mode")))) (testing "navigating away from an object changes the view mode back to normal" @@ -1397,7 +1400,7 @@ " 0. " [:value "2" pos?] [:newline] " 1. " [:value "3" pos?]] (-> (list 1 2 3) - (inspect/start) + inspect (inspect/set-view-mode :object) (inspect/down 13) render @@ -1409,7 +1412,7 @@ {:a (- i) :bb (str i i i) :ccc (range i 0 -1)}) - (inspect/start) + inspect (inspect/set-view-mode :table) render)] (is+ ["--- Contents:" [:newline] [:newline] @@ -1427,13 +1430,13 @@ " | " [:value "4" pos?] " | " [:value "-4" pos?] " | " [:value "\"444\"" pos?] " | " [:value "(4 3 2 1)" pos?] " | "] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " normal ●table object pretty"] + (is+ [#"--- View mode" [:newline] " normal ●table object pretty sort-maps"] (section rendered "View mode")))) (testing "in :table view-mode lists of vectors are rendered as tables" (let [rendered (-> (for [i (range 5)] [(- i) (str i i i) (range i 0 -1)]) - (inspect/start) + inspect (inspect/set-view-mode :table) render)] (is+ ["--- Contents:" [:newline] [:newline] @@ -1451,12 +1454,12 @@ " | " [:value "4" pos?] " | " [:value "-4" pos?] " | " [:value "\"444\"" pos?] " | " [:value "(4 3 2 1)" pos?] " | "] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " normal ●table object pretty"] + (is+ [#"--- View mode" [:newline] " normal ●table object pretty sort-maps"] (section rendered "View mode")))) (testing "breaks if table mode is requested for unsupported value" (is (thrown? Exception (-> {:a 1} - (inspect/start) + inspect (inspect/set-view-mode :table) render contents-section)))) @@ -1470,7 +1473,7 @@ " | " [:value "2" pos?] " | " [:value "2" pos?] " | " [:value "2" pos?] " | " [:newline] " ..."] (-> (map #(vector % %) (range 9)) - (inspect/start) + inspect (set-page-size 3) (inspect/set-view-mode :table) render @@ -1485,7 +1488,7 @@ " | " [:value "5" pos?] " | " [:value "5" pos?] " | " [:value "5" pos?] " | " [:newline] " ..."] (-> (map #(vector % %) (range 9)) - (inspect/start) + inspect (set-page-size 3) (inspect/next-page) (inspect/set-view-mode :table) @@ -1500,7 +1503,7 @@ " | " [:value "7" pos?] " | " [:value "7" pos?] " | " [:value "7" pos?] " | " [:newline] " | " [:value "8" pos?] " | " [:value "8" pos?] " | " [:value "8" pos?] " | "] (-> (map #(vector % %) (range 9)) - (inspect/start) + inspect (set-page-size 3) (inspect/next-page) (inspect/next-page) @@ -1510,7 +1513,7 @@ (testing "map is not reported as table-viewable when paged" (is (not (-> (zipmap (range 100) (range)) - (inspect/start) + inspect (set-page-size 30) (inspect/view-mode-supported? :table)))))) @@ -1529,7 +1532,7 @@ " 0x00000050 │ 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f │ PQRSTUVWXYZ[\\]^_" [:newline] " 0x00000060 │ 60 61 62 63 │ `abc"] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " ●hex normal object pretty"] + (is+ [#"--- View mode" [:newline] " ●hex normal object pretty sort-maps"] (section rendered "View mode")))) (testing "works with paging" @@ -1577,21 +1580,21 @@ (deftest toggle-view-mode-test (is+ :normal (-> (repeat 10 [1 2]) inspect :view-mode)) - (is+ " ●normal table object pretty" + (is+ " ●normal table object pretty sort-maps" (-> (repeat 10 [1 2]) inspect render (section "View mode") last)) (is+ :table (-> (repeat 10 [1 2]) inspect inspect/toggle-view-mode :view-mode)) - (is+ " normal ●table object pretty" + (is+ " normal ●table object pretty sort-maps" (-> (repeat 10 [1 2]) inspect inspect/toggle-view-mode render (section "View mode") last)) (is+ :object (-> (repeat 10 [1 2]) inspect inspect/toggle-view-mode inspect/toggle-view-mode :view-mode)) - (is+ " normal table ●object pretty" + (is+ " normal table ●object pretty sort-maps" (-> (repeat 10 [1 2]) inspect inspect/toggle-view-mode inspect/toggle-view-mode render (section "View mode") last)) (is+ :normal (-> (repeat 10 [1 2]) inspect inspect/toggle-view-mode inspect/toggle-view-mode inspect/toggle-view-mode :view-mode)) - (is+ " ●normal table object ●pretty" - (-> (inspect {:pretty-print true} (repeat 10 [1 2])) render (section "View mode") last))) + (is+ " ●normal table object ●pretty sort-maps" + (-> (repeat 10 [1 2]) (inspect {:pretty-print true}) render (section "View mode") last))) (deftest pretty-print-map-test (testing "in :pretty view-mode are pretty printed" @@ -1601,7 +1604,7 @@ :d [{:a 0 :bb "000" :ccc [[]]} {:a -1 :bb "111" :ccc [1]} {:a 2 :bb "222" :ccc [1 2]}]} - (inspect/start) + inspect (set-pretty-print true) render)] (is+ ["--- Contents:" [:newline] " " @@ -1616,7 +1619,7 @@ " {:a -1, :bb \"111\", :ccc [1]}\n" " {:a 2, :bb \"222\", :ccc [1 2]}]") 8]] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " ●normal object ●pretty"] + (is+ [#"--- View mode" [:newline] " ●normal object ●pretty sort-maps"] (section rendered "View mode"))))) (deftest pretty-print-map-in-object-view-test @@ -1627,7 +1630,7 @@ :d [{:a 0 :bb "000" :ccc [[]]} {:a -1 :bb "111" :ccc [1]} {:a 2 :bb "222" :ccc [1 2]}]} - (inspect/start) + inspect (inspect/set-view-mode :object) (set-pretty-print true) render)] @@ -1645,7 +1648,7 @@ {:a (- i) :bb (str i i i) :ccc (range i 0 -1)})}) - (inspect/start) + inspect (set-pretty-print true) render)] (is+ ["--- Contents:" [:newline] @@ -1665,7 +1668,7 @@ ":ccc (3 2 1)}\n {:a -4, :bb \"444\", " ":ccc (4 3 2 1)})}") 2]] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " ●normal table object ●pretty"] + (is+ [#"--- View mode" [:newline] " ●normal table object ●pretty sort-maps"] (section rendered "View mode"))))) (deftest pretty-print-map-as-key-test @@ -1686,7 +1689,7 @@ {:a -2 :bb "222" :ccc [2 1]} {:a -3 :bb "333" :ccc [3 2 1]} {:a -4 :bb "444" :ccc [4 3 2 1]}]}} - (inspect/start) + inspect (set-pretty-print true) render)] (is+ ["--- Contents:" [:newline] " " @@ -1703,7 +1706,7 @@ "\"333\", :ccc [3 2 1]}\n {:a -4, :bb " "\"444\", :ccc [4 3 2 1]}]}") 2]] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " ●normal object ●pretty"] + (is+ [#"--- View mode" [:newline] " ●normal object ●pretty sort-maps"] (section rendered "View mode"))))) (deftest pretty-print-seq-of-map-as-key-test @@ -1720,7 +1723,7 @@ :d [{:a 0 :bb "000" :ccc [[]]} {:a -1 :bb "111" :ccc [1]} {:a 2 :bb "222" :ccc [1 2]}]}} - (inspect/start) + inspect (set-pretty-print true) render)] (is+ ["--- Contents:" [:newline] " " @@ -1733,22 +1736,25 @@ ":bb \"111\", :ccc [1]}\n {:a 2, :bb \"222\", " ":ccc [1 2]}]}") 2]] (contents-section rendered)) - (is+ [#"--- View mode" [:newline] " ●normal object ●pretty"] + (is+ [#"--- View mode" [:newline] " ●normal object ●pretty sort-maps"] (section rendered "View mode"))))) (deftest sort-maps-test - (testing "with :sort-map-keys enabled, may keys are sorted" - (is+ (matchers/prefix - ["--- Contents:" [:newline] - " " [:value "0" pos?] " = " [:value "0" pos?] [:newline] - " " [:value "1" pos?] " = " [:value "1" pos?] [:newline] - " " [:value "2" pos?] " = " [:value "2" pos?] [:newline] - " " [:value "3" pos?] " = " [:value "3" pos?] [:newline]]) - (-> (zipmap (range 100) (range 100)) - inspect - (inspect/refresh {:sort-maps true}) - render - contents-section))) + (testing "with :sort-map-keys enabled, map keys are sorted" + (let [rendered (-> (zipmap (range 100) (range 100)) + inspect + (inspect/refresh {:sort-maps true}) + render)] + (is+ (matchers/prefix + ["--- Contents:" [:newline] + " " [:value "0" pos?] " = " [:value "0" pos?] [:newline] + " " [:value "1" pos?] " = " [:value "1" pos?] [:newline] + " " [:value "2" pos?] " = " [:value "2" pos?] [:newline] + " " [:value "3" pos?] " = " [:value "3" pos?] [:newline]]) + (contents-section rendered)) + + (is+ [#"--- View mode" [:newline] " ●normal object pretty ●sort-maps"] + (section rendered "View mode")))) (testing "works if map is smaller than page size" (is+ ["--- Contents:" [:newline] @@ -1941,7 +1947,9 @@ (testing "analytics hint is displayed if requested" (is+ ["--- Analytics:" [:newline] " Press 'y' or M-x cider-inspector-display-analytics to analyze this value."] - (-> (inspect {:display-analytics-hint "true"} (range 100)) render + (-> (range 100) + (inspect {:display-analytics-hint "true"}) + render (section "Analytics")))) (testing "analytics is shown when requested" @@ -1964,3 +1972,110 @@ inspect/display-analytics render (section "Analytics"))))) + +(def data1 [{:tea/type "Jinxuan Oolong" + :tea/color "Green" + :tea/region "Alishan" + :aliases ["Milky Wulong" "Jinxuan"] + :temperature 80} + {:tea/type "Dong Ding" + :tea/region "Nantou" + :aliases ["Frozen summit" "Dongti" "Dong ding wulong"]} + "same string" + 3]) + +(def data2 [{:tea/type "Jinxuan Wulong" + :tea/color "Green" + :tea/region "Alishan" + :aliases ["Milky Wulong" "金宣" "Jinxuan"] + :temperature 75} + {:tea/type "Dong Ding" + :tea/region "Nantou" + :aliases ["Frozen summit" "Dongti" "Dong ding wulong"] + :temperature 85} + "same string" + 4]) + +(deftest diff-test + (let [rendered (-> (inspect/diff data1 data2) + inspect + render)] + (is+ ["--- Diff contents:" [:newline] + " 0. " [:value "#≠{:tea/type #±[\"Jinxuan Oolong\" ~~ \"Jinxuan Wulong\"], :tea/color \"Green\", :tea/region \"Alishan\", :aliases #≠[\"Milky Wulong\" #±[\"Jinxuan\" ~~ \"金宣\"] #±[ ~~ \"Jinxuan\"]], :temperature #±[80 ~~ 75]}" pos?] [:newline] + " 1. " [:value "#≠{:tea/type \"Dong Ding\", :tea/region \"Nantou\", :aliases [\"Frozen summit\" \"Dongti\" \"Dong ding wulong\"], :temperature #±[ ~~ 85]}" pos?] [:newline] + " 2. " [:value "\"same string\"" pos?] [:newline] + " 3. " [:value "#±[3 ~~ 4]" pos?]] + (section rendered "Diff")) + + (is+ [string? [:newline] " ●normal pretty sort-maps only-diff"] + (section rendered "View mode"))) + + (is+ ["--- Diff contents:" [:newline] + " " [:value ":tea/type" pos?] " = " [:value "#±[\"Jinxuan Oolong\" ~~ \"Jinxuan Wulong\"]" pos?] [:newline] + " " [:value ":tea/color" pos?] " = " [:value "\"Green\"" pos?] [:newline] + " " [:value ":tea/region" pos?] " = " [:value "\"Alishan\"" pos?] [:newline] + " " [:value ":aliases" pos?] " = " [:value "#≠[\"Milky Wulong\" #±[\"Jinxuan\" ~~ \"金宣\"] #±[ ~~ \"Jinxuan\"]]" pos?] [:newline] + " " [:value ":temperature" pos?] " = " [:value "#±[80 ~~ 75]" pos?]] + (-> (inspect/diff data1 data2) + inspect + (inspect/down 1) + render + (section "Diff"))) + + (is+ ["--- Diff:" [:newline] + " Left: " [:value "\"Jinxuan Oolong\"" pos?] [:newline] + " Right: " [:value "\"Jinxuan Wulong\"" pos?]] + (-> (inspect/diff data1 data2) + inspect + (inspect/down 1) + (inspect/down 2) + render + (section "Diff"))) + + (is+ ["--- Diff contents:" [:newline] + " 0. " [:value "\"Milky Wulong\"" pos?] [:newline] + " 1. " [:value "#±[\"Jinxuan\" ~~ \"金宣\"]" pos?] [:newline] + " 2. " [:value "#±[ ~~ \"Jinxuan\"]" 3]] + (-> (inspect/diff data1 data2) + inspect + (inspect/down 1) + (inspect/down 8) + render + (section "Diff"))) + + (testing "in :only-diff mode, render only differing subvalues" + (let [rendered (-> (inspect/diff data1 data2) + (inspect {:only-diff true}) + render)] + (is+ ["--- Diff contents:" [:newline] + " 0. " [:value "#≠{:tea/type #±[\"Jinxuan Oolong\" ~~ \"Jinxuan Wulong\"], :aliases #≠[ #±[\"Jinxuan\" ~~ \"金宣\"] #±[ ~~ \"Jinxuan\"]], :temperature #±[80 ~~ 75]}" pos?] [:newline] + " 1. " [:value "#≠{:temperature #±[ ~~ 85]}" pos?] [:newline] + " 2. " [:value "" pos?] [:newline] + " 3. " [:value "#±[3 ~~ 4]" pos?]] + (section rendered "Diff")) + + (is+ [string? [:newline] " ●normal pretty sort-maps ●only-diff"] + (section rendered "View mode"))) + + (is+ ["--- Diff contents:" [:newline] + " " [:value ":tea/type" pos?] " = " [:value "#±[\"Jinxuan Oolong\" ~~ \"Jinxuan Wulong\"]" pos?] [:newline] + " " [:value ":aliases" pos?] " = " [:value "#≠[ #±[\"Jinxuan\" ~~ \"金宣\"] #±[ ~~ \"Jinxuan\"]]" pos?] [:newline] + " " [:value ":temperature" pos?] " = " [:value "#±[80 ~~ 75]" pos?]] + (-> (inspect/diff data1 data2) + (inspect {:only-diff true}) + (inspect/down 1) + render + (section "Diff")))) + + (testing "works with :pretty-print" + (is+ ["--- Diff contents:" [:newline] + " 0. " [:value "#≠{:tea/type #±[\"Jinxuan Oolong\" ~~ \"Jinxuan Wulong\"], + :aliases #≠[ #±[\"Jinxuan\" ~~ \"金宣\"] #±[ ~~ \"Jinxuan\"]], + :temperature #±[80 ~~ 75]}" pos?] [:newline] + " 1. " [:value "#≠{:temperature #±[ ~~ 85]}" pos?] [:newline] + " 2. " [:value "" pos?] [:newline] + " 3. " [:value "#±[3 ~~ 4]" pos?]] + (-> (inspect/diff data1 data2) + (inspect {:only-diff true, :pretty-print true}) + render + (section "Diff")))))