|
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