diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e66cff8..a246ed32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ ### New features * [#42](https://github.com/clojure-emacs/orchard/pull/42): Add namespace alias support for `spec-list` and `spec-form`. +* [#46](https://github.com/clojure-emacs/orchard/pull/46): [Inspector] Separate static from non-static fields when rendering raw objects. +* [#46](https://github.com/clojure-emacs/orchard/pull/46): [Inspector] Show fields inherited from superclasses when rendering raw objects. ## 0.4.0 (2019-01-14) diff --git a/project.clj b/project.clj index 1eeb8580..7075fa57 100644 --- a/project.clj +++ b/project.clj @@ -57,7 +57,8 @@ :cljfmt {:plugins [[lein-cljfmt "0.5.7"]] :cljfmt {:indents {as-> [[:inner 0]] with-debug-bindings [[:inner 0]] - merge-meta [[:inner 0]]}}} + merge-meta [[:inner 0]] + letfn [[:block 1] [:inner 2]]}}} :eastwood {:plugins [[jonase/eastwood "0.3.4"]] :eastwood {:config-files ["eastwood.clj"]}}}) diff --git a/src/orchard/inspect.clj b/src/orchard/inspect.clj index 823399f7..a18d4ac7 100644 --- a/src/orchard/inspect.clj +++ b/src/orchard/inspect.clj @@ -11,7 +11,7 @@ (:require [clojure.string :as s]) (:import - (java.lang.reflect Field) + (java.lang.reflect Field Modifier) (java.util List Map) clojure.lang.Seqable)) @@ -413,21 +413,39 @@ (render "Value: " (pr-str obj)))) (defmethod inspect :default [inspector obj] - (let [^"[Ljava.lang.reflect.Field;" fields (.getDeclaredFields (class obj)) - names (map #(.getName ^Field %) fields) - get (fn [^Field f] + (let [class-chain (loop [c (class obj), res ()] + (if c + (recur (.getSuperclass c) (cons c res)) + res)) + all-fields (mapcat #(.getDeclaredFields ^Class %) class-chain) + + {static true, non-static false} + (group-by #(Modifier/isStatic (.getModifiers ^Field %)) all-fields)] + (letfn [(field-name [^Field f] + (.getName f)) + + (field-val [^Field f] (try (.setAccessible f true) (catch java.lang.SecurityException e)) (try (.get f obj) (catch java.lang.IllegalAccessException e "Access denied."))) - vals (map get fields)] - (-> inspector - (render-labeled-value "Type" (class obj)) - (render-labeled-value "Value" (pr-str obj)) - (render-ln "---") - (render-ln "Fields: ") - (render-map-values (zipmap names vals))))) + + (render-fields [inspector section-name fields] + (if (seq fields) + (-> inspector + (render-ln section-name) + (render-map-values (->> fields + (map (fn [f] [(field-name f) (field-val f)])) + (into (sorted-map)))) + (render-ln)) + inspector))] + (-> inspector + (render-labeled-value "Class" (class obj)) + (render-labeled-value "Value" (pr-str obj)) + (render-ln "---") + (render-fields "Fields:" non-static) + (render-fields "Static fields:" static))))) (defn- render-class-section [inspector obj section] (let [method (symbol (str ".get" (name section))) @@ -454,12 +472,12 @@ (defmethod inspect :class [inspector ^Class obj] (reduce (partial render-section obj) - (render-labeled-value inspector "Type" (class obj)) + (render-labeled-value inspector "Class" (class obj)) [:Interfaces :Constructors :Fields :Methods])) (defmethod inspect :aref [inspector ^clojure.lang.ARef obj] (-> inspector - (render-labeled-value "Type" (class obj)) + (render-labeled-value "Class" (class obj)) (render-ln "Contains:") (render-ln) (inspect (deref obj)))) diff --git a/test/orchard/inspect_test.clj b/test/orchard/inspect_test.clj index 3c51589c..28076670 100644 --- a/test/orchard/inspect_test.clj +++ b/test/orchard/inspect_test.clj @@ -21,6 +21,8 @@ (def java-hashmap-inspect-result ["(\"Class\" \": \" (:value \"java.util.HashMap\" 0) (:newline) \"Contents: \" (:newline) \" \" (:value \":b\" 1) \" = \" (:value \"2\" 2) (:newline) \" \" (:value \":c\" 3) \" = \" (:value \"3\" 4) (:newline) \" \" (:value \":a\" 5) \" = \" (:value \"1\" 6) (:newline))"]) +(def tagged-literal-inspect-result ["(\"Class\" \": \" (:value \"clojure.lang.TaggedLiteral\" 0) (:newline) \"Value\" \": \" (:value \"\\\"#foo ()\\\"\" 1) (:newline) \"---\" (:newline) \"Fields:\" (:newline) \" \" (:value \"\\\"form\\\"\" 2) \" = \" (:value \"()\" 3) (:newline) \" \" (:value \"\\\"tag\\\"\" 4) \" = \" (:value \"foo\" 5) (:newline) (:newline) \"Static fields:\" (:newline) \" \" (:value \"\\\"FORM_KW\\\"\" 6) \" = \" (:value \":form\" 7) (:newline) \" \" (:value \"\\\"TAG_KW\\\"\" 8) \" = \" (:value \":tag\" 9) (:newline) (:newline))"]) + (def long-sequence (range 70)) (def long-vector (vec (range 70))) (def long-map (zipmap (range 70) (range 70))) @@ -275,6 +277,12 @@ (render (inspect/start (inspect/fresh) (java.util.HashMap. {:a 1, :b 2, :c 3}))))))) +(deftest inspect-java-object-test + (testing "inspecting any Java object prints its fields" + (is (= tagged-literal-inspect-result + (render (inspect/start (inspect/fresh) + (clojure.lang.TaggedLiteral/create 'foo ()))))))) + (deftest inspect-path (testing "inspector keeps track of the path in the inspected structure" (let [t {:a (list 1 2 {:b {:c (vec (map (fn [x] {:foo (* x 10)}) (range 100)))}})