Skip to content

Commit 868047d

Browse files
[inspect] Add hexdump view-mode
1 parent d7a0991 commit 868047d

File tree

3 files changed

+102
-13
lines changed

3 files changed

+102
-13
lines changed

CHANGELOG.md

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

33
## master (unreleased)
44

5+
- [#342](https://github.com/clojure-emacs/orchard/pull/342): Inspector: add hexdump view mode.
6+
57
## 0.34.3 (2025-04-28)
68

79
- Inspector: fix multiple frequencies not shown for the same value in analytics.

src/orchard/inspect.clj

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
(list 'get key)))
3636
(conj path '<unknown>)))
3737

38-
(def ^:private supported-view-modes #{:normal :object :table})
38+
(def ^:private supported-view-modes #{:normal :object :table :hex})
3939

4040
(def ^:private default-inspector-config
4141
"Default configuration values for the inspector."
@@ -121,8 +121,11 @@
121121
(defn- decide-if-paginated
122122
"Make early decision if the inspected object should be paginated. If so,
123123
assoc the `:chunk` to be displayed to `inspector`."
124-
[{:keys [value current-page page-size] :as inspector}]
125-
(let [pageable? (boolean (#{:list :map :set :array} (object-type value)))]
124+
[{:keys [value current-page page-size view-mode] :as inspector}]
125+
(let [pageable? (boolean (#{:list :map :set :array} (object-type value)))
126+
page-size (if (= view-mode :hex)
127+
(* page-size 16) ;; In hex view mode, each row is 16 bytes.
128+
page-size)]
126129
(cond-> (assoc inspector :pageable pageable?)
127130
pageable? (merge (pagination-info value page-size current-page)))))
128131

@@ -653,6 +656,45 @@
653656
(unindent ins))
654657
inspector))
655658

659+
;; Hex view mode
660+
661+
(defn- byte->ascii [b]
662+
(let [c (bit-and b 0xFF)]
663+
(if (and (>= c 32) (<= c 126))
664+
(char c)
665+
;; Use MIDDLE DOT for non-printed chars as it is distinct from 0x2E.
666+
\·)))
667+
668+
(defn- format-hex-row
669+
"Format 16 bytes as hex values."
670+
[bytes]
671+
(let [hex-strs (mapv #(format "%02x" (bit-and % 0xFF)) bytes)
672+
padded (concat hex-strs (repeat (- 16 (count bytes)) " "))
673+
[left-half right-half] (split-at 8 padded)]
674+
(str (str/join " " left-half) " " (str/join " " right-half))))
675+
676+
(defn format-ascii-row
677+
"Format 16 bytes as ASCII characters."
678+
[bytes]
679+
(str/join (map byte->ascii bytes)))
680+
681+
(defn render-hexdump
682+
"Render the current array or array chunk as a hexdump-style table."
683+
[{:keys [value chunk start-idx] :as inspector}]
684+
(let [start-idx (or start-idx 0)
685+
lines (eduction (comp (partition-all 16)
686+
(map-indexed vector))
687+
(or chunk value))]
688+
(as-> inspector ins
689+
(render-leading-page-ellipsis ins)
690+
(reduce (fn [ins [i line]]
691+
(let [addr (+ (* i 16) start-idx)]
692+
(render-indent-ln
693+
ins (format "0x%08x │ %s │ %s" addr (format-hex-row line)
694+
(format-ascii-row line)))))
695+
ins lines)
696+
(render-trailing-page-ellipsis ins))))
697+
656698
;; Inspector multimethod
657699
(defn- dispatch-inspect [{:keys [view-mode] :as _ins} obj]
658700
(if (= view-mode :object)
@@ -683,16 +725,18 @@
683725
(defmethod inspect :map [inspector obj] (inspect-coll inspector obj))
684726

685727
(defmethod inspect :array [inspector obj]
686-
(-> (render-class-name inspector obj)
687-
(render-counted-length obj)
688-
(render-labeled-value "Component Type" (.getComponentType (class obj)))
689-
(render-analytics)
690-
(render-section-header "Contents")
691-
(indent)
692-
(render-collection-paged)
693-
(unindent)
694-
(render-datafy)
695-
(render-page-info)))
728+
(as-> (render-class-name inspector obj) ins
729+
(render-counted-length ins obj)
730+
(render-labeled-value ins "Component Type" (.getComponentType (class obj)))
731+
(render-analytics ins)
732+
(render-section-header ins "Contents")
733+
(indent ins)
734+
(if (= (:view-mode inspector) :hex)
735+
(render-hexdump ins)
736+
(render-collection-paged ins))
737+
(unindent ins)
738+
(render-datafy ins)
739+
(render-page-info ins)))
696740

697741
(defn- render-var-value [inspector ^clojure.lang.Var obj]
698742
(if-not (.isBound obj)

test/orchard/inspect_test.clj

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,49 @@
15611561
(set-page-size 30)
15621562
(inspect/supports-table-view-mode?))))))
15631563

1564+
(deftest hex-view-mode-test
1565+
(testing "in :hex view-mode byte arrays are rendered as hexdump tables"
1566+
(let [rendered (-> (byte-array (range 100))
1567+
inspect
1568+
(inspect/set-view-mode :hex)
1569+
render)]
1570+
(is+ ["--- Contents:" [:newline]
1571+
" 0x00000000 │ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f │ ················" [:newline]
1572+
" 0x00000010 │ 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f │ ················" [:newline]
1573+
" 0x00000020 │ 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f │ !\"#$%&'()*+,-./" [:newline]
1574+
" 0x00000030 │ 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f │ 0123456789:;<=>?" [:newline]
1575+
" 0x00000040 │ 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f │ @ABCDEFGHIJKLMNO" [:newline]
1576+
" 0x00000050 │ 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f │ PQRSTUVWXYZ[\\]^_" [:newline]
1577+
" 0x00000060 │ 60 61 62 63 │ `abc" [:newline] [:newline]]
1578+
(contents-section rendered))
1579+
(is+ ["--- View mode:" [:newline] " :hex"]
1580+
(section rendered "View mode"))))
1581+
1582+
(testing "works with paging"
1583+
(is+ ["--- Contents:" [:newline]
1584+
" 0x00000000 │ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f │ ················" [:newline]
1585+
" 0x00000010 │ 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f │ ················" [:newline]
1586+
" ..." [:newline] [:newline]]
1587+
(-> (byte-array (range 100))
1588+
inspect
1589+
(inspect/set-view-mode :hex)
1590+
(set-page-size 2)
1591+
render
1592+
contents-section))
1593+
1594+
(is+ ["--- Contents:" [:newline]
1595+
" ..." [:newline]
1596+
" 0x00000020 │ 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f │ !\"#$%&'()*+,-./" [:newline]
1597+
" 0x00000030 │ 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f │ 0123456789:;<=>?" [:newline]
1598+
" ..." [:newline] [:newline]]
1599+
(-> (byte-array (range 100))
1600+
inspect
1601+
(inspect/set-view-mode :hex)
1602+
(set-page-size 2)
1603+
inspect/next-page
1604+
render
1605+
contents-section))))
1606+
15641607
(deftest pretty-print-map-test
15651608
(testing "in :pretty view-mode are pretty printed"
15661609
(let [rendered (-> {:a 0

0 commit comments

Comments
 (0)