mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 00:12:27 +03:00
Capture language server stderr during startup/init and log if failure
This commit is contained in:
parent
1936ba5e30
commit
170ebd8221
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1713,6 +1713,7 @@ dependencies = [
|
||||
"log",
|
||||
"lsp",
|
||||
"node_runtime",
|
||||
"parking_lot 0.11.2",
|
||||
"rpc",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
@ -5562,6 +5563,7 @@ dependencies = [
|
||||
"log",
|
||||
"lsp",
|
||||
"node_runtime",
|
||||
"parking_lot 0.11.2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
|
@ -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" }
|
||||
|
@ -16,6 +16,7 @@ use language::{
|
||||
};
|
||||
use lsp::{LanguageServer, LanguageServerBinary, LanguageServerId};
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use request::StatusNotification;
|
||||
use settings::SettingsStore;
|
||||
use smol::{fs, io::BufReader, stream::StreamExt};
|
||||
@ -387,8 +388,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::<StatusNotification, _>(
|
||||
|
@ -645,7 +645,7 @@ struct LanguageRegistryState {
|
||||
|
||||
pub struct PendingLanguageServer {
|
||||
pub server_id: LanguageServerId,
|
||||
pub task: Task<Result<Option<lsp::LanguageServer>>>,
|
||||
pub task: Task<Result<lsp::LanguageServer>>,
|
||||
pub container_dir: Option<Arc<Path>>,
|
||||
}
|
||||
|
||||
@ -884,6 +884,7 @@ impl LanguageRegistry {
|
||||
|
||||
pub fn create_pending_language_server(
|
||||
self: &Arc<Self>,
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
language: Arc<Language>,
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
root_path: Arc<Path>,
|
||||
@ -923,7 +924,7 @@ impl LanguageRegistry {
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(Some(server))
|
||||
Ok(server)
|
||||
});
|
||||
|
||||
return Some(PendingLanguageServer {
|
||||
@ -971,24 +972,23 @@ impl LanguageRegistry {
|
||||
.clone();
|
||||
drop(lock);
|
||||
|
||||
let binary = match entry.clone().await.log_err() {
|
||||
Some(binary) => binary,
|
||||
None => return Ok(None),
|
||||
let binary = match entry.clone().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(lsp::LanguageServer::new(
|
||||
lsp::LanguageServer::new(
|
||||
stderr_capture,
|
||||
server_id,
|
||||
binary,
|
||||
&root_path,
|
||||
adapter.code_action_kinds(),
|
||||
cx,
|
||||
)?))
|
||||
)
|
||||
})
|
||||
};
|
||||
|
||||
|
@ -136,6 +136,7 @@ struct Error {
|
||||
|
||||
impl LanguageServer {
|
||||
pub fn new(
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
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>,
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
server: Option<Child>,
|
||||
root_path: &Path,
|
||||
code_action_kinds: Option<Vec<CodeActionKind>>,
|
||||
@ -218,20 +221,23 @@ impl LanguageServer {
|
||||
let io_handlers = Arc::new(Mutex::new(HashMap::default()));
|
||||
|
||||
let stdout_input_task = cx.spawn(|cx| {
|
||||
{
|
||||
Self::handle_input(
|
||||
stdout,
|
||||
on_unhandled_notification.clone(),
|
||||
notification_handlers.clone(),
|
||||
response_handlers.clone(),
|
||||
io_handlers.clone(),
|
||||
cx,
|
||||
)
|
||||
}
|
||||
Self::handle_input(
|
||||
stdout,
|
||||
on_unhandled_notification.clone(),
|
||||
notification_handlers.clone(),
|
||||
response_handlers.clone(),
|
||||
io_handlers.clone(),
|
||||
cx,
|
||||
)
|
||||
.log_err()
|
||||
});
|
||||
let stderr_input_task = stderr
|
||||
.map(|stderr| cx.spawn(|_| Self::handle_stderr(stderr, io_handlers.clone()).log_err()))
|
||||
.map(|stderr| {
|
||||
cx.spawn(|_| {
|
||||
Self::handle_stderr(stderr, io_handlers.clone(), stderr_capture.clone())
|
||||
.log_err()
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| Task::Ready(Some(None)));
|
||||
let input_task = cx.spawn(|_| async move {
|
||||
let (stdout, stderr) = futures::join!(stdout_input_task, stderr_input_task);
|
||||
@ -353,12 +359,14 @@ impl LanguageServer {
|
||||
async fn handle_stderr<Stderr>(
|
||||
stderr: Stderr,
|
||||
io_handlers: Arc<Mutex<HashMap<usize, IoHandler>>>,
|
||||
stderr_capture: Arc<Mutex<Option<String>>>,
|
||||
) -> 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?;
|
||||
@ -367,6 +375,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.
|
||||
@ -938,6 +950,7 @@ impl LanguageServer {
|
||||
stdin_writer,
|
||||
stdout_reader,
|
||||
None::<async_pipe::PipeReader>,
|
||||
Arc::new(Mutex::new(None)),
|
||||
None,
|
||||
Path::new("/"),
|
||||
None,
|
||||
@ -950,6 +963,7 @@ impl LanguageServer {
|
||||
stdout_writer,
|
||||
stdin_reader,
|
||||
None::<async_pipe::PipeReader>,
|
||||
Arc::new(Mutex::new(None)),
|
||||
None,
|
||||
Path::new("/"),
|
||||
None,
|
||||
|
@ -27,6 +27,7 @@ serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
anyhow.workspace = true
|
||||
futures.workspace = true
|
||||
parking_lot.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
language = { path = "../language", features = ["test-support"] }
|
||||
|
@ -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,
|
||||
|
@ -52,6 +52,7 @@ use lsp::{
|
||||
};
|
||||
use lsp_command::*;
|
||||
use node_runtime::NodeRuntime;
|
||||
use parking_lot::Mutex;
|
||||
use postage::watch;
|
||||
use prettier::{LocateStart, Prettier};
|
||||
use project_settings::{LspSettings, ProjectSettings};
|
||||
@ -2726,7 +2727,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,
|
||||
@ -2763,10 +2766,14 @@ impl Project {
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(server) => server,
|
||||
Ok(server) => {
|
||||
stderr_capture.lock().take();
|
||||
Some(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(&cx) {
|
||||
if let Some(container_dir) = container_dir {
|
||||
@ -2862,20 +2869,17 @@ impl Project {
|
||||
server_id: LanguageServerId,
|
||||
key: (WorktreeId, LanguageServerName),
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Option<Arc<LanguageServer>>> {
|
||||
let setup = Self::setup_pending_language_server(
|
||||
) -> Result<Arc<LanguageServer>> {
|
||||
let language_server = Self::setup_pending_language_server(
|
||||
this,
|
||||
override_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(cx) {
|
||||
Some(this) => this,
|
||||
None => return Err(anyhow!("failed to upgrade project handle")),
|
||||
@ -2892,7 +2896,7 @@ impl Project {
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Some(language_server))
|
||||
Ok(language_server)
|
||||
}
|
||||
|
||||
async fn setup_pending_language_server(
|
||||
@ -2902,12 +2906,9 @@ impl Project {
|
||||
adapter: Arc<CachedLspAdapter>,
|
||||
server_id: LanguageServerId,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Option<Arc<LanguageServer>>> {
|
||||
) -> Result<Arc<LanguageServer>> {
|
||||
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::<lsp::notification::PublishDiagnostics, _>({
|
||||
@ -2978,6 +2979,7 @@ impl Project {
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
||||
language_server
|
||||
.on_request::<lsp::request::RegisterCapability, _, _>({
|
||||
move |params, mut cx| async move {
|
||||
@ -3043,6 +3045,7 @@ impl Project {
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let mut initialization_options = adapter.adapter.initialization_options().await;
|
||||
match (&mut initialization_options, override_options) {
|
||||
(Some(initialization_options), Some(override_options)) => {
|
||||
@ -3062,7 +3065,7 @@ impl Project {
|
||||
)
|
||||
.ok();
|
||||
|
||||
Ok(Some(language_server))
|
||||
Ok(language_server)
|
||||
}
|
||||
|
||||
fn insert_newly_running_language_server(
|
||||
|
Loading…
Reference in New Issue
Block a user