Correctly handle network issues during LSP server installation (#9460)
Closes https://github.com/zed-industries/zed/issues/9458 When flying in a plane being totally offline, I've discovered that my Rust projects do not have any LSP support and rust-analyzer disappeared out of `~/Library/Application Support/Zed/languages/rust-analyzer/` directory. Looking at the [bad.log](https://github.com/zed-industries/zed/files/14627508/bad.log), it appears that `get_language_server_command` tries to find a newer LSP server version and fails on80bc6c8cc8/crates/language/src/language.rs (L339)
bailing out of all installation related-methods up to here:80bc6c8cc8/crates/project/src/project.rs (L2916)
where the code thinks that the binary installation process had failed, cleans the existing directory and tries to install the language server again: ```log [2024-03-17T15:14:13+02:00 WARN isahc::handler] request completed with error: failed to resolve host name [2024-03-17T15:14:13+02:00 ERROR project] failed to start language server "rust-analyzer": error fetching latest release [2024-03-17T15:14:13+02:00 ERROR project] server stderr: Some("") [2024-03-17T15:14:13+02:00 INFO project] retrying installation of language server "rust-analyzer" in 1s [2024-03-17T15:14:13+02:00 ERROR util] crates/lsp/src/lsp.rs:720: oneshot canceled [2024-03-17T15:14:14+02:00 INFO project] About to spawn test binary [2024-03-17T15:14:14+02:00 WARN project] test binary failed to launch [2024-03-17T15:14:14+02:00 WARN project] test binary check failed [2024-03-17T15:14:14+02:00 INFO project] beginning to reinstall server [2024-03-17T15:14:14+02:00 INFO language::language_registry] deleting server container [2024-03-17T15:14:14+02:00 INFO language::language_registry] starting language server "rust-analyzer", path: "/Users/someonetoignore/work/other/local_test", id: 2 [2024-03-17T15:14:14+02:00 INFO language] fetching latest version of language server "rust-analyzer" [2024-03-17T15:14:14+02:00 WARN isahc::handler] request completed with error: failed to resolve host name [2024-03-17T15:14:14+02:00 ERROR project] failed to start language server "rust-analyzer": error fetching latest release [2024-03-17T15:14:14+02:00 ERROR project] server stderr: Some("") [2024-03-17T15:14:14+02:00 INFO project] retrying installation of language server "rust-analyzer" in 1s [2024-03-17T15:14:15+02:00 ERROR util] crates/languages/src/rust.rs:335: no cached binary [2024-03-17T15:14:15+02:00 INFO project] About to spawn test binary ............ ``` The PR extracts away all binary fetching-related code into a single method that does not fail the entire `get_language_server_command` and allows it to recover and reuse the existing binary: [good.log](https://github.com/zed-industries/zed/files/14627507/good.log) ```log [2024-03-17T15:12:24+02:00 INFO language::language_registry] starting language server "rust-analyzer", path: "/Users/someonetoignore/work/other/local_test", id: 1 [2024-03-17T15:12:24+02:00 INFO language] fetching latest version of language server "rust-analyzer" [2024-03-17T15:12:24+02:00 WARN isahc::handler] request completed with error: failed to resolve host name [2024-03-17T15:12:24+02:00 INFO language] failed to fetch newest version of language server LanguageServerName("rust-analyzer"). falling back to using "/Users/someonetoignore/Library/Application Support/Zed/languages/rust-analyzer/rust-analyzer-2024-03-11" [2024-03-17T15:12:24+02:00 INFO lsp] starting language server. binary path: "/Users/someonetoignore/Library/Application Support/Zed/languages/rust-analyzer/rust-analyzer-2024-03-11", working directory: "/Users/someonetoignore/work/other/local_test", args: [] ``` Release Notes: - Fixed language servers erased from the disk when project is opened offline
This commit is contained in:
parent
80bc6c8cc8
commit
9df92c3fb6
2 changed files with 35 additions and 26 deletions
|
@ -326,43 +326,25 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
.context("failed to create container directory")?;
|
.context("failed to create container directory")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(task) = self.will_fetch_server(&delegate, cx) {
|
let mut binary = try_fetch_server_binary(self.as_ref(), &delegate, container_dir.to_path_buf(), cx).await;
|
||||||
task.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let name = self.name();
|
|
||||||
log::info!("fetching latest version of language server {:?}", name.0);
|
|
||||||
delegate.update_status(
|
|
||||||
name.clone(),
|
|
||||||
LanguageServerBinaryStatus::CheckingForUpdate,
|
|
||||||
);
|
|
||||||
let latest_version = self.fetch_latest_server_version(delegate.as_ref()).await?;
|
|
||||||
|
|
||||||
log::info!("downloading language server {:?}", name.0);
|
|
||||||
delegate.update_status(self.name(), LanguageServerBinaryStatus::Downloading);
|
|
||||||
let mut binary = self
|
|
||||||
.fetch_server_binary(latest_version, container_dir.to_path_buf(), delegate.as_ref())
|
|
||||||
.await;
|
|
||||||
|
|
||||||
delegate.update_status(name.clone(), LanguageServerBinaryStatus::Downloaded);
|
|
||||||
|
|
||||||
if let Err(error) = binary.as_ref() {
|
if let Err(error) = binary.as_ref() {
|
||||||
if let Some(prev_downloaded_binary) = self
|
if let Some(prev_downloaded_binary) = self
|
||||||
.cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
|
.cached_server_binary(container_dir.to_path_buf(), delegate.as_ref())
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
delegate.update_status(name.clone(), LanguageServerBinaryStatus::Cached);
|
delegate.update_status(self.name(), LanguageServerBinaryStatus::Cached);
|
||||||
log::info!(
|
log::info!(
|
||||||
"failed to fetch newest version of language server {:?}. falling back to using {:?}",
|
"failed to fetch newest version of language server {:?}. falling back to using {:?}",
|
||||||
name.clone(),
|
self.name(),
|
||||||
prev_downloaded_binary.path.display()
|
prev_downloaded_binary.path
|
||||||
);
|
);
|
||||||
binary = Ok(prev_downloaded_binary);
|
binary = Ok(prev_downloaded_binary);
|
||||||
} else {
|
} else {
|
||||||
delegate.update_status(
|
delegate.update_status(
|
||||||
name.clone(),
|
self.name(),
|
||||||
LanguageServerBinaryStatus::Failed {
|
LanguageServerBinaryStatus::Failed {
|
||||||
error: format!("{:?}", error),
|
error: format!("{error:?}"),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -500,6 +482,33 @@ pub trait LspAdapter: 'static + Send + Sync {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn try_fetch_server_binary<L: LspAdapter + 'static + Send + Sync + ?Sized>(
|
||||||
|
adapter: &L,
|
||||||
|
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||||
|
container_dir: PathBuf,
|
||||||
|
cx: &mut AsyncAppContext,
|
||||||
|
) -> Result<LanguageServerBinary> {
|
||||||
|
if let Some(task) = adapter.will_fetch_server(delegate, cx) {
|
||||||
|
task.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = adapter.name();
|
||||||
|
log::info!("fetching latest version of language server {:?}", name.0);
|
||||||
|
delegate.update_status(name.clone(), LanguageServerBinaryStatus::CheckingForUpdate);
|
||||||
|
let latest_version = adapter
|
||||||
|
.fetch_latest_server_version(delegate.as_ref())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
log::info!("downloading language server {:?}", name.0);
|
||||||
|
delegate.update_status(adapter.name(), LanguageServerBinaryStatus::Downloading);
|
||||||
|
let binary = adapter
|
||||||
|
.fetch_server_binary(latest_version, container_dir, delegate.as_ref())
|
||||||
|
.await;
|
||||||
|
|
||||||
|
delegate.update_status(name.clone(), LanguageServerBinaryStatus::Downloaded);
|
||||||
|
binary
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct CodeLabel {
|
pub struct CodeLabel {
|
||||||
/// The text to display.
|
/// The text to display.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Context, Result};
|
||||||
use async_compression::futures::bufread::GzipDecoder;
|
use async_compression::futures::bufread::GzipDecoder;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use futures::{io::BufReader, StreamExt};
|
use futures::{io::BufReader, StreamExt};
|
||||||
|
@ -79,7 +79,7 @@ impl LspAdapter for RustLspAdapter {
|
||||||
.assets
|
.assets
|
||||||
.iter()
|
.iter()
|
||||||
.find(|asset| asset.name == asset_name)
|
.find(|asset| asset.name == asset_name)
|
||||||
.ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?;
|
.with_context(|| format!("no asset found matching `{asset_name:?}`"))?;
|
||||||
Ok(Box::new(GitHubLspBinaryVersion {
|
Ok(Box::new(GitHubLspBinaryVersion {
|
||||||
name: release.tag_name,
|
name: release.tag_name,
|
||||||
url: asset.browser_download_url.clone(),
|
url: asset.browser_download_url.clone(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue