Skip to content

Commit cc707b2

Browse files
[inspect] Add :table view-mode
1 parent 515486a commit cc707b2

File tree

3 files changed

+168
-3
lines changed

3 files changed

+168
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
* [#328](https://github.com/clojure-emacs/orchard/pull/328): Inspector: display identity hashcode for Java objects.
66
* [#329](https://github.com/clojure-emacs/orchard/pull/329): Inspector: add analytics.
7+
* [#331](https://github.com/clojure-emacs/orchard/pull/331): Inspector: add table view mode.
78

89
## 0.31.1 (2025-03-19)
910

src/orchard/inspect.clj

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
[orchard.print :as print])
1616
(:import
1717
(java.lang.reflect Constructor Field Method Modifier)
18-
(java.util List Map)))
18+
(java.util Arrays List Map)))
1919

2020
;;
2121
;; Navigating Inspector State
@@ -34,7 +34,7 @@
3434
(list 'get key)))
3535
(conj path '<unknown>)))
3636

37-
(def ^:private supported-view-modes #{:normal :object})
37+
(def ^:private supported-view-modes #{:normal :object :table})
3838

3939
(def ^:private default-inspector-config
4040
"Default configuration values for the inspector."
@@ -371,6 +371,57 @@
371371
inspector
372372
mappable))
373373

374+
(defn supports-table-view-mode?
375+
"Return whether the inspected object can be rendered in :table view-mode."
376+
[{:keys [chunk value] :as _inspector}]
377+
(let [val (or chunk value)]
378+
(and (#{:list :array} (object-type val))
379+
(#{:list :array :map} (object-type (first val))))))
380+
381+
(defn- render-chunk-as-table [inspector chunk idx-starts-from]
382+
(let [m-i map-indexed
383+
fst (first chunk)
384+
;; If items are maps, use map keys as keys. Otherwise assume items are
385+
;; lists/vectors, so we use indices as keys.
386+
getter (if (map? fst) get nth)
387+
ks (vec (if (map? fst)
388+
(keys fst)
389+
(range (count fst))))
390+
pr-rows (into []
391+
(m-i (fn [i row]
392+
(let [i (+ i idx-starts-from)]
393+
(into [[i (str i)]]
394+
(map (fn [k]
395+
(let [v (getter row k)]
396+
[v (print/print-str v)])))
397+
ks))))
398+
chunk)
399+
pr-ks (into [["#" "#"]] (map (fn [k] [k (print/print-str k)])) ks)
400+
401+
widths (m-i (fn [i [_ pr-k]]
402+
(apply max (count pr-k)
403+
(map #(count (second (nth % i))) pr-rows)))
404+
pr-ks)
405+
pad #(String. (doto (char-array %1) (Arrays/fill ^char %2)))
406+
spacers (mapv #(fn [c] (pad (- % c) \space)) widths)
407+
divider (format "|-%s-|" (str/join "-+-" (map #(pad % \-) widths)))
408+
409+
render-row #(as-> %1 ins
410+
(render-indent ins "| ")
411+
(reduce-kv (fn [ins i [val pr-val]]
412+
(-> ins
413+
(render ((nth spacers i) (count pr-val)))
414+
(render-value val {:display-value pr-val})
415+
(render " | ")))
416+
ins %2)
417+
(render-ln ins))]
418+
(as-> inspector ins
419+
(render-ln ins)
420+
(render-row ins pr-ks)
421+
(render-indent ins)
422+
(render-ln ins divider)
423+
(reduce render-row ins pr-rows))))
424+
374425
(defn- render-indexed-chunk
375426
"Render an indexed chunk of values. Renders all values in `chunk`, so `chunk`
376427
must be finite. If `mark-values?` is true, attach the indices to the values in
@@ -409,7 +460,9 @@
409460
(defn- render-items [inspector items map? start-idx mark-values?]
410461
(if map?
411462
(render-map-values inspector items mark-values?)
412-
(render-indexed-chunk inspector items start-idx mark-values?)))
463+
(if (and (= (:view-mode inspector) :table) (supports-table-view-mode? inspector))
464+
(render-chunk-as-table inspector items start-idx)
465+
(render-indexed-chunk inspector items start-idx mark-values?))))
413466

414467
(defn- render-value-maybe-expand
415468
"If `obj` is a collection smaller than page-size, then render it as a

test/orchard/inspect_test.clj

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,6 +1448,117 @@
14481448
" " [:value "_hash" pos?] " = " [:value "0" pos?] [:newline]])
14491449
(section "Instance fields" rendered)))))
14501450

1451+
(deftest table-view-mode-test
1452+
(testing "in :table view-mode lists of maps are rendered as tables"
1453+
(let [rendered (-> (for [i (range 5)]
1454+
{:a (- i)
1455+
:bb (str i i i)
1456+
:ccc (range i 0 -1)})
1457+
(inspect/start)
1458+
(inspect/set-view-mode :table)
1459+
render)]
1460+
(is+ ["--- Contents:" [:newline] [:newline]
1461+
" | " [:value "#" pos?] " | " [:value ":a" pos?] " | "
1462+
[:value ":bb" pos?] " | " [:value ":ccc" pos?] " | " [:newline]
1463+
" |---+----+-------+-----------|" [:newline]
1464+
" | " [:value "0" pos?] " | " [:value "0" pos?] " | "
1465+
[:value "\"000\"" pos?] " | " [:value "()" pos?] " | " [:newline]
1466+
" | " [:value "1" pos?] " | " [:value "-1" pos?] " | "
1467+
[:value "\"111\"" pos?] " | " [:value "(1)" pos?] " | " [:newline]
1468+
" | " [:value "2" pos?] " | " [:value "-2" pos?] " | "
1469+
[:value "\"222\"" pos?] " | " [:value "(2 1)" pos?] " | " [:newline]
1470+
" | " [:value "3" pos?] " | " [:value "-3" pos?] " | "
1471+
[:value "\"333\"" pos?] " | " [:value "(3 2 1)" pos?] " | " [:newline]
1472+
" | " [:value "4" pos?] " | " [:value "-4" pos?] " | "
1473+
[:value "\"444\"" pos?] " | " [:value "(4 3 2 1)" pos?] " | " [:newline]
1474+
[:newline]]
1475+
(section "Contents" rendered))
1476+
(is+ ["--- View mode:" [:newline] " :table"]
1477+
(section "View mode" rendered))))
1478+
1479+
(testing "in :table view-mode lists of vectors are rendered as tables"
1480+
(let [rendered (-> (for [i (range 5)]
1481+
[(- i) (str i i i) (range i 0 -1)])
1482+
(inspect/start)
1483+
(inspect/set-view-mode :table)
1484+
render)]
1485+
(is+ ["--- Contents:" [:newline] [:newline]
1486+
" | " [:value "#" pos?] " | " [:value "0" pos?] " | "
1487+
[:value "1" pos?] " | " [:value "2" pos?] " | " [:newline]
1488+
" |---+----+-------+-----------|" [:newline]
1489+
" | " [:value "0" pos?] " | " [:value "0" pos?] " | "
1490+
[:value "\"000\"" pos?] " | " [:value "()" pos?] " | " [:newline]
1491+
" | " [:value "1" pos?] " | " [:value "-1" pos?] " | "
1492+
[:value "\"111\"" pos?] " | " [:value "(1)" pos?] " | " [:newline]
1493+
" | " [:value "2" pos?] " | " [:value "-2" pos?] " | "
1494+
[:value "\"222\"" pos?] " | " [:value "(2 1)" pos?] " | " [:newline]
1495+
" | " [:value "3" pos?] " | " [:value "-3" pos?] " | "
1496+
[:value "\"333\"" pos?] " | " [:value "(3 2 1)" pos?] " | " [:newline]
1497+
" | " [:value "4" pos?] " | " [:value "-4" pos?] " | "
1498+
[:value "\"444\"" pos?] " | " [:value "(4 3 2 1)" pos?] " | " [:newline]
1499+
[:newline]]
1500+
(section "Contents" rendered))
1501+
(is+ ["--- View mode:" [:newline] " :table"]
1502+
(section "View mode" rendered))))
1503+
1504+
(testing "doesn't break if table mode is requested for unsupported value"
1505+
(let [rendered (-> {:a 1}
1506+
(inspect/start)
1507+
(inspect/set-view-mode :table)
1508+
render)]
1509+
(is+ ["--- Contents:" [:newline]
1510+
" " [:value ":a" pos?] " = " [:value "1" pos?] [:newline]
1511+
[:newline]]
1512+
(section "Contents" rendered))))
1513+
1514+
(testing "works with paging"
1515+
(let [rendered (-> (map #(vector % %) (range 9))
1516+
(inspect/start)
1517+
(set-page-size 3)
1518+
(inspect/set-view-mode :table)
1519+
render)]
1520+
(is+ ["--- Contents:" [:newline] [:newline]
1521+
" | " [:value "#" pos?] " | " [:value "0" pos?] " | " [:value "1" pos?] " | " [:newline]
1522+
" |---+---+---|" [:newline]
1523+
" | " [:value "0" pos?] " | " [:value "0" pos?] " | " [:value "0" pos?] " | " [:newline]
1524+
" | " [:value "1" pos?] " | " [:value "1" pos?] " | " [:value "1" pos?] " | " [:newline]
1525+
" | " [:value "2" pos?] " | " [:value "2" pos?] " | " [:value "2" pos?] " | " [:newline]
1526+
" ..." [:newline] [:newline]]
1527+
(section "Contents" rendered)))
1528+
1529+
(let [rendered (-> (map #(vector % %) (range 9))
1530+
(inspect/start)
1531+
(set-page-size 3)
1532+
(inspect/next-page)
1533+
(inspect/set-view-mode :table)
1534+
render)]
1535+
(is+ ["--- Contents:" [:newline]
1536+
" ..." [:newline] [:newline]
1537+
" | " [:value "#" pos?] " | " [:value "0" pos?] " | " [:value "1" pos?] " | " [:newline]
1538+
" |---+---+---|" [:newline]
1539+
" | " [:value "3" pos?] " | " [:value "3" pos?] " | " [:value "3" pos?] " | " [:newline]
1540+
" | " [:value "4" pos?] " | " [:value "4" pos?] " | " [:value "4" pos?] " | " [:newline]
1541+
" | " [:value "5" pos?] " | " [:value "5" pos?] " | " [:value "5" pos?] " | " [:newline]
1542+
" ..." [:newline] [:newline]]
1543+
(section "Contents" rendered)))
1544+
1545+
(let [rendered (-> (map #(vector % %) (range 9))
1546+
(inspect/start)
1547+
(set-page-size 3)
1548+
(inspect/next-page)
1549+
(inspect/next-page)
1550+
(inspect/set-view-mode :table)
1551+
render)]
1552+
(is+ ["--- Contents:" [:newline]
1553+
" ..." [:newline] [:newline]
1554+
" | " [:value "#" pos?] " | " [:value "0" pos?] " | " [:value "1" pos?] " | " [:newline]
1555+
" |---+---+---|" [:newline]
1556+
" | " [:value "6" pos?] " | " [:value "6" pos?] " | " [:value "6" pos?] " | " [:newline]
1557+
" | " [:value "7" pos?] " | " [:value "7" pos?] " | " [:value "7" pos?] " | " [:newline]
1558+
" | " [:value "8" pos?] " | " [:value "8" pos?] " | " [:value "8" pos?] " | " [:newline]
1559+
[:newline]]
1560+
(section "Contents" rendered)))))
1561+
14511562
(deftest tap-test
14521563
(testing "tap-current-value"
14531564
(let [proof (atom [])

0 commit comments

Comments
 (0)