Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions src/cider/nrepl/middleware/apropos.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
(:require [cider.nrepl.middleware.util.error-handling :refer [with-safe-transport]]
[clojure.string :as str]
[clojure.tools.nrepl.middleware :refer [set-descriptor!]]
[cider.nrepl.middleware.info :as info]
[cider.nrepl.middleware.util.namespace :as ns]))

;;; ## Overview
Expand All @@ -17,17 +18,26 @@
;; abbreviated version (i.e. first sentence only) may be returned for
;; symbol-only searches.

(def special-forms
"Special forms that can be apropo'ed."
(concat (keys (var-get #'clojure.repl/special-doc-map))
'[& catch finally]))

(defn var-name
"Return a var's namespace-qualified name as a string."
"Return a special form's name or var's namespace-qualified name as a string."
[v]
(str/join "/" ((juxt (comp ns-name :ns) :name)
(meta v))))
(if (special-symbol? v)
(str (:name (info/resolve-special v)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So all these functions conflate symbols and vars now?

If that's the way, then I think instead of inserting (special-symbol? ...) checks everywhere it's better to have all these internals in one place - (var-meta ..). Also IMO including one middleware into another is not ideal. All these small utilities could easily live in a separate utility namespace (precisely what has been done as an aside of #405 btw).

@xiongtx do you plan for more imediate changes? Could you please give me a day or so to merge #405 as any changes to info and apropos will likely introduce hard conflicts with that branch. Thanks.

(str/join "/" ((juxt (comp ns-name :ns) :name) (meta v)))))

(defn var-doc
"Return a var's docstring, optionally limiting the number of sentences
returned."
"Return a special form or var's docstring, optionally limiting the number of
sentences returned."
([v]
(or (:doc (meta v)) "(not documented)"))
(or (if (special-symbol? v)
(:doc (info/resolve-special v))
(:doc (meta v)))
"(not documented)"))
([n v]
(->> (-> (var-doc v)
(str/replace #"\s+" " ") ; normalize whitespace
Expand Down Expand Up @@ -70,12 +80,16 @@
regex (-> (if case-sensitive? query (format "(?i:%s)" query)) re-pattern)]
(->> (namespaces ns search-ns filter-regexps)
(mapcat (comp (partial sort-by var-name) vals ns-vars))
(concat (when (or (empty? search-ns)
(= 'clojure.core (symbol search-ns)))
special-forms))
(filter (comp (partial re-find regex) search-prop))
(map (fn [v] {:name (var-name v)
:doc (var-doc* v)
:type (cond (:macro (meta v)) :macro
(fn? (deref v)) :function
:else :variable)})))))
:type (cond (special-symbol? v) :special-form
(:macro (meta v)) :macro
(fn? (deref v)) :function
:else :variable)})))))

;;; ## Middleware

Expand Down
14 changes: 8 additions & 6 deletions src/cider/nrepl/middleware/info.clj
Original file line number Diff line number Diff line change
Expand Up @@ -139,15 +139,17 @@
used by `clojure.repl/doc`."
[sym]
(try
(let [sym (get '{& fn, catch try, finally try} sym sym)
(let [orig-sym sym
sym (get '{& fn, catch try, finally try} sym sym)
v (meta (ns-resolve (find-ns 'clojure.core) sym))]
(when-let [m (cond (special-symbol? sym) (#'repl/special-doc sym)
(:special-form v) v)]
(assoc m
:url (if (contains? m :url)
(when (:url m)
(str "https://clojure.org/" (:url m)))
(str "https://clojure.org/special_forms#" (:name m))))))
(-> m
(assoc :name orig-sym)
(assoc :url (if (contains? m :url)
(when (:url m)
(str "https://clojure.org/" (:url m)))
(str "https://clojure.org/special_forms#" (:name m)))))))
(catch NoClassDefFoundError _)
(catch Exception _)))

Expand Down
34 changes: 33 additions & 1 deletion test/clj/cider/nrepl/middleware/apropos_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,28 @@
(:require [cider.nrepl.middleware.apropos :refer :all]
[clojure.test :refer :all]
[cider.nrepl.test-session :as session]
[clojure.repl :as repl]
[clojure.string :as str]))

(def ^{:doc "Test1. Test2. Test3."} public-var [1 2 3])
(def ^:private ^{:doc "Can't. See. Me"} private-var [:a :b :c])

(deftest var-name-test
(testing "Returns Var's namespace-qualified name"
(is (= "clojure.core/conj" (var-name #'clojure.core/conj))))

(testing "Returns special form's name"
(is (= "if" (var-name 'if)))))

(deftest var-doc-test
(testing "Returns Var's doc"
(is (= (:doc (meta #'clojure.core/conj))
(var-doc #'clojure.core/conj))))

(testing "Returns special form's doc"
(is (= (:doc (#'repl/special-doc 'if))
(var-doc 'if)))))

(deftest unit-test-metadata
(is (= (var-name #'public-var) "cider.nrepl.middleware.apropos-test/public-var"))
(is (= (var-doc #'public-var) "Test1. Test2. Test3."))
Expand Down Expand Up @@ -52,7 +69,22 @@
"Symbol search should return an abbreviated docstring.")
(is (= (take 20 (:doc x))
(take 20 (:doc y)))
"The abbreviated docstring should be the start of the full docstring."))))
"The abbreviated docstring should be the start of the full docstring.")))

(testing "Includes special forms when `search-ns` is nil"
(is (not-empty (filter #(= "if" (:name %))
(find-symbols nil "if" nil
false false false nil)))))

(testing "Includes special forms when `search-ns` is \"clojure.core\""
(is (not-empty (filter #(= "if" (:name %))
(find-symbols nil "if" "clojure.core"
false false false nil)))))

(testing "Excludes special forms when `search-ns` is some other ns"
(is (empty? (filter #(= "if" (:name %))
(find-symbols nil "if" "clojure.set"
false false false nil))))))

(use-fixtures :each session/session-fixture)
(deftest integration-test
Expand Down
18 changes: 17 additions & 1 deletion test/clj/cider/nrepl/middleware/info_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
(spit tmp-file-path "test")
(testing "when fake.class.path is not set"
(is (not (= (class (file tmp-file-name))
java.net.URL)))
java.net.URL)))
(is (= (file tmp-file-name) tmp-file-name)))
(testing "when fake.class.path is set"
(try
Expand All @@ -73,6 +73,22 @@
(finally
(System/clearProperty "fake.class.path")))))))

(deftest resolve-special-test
(testing "Resolves all special forms"
(let [specials (keys clojure.lang.Compiler/specials)]
(is (every? (fn [[sym {:keys [name special-form]}]]
(and (= sym name)
(true? special-form)))
(map #(vector % (info/resolve-special %)) specials)))))

(testing "Names are correct for symbols #{&, catch, finally}"
(is (= '& (:name (info/resolve-special '&))))
(is (= 'catch (:name (info/resolve-special 'catch))))
(is (= 'finally (:name (info/resolve-special 'finally)))))

(testing "Returns nil for unknown symbol"
(is (nil? (info/resolve-special 'unknown)))))

(deftype T [])

(deftest info-test
Expand Down