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