diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index f39d97aeb5..6a9e48b481 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -4,7 +4,7 @@ pub use lsp_types::*; use anyhow::{anyhow, Context, Result}; use collections::HashMap; -use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite}; +use futures::{channel::oneshot, io::BufWriter, AsyncRead, AsyncWrite, FutureExt}; use gpui::{executor, AsyncAppContext, Task}; use parking_lot::Mutex; use postage::{barrier, prelude::Stream}; @@ -26,12 +26,14 @@ use std::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, Weak, }, + time::{Duration, Instant}, }; use std::{path::Path, process::Stdio}; use util::{ResultExt, TryFutureExt}; const JSON_RPC_VERSION: &str = "2.0"; const CONTENT_LEN_HEADER: &str = "Content-Length: "; +const LSP_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 2); type NotificationHandler = Box, &str, AsyncAppContext)>; type ResponseHandler = Box)>; @@ -697,7 +699,7 @@ impl LanguageServer { outbound_tx: &channel::Sender, executor: &Arc, params: T::Params, - ) -> impl 'static + Future> + ) -> impl 'static + Future> where T::Result: 'static + Send, { @@ -738,10 +740,25 @@ impl LanguageServer { .try_send(message) .context("failed to write to language server's stdin"); + let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse(); + let started = Instant::now(); async move { handle_response?; send?; - rx.await? + + let method = T::METHOD; + futures::select! { + response = rx.fuse() => { + let elapsed = started.elapsed(); + log::trace!("Took {elapsed:?} to recieve response to {method:?} id {id}"); + response? + } + + _ = timeout => { + log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}"); + anyhow::bail!("LSP request timeout"); + } + } } }