diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 4f03da1afe..e8450344b8 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -160,6 +160,10 @@ impl CachedLspAdapter { .await } + pub fn can_be_reinstalled(&self) -> bool { + self.adapter.can_be_reinstalled() + } + pub async fn installation_test_binary( &self, container_dir: PathBuf, @@ -249,12 +253,14 @@ pub trait LspAdapter: 'static + Send + Sync { delegate: &dyn LspAdapterDelegate, ) -> Option; + fn can_be_reinstalled(&self) -> bool { + true + } + async fn installation_test_binary( &self, - _container_dir: PathBuf, - ) -> Option { - unimplemented!(); - } + container_dir: PathBuf, + ) -> Option; async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} @@ -1667,6 +1673,10 @@ impl LspAdapter for Arc { unreachable!(); } + async fn installation_test_binary(&self, _: PathBuf) -> Option { + unreachable!(); + } + async fn process_diagnostics(&self, _: &mut lsp::PublishDiagnosticsParams) {} async fn disk_based_diagnostic_sources(&self) -> Vec { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 7be57e921d..0820eaf26e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -3057,6 +3057,14 @@ impl Project { installation_test_binary: Option, cx: &mut ModelContext, ) { + if !adapter.can_be_reinstalled() { + log::info!( + "Validation check requested for {:?} but it cannot be reinstalled", + adapter.name.0 + ); + return; + } + cx.spawn(|this, mut cx| async move { log::info!("About to spawn test binary"); @@ -3086,6 +3094,7 @@ impl Project { _ = timeout => { log::info!("test binary time-ed out, this counts as a success"); + _ = process.kill(); } } } else { diff --git a/crates/zed/src/languages/elixir.rs b/crates/zed/src/languages/elixir.rs index afac5abeb0..c32927e15c 100644 --- a/crates/zed/src/languages/elixir.rs +++ b/crates/zed/src/languages/elixir.rs @@ -140,20 +140,14 @@ impl LspAdapter for ElixirLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - last = Some(entry?.path()); - } - last.map(|path| LanguageServerBinary { - path, - arguments: vec![], - }) - .ok_or_else(|| anyhow!("no cached binary")) - })() - .await - .log_err() + get_cached_server_binary(container_dir).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir).await } async fn label_for_completion( @@ -239,3 +233,20 @@ impl LspAdapter for ElixirLspAdapter { }) } } + +async fn get_cached_server_binary(container_dir: PathBuf) -> Option { + (|| async move { + let mut last = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + last = Some(entry?.path()); + } + last.map(|path| LanguageServerBinary { + path, + arguments: vec![], + }) + .ok_or_else(|| anyhow!("no cached binary")) + })() + .await + .log_err() +} diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index 9821dcd1eb..d7982f7bdb 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -149,32 +149,19 @@ impl super::LspAdapter for GoLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last_binary_path = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_file() - && entry - .file_name() - .to_str() - .map_or(false, |name| name.starts_with("gopls_")) - { - last_binary_path = Some(entry.path()); - } - } + get_cached_server_binary(container_dir).await + } - if let Some(path) = last_binary_path { - Ok(LanguageServerBinary { - path, - arguments: server_binary_arguments(), - }) - } else { - Err(anyhow!("no cached binary")) - } - })() - .await - .log_err() + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir) + .await + .map(|mut binary| { + binary.arguments = vec!["--help".into()]; + binary + }) } async fn label_for_completion( @@ -337,6 +324,35 @@ impl super::LspAdapter for GoLspAdapter { } } +async fn get_cached_server_binary(container_dir: PathBuf) -> Option { + (|| async move { + let mut last_binary_path = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_file() + && entry + .file_name() + .to_str() + .map_or(false, |name| name.starts_with("gopls_")) + { + last_binary_path = Some(entry.path()); + } + } + + if let Some(path) = last_binary_path { + Ok(LanguageServerBinary { + path, + arguments: server_binary_arguments(), + }) + } else { + Err(anyhow!("no cached binary")) + } + })() + .await + .log_err() +} + fn adjust_runs( delta: usize, mut runs: Vec<(Range, HighlightId)>, diff --git a/crates/zed/src/languages/html.rs b/crates/zed/src/languages/html.rs index 5e6f956b64..ecc839fca6 100644 --- a/crates/zed/src/languages/html.rs +++ b/crates/zed/src/languages/html.rs @@ -14,6 +14,9 @@ use std::{ }; use util::ResultExt; +const SERVER_PATH: &'static str = + "node_modules/vscode-langservers-extracted/bin/vscode-html-language-server"; + fn server_binary_arguments(server_path: &Path) -> Vec { vec![server_path.into(), "--stdio".into()] } @@ -23,9 +26,6 @@ pub struct HtmlLspAdapter { } impl HtmlLspAdapter { - const SERVER_PATH: &'static str = - "node_modules/vscode-langservers-extracted/bin/vscode-html-language-server"; - pub fn new(node: Arc) -> Self { HtmlLspAdapter { node } } @@ -55,7 +55,7 @@ impl LspAdapter for HtmlLspAdapter { _: &dyn LspAdapterDelegate, ) -> Result { let version = version.downcast::().unwrap(); - let server_path = container_dir.join(Self::SERVER_PATH); + let server_path = container_dir.join(SERVER_PATH); if fs::metadata(&server_path).await.is_err() { self.node @@ -77,31 +77,14 @@ impl LspAdapter for HtmlLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last_version_dir = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_dir() { - last_version_dir = Some(entry.path()); - } - } - let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; - let server_path = last_version_dir.join(Self::SERVER_PATH); - if server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: server_binary_arguments(&server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - last_version_dir - )) - } - })() - .await - .log_err() + get_cached_server_binary(container_dir, &self.node).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await } async fn initialization_options(&self) -> Option { @@ -110,3 +93,34 @@ impl LspAdapter for HtmlLspAdapter { })) } } + +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index f40e35ebd7..b7e4ab4ba7 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -83,32 +83,14 @@ impl LspAdapter for JsonLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last_version_dir = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_dir() { - last_version_dir = Some(entry.path()); - } - } + get_cached_server_binary(container_dir, &self.node).await + } - let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; - let server_path = last_version_dir.join(SERVER_PATH); - if server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: server_binary_arguments(&server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - last_version_dir - )) - } - })() - .await - .log_err() + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await } async fn initialization_options(&self) -> Option { @@ -161,6 +143,38 @@ impl LspAdapter for JsonLspAdapter { } } +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} + fn schema_file_match(path: &Path) -> &Path { path.strip_prefix(path.parent().unwrap().parent().unwrap()) .unwrap() diff --git a/crates/zed/src/languages/lua.rs b/crates/zed/src/languages/lua.rs index 3daabb64d0..7c5c7179d0 100644 --- a/crates/zed/src/languages/lua.rs +++ b/crates/zed/src/languages/lua.rs @@ -92,31 +92,47 @@ impl super::LspAdapter for LuaLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - async_iife!({ - let mut last_binary_path = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_file() - && entry - .file_name() - .to_str() - .map_or(false, |name| name == "lua-language-server") - { - last_binary_path = Some(entry.path()); - } - } + get_cached_server_binary(container_dir).await + } - if let Some(path) = last_binary_path { - Ok(LanguageServerBinary { - path, - arguments: server_binary_arguments(), - }) - } else { - Err(anyhow!("no cached binary")) - } - }) - .await - .log_err() + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir) + .await + .map(|mut binary| { + binary.arguments = vec!["--version".into()]; + binary + }) } } + +async fn get_cached_server_binary(container_dir: PathBuf) -> Option { + async_iife!({ + let mut last_binary_path = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_file() + && entry + .file_name() + .to_str() + .map_or(false, |name| name == "lua-language-server") + { + last_binary_path = Some(entry.path()); + } + } + + if let Some(path) = last_binary_path { + Ok(LanguageServerBinary { + path, + arguments: server_binary_arguments(), + }) + } else { + Err(anyhow!("no cached binary")) + } + }) + .await + .log_err() +} diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index 5be05bea2e..41ad28ba86 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -13,6 +13,8 @@ use std::{ }; use util::ResultExt; +const SERVER_PATH: &'static str = "node_modules/pyright/langserver.index.js"; + fn server_binary_arguments(server_path: &Path) -> Vec { vec![server_path.into(), "--stdio".into()] } @@ -22,8 +24,6 @@ pub struct PythonLspAdapter { } impl PythonLspAdapter { - const SERVER_PATH: &'static str = "node_modules/pyright/langserver.index.js"; - pub fn new(node: Arc) -> Self { PythonLspAdapter { node } } @@ -49,7 +49,7 @@ impl LspAdapter for PythonLspAdapter { _: &dyn LspAdapterDelegate, ) -> Result { let version = version.downcast::().unwrap(); - let server_path = container_dir.join(Self::SERVER_PATH); + let server_path = container_dir.join(SERVER_PATH); if fs::metadata(&server_path).await.is_err() { self.node @@ -68,31 +68,14 @@ impl LspAdapter for PythonLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last_version_dir = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_dir() { - last_version_dir = Some(entry.path()); - } - } - let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; - let server_path = last_version_dir.join(Self::SERVER_PATH); - if server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: server_binary_arguments(&server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - last_version_dir - )) - } - })() - .await - .log_err() + get_cached_server_binary(container_dir, &self.node).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await } async fn process_completion(&self, item: &mut lsp::CompletionItem) { @@ -171,6 +154,37 @@ impl LspAdapter for PythonLspAdapter { } } +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +} + #[cfg(test)] mod tests { use gpui::{ModelContext, TestAppContext}; diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs index f88f4af5fd..358441352a 100644 --- a/crates/zed/src/languages/ruby.rs +++ b/crates/zed/src/languages/ruby.rs @@ -39,6 +39,14 @@ impl LspAdapter for RubyLanguageServer { }) } + fn can_be_reinstalled(&self) -> bool { + false + } + + async fn installation_test_binary(&self, _: PathBuf) -> Option { + None + } + async fn label_for_completion( &self, item: &lsp::CompletionItem, diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 0a2c93f422..97549b0058 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -79,20 +79,19 @@ impl LspAdapter for RustLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - last = Some(entry?.path()); - } + get_cached_server_binary(container_dir).await + } - anyhow::Ok(LanguageServerBinary { - path: last.ok_or_else(|| anyhow!("no cached binary"))?, - arguments: Default::default(), + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir) + .await + .map(|mut binary| { + binary.arguments = vec!["--help".into()]; + binary }) - })() - .await - .log_err() } async fn disk_based_diagnostic_sources(&self) -> Vec { @@ -259,6 +258,22 @@ impl LspAdapter for RustLspAdapter { }) } } +async fn get_cached_server_binary(container_dir: PathBuf) -> Option { + (|| async move { + let mut last = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + last = Some(entry?.path()); + } + + anyhow::Ok(LanguageServerBinary { + path: last.ok_or_else(|| anyhow!("no cached binary"))?, + arguments: Default::default(), + }) + })() + .await + .log_err() +} #[cfg(test)] mod tests { diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index c1df52c161..e6d1466731 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -104,28 +104,14 @@ impl LspAdapter for TypeScriptLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let old_server_path = container_dir.join(Self::OLD_SERVER_PATH); - let new_server_path = container_dir.join(Self::NEW_SERVER_PATH); - if new_server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: typescript_server_binary_arguments(&new_server_path), - }) - } else if old_server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: typescript_server_binary_arguments(&old_server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - container_dir - )) - } - })() - .await - .log_err() + get_cached_ts_server_binary(container_dir, &self.node).await + } + + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_ts_server_binary(container_dir, &self.node).await } fn code_action_kinds(&self) -> Option> { @@ -173,6 +159,34 @@ impl LspAdapter for TypeScriptLspAdapter { } } +async fn get_cached_ts_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let old_server_path = container_dir.join(TypeScriptLspAdapter::OLD_SERVER_PATH); + let new_server_path = container_dir.join(TypeScriptLspAdapter::NEW_SERVER_PATH); + if new_server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: typescript_server_binary_arguments(&new_server_path), + }) + } else if old_server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: typescript_server_binary_arguments(&old_server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + container_dir + )) + } + })() + .await + .log_err() +} + pub struct EsLintLspAdapter { node: Arc, } @@ -268,21 +282,14 @@ impl LspAdapter for EsLintLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - // This is unfortunate but we don't know what the version is to build a path directly - let mut dir = fs::read_dir(&container_dir).await?; - let first = dir.next().await.ok_or(anyhow!("missing first file"))??; - if !first.file_type().await?.is_dir() { - return Err(anyhow!("First entry is not a directory")); - } + get_cached_eslint_server_binary(container_dir, &self.node).await + } - Ok(LanguageServerBinary { - path: first.path().join(Self::SERVER_PATH), - arguments: Default::default(), - }) - })() - .await - .log_err() + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_eslint_server_binary(container_dir, &self.node).await } async fn label_for_completion( @@ -298,6 +305,28 @@ impl LspAdapter for EsLintLspAdapter { } } +async fn get_cached_eslint_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + // This is unfortunate but we don't know what the version is to build a path directly + let mut dir = fs::read_dir(&container_dir).await?; + let first = dir.next().await.ok_or(anyhow!("missing first file"))??; + if !first.file_type().await?.is_dir() { + return Err(anyhow!("First entry is not a directory")); + } + let server_path = first.path().join(EsLintLspAdapter::SERVER_PATH); + + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: eslint_server_binary_arguments(&server_path), + }) + })() + .await + .log_err() +} + #[cfg(test)] mod tests { use gpui::TestAppContext; diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index c03bcf405e..b57c6f5699 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -18,6 +18,8 @@ use std::{ }; use util::ResultExt; +const SERVER_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server"; + fn server_binary_arguments(server_path: &Path) -> Vec { vec![server_path.into(), "--stdio".into()] } @@ -27,8 +29,6 @@ pub struct YamlLspAdapter { } impl YamlLspAdapter { - const SERVER_PATH: &'static str = "node_modules/yaml-language-server/bin/yaml-language-server"; - pub fn new(node: Arc) -> Self { YamlLspAdapter { node } } @@ -58,7 +58,7 @@ impl LspAdapter for YamlLspAdapter { _: &dyn LspAdapterDelegate, ) -> Result { let version = version.downcast::().unwrap(); - let server_path = container_dir.join(Self::SERVER_PATH); + let server_path = container_dir.join(SERVER_PATH); if fs::metadata(&server_path).await.is_err() { self.node @@ -77,33 +77,15 @@ impl LspAdapter for YamlLspAdapter { container_dir: PathBuf, _: &dyn LspAdapterDelegate, ) -> Option { - (|| async move { - let mut last_version_dir = None; - let mut entries = fs::read_dir(&container_dir).await?; - while let Some(entry) = entries.next().await { - let entry = entry?; - if entry.file_type().await?.is_dir() { - last_version_dir = Some(entry.path()); - } - } - let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; - let server_path = last_version_dir.join(Self::SERVER_PATH); - if server_path.exists() { - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - arguments: server_binary_arguments(&server_path), - }) - } else { - Err(anyhow!( - "missing executable in directory {:?}", - last_version_dir - )) - } - })() - .await - .log_err() + get_cached_server_binary(container_dir, &self.node).await } + async fn installation_test_binary( + &self, + container_dir: PathBuf, + ) -> Option { + get_cached_server_binary(container_dir, &self.node).await + } fn workspace_configuration(&self, cx: &mut AppContext) -> Option> { let tab_size = all_language_settings(None, cx) .language(Some("YAML")) @@ -121,3 +103,34 @@ impl LspAdapter for YamlLspAdapter { ) } } + +async fn get_cached_server_binary( + container_dir: PathBuf, + node: &NodeRuntime, +) -> Option { + (|| async move { + let mut last_version_dir = None; + let mut entries = fs::read_dir(&container_dir).await?; + while let Some(entry) = entries.next().await { + let entry = entry?; + if entry.file_type().await?.is_dir() { + last_version_dir = Some(entry.path()); + } + } + let last_version_dir = last_version_dir.ok_or_else(|| anyhow!("no cached binary"))?; + let server_path = last_version_dir.join(SERVER_PATH); + if server_path.exists() { + Ok(LanguageServerBinary { + path: node.binary_path().await?, + arguments: server_binary_arguments(&server_path), + }) + } else { + Err(anyhow!( + "missing executable in directory {:?}", + last_version_dir + )) + } + })() + .await + .log_err() +}