Separate timeout and connection dropped errors out (#30457)
This commit is contained in:
parent
39da72161f
commit
471e02d48f
25 changed files with 313 additions and 115 deletions
|
@ -5,7 +5,12 @@ pub use lsp_types::*;
|
|||
|
||||
use anyhow::{Context as _, Result, anyhow};
|
||||
use collections::HashMap;
|
||||
use futures::{AsyncRead, AsyncWrite, Future, FutureExt, channel::oneshot, io::BufWriter, select};
|
||||
use futures::{
|
||||
AsyncRead, AsyncWrite, Future, FutureExt,
|
||||
channel::oneshot::{self, Canceled},
|
||||
io::BufWriter,
|
||||
select,
|
||||
};
|
||||
use gpui::{App, AppContext as _, AsyncApp, BackgroundExecutor, SharedString, Task};
|
||||
use notification::DidChangeWorkspaceFolders;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
@ -39,7 +44,7 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
use std::{path::Path, process::Stdio};
|
||||
use util::{ResultExt, TryFutureExt};
|
||||
use util::{ConnectionResult, ResultExt, TryFutureExt};
|
||||
|
||||
const JSON_RPC_VERSION: &str = "2.0";
|
||||
const CONTENT_LEN_HEADER: &str = "Content-Length: ";
|
||||
|
@ -259,7 +264,7 @@ struct Error {
|
|||
message: String,
|
||||
}
|
||||
|
||||
pub trait LspRequestFuture<O>: Future<Output = O> {
|
||||
pub trait LspRequestFuture<O>: Future<Output = ConnectionResult<O>> {
|
||||
fn id(&self) -> i32;
|
||||
}
|
||||
|
||||
|
@ -284,7 +289,10 @@ impl<F: Future> Future for LspRequest<F> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<F: Future> LspRequestFuture<F::Output> for LspRequest<F> {
|
||||
impl<F, O> LspRequestFuture<O> for LspRequest<F>
|
||||
where
|
||||
F: Future<Output = ConnectionResult<O>>,
|
||||
{
|
||||
fn id(&self) -> i32 {
|
||||
self.id
|
||||
}
|
||||
|
@ -824,7 +832,17 @@ impl LanguageServer {
|
|||
cx: &App,
|
||||
) -> Task<Result<Arc<Self>>> {
|
||||
cx.spawn(async move |_| {
|
||||
let response = self.request::<request::Initialize>(params).await?;
|
||||
let response = self
|
||||
.request::<request::Initialize>(params)
|
||||
.await
|
||||
.into_response()
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"initializing server {}, id {}",
|
||||
self.name(),
|
||||
self.server_id()
|
||||
)
|
||||
})?;
|
||||
if let Some(info) = response.server_info {
|
||||
self.process_name = info.name.into();
|
||||
}
|
||||
|
@ -863,7 +881,13 @@ impl LanguageServer {
|
|||
|
||||
select! {
|
||||
request_result = shutdown_request.fuse() => {
|
||||
request_result?;
|
||||
match request_result {
|
||||
ConnectionResult::Timeout => {
|
||||
log::warn!("timeout waiting for language server {name} to shutdown");
|
||||
},
|
||||
ConnectionResult::ConnectionReset => {},
|
||||
ConnectionResult::Result(r) => r?,
|
||||
}
|
||||
}
|
||||
|
||||
_ = timer => {
|
||||
|
@ -1084,7 +1108,7 @@ impl LanguageServer {
|
|||
pub fn request<T: request::Request>(
|
||||
&self,
|
||||
params: T::Params,
|
||||
) -> impl LspRequestFuture<Result<T::Result>> + use<T>
|
||||
) -> impl LspRequestFuture<T::Result> + use<T>
|
||||
where
|
||||
T::Result: 'static + Send,
|
||||
{
|
||||
|
@ -1097,15 +1121,18 @@ impl LanguageServer {
|
|||
)
|
||||
}
|
||||
|
||||
fn request_internal<T: request::Request>(
|
||||
fn request_internal<T>(
|
||||
next_id: &AtomicI32,
|
||||
response_handlers: &Mutex<Option<HashMap<RequestId, ResponseHandler>>>,
|
||||
outbound_tx: &channel::Sender<String>,
|
||||
executor: &BackgroundExecutor,
|
||||
params: T::Params,
|
||||
) -> impl LspRequestFuture<Result<T::Result>> + use<T>
|
||||
) -> impl LspRequestFuture<T::Result> + use<T>
|
||||
where
|
||||
T::Result: 'static + Send,
|
||||
T: request::Request,
|
||||
// TODO kb
|
||||
// <T as lsp_types::request::Request>::Result: ConnectionResult,
|
||||
{
|
||||
let id = next_id.fetch_add(1, SeqCst);
|
||||
let message = serde_json::to_string(&Request {
|
||||
|
@ -1120,7 +1147,7 @@ impl LanguageServer {
|
|||
let handle_response = response_handlers
|
||||
.lock()
|
||||
.as_mut()
|
||||
.ok_or_else(|| anyhow!("server shut down"))
|
||||
.context("server shut down")
|
||||
.map(|handlers| {
|
||||
let executor = executor.clone();
|
||||
handlers.insert(
|
||||
|
@ -1153,8 +1180,12 @@ impl LanguageServer {
|
|||
let mut timeout = executor.timer(LSP_REQUEST_TIMEOUT).fuse();
|
||||
let started = Instant::now();
|
||||
LspRequest::new(id, async move {
|
||||
handle_response?;
|
||||
send?;
|
||||
if let Err(e) = handle_response {
|
||||
return ConnectionResult::Result(Err(e));
|
||||
}
|
||||
if let Err(e) = send {
|
||||
return ConnectionResult::Result(Err(e));
|
||||
}
|
||||
|
||||
let cancel_on_drop = util::defer(move || {
|
||||
if let Some(outbound_tx) = outbound_tx.upgrade() {
|
||||
|
@ -1174,12 +1205,18 @@ impl LanguageServer {
|
|||
let elapsed = started.elapsed();
|
||||
log::trace!("Took {elapsed:?} to receive response to {method:?} id {id}");
|
||||
cancel_on_drop.abort();
|
||||
response?
|
||||
match response {
|
||||
Ok(response_result) => ConnectionResult::Result(response_result),
|
||||
Err(Canceled) => {
|
||||
log::error!("Server reset connection for a request {method:?} id {id}");
|
||||
ConnectionResult::ConnectionReset
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
_ = timeout => {
|
||||
log::error!("Cancelled LSP request task for {method:?} id {id} which took over {LSP_REQUEST_TIMEOUT:?}");
|
||||
anyhow::bail!("LSP request timeout");
|
||||
ConnectionResult::Timeout
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@ -1506,7 +1543,7 @@ impl FakeLanguageServer {
|
|||
}
|
||||
|
||||
/// See [`LanguageServer::request`].
|
||||
pub async fn request<T>(&self, params: T::Params) -> Result<T::Result>
|
||||
pub async fn request<T>(&self, params: T::Params) -> ConnectionResult<T::Result>
|
||||
where
|
||||
T: request::Request,
|
||||
T::Result: 'static + Send,
|
||||
|
@ -1608,6 +1645,7 @@ impl FakeLanguageServer {
|
|||
token: NumberOrString::String(token.clone()),
|
||||
})
|
||||
.await
|
||||
.into_response()
|
||||
.unwrap();
|
||||
self.notify::<notification::Progress>(&ProgressParams {
|
||||
token: NumberOrString::String(token),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue