|
3 | 3 | (:require [cljs-tooling.util.analysis :as a]
|
4 | 4 | [cljs-tooling.util.misc :as u]))
|
5 | 5 |
|
| 6 | +(defn- candidate-data |
| 7 | + "Returns a map of candidate data for the given arguments." |
| 8 | + [candidate ns qualified-name type] |
| 9 | + {:candidate (name candidate) |
| 10 | + :ns (symbol ns) |
| 11 | + :name (symbol qualified-name) |
| 12 | + :type type}) |
| 13 | + |
| 14 | +(defn- var->type |
| 15 | + "Returns the candidate type corresponding to the given metadata map." |
| 16 | + [var] |
| 17 | + (condp #(get %2 %1) var |
| 18 | + :protocol :protocol-fn |
| 19 | + :fn-var :fn |
| 20 | + :record :record |
| 21 | + :protocols :type |
| 22 | + :protocol-symbol :protocol |
| 23 | + :var)) |
| 24 | + |
6 | 25 | (def special-forms
|
7 | 26 | '#{& . case* catch def defrecord* deftype* do finally fn* if js* let*
|
8 | 27 | letfn* loop* new ns quote recur set! throw try})
|
9 | 28 |
|
10 |
| -(defn prefix-completions |
11 |
| - [prefix completions] |
12 |
| - (map #(symbol (str prefix "/" %)) completions)) |
| 29 | +(def special-form-candidates |
| 30 | + "Candidate data for all special forms." |
| 31 | + (for [form special-forms |
| 32 | + :let [qualified-name (symbol "cljs.core" (name form))]] |
| 33 | + (candidate-data form qualified-name 'cljs.core :special))) |
| 34 | + |
| 35 | +(defn all-ns-candidates |
| 36 | + "Returns candidate data for all namespaces in the environment." |
| 37 | + [env] |
| 38 | + (for [[ns _] (a/all-ns env)] |
| 39 | + (candidate-data ns ns ns :ns))) |
| 40 | + |
| 41 | +(defn ns-candidates |
| 42 | + "Returns candidate data for all referred namespaces (and their aliases) in context-ns." |
| 43 | + [env context-ns] |
| 44 | + (for [[alias ns] (a/ns-aliases env context-ns)] |
| 45 | + (candidate-data alias ns ns :ns))) |
| 46 | + |
| 47 | +(defn macro-ns-candidates |
| 48 | + "Returns candidate data for all referred macro namespaces (and their aliases) in |
| 49 | + context-ns." |
| 50 | + [env context-ns] |
| 51 | + (for [[alias ns] (a/macro-ns-aliases env context-ns)] |
| 52 | + (candidate-data alias ns ns :ns))) |
| 53 | + |
| 54 | +(defn referred-var-candidates |
| 55 | + "Returns candidate data for all referred vars in context-ns." |
| 56 | + [env context-ns] |
| 57 | + (for [[refer qualified-name] (a/referred-vars env context-ns) |
| 58 | + :let [ns (namespace qualified-name) |
| 59 | + type (var->type (a/find-var env qualified-name))]] |
| 60 | + (candidate-data refer ns qualified-name type))) |
| 61 | + |
| 62 | +(defn referred-macro-candidates |
| 63 | + "Returns candidate data for all referred macros in context-ns." |
| 64 | + [env context-ns] |
| 65 | + (for [[refer qualified-name] (a/referred-macros env context-ns) |
| 66 | + :let [ns (namespace qualified-name)]] |
| 67 | + (candidate-data refer ns qualified-name :macro))) |
| 68 | + |
| 69 | +(defn- var-candidates |
| 70 | + [vars] |
| 71 | + (for [[name meta] vars |
| 72 | + :let [qualified-name (:name meta) |
| 73 | + ns (namespace qualified-name) |
| 74 | + type (var->type meta)]] |
| 75 | + (candidate-data name ns qualified-name type))) |
| 76 | + |
| 77 | +(defn ns-var-candidates |
| 78 | + "Returns candidate data for all vars defined in ns." |
| 79 | + [env ns] |
| 80 | + (var-candidates (a/ns-vars env ns))) |
| 81 | + |
| 82 | +(defn core-var-candidates |
| 83 | + "Returns candidate data for all cljs.core vars visible in context-ns." |
| 84 | + [env context-ns] |
| 85 | + (var-candidates (a/core-vars env context-ns))) |
| 86 | + |
| 87 | +(defn macro-candidates |
| 88 | + [macros] |
| 89 | + (for [[name var] macros |
| 90 | + :let [var-meta (meta var) |
| 91 | + ns (ns-name (:ns var-meta)) |
| 92 | + qualified-name (:name var-meta)]] |
| 93 | + (candidate-data name ns qualified-name :macro))) |
| 94 | + |
| 95 | +(defn core-macro-candidates |
| 96 | + [env ns] |
| 97 | + "Returns candidate data for all cljs.core macros visible in ns." |
| 98 | + (macro-candidates (a/core-macros env ns))) |
| 99 | + |
| 100 | +(defn import-candidates |
| 101 | + "Returns candidate data for all imports in context-ns." |
| 102 | + [env context-ns] |
| 103 | + (flatten |
| 104 | + (for [[import qualified-name] (a/imports env context-ns)] |
| 105 | + [(candidate-data import qualified-name qualified-name :import) |
| 106 | + (candidate-data qualified-name qualified-name qualified-name :import)]))) |
| 107 | + |
| 108 | +(defn unscoped-candidates |
| 109 | + "Returns all non-namespace-qualified potential candidates in context-ns." |
| 110 | + [env context-ns] |
| 111 | + (concat special-form-candidates |
| 112 | + (all-ns-candidates env) |
| 113 | + (ns-candidates env context-ns) |
| 114 | + (macro-ns-candidates env context-ns) |
| 115 | + (referred-var-candidates env context-ns) |
| 116 | + (referred-macro-candidates env context-ns) |
| 117 | + (ns-var-candidates env context-ns) |
| 118 | + (core-var-candidates env context-ns) |
| 119 | + (core-macro-candidates env context-ns) |
| 120 | + (import-candidates env context-ns))) |
13 | 121 |
|
14 |
| -(defn ns-completions |
15 |
| - "Returns a list of public vars in the given namespace." |
16 |
| - ([env ns] (keys (a/public-vars env ns))) |
17 |
| - ([env ns prefix] (prefix-completions prefix (ns-completions env ns)))) |
| 122 | +(defn- prefix-candidate |
| 123 | + [prefix candidate-data] |
| 124 | + (let [candidate (:candidate candidate-data) |
| 125 | + prefixed-candidate (str prefix "/" candidate)] |
| 126 | + (assoc candidate-data :candidate prefixed-candidate))) |
18 | 127 |
|
19 |
| -(defn macro-ns-completions |
20 |
| - "Returns a list of macro names in the given namespace." |
21 |
| - ([ns] (keys (a/public-macros ns))) |
22 |
| - ([ns prefix] (prefix-completions prefix (macro-ns-completions ns)))) |
| 128 | +(defn- prefix-candidates |
| 129 | + [prefix candidates] |
| 130 | + (map #(prefix-candidate prefix %) candidates)) |
23 | 131 |
|
24 | 132 | (defn- scope->ns
|
25 | 133 | [env scope context-ns]
|
|
33 | 141 | scope
|
34 | 142 | (a/to-macro-ns env scope context-ns)))
|
35 | 143 |
|
36 |
| -(defn scoped-completions |
| 144 | +(defn ns-public-var-candidates |
| 145 | + "Returns candidate data for all public vars defined in ns." |
| 146 | + [env ns] |
| 147 | + (var-candidates (a/public-vars env ns))) |
| 148 | + |
| 149 | +(defn ns-macro-candidates |
| 150 | + "Returns candidate data for all macros defined in ns." |
| 151 | + [env ns] |
| 152 | + (macro-candidates (a/public-macros ns))) |
| 153 | + |
| 154 | +(defn scoped-candidates |
| 155 | + "Returns all candidates for the namespace of sym. Sym must be |
| 156 | + namespace-qualified. Macro candidates are included if the namespace has its |
| 157 | + macros required in context-ns." |
37 | 158 | [env sym context-ns]
|
38 | 159 | (let [scope (symbol (namespace sym))
|
39 |
| - ns (scope->ns scope) |
40 |
| - macro-ns (scope->macro-ns scope)] |
41 |
| - (concat (ns-completions env ns scope) |
42 |
| - (macro-ns-completions macro-ns scope)))) |
| 160 | + ns (scope->ns env scope context-ns) |
| 161 | + macro-ns (scope->macro-ns env scope context-ns)] |
| 162 | + (mapcat #(prefix-candidates scope %) |
| 163 | + [(ns-public-var-candidates env ns) |
| 164 | + (ns-macro-candidates env macro-ns)]))) |
43 | 165 |
|
44 |
| -(defn unscoped-completions |
45 |
| - [env context-ns] |
46 |
| - (concat special-forms |
47 |
| - (keys (a/all-ns env)) |
48 |
| - (keys (a/ns-aliases env context-ns)) |
49 |
| - (keys (a/macro-ns-aliases env context-ns)) |
50 |
| - (keys (a/referred-vars env context-ns)) |
51 |
| - (keys (a/referred-macros env context-ns)) |
52 |
| - (keys (a/ns-vars env context-ns true)) |
53 |
| - (keys (a/core-macros env context-ns)) |
54 |
| - (keys (a/imports env context-ns)) |
55 |
| - (vals (a/imports env context-ns)))) |
56 |
| - |
57 |
| -(defn potential-completions |
| 166 | +(defn potential-candidates |
| 167 | + "Returns all candidates for sym. If sym is namespace-qualified, the candidates |
| 168 | + for that namespace will be returned (including macros if the namespace has its |
| 169 | + macros required in context-ns). Otherwise, all non-namespace-qualified |
| 170 | + candidates for context-ns will be returned." |
58 | 171 | [env sym context-ns]
|
59 | 172 | (if (namespace sym)
|
60 |
| - (scoped-completions env sym context-ns) |
61 |
| - (unscoped-completions env context-ns))) |
| 173 | + (scoped-candidates env sym context-ns) |
| 174 | + (unscoped-candidates env context-ns))) |
| 175 | + |
| 176 | +(def ^:private type-ranking |
| 177 | + [:special |
| 178 | + :ns |
| 179 | + :fn |
| 180 | + :protocol-fn |
| 181 | + :record |
| 182 | + :type |
| 183 | + :protocol |
| 184 | + :var |
| 185 | + :macro |
| 186 | + :import]) |
| 187 | + |
| 188 | +(defn- candidate-rank |
| 189 | + [candidate types] |
| 190 | + (.indexOf (vec types) (:type candidate))) |
| 191 | + |
| 192 | +(defn- distinct-candidates |
| 193 | + "Filters candidates to have only one entry for each value of :candidate. If |
| 194 | + multiple such entries do exist, the one with the earliest appearing value |
| 195 | + of :type in types will be used." |
| 196 | + [candidates types] |
| 197 | + (let [grouped-candidates (group-by :candidate candidates)] |
| 198 | + (for [[candidate group] grouped-candidates] |
| 199 | + (first (sort-by #(candidate-rank % types) group))))) |
| 200 | + |
| 201 | +(defn- candidate-match? |
| 202 | + [candidate prefix] |
| 203 | + (.startsWith ^String (:candidate candidate) (str prefix))) |
62 | 204 |
|
63 | 205 | (defn completions
|
64 |
| - "Return a sequence of matching completions given current namespace and a prefix string" |
| 206 | + "Returns a sequence of matching completions given current namespace and a |
| 207 | + prefix string." |
65 | 208 | ([env prefix] (completions env prefix nil))
|
66 | 209 | ([env prefix context-ns]
|
67 |
| - (->> (potential-completions env (u/as-sym prefix) (u/as-sym context-ns)) |
68 |
| - distinct |
69 |
| - (map str) |
70 |
| - (filter #(.startsWith % prefix)) |
71 |
| - sort))) |
| 210 | + (let [prefix (u/as-sym prefix) |
| 211 | + context-ns (u/as-sym context-ns) |
| 212 | + candidates (potential-candidates env prefix context-ns)] |
| 213 | + (as-> candidates $ |
| 214 | + (distinct-candidates $ type-ranking) |
| 215 | + (filter #(candidate-match? % prefix) $) |
| 216 | + (sort-by :candidate $))))) |
72 | 217 |
|
73 | 218 |
|
0 commit comments