From 8a0fb668f726200a5d37fdc5d84bc2e6a10aadbe Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 26 Oct 2023 13:50:24 +0200 Subject: [PATCH] Capture language server stderr during startup/init and log if failure zed2 electric boogaloo --- Cargo.lock | 2 ++ crates/copilot2/Cargo.toml | 1 + crates/copilot2/src/copilot2.rs | 12 ++++++++++-- crates/language2/src/language2.rs | 20 ++++++++++---------- crates/lsp2/src/lsp2.rs | 14 +++++++++++++- crates/prettier2/Cargo.toml | 1 + crates/prettier2/src/prettier2.rs | 1 + crates/project2/src/project2.rs | 30 ++++++++++++++++-------------- 8 files changed, 54 insertions(+), 27 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cbbcbc7914..ade49be3d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1836,6 +1836,7 @@ dependencies = [ "log", "lsp2", "node_runtime", + "parking_lot 0.11.2", "rpc", "serde", "serde_derive", @@ -5907,6 +5908,7 @@ dependencies = [ "log", "lsp2", "node_runtime", + "parking_lot 0.11.2", "serde", "serde_derive", "serde_json", diff --git a/crates/copilot2/Cargo.toml b/crates/copilot2/Cargo.toml index 161a9f3bd8..f83824d808 100644 --- a/crates/copilot2/Cargo.toml +++ b/crates/copilot2/Cargo.toml @@ -36,6 +36,7 @@ serde.workspace = true serde_derive.workspace = true smol.workspace = true futures.workspace = true +parking_lot.workspace = true [dev-dependencies] clock = { path = "../clock" } diff --git a/crates/copilot2/src/copilot2.rs b/crates/copilot2/src/copilot2.rs index 834750b25d..149b01aa82 100644 --- a/crates/copilot2/src/copilot2.rs +++ b/crates/copilot2/src/copilot2.rs @@ -17,6 +17,7 @@ use language2::{ }; use lsp2::{LanguageServer, LanguageServerBinary, LanguageServerId}; use node_runtime::NodeRuntime; +use parking_lot::Mutex; use request::StatusNotification; use settings2::SettingsStore; use smol::{fs, io::BufReader, stream::StreamExt}; @@ -394,8 +395,15 @@ impl Copilot { path: node_path, arguments, }; - let server = - LanguageServer::new(new_server_id, binary, Path::new("/"), None, cx.clone())?; + + let server = LanguageServer::new( + Arc::new(Mutex::new(None)), + new_server_id, + binary, + Path::new("/"), + None, + cx.clone(), + )?; server .on_notification::( diff --git a/crates/language2/src/language2.rs b/crates/language2/src/language2.rs index d21ebd572a..d0dd7c4ff7 100644 --- a/crates/language2/src/language2.rs +++ b/crates/language2/src/language2.rs @@ -667,7 +667,7 @@ struct LanguageRegistryState { pub struct PendingLanguageServer { pub server_id: LanguageServerId, - pub task: Task>>, + pub task: Task>, pub container_dir: Option>, } @@ -906,6 +906,7 @@ impl LanguageRegistry { pub fn create_pending_language_server( self: &Arc, + stderr_capture: Arc>>, language: Arc, adapter: Arc, root_path: Arc, @@ -945,7 +946,7 @@ impl LanguageRegistry { }) .detach(); - Ok(Some(server)) + Ok(server) }); return Some(PendingLanguageServer { @@ -996,24 +997,23 @@ impl LanguageRegistry { }) .clone(); - let binary = match entry.await.log_err() { - Some(binary) => binary, - None => return Ok(None), + let binary = match entry.await { + Ok(binary) => binary, + Err(err) => anyhow::bail!("{err}"), }; if let Some(task) = adapter.will_start_server(&delegate, &mut cx) { - if task.await.log_err().is_none() { - return Ok(None); - } + task.await?; } - Ok(Some(lsp2::LanguageServer::new( + lsp2::LanguageServer::new( + stderr_capture, server_id, binary, &root_path, adapter.code_action_kinds(), cx, - )?)) + ) }) }; diff --git a/crates/lsp2/src/lsp2.rs b/crates/lsp2/src/lsp2.rs index f874d9f118..a6a9a4f639 100644 --- a/crates/lsp2/src/lsp2.rs +++ b/crates/lsp2/src/lsp2.rs @@ -136,6 +136,7 @@ struct Error { impl LanguageServer { pub fn new( + stderr_capture: Arc>>, server_id: LanguageServerId, binary: LanguageServerBinary, root_path: &Path, @@ -165,6 +166,7 @@ impl LanguageServer { stdin, stdout, Some(stderr), + stderr_capture, Some(server), root_path, code_action_kinds, @@ -197,6 +199,7 @@ impl LanguageServer { stdin: Stdin, stdout: Stdout, stderr: Option, + stderr_capture: Arc>>, server: Option, root_path: &Path, code_action_kinds: Option>, @@ -237,7 +240,8 @@ impl LanguageServer { let stderr_input_task = stderr .map(|stderr| { let io_handlers = io_handlers.clone(); - cx.spawn(|_| Self::handle_stderr(stderr, io_handlers).log_err()) + let stderr_captures = stderr_captures.clone(); + cx.spawn(|_| Self::handle_stderr(stderr, io_handlers, stderr_captures).log_err()) }) .unwrap_or_else(|| Task::Ready(Some(None))); let input_task = cx.spawn(|_| async move { @@ -360,12 +364,14 @@ impl LanguageServer { async fn handle_stderr( stderr: Stderr, io_handlers: Arc>>, + stderr_capture: Arc>>, ) -> anyhow::Result<()> where Stderr: AsyncRead + Unpin + Send + 'static, { let mut stderr = BufReader::new(stderr); let mut buffer = Vec::new(); + loop { buffer.clear(); stderr.read_until(b'\n', &mut buffer).await?; @@ -374,6 +380,10 @@ impl LanguageServer { for handler in io_handlers.lock().values_mut() { handler(IoKind::StdErr, message); } + + if let Some(stderr) = stderr_capture.lock().as_mut() { + stderr.push_str(message); + } } // Don't starve the main thread when receiving lots of messages at once. @@ -933,6 +943,7 @@ impl LanguageServer { stdin_writer, stdout_reader, None::, + Arc::new(Mutex::new(None)), None, Path::new("/"), None, @@ -945,6 +956,7 @@ impl LanguageServer { stdout_writer, stdin_reader, None::, + Arc::new(Mutex::new(None)), None, Path::new("/"), None, diff --git a/crates/prettier2/Cargo.toml b/crates/prettier2/Cargo.toml index 0fca004344..8defd40262 100644 --- a/crates/prettier2/Cargo.toml +++ b/crates/prettier2/Cargo.toml @@ -27,6 +27,7 @@ serde_derive.workspace = true serde_json.workspace = true anyhow.workspace = true futures.workspace = true +parking_lot.workspace = true [dev-dependencies] language2 = { path = "../language2", features = ["test-support"] } diff --git a/crates/prettier2/src/prettier2.rs b/crates/prettier2/src/prettier2.rs index 4e8d8b9070..ed22d23157 100644 --- a/crates/prettier2/src/prettier2.rs +++ b/crates/prettier2/src/prettier2.rs @@ -210,6 +210,7 @@ impl Prettier { .spawn(async move { node.binary_path().await }) .await?; let server = LanguageServer::new( + Arc::new(parking_lot::Mutex::new(None)), server_id, LanguageServerBinary { path: node_path, diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index a49a17a3b6..4e52992380 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -52,6 +52,7 @@ use lsp2::{ }; use lsp_command::*; use node_runtime::NodeRuntime; +use parking_lot::Mutex; use postage::watch; use prettier2::{LocateStart, Prettier, PRETTIER_SERVER_FILE, PRETTIER_SERVER_JS}; use project_settings::{LspSettings, ProjectSettings}; @@ -2778,7 +2779,9 @@ impl Project { return; } + let stderr_capture = Arc::new(Mutex::new(Some(String::new()))); let pending_server = match self.languages.create_pending_language_server( + stderr_capture.clone(), language.clone(), adapter.clone(), worktree_path, @@ -2824,10 +2827,14 @@ impl Project { .await; match result { - Ok(server) => server, + Ok(server) => { + stderr_capture.lock().take(); + server + } Err(err) => { log::error!("failed to start language server {:?}: {}", server_name, err); + log::error!("server stderr: {:?}", stderr_capture.lock().take()); if let Some(this) = this.upgrade() { if let Some(container_dir) = container_dir { @@ -2931,19 +2938,16 @@ impl Project { key: (WorktreeId, LanguageServerName), cx: &mut AsyncAppContext, ) -> Result>> { - let setup = Self::setup_pending_language_server( + let language_server = Self::setup_pending_language_server( this.clone(), initialization_options, pending_server, adapter.clone(), server_id, cx, - ); + ) + .await?; - let language_server = match setup.await? { - Some(language_server) => language_server, - None => return Ok(None), - }; let this = match this.upgrade() { Some(this) => this, None => return Err(anyhow!("failed to upgrade project handle")), @@ -2960,7 +2964,7 @@ impl Project { ) })??; - Ok(Some(language_server)) + Ok(language_server) } async fn setup_pending_language_server( @@ -2970,12 +2974,9 @@ impl Project { adapter: Arc, server_id: LanguageServerId, cx: &mut AsyncAppContext, - ) -> Result>> { + ) -> Result> { let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx))?.await; - let language_server = match pending_server.task.await? { - Some(server) => server, - None => return Ok(None), - }; + let language_server = pending_server.task.await?; language_server .on_notification::({ @@ -3050,6 +3051,7 @@ impl Project { } }) .detach(); + language_server .on_request::({ let this = this.clone(); @@ -3138,7 +3140,7 @@ impl Project { ) .ok(); - Ok(Some(language_server)) + Ok(language_server) } fn insert_newly_running_language_server(