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