Skip to content

Commit 05485fb

Browse files
dotemacsvemv
authored andcommitted
analyzer, tracker: also parse .cljc files
Fixes #396 Closes #398
1 parent 41d2f50 commit 05485fb

File tree

8 files changed

+132
-20
lines changed

8 files changed

+132
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
## Unreleased
44

5+
* [#396](https://github.com/clojure-emacs/refactor-nrepl/pull/396) Handle the analyzing and parsing of Clojure code from .cljc files.
56
* Upgrade various dependencies: [#393](https://github.com/clojure-emacs/refactor-nrepl/pull/393), [#394](https://github.com/clojure-emacs/refactor-nrepl/pull/394) & [#395](https://github.com/clojure-emacs/refactor-nrepl/pull/395).
67
* Does not impact users, since we use [mranderson](https://github.com/benedekfazekas/mranderson).
7-
* The `rename-file-or-dir` is now also able to rename .cljs files that depended on .clj files via :require-macros [#394](https://github.com/clojure-emacs/refactor-nrepl/pull/394).
8+
* The `rename-file-or-dir` op is now also able to rename .cljs files that depended on .clj files via :require-macros [#394](https://github.com/clojure-emacs/refactor-nrepl/pull/394).
89
* `ns-tracker` no longer ignores cljs files with string requires [#394](https://github.com/clojure-emacs/refactor-nrepl/pull/394).
910
* note that `ns-tracker` is normally used for clj files only.
1011

src/refactor_nrepl/analyzer.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@
148148
(defn warm-ast-cache []
149149
(doseq [f (tracker/project-files-in-topo-order true)]
150150
(try
151-
(ns-ast (slurp f))
151+
(ns-ast (core/file-forms f #{:clj}))
152152
(catch Throwable th
153153
(when (System/getProperty "refactor-nrepl.internal.log-exceptions")
154154
(-> th .printStackTrace))

src/refactor_nrepl/core.clj

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
[clojure.java.io :as io]
44
[clojure.string :as str]
55
[clojure.tools.namespace.parse :as parse]
6+
[clojure.tools.reader :as reader]
67
[clojure.tools.reader.reader-types :as readers]
78
[orchard.java.classpath :as cp]
89
[orchard.misc :as misc]
910
[refactor-nrepl.config :as config]
1011
[refactor-nrepl.s-expressions :as sexp]
1112
[refactor-nrepl.util :as util :refer [normalize-to-unix-path]])
1213
(:import
14+
(clojure.lang LineNumberingPushbackReader)
1315
(java.io File FileNotFoundException FileReader PushbackReader StringReader)))
1416

1517
;; Require our `fs` customizations before `fs` is loaded:
@@ -182,27 +184,32 @@
182184
(defn cljc-file?
183185
[path-or-file]
184186
(let [path (.getPath (io/file path-or-file))]
185-
(and (cljc-extension? path)
186-
(read-ns-form path))))
187+
(boolean (and (cljc-extension? path)
188+
(read-ns-form path)))))
187189

188190
(defn cljs-extension? [^String path]
189191
(.endsWith path ".cljs"))
190192

191193
(defn cljs-file?
192194
[path-or-file]
193195
(let [path (.getPath (io/file path-or-file))]
194-
(and (cljs-extension? path)
195-
(read-ns-form path))))
196+
(boolean (and (cljs-extension? path)
197+
(read-ns-form path)))))
196198

197199
(defn clj-extension? [^String path]
198200
(.endsWith path ".clj"))
199201

200202
(defn clj-file?
201203
[path-or-file]
202204
(let [path (.getPath (io/file path-or-file))]
203-
(and (not (util/data-file? path-or-file))
204-
(clj-extension? path)
205-
(read-ns-form path))))
205+
(boolean (and (not (util/data-file? path-or-file))
206+
(clj-extension? path)
207+
(read-ns-form path)))))
208+
209+
(defn clj-or-cljc-file?
210+
[path-or-file]
211+
(or (clj-file? path-or-file)
212+
(cljc-file? path-or-file)))
206213

207214
(defn source-file?
208215
"True for clj, cljs or cljc files.
@@ -409,6 +416,28 @@
409416
([no-error path] (when-not (cljs-file? path)
410417
(some-> path read-ns-form-with-meta parse/name-from-ns-decl (safe-find-ns no-error)))))
411418

419+
(defn file-forms
420+
"For a given `file`, get all the forms from it.
421+
422+
If `file` is .cljc, `features` (a set) will be used.
423+
424+
Please prefer this helper over `#'slurp`,
425+
so that reader conditionals are properly handled."
426+
[file features]
427+
(let [reader (LineNumberingPushbackReader. (StringReader. (slurp file)))
428+
reader-opts {:read-cond :allow
429+
:eof ::eof
430+
:features (case (file->dialect file)
431+
:clj #{:clj}
432+
:cljc features
433+
:cljs #{:cljs})}]
434+
(loop [forms []
435+
form (reader/read reader-opts reader)]
436+
(if (not= form ::eof)
437+
(recur (conj forms form)
438+
(reader/read reader-opts reader))
439+
(str/join " " forms)))))
440+
412441
(defn file-content-sans-ns
413442
"Read the content of file after the ns.
414443
@@ -420,10 +449,10 @@
420449
([file-content dialect]
421450
;; NOTE: It's tempting to trim this result but
422451
;; find-macros relies on this not being trimmed
423-
(let [rdr-opts {:read-cond :allow :features #{dialect}}
424-
rdr (PushbackReader. (StringReader. file-content))]
425-
(read rdr-opts rdr)
426-
(slurp rdr))))
452+
(let [reader-opts {:read-cond :allow :features #{dialect}}
453+
reader (PushbackReader. (StringReader. file-content))]
454+
(read reader-opts reader)
455+
(slurp reader))))
427456

428457
(defn ns-form-from-string
429458
([file-content]
@@ -433,9 +462,9 @@
433462
(catch Exception _e
434463
(throw (IllegalArgumentException. "Malformed ns form!")))))
435464
([dialect file-content]
436-
(let [rdr-opts {:read-cond :allow :features #{dialect}}]
465+
(let [reader-opts {:read-cond :allow :features #{dialect}}]
437466
(try
438-
(with-meta (parse/read-ns-decl (PushbackReader. (StringReader. file-content)) rdr-opts)
467+
(with-meta (parse/read-ns-decl (PushbackReader. (StringReader. file-content)) reader-opts)
439468
(extract-ns-meta file-content))
440469
(catch Exception _e
441470
(throw (IllegalArgumentException. "Malformed ns form!")))))))

src/refactor_nrepl/ns/tracker.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@
8282
(let [refresh-dirs (user-refresh-dirs)
8383
tracker (build-tracker (util/with-suppressed-errors
8484
(every-pred (partial in-refresh-dirs? refresh-dirs (absolutize-dirs refresh-dirs))
85-
core/clj-file?)
85+
core/clj-or-cljc-file?)
8686
ignore-errors?))
8787
nses (dep/topo-sort (:clojure.tools.namespace.track/deps tracker))
8888
filemap (:clojure.tools.namespace.file/filemap tracker)

test/refactor_nrepl/core_test.clj

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,65 @@
8787
:foo true}
8888
(:top-level-meta ns-meta)))
8989
(is (= {:c 3, :d 4} (:attr-map ns-meta))))))
90+
91+
(deftest clj-or-cljc-file-test
92+
(let [clj "testproject/src/com/move/dependent_ns1.clj"
93+
cljs "testproject/src/com/move/dependent_ns1_cljs.cljs"
94+
cljc "testproject/src/com/move/cljc_test_file.cljc"]
95+
(is (= true
96+
(sut/clj-or-cljc-file? cljc)))
97+
(is (= true
98+
(sut/clj-or-cljc-file? clj)))
99+
(is (= false
100+
(sut/clj-or-cljc-file? cljs)))
101+
102+
(is (= false
103+
(sut/clj-file? cljc)))
104+
(is (= true
105+
(sut/clj-file? clj)))
106+
(is (= false
107+
(sut/clj-file? cljs)))
108+
109+
(is (= true
110+
(sut/cljc-file? cljc)))
111+
(is (= false
112+
(sut/cljc-file? clj)))
113+
(is (= false
114+
(sut/cljc-file? cljs)))
115+
116+
(is (= false
117+
(sut/cljs-file? cljc)))
118+
(is (= false
119+
(sut/cljs-file? clj)))
120+
(is (= true
121+
(sut/cljs-file? cljs)))))
122+
123+
(deftest file-forms-test
124+
(is (= "(ns com.move.subdir.dependent-ns-3-cljs (:require com.move.ns-to-be-moved-cljs) (:require-macros [com.move.ns-to-be-moved :refer [macro-to-be-moved]]))"
125+
(sut/file-forms "testproject/src/com/move/subdir/dependent_ns_3_cljs.cljs"
126+
#{:clj}))
127+
"cljs file parsing")
128+
129+
(is (= "(ns com.move.subdir.dependent-ns-3 (:require [com.move.ns-to-be-moved :refer [fn-to-be-moved]]))"
130+
(sut/file-forms "testproject/src/com/move/subdir/dependent_ns_3.clj"
131+
#{:clj}))
132+
"clj file parsing")
133+
134+
(let [clj-content
135+
"(ns com.move.cljc-test-file (:require [clj-namespace-from.cljc-file :as foo])) (declare something-or-other)"
136+
137+
cljs-content
138+
"(ns com.move.cljc-test-file (:require [cljs-namespace-from.cljc-file :as bar :include-macros true])) (declare something-or-other)"]
139+
140+
(is (not= clj-content cljs-content)
141+
"Sanity check")
142+
143+
(is (= clj-content
144+
(sut/file-forms "testproject/src/com/move/cljc_test_file.cljc"
145+
#{:clj}))
146+
"cljc file parsing, `:clj` choice")
147+
148+
(is (= cljs-content
149+
(sut/file-forms "testproject/src/com/move/cljc_test_file.cljc"
150+
#{:cljs}))
151+
"cljc file parsing, `:cljs` choice")))

test/refactor_nrepl/ns/tracker_test.clj

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
(ns refactor-nrepl.ns.tracker-test
22
(:require
3-
[clojure.test :refer [are deftest is]]
4-
[refactor-nrepl.ns.tracker :as sut]))
3+
[clojure.test :refer [are deftest is testing]]
4+
[refactor-nrepl.core :as core]
5+
[refactor-nrepl.ns.tracker :as sut]
6+
[refactor-nrepl.util :as util]))
57

68
(deftest in-refresh-dirs?
79
(are [refresh-dirs file-ns expected] (= expected
@@ -23,3 +25,15 @@
2325
(is (seq (sut/project-files-in-topo-order false))
2426
"Does not throw exceptions even when specifying to not ignore errors,
2527
i.e. it doesn't have bugs"))
28+
29+
(deftest build-tracker-test
30+
(let [tracker (sut/build-tracker (util/with-suppressed-errors core/clj-or-cljc-file? true))
31+
target-cljc-namespace 'com.move.cljc-test-file
32+
found-namespace (-> tracker
33+
vals
34+
first
35+
:dependencies
36+
(get target-cljc-namespace)
37+
first)]
38+
(testing "that tracker is picking up .cljc file"
39+
(is (= 'clj-namespace-from.cljc-file found-namespace)))))

test/refactor_nrepl/rename_file_or_dir_test.clj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,8 @@
148148

149149
(deftest calls-rename-file!-on-the-right-files-when-moving-dirs
150150
(let [files (atom [])
151-
original-files ["testproject/src/com/move/dependent_ns1.clj"
151+
original-files ["testproject/src/com/move/cljc_test_file.cljc"
152+
"testproject/src/com/move/dependent_ns1.clj"
152153
"testproject/src/com/move/dependent_ns1_cljs.cljs"
153154
"testproject/src/com/move/dependent_ns2.clj"
154155
"testproject/src/com/move/dependent_ns2_cljs.cljs"
@@ -315,7 +316,7 @@ and the string requires remain there as-is"
315316
refactor-nrepl.rename-file-or-dir/update-ns! (fn [_path _old-ns])
316317
refactor-nrepl.rename-file-or-dir/update-dependents! (fn [_deps])]
317318
(sut/rename-file-or-dir from-dir-path to-dir-path ignore-errors?)
318-
(is (= (count @files) 9))
319+
(is (= (count @files) 10))
319320
(doseq [[^String old ^String new] @files]
320321
(is (.contains old "/move/"))
321322
(is (.contains new "/moved/"))))))
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
(ns com.move.cljc-test-file
2+
(:require #?(:clj [clj-namespace-from.cljc-file :as foo]
3+
:cljs [cljs-namespace-from.cljc-file :as bar :include-macros true])))
4+
5+
(declare something-or-other)

0 commit comments

Comments
 (0)