Skip to content

Commit

Permalink
Filter special form symbols from eldoc args list
Browse files Browse the repository at this point in the history
Fixes clojure-emacs#165.

Currently, the eldoc args list for special forms includes the special
form itself, which throws off the highlighting in Emacs' CIDER. This
patch removes the special forms themselves from the args list.

To do this, the code first checks the `:special-form` property. If it
is a special form, the code compares the `:name` property, which is
the symbol representing the special form, with the first element of
each of the lists in `:forms`. If the two are equal, that element is
omitted.
  • Loading branch information
quanticle committed Jan 27, 2023
1 parent 464b6c8 commit e0ef5c4
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/orchard/eldoc.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
(cond
(:special-form info) (->> (:forms info)
;; :forms contains a vector of sequences or symbols
;; which we have to convert the format employed by :arglists
;; which we have to convert the format employed by :arglists.
(map #(if (coll? %) (vec %) (vector %))))
(:candidates info) (->> (:candidates info)
vals
Expand Down
20 changes: 19 additions & 1 deletion src/orchard/info.clj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,21 @@
[x]
(boolean (and (symbol? x) (namespace x) true)))

(defn dedup-forms-list
"Removes the symbol itself from the :forms lists returned by clj-meta for
special forms. This is necessary because emacs' eldoc expects just the args
that the special form takes, whereas Clojure gives us the args list prefixed
by the form itself."
[info-map]
(if (:special-form info-map)
(assoc info-map
:forms
(map #(if (and (seq? %) (= (first %) (:name info-map)))
(rest %)
%)
(:forms info-map)))
info-map))

(defn normalize-params
"Normalize the info params.
Expand Down Expand Up @@ -143,8 +158,11 @@
[params]
(let [params (normalize-params params)
dialect (:dialect params)
dedup-special-forms (:dedup-special-forms params)
meta (cond
(= dialect :clj) (clj-meta params)
(= dialect :clj) (if dedup-special-forms
(dedup-forms-list (clj-meta params))
(clj-meta params))
(= dialect :cljs) (cljs-meta params))]

;; TODO: Split the responsibility of finding meta and normalizing the meta map.
Expand Down
11 changes: 6 additions & 5 deletions test/orchard/eldoc_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@
;; test data
(def test-eldoc-info {:arglists '([x] [x y])})

(def test-eldoc-info-special-form {:forms ['(.instanceMember instance args*)
'(.instanceMember Classname args*)
'(Classname/staticMethod args*)
'Classname/staticField]
:special-form true})
(def test-eldoc-info-special-form {:name "."
:forms ['(.instanceMember instance args*)
'(.instanceMember Classname args*)
'(Classname/staticMethod args*)
'Classname/staticField]
:special-form true})

(def test-eldoc-info-candidates
{:candidates '{X {:arglists ([x])}
Expand Down
65 changes: 65 additions & 0 deletions test/orchard/info_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,61 @@

(use-fixtures :once wrap-info-params)

(deftest dedup-forms-list-test
(testing "Dedup non-special form should return the original info-map"
(let [info-map {:added "1.0",
:ns 'clojure.core,
:name 'when,
:file "clojure/core.clj",
:column 1,
:line 495,
:macro true,
:arglists '([test & body]),
:see-also
'(clojure.core/when-not clojure.core/when-let clojure.core/if),
:doc
"Evaluates test. If logical true, evaluates body in an implicit do."}]
(is (= (info/dedup-forms-list info-map) info-map))))
(testing "Dedup special form should dedup the info map"
(let [info-maps [{:forms '[(def symbol doc-string? init?)]
:name 'def
:special-form true}
{:forms '[(do exprs*)]
:name 'do
:special-form true}
{:forms '[(if test then else?)]
:name 'if
:special-form true}
{:forms '[(recur exprs*)]
:name 'recur
:special-form true}
{:forms '[(throw expr)]
:name 'throw
:special-form true}
{:forms '[(try expr* catch-clause* finally-clause?)]
:name 'try
:special-form true}]]
(is (= (map #(:forms (info/dedup-forms-list %)) info-maps)
['[(symbol doc-string? init?)]
'[(exprs*)]
'[(test then else?)]
'[(exprs*)]
'[(expr)]
'[(expr* catch-clause* finally-clause?)]]))))
(testing "Dedup special form shouldn't affect the info map if there are no duplicates"
(let [info-map {:url "https://clojure.org/java_interop#dot",
:forms
'[(.instanceMember instance args*)
(.instanceMember Classname args*)
(Classname/staticMethod args*)
Classname/staticField],
:doc
"The instance member form works for both fields and methods.\n They all expand into calls to the dot operator at macroexpansion time.",
:name '.,
:special-form true,
:see-also '(clojure.core/..)}]
(is (= (info/dedup-forms-list info-map) info-map)))))

(deftest info-non-existing-test
(testing "Non existing symbol in clojure.core"
(is (nil? (info/info* {:ns 'clojure.core :sym (gensym "non-existing")}))))
Expand Down Expand Up @@ -119,6 +174,16 @@
(map #(hash-map :ns target-ns :sym %))
(info-specials)))))))))

(deftest info-special-form-dedup-test
(testing "When the :dedup-special-forms flag is given, remove the special form symbol from the :forms list"
(let [special-syms (into #{'letfn 'loop 'let 'fn} (keys (misc/require-and-resolve 'clojure.repl/special-doc-map)))
info-params (map #(hash-map :ns 'orchard.info
:sym %
:dialect :clj
:dedup-special-forms true)
special-syms)]
(is (not-any? special-syms (map #(-> % :forms first first) (map info/info* info-params)))))))

(deftest info-var-alias-test
(testing "Aliased var"
(let [params '{:ns orchard.test-ns
Expand Down

0 comments on commit e0ef5c4

Please sign in to comment.