|
46 | 46 | (defn get-last-object-from-current-history []
|
47 | 47 | (first (get-current-history))) ; note the list is reversed
|
48 | 48 |
|
| 49 | +(defn get-second-last-object-from-current-history [] |
| 50 | + (second (get-current-history))) ; note the list is reversed |
| 51 | + |
49 | 52 | (defn present-path-segment [v]
|
50 | 53 | (cond
|
51 | 54 | (string? v) v
|
52 |
| - (keyword? v) (str v) |
| 55 | + ;; we'd like to preserve keywords for easy get |
| 56 | + (keyword? v) v |
53 | 57 | (number? v) v
|
54 | 58 | :else "?"))
|
55 | 59 |
|
56 | 60 | (defn seek-path-segment [coll val]
|
57 | 61 | (let [* (fn [[k v]]
|
58 |
| - (if (identical? v val) |
| 62 | + (cond |
| 63 | + ;; we need to know the paths for keywords, these are clickable |
| 64 | + (identical? k val) |
| 65 | + (present-path-segment k) |
| 66 | + |
| 67 | + (identical? v val) |
59 | 68 | (present-path-segment k)))]
|
60 | 69 | (some * coll)))
|
61 | 70 |
|
|
64 | 73 | (map? parent-object) (seek-path-segment (seq parent-object) object)
|
65 | 74 | (sequential? parent-object) (seek-path-segment (map-indexed (fn [i x] [i x]) parent-object) object)))
|
66 | 75 |
|
| 76 | +;; This function checks a unique situation of looping an immediate child element `obj` of a parent element `history` |
| 77 | +;; say we have a general map {:a 2 :b {:gh 45} :c 4} |
| 78 | +;; and we call devtools.formatters.core/body-api-call with the map, the map ends up in |
| 79 | +;; devtools.formatters.markup/<details> which then calls devtools.formatters.markup/body-lines |
| 80 | +;; where the map will get seq'd resulting in ([:a 2] [:b {:gh 45}] [:c 4]) |
| 81 | +;; these 3 vectors will then be pushed to history which will result in an issue when generating the path |
| 82 | +;; for example if we are looping over at `obj` as 2 and `history` as `[:a 2]` `build-path-segment` will return |
| 83 | +;; the path as 1 since the immediate history is a vector instead of a map. |
| 84 | +;; This function detects the condition that this is the case and then the next operation will be to |
| 85 | +;; get the first item in the vector which is the path. |
| 86 | +(defn mapping? |
| 87 | + [history obj] |
| 88 | + (let [obj-kw (when (and (vector? obj) |
| 89 | + (= (count obj) 2) |
| 90 | + ;; the map keys must always be one of these |
| 91 | + (or |
| 92 | + (-> obj first keyword?) |
| 93 | + (-> obj first string?) |
| 94 | + (-> obj first number?))) |
| 95 | + (first obj))] |
| 96 | + (when (and (map? history) obj-kw) |
| 97 | + (contains? history obj-kw)))) |
| 98 | + |
67 | 99 | (defn extend-path-info [path-info object]
|
68 | 100 | (let [parent-object (get-last-object-from-current-history)]
|
69 |
| - (if-some [path-segment (build-path-segment parent-object object)] |
70 |
| - (conj (or path-info []) path-segment) |
71 |
| - path-info))) |
| 101 | + (cond |
| 102 | + ;; if the current item we are looping at is an artificial vector (explained at `mapping` above), |
| 103 | + ;; dont append to the path |
| 104 | + (and (map? parent-object) (mapping? parent-object object)) |
| 105 | + path-info |
| 106 | + ;; if the previous item is an artificial vector, lets append to the path info but take the first item |
| 107 | + ;; in the vector as the path. (Explained in `mapping` above) |
| 108 | + (and (map? (get-second-last-object-from-current-history)) |
| 109 | + (mapping? (get-second-last-object-from-current-history) parent-object)) |
| 110 | + (conj (or path-info []) (first parent-object)) |
| 111 | + ;; the current object is an item within the parent object |
| 112 | + (some? (build-path-segment parent-object object)) |
| 113 | + (conj (or path-info []) (build-path-segment parent-object object)) |
| 114 | + :else path-info))) |
72 | 115 |
|
73 | 116 | (defn add-object-to-current-path-info! [object]
|
74 | 117 | (update-current-state! update :path-info extend-path-info object))
|
|
0 commit comments