diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs index b67b634e1a..a907292e02 100644 --- a/cli/lsp/tsc.rs +++ b/cli/lsp/tsc.rs @@ -231,8 +231,12 @@ impl TsServer { start(&mut ts_runtime, false).unwrap(); started = true; } - let value = request(&mut ts_runtime, state_snapshot, req, token); - if tx.send(value).is_err() { + let value = + request(&mut ts_runtime, state_snapshot, req, token.clone()); + let was_sent = tx.send(value).is_ok(); + // Don't print the send error if the token is cancelled, it's expected + // to fail in that case and this commonly occurs. + if !was_sent && !token.is_cancelled() { lsp_warn!("Unable to send result to client."); } } @@ -968,11 +972,24 @@ impl TsServer { where R: de::DeserializeOwned, { + // When an LSP request is cancelled by the client, the future this is being + // executed under and any local variables here will be dropped at the next + // await point. To pass on that cancellation to the TS thread, we make this + // wrapper which cancels the request's token on drop. + struct DroppableToken(CancellationToken); + impl Drop for DroppableToken { + fn drop(&mut self) { + self.0.cancel(); + } + } + let token = token.child_token(); + let droppable_token = DroppableToken(token.clone()); let (tx, rx) = oneshot::channel::>(); if self.sender.send((req, snapshot, tx, token)).is_err() { return Err(anyhow!("failed to send request to tsc thread")); } let value = rx.await??; + drop(droppable_token); Ok(serde_json::from_value::(value)?) } }