diff --git a/CHANGELOG.md b/CHANGELOG.md index 969a25c..e57839c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ - Complete fully-qualified classnames by their shortname prefix anywhere in the file (previously worked only in the `:import` section of the `ns` form). - Fix ' and #' being swallowed when completing vars prefixed by them. +- [#91](https://github.com/alexander-yakushev/compliment/pull/91): `compliment.utils/namespaces-on-classpath` + now includes namespaces from cljc. `extensions`-option allows for cljs namespaces. ### 0.3.14 (2022-07-11) diff --git a/src/compliment/sources/namespaces_and_classes.clj b/src/compliment/sources/namespaces_and_classes.clj index 8f67618..44256a3 100644 --- a/src/compliment/sources/namespaces_and_classes.clj +++ b/src/compliment/sources/namespaces_and_classes.clj @@ -93,11 +93,12 @@ (get-all-full-names prefix)) ;; If prefix doesn't contain a period, using fuziness produces too many ;; irrelevant candidates. - (for [^String ns-str (utils/namespaces-on-classpath) + (for [{^String ns-str :ns, ^String ext :extension} + (utils/namespaces-on-classpath {:name-only false, :extensions #{"clj" "cljc"}}) :when (if has-dot (nscl-matches? prefix ns-str) (.startsWith ns-str prefix))] - {:candidate ns-str, :type :namespace}) + {:candidate ns-str, :type :namespace, :extension ext}) ;; Fuzziness is too slow for all classes, so only startsWith. Also, if no ;; period in prefix, only complete root package names to maintain good ;; performance and not produce too many candidates. diff --git a/src/compliment/utils.clj b/src/compliment/utils.clj index 2376155..a98873c 100644 --- a/src/compliment/utils.clj +++ b/src/compliment/utils.clj @@ -190,16 +190,26 @@ Note that should always have the same value, regardless of OS." (group-by #(subs % 0 (max (.indexOf ^String % ".") 0))))))) (defn namespaces-on-classpath - "Returns the list of all Clojure namespaces obtained by classpath scanning." - [] - (let [classpath (classpath)] - (cache-last-result ::namespaces-on-classpath classpath - (set (for [^String file (all-files-on-classpath classpath) - :when (and (.endsWith file ".clj") - (not (.startsWith file "META-INF"))) - :let [[_ ^String nsname] (re-matches #"[^\w]?(.+)\.clj" file)] - :when nsname] - (.. nsname (replace resource-separator ".") (replace "_" "-"))))))) + "Returns the list of all clj(c) namespaces obtained by classpath scanning. + + Options: + - `extensions` (default `#{\"clj\", \"cljc\"}`) - collection of extensions to filter namespaces on. Should be subset of `#{\"clj\", \"cljc\", \"cljs\"}`. + - `name-only` (default `true`) - set to `false` to receive map with `:ns` and `:extension`." + ([] (namespaces-on-classpath nil)) + ([{:keys [extensions name-only] :or {name-only true, extensions #{"clj" "cljc"}}}] + (let [classpath (classpath) + extensions (set extensions)] + (cond->> (cache-last-result ::namespaces-on-classpath classpath + (for [^String file (all-files-on-classpath classpath) + :when (and (or (.endsWith file ".clj") (.endsWith file ".cljc") (.endsWith file ".cljs")) + (not (.startsWith file "META-INF"))) + :let [[_ ^String nsname ^String extension] (re-matches #"[^\w]?(.+)\.(clj[sc]?)" file)] + :when nsname] + (let [ns-str (.. nsname (replace resource-separator ".") (replace "_" "-"))] + {:ns ns-str, :extension extension}))) + extensions (filter (comp extensions :extension)) + name-only (map :ns) + :always set)))) (defn project-resources "Returns a list of all non-code files in the current project." @@ -208,7 +218,8 @@ Note that should always have the same value, regardless of OS." (cache-last-result ::project-resources classpath (for [path classpath ^String file (list-files path false) - :when (not (or (empty? file) (.endsWith file ".clj") + :when (not (or (empty? file) + (.endsWith file ".clj") (.endsWith file ".cljc") (.endsWith file ".cljs") (.endsWith file ".jar") (.endsWith file ".class")))] ;; resource pathes always use "/" regardless of platform (.. (if (.startsWith file File/separator) diff --git a/test/compliment/sources/t_namespaces_and_classes.clj b/test/compliment/sources/t_namespaces_and_classes.clj index 67d38b6..075ef2b 100644 --- a/test/compliment/sources/t_namespaces_and_classes.clj +++ b/test/compliment/sources/t_namespaces_and_classes.clj @@ -37,8 +37,8 @@ => (just ["src"]) (src/candidates "clojure.java." (-ns) nil) - => (contains #{{:candidate "clojure.java.browse", :type :namespace} - {:candidate "clojure.java.shell", :type :namespace}} :gaps-ok) + => (contains #{{:candidate "clojure.java.browse", :type :namespace :extension "clj"} + {:candidate "clojure.java.shell", :type :namespace :extension "clj"}} :gaps-ok) (src/candidates "java.io.Stri" (-ns) nil) => (contains #{{:candidate "java.io.StringReader", :type :class} diff --git a/test/compliment/t_core.clj b/test/compliment/t_core.clj index ea62665..e0ed91a 100644 --- a/test/compliment/t_core.clj +++ b/test/compliment/t_core.clj @@ -118,7 +118,7 @@ ;; Test for not required namespaces (core/completions "cl.test.ta" {}) => - (just [{:type :namespace, :candidate "clojure.test.tap"}]) + (just [{:type :namespace, :candidate "clojure.test.tap" :extension "clj"}]) ;; Test for aliases (core/completions "cor" {:ns 'compliment.t-core}) diff --git a/test/compliment/t_utils.clj b/test/compliment/t_utils.clj index 863e028..830e64d 100644 --- a/test/compliment/t_utils.clj +++ b/test/compliment/t_utils.clj @@ -52,3 +52,9 @@ (is (contains? all-classes "java.lang.Thread")) (is (contains? all-classes "java.io.File")) (is (contains? all-classes "java.nio.channels.FileChannel")))) + +(deftest namespaces-on-classpath-test + (is (contains? (namespaces-on-classpath {:extensions #{"clj"}}) "compliment.t-utils")) + (is (= #{"dummy"} (namespaces-on-classpath {:extensions #{"cljs"}}))) + (is (= #{{:ns "dummy" :extension "cljs"}} + (namespaces-on-classpath {:extensions #{"cljs"} :name-only false})))) diff --git a/test/dummy.cljs b/test/dummy.cljs new file mode 100644 index 0000000..a465736 --- /dev/null +++ b/test/dummy.cljs @@ -0,0 +1,2 @@ +;; For testing (compliment.t-utils) purpose +(ns dummy)