|
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