Cancel language server requests if request is dropped (#4159)

Before this change, we would send requests to language servers without
canceling them even if we never wait for their response.

Example: when doing document highlights, we'd send a request (modulo
debouncing) whenever a change was made, ignoring previously sent
requests that might still be in-flight.

With this change, we now send a Cancel request (from the LSP spec) to
the language server in case no one listens to the response anymore
(which is what happens when the `Future` returned by `request_internal`)
is dropped.

Release Notes:

- Improved performance when interacting with language servers.
This commit is contained in:
Thorsten Ball 2024-01-19 16:54:24 +01:00 committed by GitHub
commit e55e895e19
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 15 additions and 1 deletions

View File

@ -863,17 +863,31 @@ impl LanguageServer {
.try_send(message)
.context("failed to write to language server's stdin");
let outbound_tx = outbound_tx.downgrade();
let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
let started = Instant::now();
async move {
handle_response?;
send?;
let cancel_on_drop = util::defer(move || {
if let Some(outbound_tx) = outbound_tx.upgrade() {
Self::notify_internal::<notification::Cancel>(
&outbound_tx,
CancelParams {
id: NumberOrString::Number(id as i32),
},
)
.log_err();
}
});
let method = T::METHOD;
futures::select! {
response = rx.fuse() => {
let elapsed = started.elapsed();
log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
cancel_on_drop.abort();
response?
}

View File

@ -299,7 +299,7 @@ pub struct Deferred<F: FnOnce()>(Option<F>);
impl<F: FnOnce()> Deferred<F> {
/// Drop without running the deferred function.
pub fn cancel(mut self) {
pub fn abort(mut self) {
self.0.take();
}
}