|
13 | 13 |
|
14 | 14 | ;; For property based testing |
15 | 15 | (defprotocol Undiff |
16 | | - (-left-undiff [x]) |
17 | | - (-right-undiff [x])) |
| 16 | + (left-undiff [x]) |
| 17 | + (right-undiff [x])) |
18 | 18 |
|
19 | | -(defn- shift-insertions [ins] |
| 19 | +(defn shift-insertions [ins] |
20 | 20 | (reduce (fn [res idx] |
21 | 21 | (let [offset (apply + (map count (vals res)))] |
22 | 22 | (assoc res (+ idx offset) (get ins idx)))) |
23 | 23 | {} |
24 | 24 | (sort (keys ins)))) |
25 | 25 |
|
26 | | -(defn- replacements |
| 26 | +(defn replacements |
27 | 27 | "Given a set of deletion indexes and a map of insertion index to value sequence, |
28 | 28 | match up deletions and insertions into replacements, returning a map of |
29 | 29 | replacements, a set of deletions, and a map of insertions." |
|
58 | 58 | (remove (comp nil? val)) |
59 | 59 | (shift-insertions ins))]))) |
60 | 60 |
|
61 | | -(defn- del+ins |
| 61 | +(defn del+ins |
62 | 62 | "Wrapper around clj-diff that returns deletions and insertions as a set and map |
63 | 63 | respectively." |
64 | 64 | [exp act] |
65 | 65 | (let [{del :- ins :+} (seq-diff/diff exp act)] |
66 | 66 | [(into #{} del) |
67 | 67 | (into {} (map (fn [[k & vs]] [k (vec vs)])) ins)])) |
68 | 68 |
|
69 | | -(defn- diff-seq-replacements [replacements s] |
| 69 | +(defn diff-seq-replacements [replacements s] |
70 | 70 | (map-indexed |
71 | 71 | (fn [idx v] |
72 | 72 | (if (contains? replacements idx) |
73 | 73 | (diff v (get replacements idx)) |
74 | 74 | v)) |
75 | 75 | s)) |
76 | 76 |
|
77 | | -(defn- diff-seq-deletions [del s] |
| 77 | +(defn diff-seq-deletions [del s] |
78 | 78 | (map |
79 | 79 | (fn [v idx] |
80 | 80 | (if (contains? del idx) |
|
83 | 83 | s |
84 | 84 | (range))) |
85 | 85 |
|
86 | | -(defn- diff-seq-insertions [ins s] |
| 86 | +(defn diff-seq-insertions [ins s] |
87 | 87 | (reduce (fn [res [idx vs]] |
88 | 88 | (concat (take (inc idx) res) (map ->Insertion vs) (drop (inc idx) res))) |
89 | 89 | s |
90 | 90 | ins)) |
91 | 91 |
|
92 | | -(defn- diff-seq [exp act] |
| 92 | +(defn diff-seq [exp act] |
93 | 93 | (let [[rep del ins] (replacements (del+ins exp act))] |
94 | 94 | (->> exp |
95 | 95 | (diff-seq-replacements rep) |
96 | 96 | (diff-seq-deletions del) |
97 | 97 | (diff-seq-insertions ins) |
98 | 98 | (into [])))) |
99 | 99 |
|
100 | | -#?(:clj (defn- val-type [val] |
101 | | - (let [t (type val)] |
102 | | - (if (class? t) |
103 | | - (symbol (.getName ^Class t)) |
104 | | - t)))) |
105 | | - |
106 | | -(defn- diff-map [exp act] |
| 100 | +(defn diff-map [exp act] |
107 | 101 | (first |
108 | 102 | (let [exp-ks (keys exp) |
109 | 103 | act-ks (concat (filter (set (keys act)) exp-ks) |
|
126 | 120 | {}) 0] |
127 | 121 | exp-ks)))) |
128 | 122 |
|
129 | | -(defn- primitive? [x] |
| 123 | +(defn primitive? [x] |
130 | 124 | (or (number? x) (string? x) (boolean? x) (inst? x) (keyword? x) (symbol? x))) |
131 | 125 |
|
132 | | -(defn- diff-atom [exp act] |
| 126 | +(defn diff-atom [exp act] |
133 | 127 | (if (= exp act) |
134 | 128 | exp |
135 | 129 | (->Mismatch exp act))) |
136 | 130 |
|
137 | | -(defn- diff-similar [x y] |
138 | | - #?(:clj (-diff-similar x y) |
139 | | - :cljs |
140 | | - (if (primitive? x) |
141 | | - (diff-atom x y) |
142 | | - (-diff-similar x y)))) |
143 | | - |
144 | | -(defn- left-undiff [m] |
145 | | - #?(:clj (-left-undiff m) |
146 | | - :cljs |
147 | | - (if (primitive? m) |
148 | | - m |
149 | | - (-left-undiff m)))) |
150 | | - |
151 | | -(defn- right-undiff [m] |
152 | | - #?(:clj (-right-undiff m) |
153 | | - :cljs |
154 | | - (if (primitive? m) |
155 | | - m |
156 | | - (-right-undiff m)))) |
| 131 | +(defn diff-similar [x y] |
| 132 | + (if (primitive? x) |
| 133 | + (diff-atom x y) |
| 134 | + (-diff-similar x y))) |
| 135 | + |
| 136 | +(defn diffable? [exp] |
| 137 | + (or (implements? Diff exp) |
| 138 | + (satisfies? Diff exp))) |
157 | 139 |
|
158 | 140 | (defn diff [exp act] |
159 | | - (if (= (data/equality-partition exp) (data/equality-partition act)) |
| 141 | + (cond |
| 142 | + (nil? exp) |
| 143 | + (diff-atom exp act) |
| 144 | + |
| 145 | + (and (diffable? exp) |
| 146 | + (= (data/equality-partition exp) (data/equality-partition act))) |
160 | 147 | (diff-similar exp act) |
161 | | - (diff-atom exp act))) |
162 | 148 |
|
163 | | -#?(:clj (extend nil |
164 | | - Diff |
165 | | - {:-diff-similar diff-atom}) |
166 | | - :cljs (extend-type nil |
167 | | - Diff |
168 | | - (-diff-similar [x y] (diff-atom x y)))) |
| 149 | + (array? exp) |
| 150 | + (diff-seq exp act) |
169 | 151 |
|
170 | | -#?(:cljs (extend-type js/Object |
171 | | - Diff |
172 | | - (-diff-similar [x y] (diff-atom (js->clj x) (js->clj y))))) |
| 152 | + (record? exp) |
| 153 | + (diff-map exp act) |
173 | 154 |
|
174 | | -#?(:clj (extend Object |
175 | | - Diff |
176 | | - {:-diff-similar (fn [exp act] |
177 | | - (if (.isArray (.getClass ^Object exp)) |
178 | | - (diff-seq exp act) |
179 | | - (diff-atom exp act)))})) |
| 155 | + :else |
| 156 | + (diff-atom exp act))) |
180 | 157 |
|
181 | 158 | (extend-protocol Diff |
182 | | - #?(:clj java.util.List |
183 | | - :cljs cljs.core/List) |
184 | | - (-diff-similar [exp act] (diff-seq exp act)) |
| 159 | + #?(:clj java.util.Set :cljs cljs.core/PersistentHashSet) |
| 160 | + (-diff-similar [exp act] |
| 161 | + (let [exp-seq (seq exp) |
| 162 | + act-seq (seq act)] |
| 163 | + (set (diff-seq exp-seq (concat (filter act exp-seq) |
| 164 | + (remove exp act-seq)))))) |
| 165 | + #?@(:clj |
| 166 | + [java.util.List |
| 167 | + (-diff-similar [exp act] (diff-seq exp act)) |
| 168 | + |
| 169 | + java.util.Map |
| 170 | + (-diff-similar [exp act] (diff-map exp act))] |
| 171 | + |
| 172 | + :cljs |
| 173 | + [cljs.core/List |
| 174 | + (-diff-similar [exp act] (diff-seq exp act)) |
185 | 175 |
|
186 | | - #?@(:cljs |
187 | | - [cljs.core/PersistentVector |
| 176 | + cljs.core/PersistentVector |
188 | 177 | (-diff-similar [exp act] (diff-seq exp act)) |
189 | 178 |
|
190 | 179 | cljs.core/EmptyList |
|
193 | 182 | cljs.core/PersistentHashMap |
194 | 183 | (-diff-similar [exp act] (diff-map exp act)) |
195 | 184 |
|
196 | | - array |
197 | | - (-diff-similar [exp act] (diff-seq exp act))]) |
198 | | - |
199 | | - #?(:clj java.util.Set |
200 | | - :cljs cljs.core/PersistentHashSet) |
201 | | - (-diff-similar [exp act] |
202 | | - (let [exp-seq (seq exp) |
203 | | - act-seq (seq act)] |
204 | | - (set (diff-seq exp-seq (concat (filter act exp-seq) |
205 | | - (remove exp act-seq)))))) |
206 | | - |
207 | | - #?(:clj java.util.Map |
208 | | - :cljs cljs.core/PersistentArrayMap) |
209 | | - (-diff-similar [exp act] (diff-map exp act))) |
| 185 | + cljs.core/PersistentArrayMap |
| 186 | + (-diff-similar [exp act] (diff-map exp act))])) |
210 | 187 |
|
211 | 188 | (extend-protocol Undiff |
212 | | - #?(:clj java.util.List |
213 | | - :cljs cljs.core/List) |
214 | | - (-left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
215 | | - (-right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
216 | | - |
217 | | - #?(:cljs cljs.core/EmptyList) |
218 | | - #?(:cljs (-left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s)))) |
219 | | - #?(:cljs (-right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s)))) |
220 | | - |
221 | | - #?(:cljs cljs.core/PersistentVector) |
222 | | - #?(:cljs (-left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s)))) |
223 | | - #?(:cljs (-right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s)))) |
224 | | - |
225 | | - #?(:clj java.util.Set |
226 | | - :cljs cljs.core/PersistentHashSet) |
227 | | - (-left-undiff [s] (set (left-undiff (seq s)))) |
228 | | - (-right-undiff [s] (set (right-undiff (seq s)))) |
229 | | - |
230 | | - #?(:cljs cljs.core/KeySeq) |
231 | | - #?(:cljs (-left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s)))) |
232 | | - #?(:cljs (-right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s)))) |
233 | | - |
234 | | - #?(:clj java.util.Map |
235 | | - :cljs cljs.core/PersistentArrayMap) |
236 | | - (-left-undiff [m] |
237 | | - (into {} |
238 | | - (comp (remove #(instance? Insertion (key %))) |
239 | | - (map (juxt (comp left-undiff key) (comp left-undiff val)))) |
240 | | - m)) |
241 | | - (-right-undiff [m] |
242 | | - (into {} |
243 | | - (comp (remove #(instance? Deletion (key %))) |
244 | | - (map (juxt (comp right-undiff key) (comp right-undiff val)))) |
245 | | - m)) |
246 | | - |
247 | | - #?(:cljs cljs.core/PersistentHashMap) |
248 | | - #?(:cljs (-left-undiff [m] |
249 | | - (into {} |
250 | | - (comp (remove #(instance? Insertion (key %))) |
251 | | - (map (juxt (comp left-undiff key) (comp left-undiff val)))) |
252 | | - m))) |
253 | | - #?(:cljs (-right-undiff [m] |
254 | | - (into {} |
255 | | - (comp (remove #(instance? Deletion (key %))) |
256 | | - (map (juxt (comp right-undiff key) (comp right-undiff val)))) |
257 | | - m))) |
258 | | - |
259 | 189 | Mismatch |
260 | | - (-left-undiff [m] (get m :-)) |
261 | | - (-right-undiff [m] (get m :+)) |
| 190 | + (left-undiff [m] (get m :-)) |
| 191 | + (right-undiff [m] (get m :+)) |
262 | 192 |
|
263 | 193 | Insertion |
264 | | - (-right-undiff [m] (get m :+)) |
| 194 | + (right-undiff [m] (get m :+)) |
265 | 195 |
|
266 | 196 | Deletion |
267 | | - (-left-undiff [m] (get m :-))) |
268 | | - |
269 | | -(extend-type nil |
270 | | - Undiff |
271 | | - (-left-undiff [m] m) |
272 | | - (-right-undiff [m] m)) |
273 | | - |
274 | | -#?(:clj (extend Object Undiff {:-left-undiff identity :-right-undiff identity})) |
275 | | -#?(:cljs (extend-type cljs.core/UUID |
276 | | - Undiff |
277 | | - (-left-undiff [m] m) |
278 | | - (-right-undiff [m] m))) |
279 | | - |
280 | | -#?(:cljs (extend-type js/Object |
281 | | - Undiff |
282 | | - (-left-undiff [m] m) |
283 | | - (-right-undiff [m] m))) |
| 197 | + (left-undiff [m] (get m :-)) |
| 198 | + |
| 199 | + nil |
| 200 | + (left-undiff [m] m) |
| 201 | + (right-undiff [m] m) |
| 202 | + |
| 203 | + #?(:clj Object :cljs default) |
| 204 | + (left-undiff [m] m) |
| 205 | + (right-undiff [m] m) |
| 206 | + |
| 207 | + #?@(:clj |
| 208 | + [java.util.List |
| 209 | + (left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
| 210 | + (right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
| 211 | + |
| 212 | + java.util.Set |
| 213 | + (left-undiff [s] (set (left-undiff (seq s)))) |
| 214 | + (right-undiff [s] (set (right-undiff (seq s)))) |
| 215 | + |
| 216 | + java.util.Map |
| 217 | + (left-undiff [m] |
| 218 | + (into {} |
| 219 | + (comp (remove #(instance? Insertion (key %))) |
| 220 | + (map (juxt (comp left-undiff key) (comp left-undiff val)))) |
| 221 | + m)) |
| 222 | + (right-undiff [m] |
| 223 | + (into {} |
| 224 | + (comp (remove #(instance? Deletion (key %))) |
| 225 | + (map (juxt (comp right-undiff key) (comp right-undiff val)))) |
| 226 | + m))] |
| 227 | + |
| 228 | + :cljs |
| 229 | + [cljs.core/List |
| 230 | + (left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
| 231 | + (right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
| 232 | + |
| 233 | + cljs.core/EmptyList |
| 234 | + (left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
| 235 | + (right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
| 236 | + |
| 237 | + cljs.core/PersistentHashSet |
| 238 | + (left-undiff [s] (set (left-undiff (seq s)))) |
| 239 | + (right-undiff [s] (set (right-undiff (seq s)))) |
| 240 | + |
| 241 | + cljs.core/PersistentTreeSet |
| 242 | + (left-undiff [s] (set (left-undiff (seq s)))) |
| 243 | + (right-undiff [s] (set (right-undiff (seq s)))) |
| 244 | + |
| 245 | + cljs.core/PersistentVector |
| 246 | + (left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
| 247 | + (right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
| 248 | + |
| 249 | + cljs.core/KeySeq |
| 250 | + (left-undiff [s] (map left-undiff (remove #(instance? Insertion %) s))) |
| 251 | + (right-undiff [s] (map right-undiff (remove #(instance? Deletion %) s))) |
| 252 | + |
| 253 | + cljs.core/PersistentArrayMap |
| 254 | + (left-undiff [m] |
| 255 | + (into {} |
| 256 | + (comp (remove #(instance? Insertion (key %))) |
| 257 | + (map (juxt (comp left-undiff key) (comp left-undiff val)))) |
| 258 | + m)) |
| 259 | + (right-undiff [m] |
| 260 | + (into {} |
| 261 | + (comp (remove #(instance? Deletion (key %))) |
| 262 | + (map (juxt (comp right-undiff key) (comp right-undiff val)))) |
| 263 | + m)) |
| 264 | + |
| 265 | + cljs.core/PersistentHashMap |
| 266 | + (left-undiff [m] |
| 267 | + (into {} |
| 268 | + (comp (remove #(instance? Insertion (key %))) |
| 269 | + (map (juxt (comp left-undiff key) (comp left-undiff val)))) |
| 270 | + m)) |
| 271 | + (right-undiff [m] |
| 272 | + (into {} |
| 273 | + (comp (remove #(instance? Deletion (key %))) |
| 274 | + (map (juxt (comp right-undiff key) (comp right-undiff val)))) |
| 275 | + m)) |
| 276 | + |
| 277 | + cljs.core/UUID |
| 278 | + (left-undiff [m] m) |
| 279 | + (right-undiff [m] m)])) |
0 commit comments