|
9 | 9 | [orchard.java :as java] |
10 | 10 | [orchard.misc :as u] |
11 | 11 | [orchard.meta :as m] |
| 12 | + [orchard.info :as clj-info] |
| 13 | + [orchard.eldoc :as eldoc] |
12 | 14 | [cljs-tooling.info :as cljs-info] |
13 | 15 | [orchard.spec :as spec])) |
14 | 16 |
|
15 | | -(defn- resource-full-path [relative-path] |
16 | | - (io/resource relative-path (class-loader))) |
| 17 | +(declare format-response) |
17 | 18 |
|
18 | | -(def see-also-data |
19 | | - (edn/read-string (slurp (io/resource "see-also.edn")))) |
| 19 | +(defn format-nested |
| 20 | + "Apply response formatting to nested `:candidates` info for Java members." |
| 21 | + [info] |
| 22 | + (if-let [candidates (:candidates info)] |
| 23 | + (assoc info :candidates |
| 24 | + (zipmap (keys candidates) |
| 25 | + (->> (vals candidates) (map format-response)))) |
| 26 | + info)) |
20 | 27 |
|
21 | | -(defn info-clj |
22 | | - [ns sym] |
23 | | - (or |
24 | | - ;; it's a special (special-symbol?) |
25 | | - (m/special-sym-meta sym) |
26 | | - ;; it's a var |
27 | | - (m/var-meta (m/resolve-var ns sym)) |
28 | | - ;; sym is an alias for another ns |
29 | | - (m/ns-meta (get (m/resolve-aliases ns) sym)) |
30 | | - ;; it's simply a full ns |
31 | | - (m/ns-meta (find-ns sym)) |
32 | | - ;; it's a Java class/member symbol...or nil |
33 | | - (java/resolve-symbol ns sym))) |
| 28 | +(defn blacklist |
| 29 | + "Remove anything that might contain arbitrary EDN, metadata can hold anything" |
| 30 | + [info] |
| 31 | + (let [blacklisted #{:arglists :forms}] |
| 32 | + (apply dissoc info blacklisted))) |
| 33 | + |
| 34 | +(defn format-response |
| 35 | + [info] |
| 36 | + (letfn [(forms-join [forms] |
| 37 | + (->> (map pr-str forms) |
| 38 | + (str/join \newline)))] |
| 39 | + (when info |
| 40 | + (-> info |
| 41 | + (merge (when-let [ns (:ns info)] |
| 42 | + {:ns (str ns)}) |
| 43 | + (when-let [args (:arglists info)] |
| 44 | + {:arglists-str (forms-join args)}) |
| 45 | + (when-let [forms (:forms info)] |
| 46 | + {:forms-str (forms-join forms)}) |
| 47 | + (when-let [file (:file info)] |
| 48 | + (clj-info/file-info file)) |
| 49 | + (when-let [path (:javadoc info)] |
| 50 | + (clj-info/javadoc-info path))) |
| 51 | + format-nested |
| 52 | + blacklist |
| 53 | + u/transform-value)))) |
34 | 54 |
|
35 | 55 | (defn info-cljs |
36 | 56 | [env symbol ns] |
|
58 | 78 | f)) |
59 | 79 | f))))) |
60 | 80 |
|
61 | | -(defn info-java |
62 | | - [class member] |
63 | | - (java/member-info class member)) |
64 | | - |
65 | 81 | (defn info |
66 | 82 | [{:keys [ns symbol class member] :as msg}] |
67 | 83 | (let [[ns symbol class member] (map u/as-sym [ns symbol class member])] |
68 | 84 | (if-let [cljs-env (cljs/grab-cljs-env msg)] |
69 | 85 | (info-cljs cljs-env symbol ns) |
70 | | - (let [var-info (cond (and ns symbol) (info-clj ns symbol) |
71 | | - (and class member) (info-java class member) |
| 86 | + (let [var-info (cond (and ns symbol) (clj-info/info ns symbol) |
| 87 | + (and class member) (clj-info/info-java class member) |
72 | 88 | :else (throw (Exception. |
73 | 89 | "Either \"symbol\", or (\"class\", \"member\") must be supplied"))) |
74 | | - var-key (str (:ns var-info) "/" (:name var-info)) |
75 | | - see-also (->> (get see-also-data var-key) |
76 | | - (filter (comp resolve u/as-sym)))] |
| 90 | + see-also (clj-info/see-also ns symbol)] |
77 | 91 | (if (seq see-also) |
78 | 92 | (merge {:see-also see-also} var-info) |
79 | 93 | var-info))))) |
80 | 94 |
|
81 | | -(defn resource-path |
82 | | - "If it's a resource, return a tuple of the relative path and the full resource path." |
83 | | - [x] |
84 | | - (or (if-let [full (resource-full-path x)] |
85 | | - [x full]) |
86 | | - (if-let [[_ relative] (re-find #".*jar!/(.*)" x)] |
87 | | - (if-let [full (resource-full-path relative)] |
88 | | - [relative full])) |
89 | | - ;; handles load-file on jar resources from a cider buffer |
90 | | - (if-let [[_ relative] (re-find #".*jar:(.*)" x)] |
91 | | - (if-let [full (resource-full-path relative)] |
92 | | - [relative full])))) |
93 | | - |
94 | | -(defn file-path |
95 | | - "For a file path, return a URL to the file if it exists and does not |
96 | | - represent a form evaluated at the REPL." |
97 | | - [x] |
98 | | - (when (seq x) |
99 | | - (let [f (io/file x)] |
100 | | - (when (and (.exists f) |
101 | | - (not (-> f .getName (.startsWith "form-init")))) |
102 | | - (io/as-url f))))) |
103 | | - |
104 | | -(defn file-info |
105 | | - [path] |
106 | | - (let [[resource-relative resource-full] (resource-path path)] |
107 | | - (merge {:file (or (file-path path) resource-full path)} |
108 | | - ;; Classpath-relative path if possible |
109 | | - (if resource-relative |
110 | | - {:resource resource-relative})))) |
111 | | - |
112 | | -(defn javadoc-info |
113 | | - "Resolve a relative javadoc path to a URL and return as a map. Prefer javadoc |
114 | | - resources on the classpath; then use online javadoc content for core API |
115 | | - classes. If no source is available, return the relative path as is." |
116 | | - [path] |
117 | | - {:javadoc |
118 | | - (or (resource-full-path path) |
119 | | - ;; [bug#308] `*remote-javadocs*` is outdated WRT Java |
120 | | - ;; 8, so we try our own thing first. |
121 | | - (when (re-find #"^(java|javax|org.omg|org.w3c.dom|org.xml.sax)/" path) |
122 | | - (format "http://docs.oracle.com/javase/%d/docs/api/%s" |
123 | | - u/java-api-version path)) |
124 | | - ;; If that didn't work, _then_ we fallback on `*remote-javadocs*`. |
125 | | - (some (let [classname (.replaceAll path "/" ".")] |
126 | | - (fn [[prefix url]] |
127 | | - (when (.startsWith classname prefix) |
128 | | - (str url path)))) |
129 | | - @javadoc/*remote-javadocs*) |
130 | | - path)}) |
131 | | - |
132 | | -;; TODO: Seems those were hardcoded here accidentally - we should |
133 | | -;; probably provide a simple API to register remote JavaDocs. |
134 | | -(javadoc/add-remote-javadoc "com.amazonaws." "http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/") |
135 | | -(javadoc/add-remote-javadoc "org.apache.kafka." "https://kafka.apache.org/090/javadoc/index.html?") |
136 | | - |
137 | | -(declare format-response) |
138 | | - |
139 | | -(defn format-nested |
140 | | - "Apply response formatting to nested `:candidates` info for Java members." |
141 | | - [info] |
142 | | - (if-let [candidates (:candidates info)] |
143 | | - (assoc info :candidates |
144 | | - (zipmap (keys candidates) |
145 | | - (->> (vals candidates) (map format-response)))) |
146 | | - info)) |
147 | | - |
148 | | -(defn blacklist |
149 | | - "Remove anything that might contain arbitrary EDN, metadata can hold anything" |
150 | | - [info] |
151 | | - (let [blacklisted #{:arglists :forms}] |
152 | | - (apply dissoc info blacklisted))) |
153 | | - |
154 | | -(defn format-response |
155 | | - [info] |
156 | | - (letfn [(forms-join [forms] |
157 | | - (->> (map pr-str forms) |
158 | | - (str/join \newline)))] |
159 | | - (when info |
160 | | - (-> info |
161 | | - (merge (when-let [ns (:ns info)] |
162 | | - {:ns (str ns)}) |
163 | | - (when-let [args (:arglists info)] |
164 | | - {:arglists-str (forms-join args)}) |
165 | | - (when-let [forms (:forms info)] |
166 | | - {:forms-str (forms-join forms)}) |
167 | | - (when-let [file (:file info)] |
168 | | - (file-info file)) |
169 | | - (when-let [path (:javadoc info)] |
170 | | - (javadoc-info path))) |
171 | | - format-nested |
172 | | - blacklist |
173 | | - u/transform-value)))) |
174 | | - |
175 | 95 | (defn info-reply |
176 | 96 | [msg] |
177 | 97 | (if-let [var-info (format-response (info msg))] |
178 | 98 | var-info |
179 | 99 | {:status :no-info})) |
180 | 100 |
|
181 | | -(defn extract-arglists |
182 | | - [info] |
183 | | - (cond |
184 | | - (:special-form info) (->> (:forms info) |
185 | | - ;; :forms contains a vector of sequences or symbols |
186 | | - ;; which we have to convert the format employed by :arglists |
187 | | - (map #(if (coll? %) (vec %) (vector %)))) |
188 | | - (:candidates info) (->> (:candidates info) |
189 | | - vals |
190 | | - (mapcat :arglists) |
191 | | - distinct |
192 | | - (sort-by count)) |
193 | | - :else (:arglists info))) |
194 | | - |
195 | | -(defn format-arglists [raw-arglists] |
196 | | - (map #(mapv str %) raw-arglists)) |
197 | | - |
198 | | -(defn extract-ns-or-class |
199 | | - [{:keys [ns class candidates] :as info}] |
200 | | - (cond |
201 | | - ns {:ns (str ns)} |
202 | | - class {:class [(str class)]} |
203 | | - candidates {:class (map key candidates)})) |
204 | | - |
205 | | -(defn extract-name-or-member |
206 | | - [{:keys [name member candidates]}] |
207 | | - (cond |
208 | | - name {:name (str name)} |
209 | | - member {:member (str member)} |
210 | | - candidates {:member (->> candidates vals (map :member) first str)})) |
211 | | - |
212 | | -(defn extract-eldoc |
213 | | - [info] |
214 | | - (if-let [arglists (seq (-> info extract-arglists format-arglists))] |
215 | | - {:eldoc arglists :type "function"} |
216 | | - {:type "variable"})) |
217 | | - |
218 | 101 | (defn eldoc-reply |
219 | 102 | [msg] |
220 | 103 | (if-let [info (info msg)] |
221 | | - (merge (extract-ns-or-class info) |
222 | | - (extract-name-or-member info) |
223 | | - (extract-eldoc info) |
224 | | - {:docstring (:doc info)}) |
| 104 | + (eldoc/eldoc info) |
225 | 105 | {:status :no-eldoc})) |
226 | 106 |
|
227 | 107 | (defn eldoc-datomic-query-reply |
228 | | - [msg] |
| 108 | + [{:keys [ns symbol] :as msg}] |
229 | 109 | (try |
230 | | - (let [ns (read-string (:ns msg)) |
231 | | - sym (read-string (:symbol msg)) |
232 | | - query (if (symbol? sym) |
233 | | - (deref (ns-resolve ns sym)) |
234 | | - (eval sym)) |
235 | | - inputs (if (map? query) |
236 | | - ;; query as map |
237 | | - (or (:in query) "$") |
238 | | - ;; query as vector |
239 | | - (let [partitioned (partition-by keyword? query) |
240 | | - index (.indexOf partitioned '(:in))] |
241 | | - (if (= index -1) |
242 | | - "$" |
243 | | - (nth partitioned (+ 1 index)))))] |
244 | | - {:inputs (format-arglists [inputs])}) |
| 110 | + (eldoc/datomic-query ns symbol) |
245 | 111 | (catch Throwable _ {:status :no-eldoc}))) |
246 | 112 |
|
247 | 113 | (defn handle-info [handler msg] |
|
0 commit comments