|
174 | 174 | (-cancelled? [_] |
175 | 175 | @cancelled?)) |
176 | 176 |
|
177 | | -(defn pending-received-request [method context params] |
| 177 | +(defn ^:private pending-received-request [handler method context params] |
178 | 178 | (let [cancelled? (atom false) |
179 | 179 | ;; coerce result/error to promise |
180 | 180 | result-promise (p/promise |
181 | | - (receive-request method |
182 | | - (assoc context ::req-cancelled? cancelled?) |
183 | | - params))] |
| 181 | + (handler method |
| 182 | + (assoc context ::req-cancelled? cancelled?) |
| 183 | + params))] |
184 | 184 | (map->PendingReceivedRequest |
185 | 185 | {:result-promise result-promise |
186 | 186 | :cancelled? cancelled?}))) |
|
192 | 192 | ;; * send-notification should do nothing until initialize response is sent, with the exception of window/showMessage, window/logMessage, telemetry/event, and $/progress |
193 | 193 | (defrecord ChanServer [input-ch |
194 | 194 | output-ch |
| 195 | + request-handler |
| 196 | + notification-handler |
195 | 197 | log-ch |
196 | 198 | trace-ch |
197 | 199 | tracer* |
|
362 | 364 | resp (lsp.responses/response id)] |
363 | 365 | (try |
364 | 366 | (trace this trace/received-request req started) |
365 | | - (let [pending-req (pending-received-request method context params)] |
| 367 | + (let [pending-req (pending-received-request request-handler method context params)] |
366 | 368 | (swap! pending-received-requests* assoc id pending-req) |
367 | 369 | (-> pending-req |
368 | 370 | :result-promise |
|
375 | 377 | (lsp.responses/error resp (lsp.errors/not-found method))) |
376 | 378 | (lsp.responses/infer resp result)))) |
377 | 379 | ;; Handle |
378 | | - ;; 1. Exceptions thrown within p/future created by receive-request. |
| 380 | + ;; 1. Exceptions thrown within promise returned by request-handler. |
379 | 381 | ;; 2. Cancelled requests. |
380 | 382 | (p/catch |
381 | 383 | (fn [e] |
|
389 | 391 | (swap! pending-received-requests* dissoc id) |
390 | 392 | (trace this trace/sending-response req resp started (.instant clock)) |
391 | 393 | (async/>!! output-ch resp))))) |
392 | | - (catch Throwable e ;; exceptions thrown by receive-request |
| 394 | + (catch Throwable e ;; exceptions thrown by request-handler |
393 | 395 | (log-error-receiving this e req) |
394 | 396 | (async/>!! output-ch (internal-error-response resp req)))))) |
395 | 397 | (receive-notification [this context {:keys [method params] :as notif}] |
|
400 | 402 | (if-let [pending-req (get @pending-received-requests* (:id params))] |
401 | 403 | (p/cancel! pending-req) |
402 | 404 | (trace this trace/received-unmatched-cancellation-notification notif now)) |
403 | | - (let [result (receive-notification method context params)] |
| 405 | + (let [result (notification-handler method context params)] |
404 | 406 | (when (identical? ::method-not-found result) |
405 | 407 | (protocols.endpoint/log this :warn "received unexpected notification" method))))) |
406 | 408 | (catch Throwable e |
|
410 | 412 | (update server :tracer* reset! (trace/tracer-for-level trace-level))) |
411 | 413 |
|
412 | 414 | (defn chan-server |
413 | | - [{:keys [output-ch input-ch log-ch trace? trace-level trace-ch clock on-close] |
414 | | - :or {clock (java.time.Clock/systemDefaultZone) |
| 415 | + "Creates a channel-based Language Server. |
| 416 | +
|
| 417 | + The returned server will be in unstarted state. Pass it to `start` to |
| 418 | + start it. |
| 419 | +
|
| 420 | + Required options: |
| 421 | +
|
| 422 | + - `output-ch` is a core.async channel that the server puts messages to the |
| 423 | + client onto. |
| 424 | + - `input-ch` is a core.async channel that the server takes messages from the |
| 425 | + client from. |
| 426 | +
|
| 427 | + Handler functions: |
| 428 | +
|
| 429 | + - `request-handler` is a 3-arg fn `[message context params] => response` |
| 430 | + to handle incoming client requests. The response can be a response map |
| 431 | + or a promise resolving to a response map. Defaults to the `receive-request` |
| 432 | + multi-method. |
| 433 | + - `notification-handler` is a 3-arg fn `[message context params]` to handle |
| 434 | + incoming client notifications. Its return value is ignored. Defaults to |
| 435 | + the `receive-notification` multi-method. |
| 436 | +
|
| 437 | + Options for logging and tracing: |
| 438 | +
|
| 439 | + - `log-ch` is an optional core.async channel that the server will put log |
| 440 | + messages onto. If none is specified, a default one will be created. |
| 441 | + - `trace-ch` is an optional core.async channel that the server will put |
| 442 | + trace events onto. |
| 443 | + - `trace-level` is a string that determines the verbosity of trace messages, |
| 444 | + can be \"verbose\", \"messages\", or \"off\". |
| 445 | + - `trace?` is a short-hand for `:trace-level \"verbose\"` and the default |
| 446 | + when a `trace-ch` is specified. |
| 447 | +
|
| 448 | + Other options: |
| 449 | +
|
| 450 | + - `clock` is a `java.time.Clock` that provides the current time for trace |
| 451 | + messages. |
| 452 | + - `on-close` is a 0-arg fn that the server will call after it has shut down." |
| 453 | + [{:keys [output-ch input-ch |
| 454 | + request-handler notification-handler |
| 455 | + log-ch |
| 456 | + trace? trace-level trace-ch |
| 457 | + clock on-close] |
| 458 | + :or {request-handler #'receive-request |
| 459 | + notification-handler #'receive-notification |
| 460 | + clock (java.time.Clock/systemDefaultZone) |
415 | 461 | on-close (constantly nil)}}] |
416 | 462 | (let [;; before defaulting trace-ch, so that default is "off" |
417 | 463 | tracer (trace/tracer-for-level (or trace-level |
|
422 | 468 | (map->ChanServer |
423 | 469 | {:output-ch output-ch |
424 | 470 | :input-ch input-ch |
| 471 | + :request-handler request-handler |
| 472 | + :notification-handler notification-handler |
425 | 473 | :log-ch log-ch |
426 | 474 | :trace-ch trace-ch |
427 | 475 | :tracer* (atom tracer) |
|
0 commit comments