Skip to content

Commit

Permalink
Add parameters support to textDocument/signatureHelp
Browse files Browse the repository at this point in the history
Related to #324
  • Loading branch information
ericdallo committed Feb 21, 2021
1 parent 1dd6d67 commit ed9562f
Show file tree
Hide file tree
Showing 2 changed files with 170 additions and 42 deletions.
54 changes: 39 additions & 15 deletions src/clojure_lsp/feature/signature_help.clj
Expand Up @@ -6,6 +6,7 @@
[clojure-lsp.queries :as q]
[clojure-lsp.refactor.edit :as edit]
[clojure-lsp.shared :as shared]
[edamame.core :as edamame]
[rewrite-clj.node :as n]
[rewrite-clj.zip :as z]
[taoensso.timbre :as log])
Expand All @@ -21,17 +22,25 @@
(remove n/whitespace-or-comment?)
(drop 1)))

;; TODO Use it when we return parameters
;; (defn ^:private get-active-parameter-index [arglist-nodes cursor-row cursor-col]
;; (let [selected-arg (->> arglist-nodes
;; reverse
;; (filter (fn [node]
;; (let [{:keys [row col]} (meta node)]
;; (or (< row cursor-row)
;; (and (= row cursor-row)
;; (<= col cursor-col))))))
;; first)]
;; (max (.indexOf ^PersistentVector arglist-nodes selected-arg) 0)))
(defn ^:private get-active-parameter-index
[signatures active-signature arglist-nodes cursor-row cursor-col]
(let [params-count (-> (nth signatures active-signature)
:parameters
count)
selected-arg (->> arglist-nodes
reverse
(filter (fn [node]
(let [{:keys [row col]} (meta node)]
(or (< row cursor-row)
(and (= row cursor-row)
(<= col cursor-col))))))
first)]
(if selected-arg
(let [index (.indexOf ^PersistentVector (vec arglist-nodes) selected-arg)]
(if (> index (dec params-count))
(dec params-count)
index))
0)))

(defn ^:private get-active-signature-index [{:keys [fixed-arities arglist-strs]} arglist-nodes]
(let [arities (vec (sort-by max (if fixed-arities
Expand All @@ -47,9 +56,22 @@
(.indexOf ^PersistentVector arities (apply max arities))
(.indexOf ^PersistentVector arities (apply min arities))))))

(defn ^:private arglist-str->parameters [arglist-str]
(let [parameters (edamame/parse-string arglist-str {:auto-resolve #(symbol (str ":" %))})
rest-args? (some #(= '& %) parameters)
available-params (filter (complement #(= '& %)) parameters)
params-count (dec (count available-params))]
(->> available-params
(map-indexed (fn [index arg]
(let [last-arg? (= index params-count)]
(if (and rest-args? last-arg?)
{:label (format "& %s" arg)}
{:label (str arg)})))))))

(defn ^:private definition->signature-informations [{:keys [arglist-strs] :as definition}]
(map (fn [arg]
(-> {:label (format "(%s %s)" (-> definition :name str) arg)}
(map (fn [arglist-str]
(-> {:label (format "(%s %s)" (-> definition :name str) arglist-str)
:parameters (arglist-str->parameters arglist-str)}
(shared/assoc-some :documentation (:doc definition))))
arglist-strs))

Expand All @@ -62,7 +84,9 @@
(let [arglist-nodes (function-loc->arglist-nodes function-loc)
function-meta (meta (z/node function-loc))
definition (q/find-definition-from-cursor (:analysis @db/db) filename (:row function-meta) (:col function-meta))
signatures (definition->signature-informations definition)]
signatures (definition->signature-informations definition)
active-signature (get-active-signature-index definition arglist-nodes)]
(when (seq signatures)
{:signatures signatures
:active-signature (get-active-signature-index definition arglist-nodes)})))))
:active-parameter (get-active-parameter-index signatures active-signature arglist-nodes row col)
:active-signature active-signature})))))
158 changes: 131 additions & 27 deletions test/clojure_lsp/features/signature_help_test.clj
Expand Up @@ -26,19 +26,31 @@
(h/code "(defn bar [a b] 1)"
"(defn foo [a b] (|bar| |1 |2) |)"))]
(testing "before function name"
(is (= {:signatures [{:label "(bar [a b])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" before-r before-c))))
(testing "after function name"
(is (= {:signatures [{:label "(bar [a b])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" after-r after-c))))
(testing "on first arg"
(is (= {:signatures [{:label "(bar [a b])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" first-arg-r first-arg-c))))
(testing "on second arg"
(is (= {:signatures [{:label "(bar [a b])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}]
:active-parameter 1
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" second-arg-r second-arg-c))))
(testing "outside function"
Expand All @@ -59,28 +71,58 @@
"(bar| 1 2 3)"
"(bar| 1 2 3 4)"))]
(testing "zero arity"
(is (= {:signatures [{:label "(bar [a b])"}
{:label "(bar [a b c])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}
{:label "(bar [a b c])"
:parameters [{:label "a"}
{:label "b"}
{:label "c"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" zero-r zero-c))))
(testing "one arity"
(is (= {:signatures [{:label "(bar [a b])"}
{:label "(bar [a b c])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}
{:label "(bar [a b c])"
:parameters [{:label "a"}
{:label "b"}
{:label "c"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" one-r one-c))))
(testing "two arity"
(is (= {:signatures [{:label "(bar [a b])"}
{:label "(bar [a b c])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}
{:label "(bar [a b c])"
:parameters [{:label "a"}
{:label "b"}
{:label "c"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" two-r two-c))))
(testing "three arity"
(is (= {:signatures [{:label "(bar [a b])"}
{:label "(bar [a b c])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}
{:label "(bar [a b c])"
:parameters [{:label "a"}
{:label "b"}
{:label "c"}]}]
:active-parameter 0
:active-signature 1}
(f.signature-help/signature-help "file:///a.clj" three-r three-c))))
(testing "four arity"
(is (= {:signatures [{:label "(bar [a b])"}
{:label "(bar [a b c])"}]
(is (= {:signatures [{:label "(bar [a b])"
:parameters [{:label "a"}
{:label "b"}]}
{:label "(bar [a b c])"
:parameters [{:label "a"}
{:label "b"}
{:label "c"}]}]
:active-parameter 0
:active-signature 1}
(f.signature-help/signature-help "file:///a.clj" four-r four-c))))))
(testing "With & rest arity only"
Expand All @@ -92,15 +134,21 @@
"(bar| 1)"
"(bar| 1 2)"))]
(testing "zero arity"
(is (= {:signatures [{:label "(bar [& rest])"}]
(is (= {:signatures [{:label "(bar [& rest])"
:parameters [{:label "& rest"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" zero-r zero-c))))
(testing "one arity"
(is (= {:signatures [{:label "(bar [& rest])"}]
(is (= {:signatures [{:label "(bar [& rest])"
:parameters [{:label "& rest"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" one-r one-c))))
(testing "two arity"
(is (= {:signatures [{:label "(bar [& rest])"}]
(is (= {:signatures [{:label "(bar [& rest])"
:parameters [{:label "& rest"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" two-r two-c))))))
(testing "With fixed arities and & rest arity"
Expand All @@ -116,27 +164,83 @@
"(bar| 1 2 3)"
"(bar| 1 2 3 4)"))]
(testing "zero arity"
(is (= {:signatures [{:label "(bar [a])"}
{:label "(bar [a & rest])"}]
(is (= {:signatures [{:label "(bar [a])"
:parameters [{:label "a"}]}
{:label "(bar [a & rest])"
:parameters [{:label "a"}
{:label "& rest"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" zero-r zero-c))))
(testing "one arity"
(is (= {:signatures [{:label "(bar [a])"}
{:label "(bar [a & rest])"}]
(is (= {:signatures [{:label "(bar [a])"
:parameters [{:label "a"}]}
{:label "(bar [a & rest])"
:parameters [{:label "a"}
{:label "& rest"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" one-r one-c))))
(testing "two arity"
(is (= {:signatures [{:label "(bar [a])"}
{:label "(bar [a & rest])"}]
(is (= {:signatures [{:label "(bar [a])"
:parameters [{:label "a"}]}
{:label "(bar [a & rest])"
:parameters [{:label "a"}
{:label "& rest"}]}]
:active-parameter 0
:active-signature 1}
(f.signature-help/signature-help "file:///a.clj" two-r two-c))))
(testing "three arity"
(is (= {:signatures [{:label "(bar [a])"}
{:label "(bar [a & rest])"}]
(is (= {:signatures [{:label "(bar [a])"
:parameters [{:label "a"}]}
{:label "(bar [a & rest])"
:parameters [{:label "a"}
{:label "& rest"}]}]
:active-parameter 0
:active-signature 1}
(f.signature-help/signature-help "file:///a.clj" three-r three-c))))
(testing "four arity"
(is (= {:signatures [{:label "(bar [a])"}
{:label "(bar [a & rest])"}]
(is (= {:signatures [{:label "(bar [a])"
:parameters [{:label "a"}]}
{:label "(bar [a & rest])"
:parameters [{:label "a"}
{:label "& rest"}]}]
:active-parameter 0
:active-signature 1}
(f.signature-help/signature-help "file:///a.clj" four-r four-c)))))))

(deftest signature-help-active-parameter
(let [[[first-arg-r first-arg-c]
[second-arg-r second-arg-c]
[third-arg-r third-arg-c]
[end-function-r end-function-c]] (h/load-code-and-locs
(h/code "(defn bar [a & more] 1)"
"(defn foo [a b] (bar |1 |2 {:a| 2} |))"))]
(testing "on first arg"
(is (= {:signatures [{:label "(bar [a & more])"
:parameters [{:label "a"}
{:label "& more"}]}]
:active-parameter 0
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" first-arg-r first-arg-c))))
(testing "on second arg"
(is (= {:signatures [{:label "(bar [a & more])"
:parameters [{:label "a"}
{:label "& more"}]}]
:active-parameter 1
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" second-arg-r second-arg-c))))
(testing "on third arg"
(is (= {:signatures [{:label "(bar [a & more])"
:parameters [{:label "a"}
{:label "& more"}]}]
:active-parameter 1
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" third-arg-r third-arg-c))))
(testing "on end of the function"
(is (= {:signatures [{:label "(bar [a & more])"
:parameters [{:label "a"}
{:label "& more"}]}]
:active-parameter 1
:active-signature 0}
(f.signature-help/signature-help "file:///a.clj" end-function-r end-function-c))))))

0 comments on commit ed9562f

Please sign in to comment.