Skip to content

Commit b83bf54

Browse files
committed
Introduce wrap-ignore-errors
Fixes #291
1 parent 8457b03 commit b83bf54

21 files changed

+197
-95
lines changed

CHANGELOG.md

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

33
## Unreleased
44

5+
#### Changes
6+
* The `:ignore-errors` option will be honored in more places, making refactor-nrepl more robust in face of files not particularly meant to be part of the AST corpus.
7+
* Examples: WIP files, Moustache template files, scripts.
8+
* Closes https://github.com/clojure-emacs/refactor-nrepl/issues/291
9+
* Upgrade Orchard
10+
* Worth emphasizing: now the following options are available https://github.com/clojure-emacs/orchard/tree/v0.7.0#configuration-options
11+
* They can make the refactor-nrepl experience simpler / more robust.
12+
* Reliabilty improvement: try using `require` prior to `find-ns`
13+
* This increases the chances that a namespace will be found, which in turns makes refactor-nrepl more complete/accurate.
14+
* Replace Cheshire with `clojure.data.json`
15+
* Build ASTs more robustly (by using locks and ruling out certain namespaces like refactor-nrepl itself)
16+
517
### Bugs fixed
618
* [#289](https://github.com/clojure-emacs/refactor-nrepl/issues/289): Fix an edge-case with involving keywords that caused find-symbol to crash.
719
* [#305](https://github.com/clojure-emacs/refactor-nrepl/issues/305): Don't put `:as` or `:refer` on their own lines in the ns form, when the libspec is so long it causes the line to wrap.

eastwood.clj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
(disable-warning
2+
{:linter :unused-ret-vals-in-try
3+
:if-inside-macroexpansion-of #{'clojure.test/deftest
4+
'clojure.core/assert}})

project.clj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,13 @@
6666
with-debug-bindings [[:inner 0]]
6767
merge-meta [[:inner 0]]
6868
try-if-let [[:block 1]]}}}]
69-
:eastwood {:plugins [[jonase/eastwood "0.7.0"]]
69+
:eastwood {:plugins [[jonase/eastwood "0.7.1"]]
7070
:eastwood {;; vendored - shouldn't be tweaked for satisfying linters:
7171
:exclude-namespaces [refactor-nrepl.ns.slam.hound.regrow]
7272
;; :implicit-dependencies would fail spuriously when the CI matrix runs for Clojure < 1.10,
7373
;; because :implicit-dependencies can only work for a certain corner case starting from 1.10.
74-
:exclude-linters [:implicit-dependencies]}}
74+
:exclude-linters [:implicit-dependencies]
75+
:config-files ["eastwood.clj"]}}
7576
:clj-kondo [:test
7677
{:dependencies [[clj-kondo "2021.06.18"]]}]}
7778

src/refactor_nrepl/analyzer.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@
132132
"OK"))))))
133133

134134
(defn warm-ast-cache []
135-
(doseq [f (tracker/project-files-in-topo-order)]
135+
(doseq [f (tracker/project-files-in-topo-order true)]
136136
(try
137137
(ns-ast (slurp f))
138138
(catch Throwable th

src/refactor_nrepl/find/find_macros.clj

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,8 @@
8181
(defn- find-macro-definitions-in-project
8282
"Finds all macros that are defined in the project."
8383
[ignore-errors?]
84-
(->> (core/find-in-project (some-fn core/cljc-file? core/clj-file?))
84+
(->> (core/find-in-project (util/wrap-ignore-errors (some-fn core/cljc-file? core/clj-file?)
85+
ignore-errors?))
8586
(mapcat #(try
8687
(get-macro-definitions-in-file-with-caching %)
8788
(catch Exception e
@@ -215,7 +216,8 @@
215216
(when (fully-qualified-name? fully-qualified-name)
216217
(let [all-defs (find-macro-definitions-in-project ignore-errors?)
217218
macro-def (first (filter #(= (:name %) fully-qualified-name) all-defs))
218-
tracker (tracker/build-tracker)
219+
tracker (tracker/build-tracker (util/wrap-ignore-errors tracker/default-predicate
220+
ignore-errors?))
219221
origin-ns (symbol (core/prefix fully-qualified-name))
220222
dependents (tracker/get-dependents tracker origin-ns)]
221223
(some->> macro-def

src/refactor_nrepl/find/find_symbol.clj

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -135,20 +135,21 @@
135135
fully-qualified-name (if (= namespace "clojure.core")
136136
var-name
137137
(str/join "/" [namespace var-name]))
138-
referred-syms (libspecs/referred-syms-by-file&fullname)]
138+
referred-syms (libspecs/referred-syms-by-file&fullname ignore-errors)]
139139
(->> (core/dirs-on-classpath)
140-
(mapcat (partial core/find-in-dir (every-pred (some-fn core/clj-file? core/cljc-file?)
141-
(fn [f]
142-
(try
143-
(let [n (some-> f
144-
core/read-ns-form
145-
parse/name-from-ns-decl)]
146-
(if-not n
147-
false
148-
(not (self-referential? n))))
149-
(catch Exception e
150-
(util/maybe-log-exception e)
151-
false))))))
140+
(mapcat (partial core/find-in-dir (util/wrap-ignore-errors (every-pred (some-fn core/clj-file? core/cljc-file?)
141+
(fn [f]
142+
(try
143+
(let [n (some-> f
144+
core/read-ns-form
145+
parse/name-from-ns-decl)]
146+
(if-not n
147+
false
148+
(not (self-referential? n))))
149+
(catch Exception e
150+
(util/maybe-log-exception e)
151+
false))))
152+
ignore-errors)))
152153
(mapcat (partial find-symbol-in-file fully-qualified-name ignore-errors referred-syms)))))
153154

154155
(defn- get&read-enclosing-sexps

src/refactor_nrepl/middleware.clj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@
157157
(delay
158158
(require-and-resolve 'refactor-nrepl.rename-file-or-dir/rename-file-or-dir)))
159159

160-
(defn- rename-file-or-dir-reply [{:keys [transport old-path new-path] :as msg}]
161-
(reply transport msg :touched (@rename-file-or-dir old-path new-path)
160+
(defn- rename-file-or-dir-reply [{:keys [transport old-path new-path ignore-errors] :as msg}]
161+
(reply transport msg :touched (@rename-file-or-dir old-path new-path (= ignore-errors "true"))
162162
:status :done))
163163

164164
(defn- namespace-aliases-reply [{:keys [transport] :as msg}]

src/refactor_nrepl/ns/libspecs.clj

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
(ns refactor-nrepl.ns.libspecs
22
(:require [refactor-nrepl.core :as core]
3-
[refactor-nrepl.ns.ns-parser :as ns-parser])
3+
[refactor-nrepl.ns.ns-parser :as ns-parser]
4+
[refactor-nrepl.util :as util])
45
(:import [java.io File]))
56

67
;; The structure here is {path {lang [timestamp value]}}
@@ -46,13 +47,18 @@
4647
4748
{:clj {util com.acme.util str clojure.string
4849
:cljs {gstr goog.str}}}"
49-
[]
50-
{:clj (->> (core/find-in-project (some-fn core/clj-file? core/cljc-file?))
51-
(map (partial get-libspec-from-file-with-caching :clj))
52-
aliases-by-frequencies)
53-
:cljs (->> (core/find-in-project (some-fn core/cljs-file? core/cljc-file?))
54-
(map (partial get-libspec-from-file-with-caching :cljs))
55-
aliases-by-frequencies)})
50+
([]
51+
(namespace-aliases false))
52+
53+
([ignore-errors?]
54+
{:clj (->> (core/find-in-project (util/wrap-ignore-errors (some-fn core/clj-file? core/cljc-file?)
55+
ignore-errors?))
56+
(map (partial get-libspec-from-file-with-caching :clj))
57+
aliases-by-frequencies)
58+
:cljs (->> (core/find-in-project (util/wrap-ignore-errors (some-fn core/cljs-file? core/cljc-file?)
59+
ignore-errors?))
60+
(map (partial get-libspec-from-file-with-caching :cljs))
61+
aliases-by-frequencies)}))
5662

5763
(defn- unwrap-refer
5864
[file {:keys [ns refer]}]
@@ -75,10 +81,15 @@
7581
Example:
7682
{:clj {\"/home/someuser/projects/some.clj\" [\"example.com/foobar\" foobar]}
7783
:cljs}"
78-
[]
79-
{:clj (->> (core/find-in-project (some-fn core/clj-file? core/cljc-file?))
80-
(map (juxt identity (partial get-libspec-from-file-with-caching :clj)))
81-
sym-by-file&fullname)
82-
:cljs (->> (core/find-in-project (some-fn core/cljs-file? core/cljc-file?))
83-
(map (juxt identity (partial get-libspec-from-file-with-caching :cljs)))
84-
sym-by-file&fullname)})
84+
([]
85+
(referred-syms-by-file&fullname false))
86+
87+
([ignore-errors?]
88+
{:clj (->> (core/find-in-project (util/wrap-ignore-errors (some-fn core/clj-file? core/cljc-file?)
89+
ignore-errors?))
90+
(map (juxt identity (partial get-libspec-from-file-with-caching :clj)))
91+
sym-by-file&fullname)
92+
:cljs (->> (core/find-in-project (util/wrap-ignore-errors (some-fn core/cljs-file? core/cljc-file?)
93+
ignore-errors?))
94+
(map (juxt identity (partial get-libspec-from-file-with-caching :cljs)))
95+
sym-by-file&fullname)}))

src/refactor_nrepl/ns/tracker.clj

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
[repl :refer [refresh-dirs]]
88
[track :as tracker]]
99
[refactor-nrepl.core :as core]
10+
[refactor-nrepl.util :as util]
1011
[refactor-nrepl.ns.ns-parser :as ns-parser])
1112
(:import [java.io File]))
1213

@@ -36,13 +37,16 @@
3637
;; corner case - use the mranderson-ized refresh-dirs (for supporting this project's test suite):
3738
refresh-dirs))
3839

40+
(def default-predicate (every-pred core/source-file?
41+
safe-for-clojure-tools-namespace?))
42+
3943
(defn build-tracker
4044
"Build a tracker for the project.
4145
4246
If file-predicate is provided, use that instead of `core/source-file?`"
4347
([]
44-
(build-tracker #(and (core/source-file? %)
45-
(safe-for-clojure-tools-namespace? %))))
48+
(build-tracker default-predicate))
49+
4650
([file-predicate]
4751
(file/add-files (tracker/tracker) (core/find-in-project file-predicate))))
4852

@@ -74,11 +78,16 @@
7478
(boolean (some #(str/starts-with? % file-as-absolute-paths)
7579
refresh-dirs-as-absolute-paths)))))
7680

77-
(defn project-files-in-topo-order []
78-
(let [tracker (build-tracker (every-pred (partial in-refresh-dirs? (user-refresh-dirs))
79-
core/clj-file?))
80-
nses (dep/topo-sort (:clojure.tools.namespace.track/deps tracker))
81-
filemap (:clojure.tools.namespace.file/filemap tracker)
82-
ns2file (zipmap (vals filemap) (keys filemap))]
83-
(->> (map ns2file nses)
84-
(remove nil?))))
81+
(defn project-files-in-topo-order
82+
([]
83+
(project-files-in-topo-order false))
84+
85+
([ignore-errors?]
86+
(let [tracker (build-tracker (util/wrap-ignore-errors (every-pred (partial in-refresh-dirs? (user-refresh-dirs))
87+
core/clj-file?)
88+
ignore-errors?))
89+
nses (dep/topo-sort (:clojure.tools.namespace.track/deps tracker))
90+
filemap (:clojure.tools.namespace.file/filemap tracker)
91+
ns2file (zipmap (vals filemap) (keys filemap))]
92+
(->> (map ns2file nses)
93+
(remove nil?)))))

src/refactor_nrepl/rename_file_or_dir.clj

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -194,10 +194,11 @@
194194

195195
(defn- rename-source-file
196196
"Move file from old to new, updating any dependents."
197-
[old-path new-path]
197+
[old-path new-path ignore-errors?]
198198
(let [old-ns (core/ns-from-string (slurp old-path))
199199
new-ns (path->ns new-path)
200-
tracker (tracker/build-tracker)
200+
tracker (tracker/build-tracker (util/wrap-ignore-errors tracker/default-predicate
201+
ignore-errors?))
201202
dependents (tracker/get-dependents tracker old-ns)
202203
new-dependents (atom {})]
203204
(doseq [^File f dependents]
@@ -214,15 +215,17 @@
214215
[path old-parent new-parent]
215216
(str/replace path old-parent new-parent))
216217

217-
(defn- rename-dir [old-path new-path]
218+
(defn- rename-dir [old-path new-path ignore-errors?]
218219
(let [old-path (util/normalize-to-unix-path old-path)
219220
new-path (util/normalize-to-unix-path new-path)
220221
old-path (if (.endsWith old-path "/") old-path (str old-path "/"))
221222
new-path (if (.endsWith new-path "/") new-path (str new-path "/"))]
222223
(flatten (for [^File f (file-seq (File. old-path))
223224
:when (not (fs/directory? f))
224225
:let [path (util/normalize-to-unix-path (.getAbsolutePath f))]]
225-
(-rename-file-or-dir path (merge-paths path old-path new-path))))))
226+
(-rename-file-or-dir path
227+
(merge-paths path old-path new-path)
228+
ignore-errors?)))))
226229

227230
(defn- file-or-symlink-exists? [^String path]
228231
(let [f (File. path)]
@@ -234,12 +237,12 @@
234237
(when (.. target toFile exists)
235238
path)))))))
236239

237-
(defn- -rename-file-or-dir [^String old-path new-path]
240+
(defn- -rename-file-or-dir [^String old-path new-path ignore-errors?]
238241
(let [affected-files (if (fs/directory? old-path)
239-
(rename-dir old-path new-path)
242+
(rename-dir old-path new-path ignore-errors?)
240243
(if ((some-fn core/clj-file? core/cljs-file?)
241244
(File. old-path))
242-
(rename-source-file old-path new-path)
245+
(rename-source-file old-path new-path ignore-errors?)
243246
(rename-file! old-path new-path)))]
244247
(->> affected-files
245248
flatten
@@ -271,7 +274,11 @@
271274
old-path and new-path are expected to be aboslute paths.
272275
273276
Returns a list of all files that were affected."
274-
[old-path new-path]
275-
(assert-friendly old-path new-path)
276-
(binding [*print-length* nil]
277-
(-rename-file-or-dir old-path new-path)))
277+
278+
([old-path new-path]
279+
(rename-file-or-dir old-path new-path false))
280+
281+
([old-path new-path ignore-errors?]
282+
(assert-friendly old-path new-path)
283+
(binding [*print-length* nil]
284+
(-rename-file-or-dir old-path new-path ignore-errors?))))

src/refactor_nrepl/util.clj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,14 @@
7272
(defn maybe-log-exception [^Throwable e]
7373
(when (System/getProperty "refactor-nrepl.internal.log-exceptions")
7474
(.printStackTrace e)))
75+
76+
(defn wrap-ignore-errors [f ignore-errors?]
77+
(if-not ignore-errors?
78+
f
79+
(fn [x]
80+
(try
81+
(f x)
82+
(catch Exception e
83+
(maybe-log-exception e)
84+
;; return false, because `wrap-ignore-errors` is oriented for predicate usage:
85+
false)))))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
;; An incorrect alias usage (because `a` is not declared in the `ns` form):
2+
::a/foo
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
;; An incorrect data readers usage (because there's no such data reader declared anywhere):
2+
#totally.made.up/foo []

test-resources/unreadable_file.clj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
(ns unreadable-file

test/refactor_nrepl/extract_definition_test.clj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
(ns refactor-nrepl.extract-definition-test
22
(:require [clojure.java.io :as io]
33
[clojure.test :refer :all]
4-
[refactor-nrepl.extract-definition :refer :all]))
4+
[refactor-nrepl.extract-definition :as sut]
5+
[refactor-nrepl.unreadable-files :refer [ignore-errors-str]]))
56

67
(defn- -extract-definition
78
[name line col]
8-
(get-in (extract-definition
9+
(get-in (sut/extract-definition
910
{:file (.getAbsolutePath (io/file "test-resources/extract_definition.clj"))
1011
:ns "extract-definition"
1112
:line line
1213
:column col
1314
:name name
15+
:ignore-errors ignore-errors-str
1416
:dir "test-resources"})
1517
[:definition :definition]))
1618

@@ -72,11 +74,12 @@
7274
"(+ 11 17)")))
7375

7476
(deftest returns-meta-data
75-
(let [res (extract-definition
77+
(let [res (sut/extract-definition
7678
{:file (.getAbsolutePath (io/file "test-resources/extract_definition.clj"))
7779
:ns "extract-definition"
7880
:line 44
7981
:column 14
82+
:ignore-errors ignore-errors-str
8083
:name "if-let-bound"
8184
:dir "."})]
8285
(is (= (count (:occurrences res)) 1))

test/refactor_nrepl/find/find_macros_test.clj

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
(ns refactor-nrepl.find.find-macros-test
22
(:require [clojure.test :refer :all]
3-
[refactor-nrepl.find.find-macros :refer [find-macro]]))
3+
[refactor-nrepl.find.find-macros :refer [find-macro]]
4+
[refactor-nrepl.unreadable-files :refer [ignore-errors?]]))
45

56
(defn- found? [regexp occurrences]
67
(first (filter #(re-find regexp (:match %)) occurrences)))
78

89
(deftest find-macro-test
9-
(let [occurrences (find-macro "com.example.macro-def/my-macro" false)
10+
(let [occurrences (find-macro "com.example.macro-def/my-macro" ignore-errors?)
1011
{:keys [line-beg col-beg ^String file match]}
1112
(first (filter #(.contains ^String (:match %) "defmacro") occurrences))]
1213
(testing "finds the macro definition"
@@ -29,27 +30,27 @@
2930
(is (.endsWith file "macro_def.clj")))))
3031

3132
(deftest find-regular-symbol-test
32-
(is (nil? (find-macro "sym" false))))
33+
(is (nil? (find-macro "sym" ignore-errors?))))
3334

3435
(deftest find-fully-qualified-random-name
35-
(is (nil? (find-macro "asdf" false))))
36+
(is (nil? (find-macro "asdf" ignore-errors?))))
3637

3738
(deftest find-fully-qualified-fn
38-
(is (nil? (find-macro "refactor-nrepl.find.find-macros/find-macro" false))))
39+
(is (nil? (find-macro "refactor-nrepl.find.find-macros/find-macro" ignore-errors?))))
3940

4041
(deftest finds-macro-defined-in-cljc-file
4142
(is (found? #"defmacro cljc-macro"
42-
(find-macro "com.example.macro-def-cljc/cljc-macro" false))))
43+
(find-macro "com.example.macro-def-cljc/cljc-macro" ignore-errors?))))
4344

4445
(deftest finds-macro-defined-in-cljc-file-and-used-in-clj-file
4546
(is (found? #"(com.example.macro-def-cljc/cljc-macro :fully-qualified)"
46-
(find-macro "com.example.macro-def-cljc/cljc-macro" false))))
47+
(find-macro "com.example.macro-def-cljc/cljc-macro" ignore-errors?))))
4748

4849
(deftest macro-definitions-are-cached
49-
(find-macro "com.example.macro-def/my-macro" false)
50+
(find-macro "com.example.macro-def/my-macro" ignore-errors?)
5051
(with-redefs [refactor-nrepl.find.find-macros/put-cached-macro-definitions
5152
(fn [& _] (throw (ex-info "Cache miss!" {})))]
52-
(is (found? #"defmacro my-macro" (find-macro "com.example.macro-def/my-macro" false))))
53+
(is (found? #"defmacro my-macro" (find-macro "com.example.macro-def/my-macro" ignore-errors?))))
5354
(reset! @#'refactor-nrepl.find.find-macros/macro-defs-cache {})
5455
(with-redefs [refactor-nrepl.find.find-macros/put-cached-macro-definitions
5556
(fn [& _] (throw (Exception. "Expected!")))]

0 commit comments

Comments
 (0)