|
3 | 3 | Neovim." |
4 | 4 | (:require |
5 | 5 | [clojure.core.async :as async] |
| 6 | + [clojure.edn :as edn] |
6 | 7 | [clojure.java.io :as io] |
7 | 8 | [clojure.string :as string] |
8 | 9 | [clojure.tools.namespace.find :as namespace.find] |
|
28 | 29 | (string/join "\n" (map str (.getStackTrace throwable))) |
29 | 30 | "\n######################\n"))) |
30 | 31 |
|
31 | | -(defn run-command |
| 32 | +(defn run-command-async |
32 | 33 | [{:keys [nvim nrepl socket-repl]} f] |
33 | 34 | (fn [msg] |
34 | 35 | (if-not (or (socket-repl/connected? socket-repl) |
|
38 | 39 | nvim ":echo 'Use :Connect host:port to connect to a socket repl'")) |
39 | 40 | (async/thread (f msg))) |
40 | 41 | ;; Don't return an async channel, return something msg-pack can serialize. |
41 | | - :done)) |
| 42 | + "done")) |
| 43 | + |
| 44 | +(defn run-command |
| 45 | + [{:keys [nvim nrepl socket-repl]} f] |
| 46 | + (fn [msg] |
| 47 | + (if-not (or (socket-repl/connected? socket-repl) |
| 48 | + (nrepl/connected? nrepl)) |
| 49 | + (async/thread |
| 50 | + (api/command |
| 51 | + nvim ":echo 'Use :Connect host:port to connect to a socket repl'")) |
| 52 | + (f msg)))) |
42 | 53 |
|
43 | 54 | (defn get-rlog-buffer |
44 | 55 | "Returns the buffer w/ b:rlog set, if one exists." |
|
59 | 70 | (let [buffer (get-rlog-buffer nvim)] |
60 | 71 | (when buffer (api.buffer/get-number nvim buffer)))) |
61 | 72 |
|
| 73 | +(defn get-completion-context |
| 74 | + [nvim] |
| 75 | + (let [skip-exp "synIDattr(synID(line(\".\"),col(\".\"),1),\"name\") =~? \"comment\\|string\\|char\\|regexp\"" |
| 76 | + ctx-start (api/call-function "searchpairpos" ["(" "" ")" "Wrnb" skip-exp]) |
| 77 | + ctx-start-here (api/call-function "searchpairpos" ["(" "" ")" "Wrnc" skip-exp])])) |
| 78 | + |
62 | 79 | (defn code-channel |
63 | 80 | [plugin] |
64 | 81 | (:code-channel plugin)) |
|
88 | 105 | (async/>!! (socket-repl/input-channel internal-socket-repl) |
89 | 106 | '(do |
90 | 107 | (ns srepl.injection |
91 | | - (:refer-clojure :rename {eval core-eval})) |
92 | | - (defn eval |
93 | | - ([form] (eval form nil)) |
94 | | - ([form options] |
95 | | - (let [result (core-eval form)] |
96 | | - (merge |
97 | | - #:socket-repl{:result (pr-str result) |
98 | | - :form (pr-str form)} |
99 | | - options)))))) |
| 108 | + (:require |
| 109 | + [clojure.repl :as repl])) |
| 110 | + |
| 111 | + (defonce available-plugins (atom {})) |
| 112 | + |
| 113 | + (try |
| 114 | + (require '[compliment.core :as compliment]) |
| 115 | + (swap! available-plugins assoc :compliment true) |
| 116 | + (catch Exception e |
| 117 | + nil)) |
| 118 | + |
| 119 | + (defn completions |
| 120 | + [prefix options] |
| 121 | + (if |
| 122 | + (:compliment @available-plugins) |
| 123 | + (compliment/completions prefix options) |
| 124 | + (repl/apropos prefix))))) |
100 | 125 | (catch Throwable t |
101 | 126 | (log/error t "Error connecting to socket repl") |
102 | 127 | (async/thread (api/command |
|
125 | 150 | (nvim/register-method! |
126 | 151 | nvim |
127 | 152 | "eval" |
128 | | - (run-command |
| 153 | + (run-command-async |
129 | 154 | plugin |
130 | 155 | (fn [msg] |
131 | 156 | (try |
|
142 | 167 | (nvim/register-method! |
143 | 168 | nvim |
144 | 169 | "eval-form" |
145 | | - (run-command |
| 170 | + (run-command-async |
146 | 171 | plugin |
147 | 172 | (fn [msg] |
148 | 173 | (let [[row col] (api-ext/get-cursor-location nvim) |
|
157 | 182 | (nvim/register-method! |
158 | 183 | nvim |
159 | 184 | "eval-buffer" |
160 | | - (run-command |
| 185 | + (run-command-async |
161 | 186 | plugin |
162 | 187 | (fn [msg] |
163 | 188 | (let [buffer (api/get-current-buf nvim)] |
|
169 | 194 | (nvim/register-method! |
170 | 195 | nvim |
171 | 196 | "doc" |
172 | | - (run-command |
| 197 | + (run-command-async |
173 | 198 | plugin |
174 | 199 | (fn [msg] |
175 | 200 | (let [code (format "(clojure.repl/doc %s)" (-> msg |
|
180 | 205 | (nvim/register-method! |
181 | 206 | nvim |
182 | 207 | "doc-cursor" |
183 | | - (run-command |
| 208 | + (run-command-async |
184 | 209 | plugin |
185 | 210 | (fn [msg] |
186 | 211 | (api-ext/get-current-word-async |
|
192 | 217 | (nvim/register-method! |
193 | 218 | nvim |
194 | 219 | "source" |
195 | | - (run-command |
| 220 | + (run-command-async |
196 | 221 | plugin |
197 | 222 | (fn [msg] |
198 | 223 | (let [code (format "(clojure.repl/source %s)" (-> msg |
|
203 | 228 | (nvim/register-method! |
204 | 229 | nvim |
205 | 230 | "source-cursor" |
206 | | - (run-command |
| 231 | + (run-command-async |
207 | 232 | plugin |
208 | 233 | (fn [msg] |
209 | 234 | (api-ext/get-current-word-async |
|
214 | 239 |
|
215 | 240 | (nvim/register-method! |
216 | 241 | nvim |
217 | | - "cp" |
| 242 | + "complete-initial" |
| 243 | + (run-command |
| 244 | + plugin |
| 245 | + (fn [msg] |
| 246 | + (let [line (api/get-current-line nvim) |
| 247 | + [cursor-row cursor-col] (api-ext/get-cursor-location nvim)] |
| 248 | + (let [start-col (- cursor-col |
| 249 | + (->> line |
| 250 | + (take cursor-col) |
| 251 | + (reverse) |
| 252 | + (take-while #(not (#{\ \(} %))) |
| 253 | + (count)))] |
| 254 | + start-col))))) |
| 255 | + |
| 256 | + (nvim/register-method! |
| 257 | + nvim |
| 258 | + "complete-matches" |
218 | 259 | (run-command |
| 260 | + plugin |
| 261 | + (fn [msg] |
| 262 | + (let [word (first (message/params msg)) |
| 263 | + context (get-completion-context nvim) |
| 264 | + code-form (pr-str |
| 265 | + `(srepl.injection/completions |
| 266 | + ~word |
| 267 | + {:ns *ns* |
| 268 | + :context ~context})) |
| 269 | + #_code-form #_(str "(srepl.injection/completions " |
| 270 | + "\"" word "\" " |
| 271 | + "{:ns *ns*})") |
| 272 | + res-chan (async/chan 1 (filter #(= (:form %) |
| 273 | + code-form)))] |
| 274 | + (try |
| 275 | + (socket-repl/subscribe-output internal-socket-repl res-chan) |
| 276 | + (async/>!! (socket-repl/input-channel internal-socket-repl) code-form) |
| 277 | + (let [matches (async/<!! res-chan) |
| 278 | + r (map (fn [{:keys [candidate type ns] :as match}] |
| 279 | + (log/info (str "Match: " match)) |
| 280 | + {"word" candidate |
| 281 | + "menu" ns |
| 282 | + "kind" (case type |
| 283 | + :function "f" |
| 284 | + :special-form "d" |
| 285 | + :class "t" |
| 286 | + :local "v" |
| 287 | + :keyword "v" |
| 288 | + :resource "t" |
| 289 | + :namespace "t" |
| 290 | + :method "f" |
| 291 | + :static-field "m" |
| 292 | + "")}) |
| 293 | + (edn/read-string (:val matches)))] |
| 294 | + r) |
| 295 | + (finally |
| 296 | + (async/close! res-chan))))))) |
| 297 | + |
| 298 | + (nvim/register-method! |
| 299 | + nvim |
| 300 | + "cp" |
| 301 | + (run-command-async |
219 | 302 | plugin |
220 | 303 | (fn [msg] |
221 | 304 | (let [code-form "(map #(.getAbsolutePath %) (clojure.java.classpath/classpath))"] |
|
230 | 313 | (log/info (:ms res)) |
231 | 314 | (log/info (:val res))) |
232 | 315 | (finally |
233 | | - (.close res-chan))))))))) |
| 316 | + (async/close! res-chan))))))))) |
234 | 317 |
|
235 | 318 | (nvim/register-method! |
236 | 319 | nvim |
237 | 320 | "switch-buffer-ns" |
238 | | - (run-command |
| 321 | + (run-command-async |
239 | 322 | plugin |
240 | 323 | (fn [msg] |
241 | 324 | (let [buffer-name (api/get-current-buf nvim) |
|
246 | 329 | code-form (if eval-entire-declaration? |
247 | 330 | namespace-declaration |
248 | 331 | `(clojure.core/in-ns '~(second namespace-declaration)))] |
249 | | - (async/>!! (socket-repl/input-channel socket-repl) code-form))))) |
| 332 | + (async/>!! (socket-repl/input-channel socket-repl) code-form) |
| 333 | + (async/>!! (socket-repl/input-channel internal-socket-repl) code-form))))) |
250 | 334 |
|
251 | 335 | (nvim/register-method! |
252 | 336 | nvim |
253 | 337 | "show-log" |
254 | | - (run-command |
| 338 | + (run-command-async |
255 | 339 | plugin |
256 | 340 | (fn [msg] |
257 | 341 | (let [file (-> repl-log repl-log/file .getAbsolutePath)] |
|
272 | 356 | (nvim/register-method! |
273 | 357 | nvim |
274 | 358 | "dismiss-log" |
275 | | - (run-command |
| 359 | + (run-command-async |
276 | 360 | plugin |
277 | 361 | (fn [msg] |
278 | 362 | (api/command |
|
0 commit comments