Skip to content

Commit 8d3cc29

Browse files
committed
Introduce orchard.inspect/nav-to-item
Closes #209
1 parent 2850bf1 commit 8d3cc29

File tree

4 files changed

+69
-37
lines changed

4 files changed

+69
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## master (unreleased)
44

5+
## New features
6+
7+
* [#209](https://github.com/clojure-emacs/orchard/issues/209): add `orchard.inspect/nav-to-item` function which enables [nav](https://clojure.github.io/clojure/branch-master/clojure.datafy-api.html#clojure.datafy/nav)ing to a specific sub-item within a given Inspector value.
8+
59
## 0.20.0 (2023-11-11)
610

711
## New features

project.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@
8484
`add-cognitest)]}
8585

8686
;; Development tools
87-
:dev {:plugins [[cider/cider-nrepl "0.43.1"]
87+
:dev {:plugins [[cider/cider-nrepl "0.43.3"]
8888
[refactor-nrepl "3.9.0"]]
8989
:dependencies [[nrepl/nrepl "1.0.0"]
9090
[org.clojure/tools.namespace "1.4.4"]]

src/orchard/inspect.clj

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,14 @@
120120
(assoc :path new-path)
121121
(inspect-render new))))
122122

123+
(defn- nth-wrapper? [x]
124+
(and (seq? x)
125+
(= 'nth (first x))))
126+
123127
(defn- sibling* [inspector offset pred]
124128
(let [path (:path inspector)
125129
last-item (peek path)]
126-
(or (when (and (seq? last-item)
127-
(= 'nth (first last-item)))
130+
(or (when (nth-wrapper? last-item)
128131
(let [new-index (+ offset ;; for our purposes, +2 means inc, +1 nop, and +0 dec
129132
(second last-item))
130133
top (up inspector)]
@@ -150,6 +153,34 @@
150153
(< index
151154
(-> inspector :index count)))))
152155

156+
(defn nav-to-item
157+
"Shows the value per `nav` for the item at `idx`,
158+
relative to the `inspector`'s current value."
159+
[inspector idx]
160+
(cond
161+
(not nav)
162+
inspector
163+
164+
(some #{'<unknown>} (:path inspector))
165+
inspector
166+
167+
:else
168+
(let [{:keys [index value path current-page page-size]} inspector
169+
new-path (push-item-to-path index idx path current-page page-size)
170+
normalized-path (mapv (fn [x]
171+
(cond-> x
172+
(nth-wrapper? x) second))
173+
new-path)
174+
k (peek normalized-path)
175+
v (get value k)]
176+
(if (some #{'<unknown>} new-path)
177+
inspector
178+
(-> (update-in inspector [:stack] conj value)
179+
(update-in [:pages-stack] conj current-page)
180+
(assoc :current-page 0)
181+
(assoc :path new-path)
182+
(inspect-render (nav value k v)))))))
183+
153184
(defn next-page
154185
"Jump to the next page when inspecting a paginated sequence/map. Does nothing
155186
if already on the last page."
@@ -508,19 +539,25 @@
508539
(unindent))
509540
inspector))
510541

511-
(defn- nav-datafy-tx [obj remove-nil-valued-entries?]
542+
(defn- nav-datafy-tx [remove-nil-valued-entries?]
512543
(keep (fn [[k v]]
513-
(or (some->> (nav obj k v)
514-
(datafy)
515-
(vector k))
516-
(when (and (nil? v)
517-
(not remove-nil-valued-entries?))
518-
[k v])))))
544+
(or
545+
;; NOTE: this `v` used to be a `(nav obj k v)` call.
546+
;; Per Clojure's default implementation of Nav, that's equivalent to `v`.
547+
;; Calling nav is worse since in practice it means that we're eagerly invoking `nav`,
548+
;; which for custom implementations can mean that side-effects (like http or JDBC navigation) would be triggered
549+
;; before the user has expressed any intent to use those side-effects.
550+
(some->> v
551+
(datafy)
552+
(vector k))
553+
(when (and (nil? v)
554+
(not remove-nil-valued-entries?))
555+
[k v])))))
519556

520557
(defn- nav-datafy [obj remove-nil-valued-entries?]
521558
(let [data (datafy obj)]
522559
(cond (map? data)
523-
(into {} (nav-datafy-tx obj remove-nil-valued-entries?) data)
560+
(into {} (nav-datafy-tx remove-nil-valued-entries?) data)
524561
(or (sequential? data) (set? data))
525562
(map datafy data))))
526563

test/orchard/inspect_test.clj

Lines changed: 17 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,23 @@
443443
inspect/next-sibling
444444
inspect/next-sibling)))))
445445

446+
(when datafy?
447+
(deftest nav-to-item-test
448+
(let [proof (atom [])]
449+
(is (= 99
450+
(-> {:foo [(with-meta {1 1}
451+
{'clojure.core.protocols/nav (fn [self _ _]
452+
(swap! proof conj self)
453+
99)})]}
454+
inspect
455+
(inspect/down 2)
456+
(inspect/down 1)
457+
(inspect/nav-to-item 1)
458+
:value))
459+
"Inspects the value per its `nav` representation - not the value itself")
460+
(is (= [{1 1}] @proof)
461+
"Nav is performed exactly once"))))
462+
446463
(deftest path-test
447464
(testing "inspector tracks the path in the data structure"
448465
(is (= "(find 50) key" (-> long-map inspect (inspect/down 39) render last)))
@@ -941,32 +958,6 @@
941958
(:newline))
942959
(datafy-section rendered))))))))
943960

944-
(deftest inspect-navigable-metadata-extension-test
945-
(testing "inspecting a map extended with the Navigable protocol"
946-
(let [rendered (-> (extend-nav-vector {:name "John Doe"}) inspect render)]
947-
(testing "renders the header"
948-
(is (match? '("Class"
949-
": "
950-
(:value "clojure.lang.PersistentArrayMap" 0)
951-
(:newline)
952-
(:newline))
953-
(header rendered))))
954-
(testing "renders the meta information section"
955-
(is (match? '("--- Meta Information:"
956-
(:newline)
957-
" " (:value "clojure.core.protocols/nav" 1)
958-
" = " (:value "orchard.inspect_test$extend_nav_vector$fn" 2)
959-
(:newline)
960-
(:newline))
961-
(demunge (section "Meta Information" rendered)))))
962-
(when datafy?
963-
(testing "renders the datafy section"
964-
(is (match? '("--- Datafy:"
965-
(:newline)
966-
" " (:value ":name" 5) " = " (:value "[ :name \"John Doe\" ]" 6)
967-
(:newline))
968-
(datafy-section rendered))))))))
969-
970961
(deftest inspect-throwable-test
971962
(testing "inspecting a throwable"
972963
(let [rendered (-> (doto ^Throwable (ex-info "BOOM" {})

0 commit comments

Comments
 (0)