Skip to content

Commit

Permalink
[c.s.local-bindings] Read :compliment/locals metadata from custom macros
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-yakushev committed Feb 3, 2021
1 parent 53b9318 commit b059dd6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 20 deletions.
44 changes: 24 additions & 20 deletions src/compliment/sources/local_bindings.clj
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
(remove keyword?)
(mapcat parse-binding))
keys-binds (mapcat binding-node [:keys :strs :syms])
as-binds (if-let [as (:as binding-node)]
[as] ())]
(concat normal-binds keys-binds as-binds))
as-bind (:as binding-node)]
(cond-> (concat normal-binds keys-binds)
as-bind (conj as-bind)))

(not (#{'& '_} binding-node))
[binding-node]))
Expand All @@ -50,24 +50,28 @@
(defn extract-local-bindings
"When given a form that has a binding vector traverses that binding vector and
returns the list of all local bindings."
[form]
[form ns]
(when (list? form)
(cond (let-like-forms (first form))
(mapcat parse-binding (take-nth 2 (second form)))
(let [sym (first form)
locals-meta (when (symbol? sym)
(:completion/locals (meta (ns-resolve ns sym))))]
(cond (or (let-like-forms sym) (= locals-meta :let))
(mapcat parse-binding (take-nth 2 (second form)))

(defn-like-forms (first form)) (parse-fn-body (rest form))
(or (defn-like-forms sym) (= locals-meta :defn))
(parse-fn-body (rest form))

(letfn-like-forms (first form))
(mapcat parse-fn-body (second form))
(or (letfn-like-forms sym) (= locals-meta :letfn))
(mapcat parse-fn-body (second form))

(doseq-like-forms (first form))
(->> (partition 2 (second form))
(mapcat (fn [[left right]]
(if (= left :let)
(take-nth 2 right) [left])))
(mapcat parse-binding))
(or (doseq-like-forms sym) (= locals-meta :doseq))
(->> (partition 2 (second form))
(mapcat (fn [[left right]]
(if (= left :let)
(take-nth 2 right) [left])))
(mapcat parse-binding))

(= 'as-> (first form)) [(nth form 2)])))
(= sym 'as->) [(nth form 2)]))))

(defn- distinct-preserve-tags
"Like `distinct` but keeps symbols that have type tag with a higher priority."
Expand All @@ -83,17 +87,17 @@

(defn bindings-from-context
"Returns all local bindings that are established inside the given context."
[ctx]
(try (->> (mapcat (comp extract-local-bindings :form) ctx)
[ctx ns]
(try (->> (mapcat #(extract-local-bindings (:form %) ns) ctx)
(filter symbol?)
distinct-preserve-tags)
(catch Exception ex ())))

(defn candidates
"Returns a list of local bindings inside the context that match prefix."
[prefix _ context]
[prefix ns context]
(when (var-symbol? prefix)
(for [binding (bindings-from-context context)
(for [binding (bindings-from-context context ns)
:let [binding (name binding)]
:when (dash-matches? prefix binding)]
{:candidate binding, :type :local})))
Expand Down
26 changes: 26 additions & 0 deletions test/compliment/sources/t_local_bindings.clj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[compliment.t-helpers :refer :all]))

(deftest local-bindings
(defmacro ^{:completion/locals :let} like-let [& _])
(fact "local bindings are looked for in the context inside let-like forms"
(src/candidates "" *ns* (ctx/parse-context '(let [a 1, b 2] __prefix__)))
=> (just [{:candidate "a", :type :local} {:candidate "b", :type :local}] :in-any-order)
Expand All @@ -28,13 +29,23 @@
(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(dotimes [i 10]
__prefix__))))
=> (just ["i"])

(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(like-let [i 10]
__prefix__))))
=> (just ["i"]))

(defmacro ^{:completion/locals :defn} like-defn [& _])
(fact "inside defn and defmacro forms the name and the arglist is returned"
(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(defmacro amacro [bindings & body] __prefix__))))
=> (just ["amacro" "bindings" "body"] :in-any-order)

(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(like-defn amacro [bindings & body] __prefix__))))
=> (just ["amacro" "bindings" "body"] :in-any-order)

(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(defn afunction "docstring" {:meta data}
[foo bar & rest] __prefix__))))
Expand All @@ -46,10 +57,16 @@
([arg1 arg2] (do-stuff __prefix__))))))
=> (just ["multiarg-fn" "arg" "arg1" "arg2"] :in-any-order))

(defmacro ^{:completion/locals :letfn} like-letfn [& _])
(fact "letfn is supported"
(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(letfn [(local-fn [foo bar & rest] __prefix__)
(f-2 ([[a b]] a) ([c] c))]))))
=> (just ["local-fn" "foo" "bar" "rest" "f-2" "a" "b" "c"] :in-any-order)

(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(like-letfn [(local-fn [foo bar & rest] __prefix__)
(f-2 ([[a b]] a) ([c] c))]))))
=> (just ["local-fn" "foo" "bar" "rest" "f-2" "a" "b" "c"] :in-any-order))

(fact "as-> is supported"
Expand All @@ -69,13 +86,22 @@
=> (just ["foo" "bar" "baz" "a" "b" "c" "d" "key1" "key2" "key3"
"rec" "urs" "total"] :in-any-order))

(defmacro ^{:completion/locals :doseq} like-doseq [& _])
(fact "in doseq and for :let bindings are supported"
(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(doseq [a b
:let [c (first a)]
{:keys [d]} e
:let [{g :g} f, [h i] j]]
__prefix__))))
=> (just ["a" "c" "d" "g" "h" "i"] :in-any-order)

(strip-tags (src/candidates "" *ns* (ctx/parse-context
'(like-doseq [a b
:let [c (first a)]
{:keys [d]} e
:let [{g :g} f, [h i] j]]
__prefix__))))
=> (just ["a" "c" "d" "g" "h" "i"] :in-any-order))

(fact "bindings are scanned recursively"
Expand Down

0 comments on commit b059dd6

Please sign in to comment.