diff --git a/CHANGES.md b/CHANGES.md index e62d2b7..0a54c65 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +## 3.6.2 -- UNRELEASED + +- Fix a bug where the horizontal bar for repeated stack frames could be misaligned +- Hide `clojure.lang.RestFn` stack frames + +[Closed Issues](https://github.com/clj-commons/pretty/milestone/61?closed=1) + ## 3.6.1 -- 4 Aug 2025 Fix a bug where omitted stack frames were printed. diff --git a/docs/images/exception-repeat.png b/docs/images/exception-repeat.png index 8f52e9f..3582fec 100644 Binary files a/docs/images/exception-repeat.png and b/docs/images/exception-repeat.png differ diff --git a/src/clj_commons/format/exceptions.clj b/src/clj_commons/format/exceptions.clj index 8fe6a29..f6d050f 100644 --- a/src/clj_commons/format/exceptions.clj +++ b/src/clj_commons/format/exceptions.clj @@ -116,13 +116,14 @@ The default rules: * omit everything in `clojure.lang`, `java.lang.reflect` - * omit `clojure.core/with-bindings*` and `clojure.core/apply` + * hide `clojure.core/with-bindings*` and `clojure.core/apply`, `clojure.lang.RestFn` * hide everything in `sun.reflect` * omit a number of functions in `clojure.test` * terminate at `speclj.*`, `clojure.main/.*`, or `nrepl.middleware.interruptible-eval` " [[:name "clojure.core/apply" :hide] [:id #"\Qclojure.lang.AFn.applyTo\E(Helper)?.*" :hide] + [:id #"\Qclojure.lang.RestFn.\E.*" :hide] [:package "clojure.lang" :omit] [:name "clojure.core/with-bindings*" :hide] [:package #"sun\.reflect.*" :hide] @@ -382,7 +383,6 @@ dec)] (assoc frame :name-width width - :line (str line) :name [(get *fonts* (clj-frame-font-key frame)) (->> names' drop-last (str/join "/")) "/" @@ -401,7 +401,7 @@ Formatting is based on whether the frame is omitted, and whether it is a Clojure or Java frame." {:added "0.3.0"} - [{:keys [file line names repeats] :as frame}] + [{:keys [line names] :as frame}] (cond (:omitted frame) (assoc frame @@ -415,8 +415,7 @@ (let [full-name (str (:class frame) "." (:method frame))] (assoc frame :name [(:java-frame *fonts*) full-name] - :name-width (length full-name) - :line (str line))) + :name-width (length full-name))) :else (format-clojure-frame-base frame))) @@ -536,29 +535,35 @@ max-name-width (max-from rows :name-width) ;; Allow for the colon in frames w/ a line number (this assumes there's at least one) max-file-width (inc (max-from rows #(-> % :file length))) - max-line-width (max-from rows #(-> % :line length)) + max-line-width (max-from rows #(-> % :line str length)) *lines (volatile! (transient [])) format-single-frame (fn [{:keys [name file line]} repeat-count frame-index frame-count] - (list - [{:width max-name-width} name] - " " - [{:width max-file-width - :font source-font} file] - (when line ":") - " " - [{:width max-line-width} line] - (when (> repeat-count 1) - (cond - (= frame-index 0) - (format " %s (repeats %,d times)" - (if (= 1 frame-count) "─" "┐") - repeat-count) - - (= frame-index (dec frame-count)) - " ┘" - - :else - " │"))))] + (let [has-repeat? (> repeat-count 1) + show-line? (or line has-repeat?) + show-file? (or show-line? file)] + (list + [{:width max-name-width} name] + (when show-file? + (list + " " + [{:width max-file-width + :font source-font} file] + (when show-line? + (list + (if line ":" " ") + [{:width max-line-width} line] + (when (> repeat-count 1) + (cond + (= frame-index 0) + (format " %s (repeats %,d times)" + (if (= 1 frame-count) "─" "┐") + repeat-count) + + (= frame-index (dec frame-count)) + " ┘" + + :else + " │")))))))))] (doseq [[repeat-count frames] (repetitions :id rows) [frame-index frame] (map-indexed vector frames)] (vswap! *lines diff --git a/test/clj_commons/exception_test.clj b/test/clj_commons/exception_test.clj index 23a7c55..514d185 100644 --- a/test/clj_commons/exception_test.clj +++ b/test/clj_commons/exception_test.clj @@ -69,17 +69,6 @@ "tcp-client"] :package "riemann" :simple-class "client$tcp_client"} - {:class "clojure.lang.RestFn" - :file "RestFn.java" - :id "clojure.lang.RestFn.invoke:408" - :is-clojure? false - :line 408 - :method "invoke" - :name "" - :names [] - :omitted true - :package "clojure.lang" - :simple-class "RestFn"} {:class "com.example.error_monitor$make_connection" :file "error_monitor.clj" :id "com.example.error-monitor/make-connection:22" @@ -214,17 +203,6 @@ "-main"] :package "com.example.error_monitor" :simple-class "main$_main"} - {:class "clojure.lang.RestFn" - :file "RestFn.java" - :id "clojure.lang.RestFn.applyTo:137" - :is-clojure? false - :line 137 - :method "applyTo" - :name "" - :names [] - :omitted true - :package "clojure.lang" - :simple-class "RestFn"} {:class "com.example.error_monitor.main" :file "" :id "com.example.error_monitor.main.main:-1" diff --git a/test/demo.clj b/test/demo.clj index 16c2815..10cf879 100644 --- a/test/demo.clj +++ b/test/demo.clj @@ -65,12 +65,12 @@ (countdown (dec n)))) (defn nested-interloper - [f arg] - (f arg)) + [f & arg] + (apply f arg)) (defn interloper - [f arg] - (nested-interloper f arg)) + [& args] + (apply nested-interloper args)) (defn countdown-alt [n]