/
repl.clj
126 lines (109 loc) · 3.96 KB
/
repl.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
(ns clj-stacktrace.repl
(:use clj-stacktrace.core)
(:require [clj-stacktrace.utils :as utils]))
(def color-codes
{:red "\033[31m"
:green "\033[32m"
:yellow "\033[33m"
:blue "\033[34m"
:magenta "\033[35m"
:cyan "\033[36m"
:default "\033[39m"})
(defn- colored
[color? color text]
(if color?
(str (color-codes color) text (color-codes :default))
text))
(defn elem-color
"Returns a symbol identifying the color appropriate for the given trace elem.
:green All Java elems
:yellow Any fn in the user or repl* namespaces (i.e. entered at REPL)
:blue Any fn in clojure.* (e.g. clojure.core, clojure.contrib.*)
:magenta Anything else - i.e. Clojure libraries and app code."
[elem]
(if (:java elem)
(if (utils/re-match? #"^clojure\." (:class elem))
:cyan
:blue)
(cond (nil? (:ns elem)) :yellow
(utils/re-match? #"^(user|repl)" (:ns elem)) :yellow
(utils/re-match? #"^clojure\." (:ns elem)) :magenta
:user-code :green)))
(defn source-str [parsed]
(if (and (:file parsed) (:line parsed))
(str (:file parsed) ":" (:line parsed))
"(Unknown Source)"))
(defn clojure-method-str [parsed]
(str (:ns parsed) "/" (:fn parsed) (if (:annon-fn parsed) "[fn]")))
(defn java-method-str [parsed]
(str (:class parsed) "." (:method parsed)))
(defn method-str [parsed]
(if (:java parsed) (java-method-str parsed) (clojure-method-str parsed)))
(defn pst-class-on [on color? class]
(.append on (colored color? :red (str (.getName class) ": ")))
(.flush on))
(defn pst-message-on [on color? message]
(.append on (colored color? :red message))
(.append on "\n")
(.flush on))
(defn pst-elems-on
[on color? parsed-elems & [source-width]]
(let [print-width (+ 6 (or source-width
(utils/fence
(sort
(map #(.length %)
(map source-str parsed-elems))))))]
(doseq [parsed-elem parsed-elems]
(.append on
(colored color? (elem-color parsed-elem)
(str (utils/rjust print-width (source-str parsed-elem))
" " (method-str parsed-elem))))
(.append on "\n")
(.flush on))))
(defn pst-caused-by-on
[on color?]
(.append on (colored color? :red "Caused by: "))
(.flush on))
(defn- pst-cause-on
[on color? exec source-width]
(pst-caused-by-on on color?)
(pst-class-on on color? (:class exec))
(pst-message-on on color? (:message exec))
(pst-elems-on on color? (:trimmed-elems exec) source-width)
(if-let [cause (:cause exec)]
(pst-cause-on on color? cause source-width)))
(defn- find-source-width
"Returns the width of the longest source-string among all trace elems of the
excp and its causes."
[excp]
(let [this-source-width (utils/fence
(sort
(map #(.length %)
(map source-str (:trace-elems excp)))))]
(if-let [cause (:cause excp)]
(max this-source-width (find-source-width cause))
this-source-width)))
(defn pst-on [on color? e]
"Prints to the given Writer on a pretty stack trace for the given exception e,
ANSI colored if color? is true."
(let [exec (parse-exception e)
source-width (find-source-width exec)]
(pst-class-on on color? (:class exec))
(pst-message-on on color? (:message exec))
(pst-elems-on on color? (:trace-elems exec) source-width)
(if-let [cause (:cause exec)]
(pst-cause-on on color? cause source-width))))
(defn pst
"Print to *out* a pretty stack trace for an exception, by default *e."
[& [e]]
(pst-on *out* false (or e *e)))
(defn pst-str
"Like pst, but returns a string instead of printing that string to *out*"
[& [e]]
(let [sw (java.io.StringWriter.)]
(pst-on sw false (or e *e))
(str sw)))
(defn pst+
"Like pst, but with ANSI terminal color coding."
[& [e]]
(pst-on *out* true (or e *e)))