-
Notifications
You must be signed in to change notification settings - Fork 38
/
local_bindings.clj
89 lines (73 loc) · 3.19 KB
/
local_bindings.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
(ns compliment.sources.local-bindings
"Completion source for local bindings introduced by defn, let and the like."
(:require [compliment.sources :refer [defsource]]
[compliment.sources.ns-mappings :refer [var-symbol? dash-matches?]]))
(def let-like-forms '#{let if-let when-let if-some when-some loop with-open
dotimes with-local-vars})
(def defn-like-forms '#{defn defn- fn defmacro})
(def doseq-like-forms '#{doseq for})
(def letfn-like-forms '#{letfn})
(defn parse-binding
"Given a binding node returns the list of local bindings introduced by that
node. Handles vector and map destructuring."
[binding-node]
(cond (vector? binding-node)
(mapcat parse-binding binding-node)
(map? binding-node)
(let [normal-binds (->> (keys binding-node)
(remove keyword?)
(mapcat parse-binding))
keys-binds (if-let [ks (:keys binding-node)]
(mapv str ks) ())
as-binds (if-let [as (:as binding-node)]
[(str as)] ())]
(concat normal-binds keys-binds as-binds))
(not (#{'& '_} binding-node))
[(str binding-node)]))
(defn parse-fn-body
"Extract function name and arglists from the function body, return list of all
completable variables."
[fn-body]
(let [fn-name (when (symbol? (first fn-body))
(name (first fn-body)))
fn-body (if fn-name (rest fn-body) fn-body)]
(cond->
(mapcat parse-binding
(loop [[c & r] fn-body, bnodes []]
(cond (nil? c) bnodes
(list? c) (recur r (conj bnodes (first c))) ;; multi-arity case
(vector? c) c ;; single-arity case
:else (recur r bnodes))))
fn-name (conj fn-name))))
(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]
(when (list? form)
(cond (let-like-forms (first form))
(mapcat parse-binding (take-nth 2 (second form)))
(defn-like-forms (first form)) (parse-fn-body (rest form))
(letfn-like-forms (first form))
(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))
(= 'as-> (first form)) [(name (nth form 2))])))
(defn bindings-from-context
"Returns all local bindings that are established inside the given context."
[ctx]
(try (distinct (mapcat (comp extract-local-bindings :form) ctx))
(catch Exception ex ())))
(defn candidates
"Returns a list of local bindings inside the context that match prefix."
[prefix _ context]
(when (var-symbol? prefix)
(for [binding (bindings-from-context context)
:when (dash-matches? prefix binding)]
{:candidate binding, :type :local})))
(defsource ::local-bindings
:candidates #'candidates
:doc (constantly nil))