Skip to content

Commit 760f1ba

Browse files
vemvbbatsov
authored andcommitted
Make stacktrace/ns-common-prefix work in presence of user.clj files
1 parent 6d3934e commit 760f1ba

File tree

3 files changed

+81
-23
lines changed

3 files changed

+81
-23
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
* [#698](https://github.com/clojure-emacs/cider-nrepl/pull/698): Add `undef-all` op to undefine all symbols and aliases in namespace
88

9+
### Bugs fixed
10+
11+
* Make `middleware.stacktrace` detect a given project's common ns prefix even in face of single-segment namespaces such as `user`.
12+
913
## 0.26.0 (2021-04-22)
1014

1115
### New features

src/cider/nrepl/middleware/stacktrace.clj

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -119,43 +119,75 @@
119119
%)]
120120
(map flag frames)))
121121

122-
(def directory-namespaces
123-
"This looks for all namespaces inside of directories on the class
124-
path, ignoring jars."
122+
(defn directory-namespaces
123+
"Looks for all namespaces inside of directories on the class
124+
path, ignoring jars.
125+
126+
It's a defn because this set is always subject to change.
127+
128+
NOTE: depending on the use case, you might want to filter out
129+
namespaces such as `user` which while belong to the project,
130+
don't share a common naming scheme with the other namespaces."
131+
[]
125132
(into #{} (namespace/project-namespaces)))
126133

127-
(def ns-common-prefix
128-
"In order to match more namespaces, we look for a common namespace
129-
prefix across the ones we have identified."
134+
(defn ns-common-prefix* [namespaces]
130135
(let [common
131-
(try (reduce
132-
(fn [common ns]
133-
(let [ns (str/lower-case ns)
134-
matched (map vector common ns)
135-
coincident (take-while (fn [[a b]] (= a b)) matched)]
136-
(apply str (map first coincident))))
137-
(str/lower-case (first directory-namespaces))
138-
directory-namespaces)
139-
(catch Throwable _e :error))]
136+
(try
137+
(->> namespaces
138+
(pmap (fn [ns-sym]
139+
(let [segments (-> ns-sym
140+
str
141+
(str/split #"\."))]
142+
;; remove single-segment namespaces
143+
;; (such as `dev`, `test`, `test-runner`)
144+
;; that would break the commonality:
145+
(when (-> segments count (> 1))
146+
segments))))
147+
(filter identity)
148+
(reduce (fn [prev curr]
149+
(if (= ::placeholder prev)
150+
curr
151+
(let [found-commonality
152+
(reduce-kv (fn [result k v]
153+
(if (= (get prev k)
154+
(get curr k))
155+
(conj result v)
156+
(reduced result)))
157+
[]
158+
prev)]
159+
(if (seq found-commonality)
160+
found-commonality
161+
(reduced :missing)))))
162+
::placeholder))
163+
(catch Throwable _e :error))]
140164
(condp = common
141-
""
165+
::placeholder
166+
{:valid false :common :missing}
167+
168+
:missing
142169
{:valid false :common :missing}
143170

144171
:error
145172
{:valid false :common :error}
146173

147-
;;default
148-
{:valid true :common common})))
174+
{:valid true :common (str/join "." common)})))
175+
176+
(def ns-common-prefix
177+
"In order to match more namespaces, we look for a common namespace
178+
prefix across the ones we have identified."
179+
(delay
180+
(ns-common-prefix* (directory-namespaces))))
149181

150182
(defn flag-project
151183
"Flag the frame if it is from the users project. From a users
152184
project means that the namespace is one we have identified or it
153185
begins with the identified common prefix."
154186
[{:keys [ns] :as frame}]
155-
(if (and directory-namespaces ns
156-
(or (contains? directory-namespaces (symbol ns))
157-
(when (:valid ns-common-prefix)
158-
(.startsWith ^String ns (:common ns-common-prefix)))))
187+
(if (and ns
188+
(or (contains? (directory-namespaces) (symbol ns))
189+
(when (:valid @ns-common-prefix)
190+
(.startsWith ^String ns (:common @ns-common-prefix)))))
159191
(flag-frame frame :project)
160192
frame))
161193

test/clj/cider/nrepl/middleware/stacktrace_test.clj

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
(ns cider.nrepl.middleware.stacktrace-test
22
(:require
3-
[cider.nrepl.middleware.stacktrace :refer :all]
3+
[cider.nrepl.middleware.stacktrace :as sut :refer :all]
44
[cider.nrepl.pprint :refer [pprint]]
55
[clojure.test :refer :all]))
66

@@ -183,3 +183,25 @@
183183
:clojure.error/phase :macroexpand
184184
:clojure.error/symbol 'clojure.core/let}
185185
(:location cause))))))
186+
187+
(deftest ns-common-prefix*-test
188+
(are [input expected] (= expected
189+
(sut/ns-common-prefix* input))
190+
[] {:valid false :common :missing}
191+
'[a b] {:valid false :common :missing}
192+
'[a.c b.c] {:valid false :common :missing}
193+
::not-a-coll {:valid false :common :error}
194+
195+
;; single-segment namespaces are considered to never have a common part:
196+
'[user] {:valid false :common :missing}
197+
'[dev] {:valid false :common :missing}
198+
'[test-runner] {:valid false :common :missing}
199+
200+
'[a.a] {:valid true :common "a.a"}
201+
'[a.a a.b] {:valid true :common "a"}
202+
'[a.a.b a.a.c] {:valid true :common "a.a"}
203+
204+
;; single-segment namespaces cannot foil the rest of the calculation:
205+
'[dev user test-runner a.a] {:valid true :common "a.a"}
206+
'[dev user test-runner a.a a.b] {:valid true :common "a"}
207+
'[dev user test-runner a.a.b a.a.c] {:valid true :common "a.a"}))

0 commit comments

Comments
 (0)