diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 4ee8259dd695d8c384e12f199470440646432f60..642d16b6e254763f2758b3998708915acb71da5d 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -1050,47 +1050,41 @@ async fn heuristic_syntactic_expand( let node_range = node_start..node_end; let row_count = node_end.row - node_start.row + 1; let mut ancestor_range = None; - cx.background_executor() - .await_on_background(async { - // Stop if we've exceeded the row count or reached an outline node. Then, find the interval - // of node children which contains the query range. For example, this allows just returning - // the header of a declaration rather than the entire declaration. - if row_count > max_row_count || outline_range == Some(node_range.clone()) { - let mut cursor = node.walk(); - let mut included_child_start = None; - let mut included_child_end = None; - let mut previous_end = node_start; - if cursor.goto_first_child() { - loop { - let child_node = cursor.node(); - let child_range = - previous_end..Point::from_ts_point(child_node.end_position()); - if included_child_start.is_none() - && child_range.contains(&input_range.start) - { - included_child_start = Some(child_range.start); - } - if child_range.contains(&input_range.end) { - included_child_end = Some(child_range.end); - } - previous_end = child_range.end; - if !cursor.goto_next_sibling() { - break; - } - } + // Stop if we've exceeded the row count or reached an outline node. Then, find the interval + // of node children which contains the query range. For example, this allows just returning + // the header of a declaration rather than the entire declaration. + if row_count > max_row_count || outline_range == Some(node_range.clone()) { + let mut cursor = node.walk(); + let mut included_child_start = None; + let mut included_child_end = None; + let mut previous_end = node_start; + if cursor.goto_first_child() { + loop { + let child_node = cursor.node(); + let child_range = previous_end..Point::from_ts_point(child_node.end_position()); + if included_child_start.is_none() && child_range.contains(&input_range.start) { + included_child_start = Some(child_range.start); } - let end = included_child_end.unwrap_or(node_range.end); - if let Some(start) = included_child_start { - let row_count = end.row - start.row; - if row_count < max_row_count { - ancestor_range = Some(Some(RangeInclusive::new(start.row, end.row))); - return; - } + if child_range.contains(&input_range.end) { + included_child_end = Some(child_range.end); + } + previous_end = child_range.end; + if !cursor.goto_next_sibling() { + break; } - ancestor_range = Some(None); } - }) - .await; + } + let end = included_child_end.unwrap_or(node_range.end); + if let Some(start) = included_child_start { + let row_count = end.row - start.row; + if row_count < max_row_count { + ancestor_range = Some(Some(RangeInclusive::new(start.row, end.row))); + } + } + if ancestor_range.is_none() { + ancestor_range = Some(None); + } + } if let Some(node) = ancestor_range { return node; } diff --git a/crates/gpui/src/executor.rs b/crates/gpui/src/executor.rs index 07f1667b6201e4b14b93bb9d973f299dbf4ebd39..c1afce810738aa83e00036c22ef79247c4d3fe24 100644 --- a/crates/gpui/src/executor.rs +++ b/crates/gpui/src/executor.rs @@ -115,60 +115,6 @@ impl BackgroundExecutor { } } - /// Enqueues the given future to be run to completion on a background thread and blocking the current task on it. - /// - /// This allows to spawn background work that borrows from its scope. Note that the supplied future will run to - /// completion before the current task is resumed, even if the current task is slated for cancellation. - pub async fn await_on_background(&self, future: impl Future + Send) -> R - where - R: Send, - { - use crate::RunnableMeta; - use parking_lot::{Condvar, Mutex}; - - struct NotifyOnDrop<'a>(&'a (Condvar, Mutex)); - - impl Drop for NotifyOnDrop<'_> { - fn drop(&mut self) { - *self.0.1.lock() = true; - self.0.0.notify_all(); - } - } - - struct WaitOnDrop<'a>(&'a (Condvar, Mutex)); - - impl Drop for WaitOnDrop<'_> { - fn drop(&mut self) { - let mut done = self.0.1.lock(); - if !*done { - self.0.0.wait(&mut done); - } - } - } - - let dispatcher = self.dispatcher.clone(); - let location = core::panic::Location::caller(); - - let pair = &(Condvar::new(), Mutex::new(false)); - let _wait_guard = WaitOnDrop(pair); - - let (runnable, task) = unsafe { - async_task::Builder::new() - .metadata(RunnableMeta { location }) - .spawn_unchecked( - move |_| async { - let _notify_guard = NotifyOnDrop(pair); - future.await - }, - move |runnable| { - dispatcher.dispatch(runnable, Priority::default()); - }, - ) - }; - runnable.schedule(); - task.await - } - /// Scoped lets you start a number of tasks and waits /// for all of them to complete before returning. pub async fn scoped<'scope, F>(&self, scheduler: F) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 8bfc4efb1ffa00abe6dae4dec0427a4368e1bed2..a6e05fb586c37a2feaf29aacbd80df3a1af3d822 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -623,8 +623,8 @@ pub trait LspInstaller { &self, _version: &Self::BinaryVersion, _container_dir: &PathBuf, - _delegate: &dyn LspAdapterDelegate, - ) -> impl Send + Future> { + _delegate: &Arc, + ) -> impl Send + Future> + use { async { None } } @@ -632,8 +632,8 @@ pub trait LspInstaller { &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> impl Send + Future>; + _delegate: &Arc, + ) -> impl Send + Future> + use; fn cached_server_binary( &self, @@ -686,11 +686,7 @@ where if let Some(binary) = cx .background_executor() - .await_on_background(self.check_if_version_installed( - &latest_version, - &container_dir, - delegate.as_ref(), - )) + .spawn(self.check_if_version_installed(&latest_version, &container_dir, &delegate)) .await { log::debug!("language server {:?} is already installed", name.0); @@ -701,11 +697,7 @@ where delegate.update_status(name.clone(), BinaryStatus::Downloading); let binary = cx .background_executor() - .await_on_background(self.fetch_server_binary( - latest_version, - container_dir, - delegate.as_ref(), - )) + .spawn(self.fetch_server_binary(latest_version, container_dir, delegate)) .await; delegate.update_status(name.clone(), BinaryStatus::None); @@ -1421,13 +1413,15 @@ impl LspInstaller for FakeLspAdapter { Some(self.language_server_binary.clone()) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, _: (), _: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - unreachable!(); + _: &Arc, + ) -> impl Send + Future> + use<> { + async { + unreachable!(); + } } async fn cached_server_binary( diff --git a/crates/languages/src/bash.rs b/crates/languages/src/bash.rs index a002968fa4041b15142774d742811c09d3b7b509..438090e2aa9db92dc198c8c1d5e8653d5028557f 100644 --- a/crates/languages/src/bash.rs +++ b/crates/languages/src/bash.rs @@ -6,7 +6,7 @@ use lsp::LanguageServerBinary; use node_runtime::{NodeRuntime, VersionStrategy}; use project::ContextProviderWithTasks; use semver::Version; -use std::{path::PathBuf, vec}; +use std::{future::Future, path::PathBuf, sync::Arc, vec}; use task::{TaskTemplate, TaskTemplates, VariableName}; use util::{ResultExt, maybe}; @@ -90,35 +90,41 @@ impl LspInstaller for BashLspAdapter { }) } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir - .join("node_modules") - .join(Self::NODE_MODULE_RELATIVE_SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + let delegate = delegate.clone(); + + async move { + let server_path = container_dir + .join("node_modules") + .join(Self::NODE_MODULE_RELATIVE_SERVER_PATH); - if should_install_language_server { - None - } else { - let env = delegate.shell_env().await; - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: Some(env), - arguments: vec![server_path.into(), "start".into()], - }) + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + let env = delegate.shell_env().await; + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: Some(env), + arguments: vec![server_path.into(), "start".into()], + }) + } } } @@ -133,29 +139,34 @@ impl LspInstaller for BashLspAdapter { .await } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: std::path::PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir - .join("node_modules") - .join(Self::NODE_MODULE_RELATIVE_SERVER_PATH); + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let delegate = delegate.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir + .join("node_modules") + .join(Self::NODE_MODULE_RELATIVE_SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, - &[(Self::PACKAGE_NAME, &latest_version.to_string())], + &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - let env = delegate.shell_env().await; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: Some(env), - arguments: vec![server_path.into(), "start".into()], - }) + let env = delegate.shell_env().await; + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: Some(env), + arguments: vec![server_path.into(), "start".into()], + }) + } } } diff --git a/crates/languages/src/c.rs b/crates/languages/src/c.rs index 6585863f993f300f913c96c1a923a3ab699d93fa..d2e92904c6df9c371efc443bc8e9970689a80217 100644 --- a/crates/languages/src/c.rs +++ b/crates/languages/src/c.rs @@ -9,7 +9,7 @@ use lsp::{InitializeParams, LanguageServerBinary, LanguageServerName}; use project::lsp_store::clangd_ext; use serde_json::json; use smol::fs; -use std::{env::consts, path::PathBuf, sync::Arc}; +use std::{env::consts, future::Future, path::PathBuf, sync::Arc}; use util::{ResultExt, fs::remove_matching, maybe, merge_json_value_into}; pub struct CLspAdapter; @@ -66,82 +66,88 @@ impl LspInstaller for CLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, version: GitHubLspBinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - ensure_arch_compatibility()?; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); - let GitHubLspBinaryVersion { - name, - url, - digest: expected_digest, - } = version; - let version_dir = container_dir.join(format!("clangd_{name}")); - let binary_path = version_dir - .join("bin") - .join(format!("clangd{}", consts::EXE_SUFFIX)); + async move { + ensure_arch_compatibility()?; - let binary = LanguageServerBinary { - path: binary_path.clone(), - env: None, - arguments: Default::default(), - }; - - let metadata_path = version_dir.join("metadata"); - let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) - .await - .ok(); - if let Some(metadata) = metadata { - let validity_check = async || { - delegate - .try_exec(LanguageServerBinary { - path: binary_path.clone(), - arguments: vec!["--version".into()], - env: None, - }) - .await - .inspect_err(|err| { - log::warn!("Unable to run {binary_path:?} asset, redownloading: {err:#}",) - }) + let GitHubLspBinaryVersion { + name, + url, + digest: expected_digest, + } = version; + let version_dir = container_dir.join(format!("clangd_{name}")); + let binary_path = version_dir + .join("bin") + .join(format!("clangd{}", consts::EXE_SUFFIX)); + + let binary = LanguageServerBinary { + path: binary_path.clone(), + env: None, + arguments: Default::default(), }; - if let (Some(actual_digest), Some(expected_digest)) = - (&metadata.digest, &expected_digest) - { - if actual_digest == expected_digest { - if validity_check().await.is_ok() { - return Ok(binary); + + let metadata_path = version_dir.join("metadata"); + let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) + .await + .ok(); + if let Some(metadata) = metadata { + let validity_check = async || { + delegate + .try_exec(LanguageServerBinary { + path: binary_path.clone(), + arguments: vec!["--version".into()], + env: None, + }) + .await + .inspect_err(|err| { + log::warn!( + "Unable to run {binary_path:?} asset, redownloading: {err:#}", + ) + }) + }; + if let (Some(actual_digest), Some(expected_digest)) = + (&metadata.digest, &expected_digest) + { + if actual_digest == expected_digest { + if validity_check().await.is_ok() { + return Ok(binary); + } + } else { + log::info!( + "SHA-256 mismatch for {binary_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" + ); } - } else { - log::info!( - "SHA-256 mismatch for {binary_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" - ); + } else if validity_check().await.is_ok() { + return Ok(binary); } - } else if validity_check().await.is_ok() { - return Ok(binary); } - } - download_server_binary( - &*delegate.http_client(), - &url, - expected_digest.as_deref(), - &container_dir, - AssetKind::Zip, - ) - .await?; - remove_matching(&container_dir, |entry| entry != version_dir).await; - GithubBinaryMetadata::write_to_file( - &GithubBinaryMetadata { - metadata_version: 1, - digest: expected_digest, - }, - &metadata_path, - ) - .await?; + download_server_binary( + &*delegate.http_client(), + &url, + expected_digest.as_deref(), + &container_dir, + AssetKind::Zip, + ) + .await?; + remove_matching(&container_dir, |entry| entry != version_dir).await; + GithubBinaryMetadata::write_to_file( + &GithubBinaryMetadata { + metadata_version: 1, + digest: expected_digest, + }, + &metadata_path, + ) + .await?; - Ok(binary) + Ok(binary) + } } async fn cached_server_binary( diff --git a/crates/languages/src/css.rs b/crates/languages/src/css.rs index 6a8fb730a0faa6430d252cdd189d0620fcd07e4a..dfa0bc9fd3d4d9e0a138288d27289c87ca789281 100644 --- a/crates/languages/src/css.rs +++ b/crates/languages/src/css.rs @@ -9,6 +9,7 @@ use semver::Version; use serde_json::json; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -64,55 +65,63 @@ impl LspInstaller for CssLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(SERVER_PATH); - let latest_version = latest_version.to_string(); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(SERVER_PATH); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; + async move { + let server_path = container_dir.join(SERVER_PATH); - if should_install_language_server { - None - } else { - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } } diff --git a/crates/languages/src/eslint.rs b/crates/languages/src/eslint.rs index e9b94380191731e044f79863db5acba989310f3c..063cf85affd96fcab49f59595ea172b1d628b700 100644 --- a/crates/languages/src/eslint.rs +++ b/crates/languages/src/eslint.rs @@ -17,6 +17,7 @@ use settings::SettingsLocation; use smol::{fs, stream::StreamExt}; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -99,60 +100,63 @@ impl LspInstaller for EsLintLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, version: GitHubLspBinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let destination_path = Self::build_destination_path(&container_dir); - let server_path = destination_path.join(Self::SERVER_PATH); - - if fs::metadata(&server_path).await.is_err() { - remove_matching(&container_dir, |_| true).await; - - download_server_binary( - &*delegate.http_client(), - &version.url, - None, - &destination_path, - Self::GITHUB_ASSET_KIND, - ) - .await?; - - let mut dir = fs::read_dir(&destination_path).await?; - let first = dir.next().await.context("missing first file")??; - let repo_root = destination_path.join("vscode-eslint"); - fs::rename(first.path(), &repo_root).await?; - - #[cfg(target_os = "windows")] - { - handle_symlink( - repo_root.join("$shared"), - repo_root.join("client").join("src").join("shared"), - ) - .await?; - handle_symlink( - repo_root.join("$shared"), - repo_root.join("server").join("src").join("shared"), + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + let node = self.node.clone(); + + async move { + let destination_path = Self::build_destination_path(&container_dir); + let server_path = destination_path.join(Self::SERVER_PATH); + + if fs::metadata(&server_path).await.is_err() { + remove_matching(&container_dir, |_| true).await; + + download_server_binary( + &*delegate.http_client(), + &version.url, + None, + &destination_path, + Self::GITHUB_ASSET_KIND, ) .await?; - } - self.node - .run_npm_subcommand(Some(&repo_root), "install", &[]) - .await?; + let mut dir = fs::read_dir(&destination_path).await?; + let first = dir.next().await.context("missing first file")??; + let repo_root = destination_path.join("vscode-eslint"); + fs::rename(first.path(), &repo_root).await?; - self.node - .run_npm_subcommand(Some(&repo_root), "run-script", &["compile"]) - .await?; - } + #[cfg(target_os = "windows")] + { + handle_symlink( + repo_root.join("$shared"), + repo_root.join("client").join("src").join("shared"), + ) + .await?; + handle_symlink( + repo_root.join("$shared"), + repo_root.join("server").join("src").join("shared"), + ) + .await?; + } - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: eslint_server_binary_arguments(&server_path), - }) + node.run_npm_subcommand(Some(&repo_root), "install", &[]) + .await?; + + node.run_npm_subcommand(Some(&repo_root), "run-script", &["compile"]) + .await?; + } + + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: eslint_server_binary_arguments(&server_path), + }) + } } async fn cached_server_binary( diff --git a/crates/languages/src/go.rs b/crates/languages/src/go.rs index f4d0ce5f4d4b55e6905d5d5ecdb8fec92f2b9b57..3bedd62b8e67ec51baf3be03b1526fd1b32ea59a 100644 --- a/crates/languages/src/go.rs +++ b/crates/languages/src/go.rs @@ -19,6 +19,7 @@ use smol::fs; use std::{ borrow::Cow, ffi::{OsStr, OsString}, + future::Future, ops::Range, path::{Path, PathBuf}, process::Output, @@ -117,75 +118,79 @@ impl LspInstaller for GoLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, version: Option, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let go = delegate.which("go".as_ref()).await.unwrap_or("go".into()); - let go_version_output = util::command::new_command(&go) - .args(["version"]) - .output() - .await - .context("failed to get go version via `go version` command`")?; - let go_version = parse_version_output(&go_version_output)?; - - if let Some(version) = version { - let binary_path = container_dir.join(format!("gopls_{version}_go_{go_version}")); - if let Ok(metadata) = fs::metadata(&binary_path).await - && metadata.is_file() - { - remove_matching(&container_dir, |entry| { - entry != binary_path && entry.file_name() != Some(OsStr::new("gobin")) - }) - .await; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + + async move { + let go = delegate.which("go".as_ref()).await.unwrap_or("go".into()); + let go_version_output = util::command::new_command(&go) + .args(["version"]) + .output() + .await + .context("failed to get go version via `go version` command`")?; + let go_version = parse_version_output(&go_version_output)?; + + if let Some(version) = version { + let binary_path = container_dir.join(format!("gopls_{version}_go_{go_version}")); + if let Ok(metadata) = fs::metadata(&binary_path).await + && metadata.is_file() + { + remove_matching(&container_dir, |entry| { + entry != binary_path && entry.file_name() != Some(OsStr::new("gobin")) + }) + .await; - return Ok(LanguageServerBinary { - path: binary_path.to_path_buf(), - arguments: server_binary_arguments(), - env: None, - }); + return Ok(LanguageServerBinary { + path: binary_path.to_path_buf(), + arguments: server_binary_arguments(), + env: None, + }); + } + } else if let Some(path) = get_cached_server_binary(&container_dir).await { + return Ok(path); } - } else if let Some(path) = get_cached_server_binary(&container_dir).await { - return Ok(path); - } - let gobin_dir = container_dir.join("gobin"); - fs::create_dir_all(&gobin_dir).await?; - let install_output = util::command::new_command(go) - .env("GO111MODULE", "on") - .env("GOBIN", &gobin_dir) - .args(["install", "golang.org/x/tools/gopls@latest"]) - .output() - .await?; - - if !install_output.status.success() { - log::error!( - "failed to install gopls via `go install`. stdout: {:?}, stderr: {:?}", - String::from_utf8_lossy(&install_output.stdout), - String::from_utf8_lossy(&install_output.stderr) - ); - anyhow::bail!( - "failed to install gopls with `go install`. Is `go` installed and in the PATH? Check logs for more information." - ); - } + let gobin_dir = container_dir.join("gobin"); + fs::create_dir_all(&gobin_dir).await?; + let install_output = util::command::new_command(go) + .env("GO111MODULE", "on") + .env("GOBIN", &gobin_dir) + .args(["install", "golang.org/x/tools/gopls@latest"]) + .output() + .await?; + + if !install_output.status.success() { + log::error!( + "failed to install gopls via `go install`. stdout: {:?}, stderr: {:?}", + String::from_utf8_lossy(&install_output.stdout), + String::from_utf8_lossy(&install_output.stderr) + ); + anyhow::bail!( + "failed to install gopls with `go install`. Is `go` installed and in the PATH? Check logs for more information." + ); + } - let installed_binary_path = gobin_dir.join(BINARY); - let version_output = util::command::new_command(&installed_binary_path) - .arg("version") - .output() - .await - .context("failed to run installed gopls binary")?; - let gopls_version = parse_version_output(&version_output)?; - let binary_path = container_dir.join(format!("gopls_{gopls_version}_go_{go_version}")); - fs::rename(&installed_binary_path, &binary_path).await?; - - Ok(LanguageServerBinary { - path: binary_path.to_path_buf(), - arguments: server_binary_arguments(), - env: None, - }) + let installed_binary_path = gobin_dir.join(BINARY); + let version_output = util::command::new_command(&installed_binary_path) + .arg("version") + .output() + .await + .context("failed to run installed gopls binary")?; + let gopls_version = parse_version_output(&version_output)?; + let binary_path = container_dir.join(format!("gopls_{gopls_version}_go_{go_version}")); + fs::rename(&installed_binary_path, &binary_path).await?; + + Ok(LanguageServerBinary { + path: binary_path.to_path_buf(), + arguments: server_binary_arguments(), + env: None, + }) + } } async fn cached_server_binary( diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index b1bcd0043e520730631b30818e0e504a50b0e84e..9cd6c1565ad46d1b26ef1a9176acc68cde82018e 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -24,6 +24,7 @@ use std::{ borrow::Cow, env::consts, ffi::OsString, + future::Future, path::{Path, PathBuf}, str::FromStr, sync::Arc, @@ -176,56 +177,64 @@ impl LspInstaller for JsonLspAdapter { }) } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(SERVER_PATH); - let latest_version = latest_version.to_string(); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } async fn cached_server_binary( @@ -478,51 +487,55 @@ impl LspInstaller for NodeVersionAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: GitHubLspBinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let version = &latest_version; - let destination_path = container_dir.join(format!( - "{}-{}{}", - Self::SERVER_NAME, - version.name, - std::env::consts::EXE_SUFFIX - )); - let destination_container_path = - container_dir.join(format!("{}-{}-tmp", Self::SERVER_NAME, version.name)); - if fs::metadata(&destination_path).await.is_err() { - let mut response = delegate - .http_client() - .get(&version.url, Default::default(), true) - .await - .context("downloading release")?; - if version.url.ends_with(".zip") { - extract_zip(&destination_container_path, response.body_mut()).await?; - } else if version.url.ends_with(".tar.gz") { - let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); - let archive = Archive::new(decompressed_bytes); - archive.unpack(&destination_container_path).await?; - } + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + + async move { + let version = &latest_version; + let destination_path = container_dir.join(format!( + "{}-{}{}", + Self::SERVER_NAME, + version.name, + std::env::consts::EXE_SUFFIX + )); + let destination_container_path = + container_dir.join(format!("{}-{}-tmp", Self::SERVER_NAME, version.name)); + if fs::metadata(&destination_path).await.is_err() { + let mut response = delegate + .http_client() + .get(&version.url, Default::default(), true) + .await + .context("downloading release")?; + if version.url.ends_with(".zip") { + extract_zip(&destination_container_path, response.body_mut()).await?; + } else if version.url.ends_with(".tar.gz") { + let decompressed_bytes = GzipDecoder::new(BufReader::new(response.body_mut())); + let archive = Archive::new(decompressed_bytes); + archive.unpack(&destination_container_path).await?; + } - fs::copy( - destination_container_path.join(format!( - "{}{}", - Self::SERVER_NAME, - std::env::consts::EXE_SUFFIX - )), - &destination_path, - ) - .await?; - remove_matching(&container_dir, |entry| entry != destination_path).await; + fs::copy( + destination_container_path.join(format!( + "{}{}", + Self::SERVER_NAME, + std::env::consts::EXE_SUFFIX + )), + &destination_path, + ) + .await?; + remove_matching(&container_dir, |entry| entry != destination_path).await; + } + Ok(LanguageServerBinary { + path: destination_path, + env: None, + arguments: Default::default(), + }) } - Ok(LanguageServerBinary { - path: destination_path, - env: None, - arguments: Default::default(), - }) } async fn cached_server_binary( diff --git a/crates/languages/src/python.rs b/crates/languages/src/python.rs index 26e96789a0d4b6a96461574fc6ef2abac51d7b27..483430bd75d5eeae4d36e3b12948230f016e0044 100644 --- a/crates/languages/src/python.rs +++ b/crates/languages/src/python.rs @@ -1,5 +1,5 @@ +use anyhow::Result; use anyhow::{Context as _, ensure}; -use anyhow::{Result, anyhow}; use async_trait::async_trait; use collections::HashMap; use futures::future::BoxFuture; @@ -45,6 +45,7 @@ use std::str::FromStr; use std::{ borrow::Cow, fmt::Write, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -447,92 +448,98 @@ impl LspInstaller for TyLspAdapter { None } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let GitHubLspBinaryVersion { - name, - url, - digest: expected_digest, - } = latest_version; - let destination_path = container_dir.join(format!("ty-{name}")); - - async_fs::create_dir_all(&destination_path).await?; - - let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path - .join(Self::build_asset_name()?.0) - .join("ty"), - AssetKind::Zip => destination_path.clone().join("ty.exe"), - }; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); - let binary = LanguageServerBinary { - path: server_path.clone(), - env: None, - arguments: vec!["server".into()], - }; + async move { + let GitHubLspBinaryVersion { + name, + url, + digest: expected_digest, + } = latest_version; + let destination_path = container_dir.join(format!("ty-{name}")); - let metadata_path = destination_path.with_extension("metadata"); - let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) - .await - .ok(); - if let Some(metadata) = metadata { - let validity_check = async || { - delegate - .try_exec(LanguageServerBinary { - path: server_path.clone(), - arguments: vec!["--version".into()], - env: None, - }) - .await - .inspect_err(|err| { - log::warn!("Unable to run {server_path:?} asset, redownloading: {err:#}",) - }) + async_fs::create_dir_all(&destination_path).await?; + + let server_path = match Self::GITHUB_ASSET_KIND { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path + .join(Self::build_asset_name()?.0) + .join("ty"), + AssetKind::Zip => destination_path.clone().join("ty.exe"), }; - if let (Some(actual_digest), Some(expected_digest)) = - (&metadata.digest, &expected_digest) - { - if actual_digest == expected_digest { - if validity_check().await.is_ok() { - return Ok(binary); + + let binary = LanguageServerBinary { + path: server_path.clone(), + env: None, + arguments: vec!["server".into()], + }; + + let metadata_path = destination_path.with_extension("metadata"); + let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) + .await + .ok(); + if let Some(metadata) = metadata { + let validity_check = async || { + delegate + .try_exec(LanguageServerBinary { + path: server_path.clone(), + arguments: vec!["--version".into()], + env: None, + }) + .await + .inspect_err(|err| { + log::warn!( + "Unable to run {server_path:?} asset, redownloading: {err:#}", + ) + }) + }; + if let (Some(actual_digest), Some(expected_digest)) = + (&metadata.digest, &expected_digest) + { + if actual_digest == expected_digest { + if validity_check().await.is_ok() { + return Ok(binary); + } + } else { + log::info!( + "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" + ); } - } else { - log::info!( - "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" - ); + } else if validity_check().await.is_ok() { + return Ok(binary); } - } else if validity_check().await.is_ok() { - return Ok(binary); } - } - download_server_binary( - &*delegate.http_client(), - &url, - expected_digest.as_deref(), - &destination_path, - Self::GITHUB_ASSET_KIND, - ) - .await?; - make_file_executable(&server_path).await?; - remove_matching(&container_dir, |path| path != destination_path).await; - GithubBinaryMetadata::write_to_file( - &GithubBinaryMetadata { - metadata_version: 1, - digest: expected_digest, - }, - &metadata_path, - ) - .await?; + download_server_binary( + &*delegate.http_client(), + &url, + expected_digest.as_deref(), + &destination_path, + Self::GITHUB_ASSET_KIND, + ) + .await?; + make_file_executable(&server_path).await?; + remove_matching(&container_dir, |path| path != destination_path).await; + GithubBinaryMetadata::write_to_file( + &GithubBinaryMetadata { + metadata_version: 1, + digest: expected_digest, + }, + &metadata_path, + ) + .await?; - Ok(LanguageServerBinary { - path: server_path, - env: None, - arguments: vec!["server".into()], - }) + Ok(LanguageServerBinary { + path: server_path, + env: None, + arguments: vec!["server".into()], + }) + } } async fn cached_server_binary( @@ -777,57 +784,67 @@ impl LspInstaller for PyrightLspAdapter { } } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(Self::SERVER_PATH); - let latest_version = latest_version.to_string(); + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(Self::SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::SERVER_NAME.as_ref(), latest_version.as_str())], ) .await?; - let env = delegate.shell_env().await; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: Some(env), - arguments: vec![server_path.into(), "--stdio".into()], - }) + let env = delegate.shell_env().await; + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: Some(env), + arguments: vec![server_path.into(), "--stdio".into()], + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(Self::SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::SERVER_NAME.as_ref(), - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - let env = delegate.shell_env().await; - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: Some(env), - arguments: vec![server_path.into(), "--stdio".into()], - }) + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(Self::SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::SERVER_NAME.as_ref(), + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + let env = delegate.shell_env().await; + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: Some(env), + arguments: vec![server_path.into(), "--stdio".into()], + }) + } } } @@ -1949,46 +1966,50 @@ impl LspInstaller for PyLspAdapter { Ok(()) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, _: (), _: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let venv = self.base_venv(delegate).await.map_err(|e| anyhow!(e))?; - let pip_path = venv.join(BINARY_DIR).join("pip3"); - ensure!( - util::command::new_command(pip_path.as_path()) - .arg("install") - .arg("python-lsp-server[all]") - .arg("--upgrade") - .output() - .await? - .status - .success(), - "python-lsp-server[all] installation failed" - ); - ensure!( - util::command::new_command(pip_path) - .arg("install") - .arg("pylsp-mypy") - .arg("--upgrade") - .output() - .await? - .status - .success(), - "pylsp-mypy installation failed" - ); - let pylsp = venv.join(BINARY_DIR).join("pylsp"); - ensure!( - delegate.which(pylsp.as_os_str()).await.is_some(), - "pylsp installation was incomplete" - ); - Ok(LanguageServerBinary { - path: pylsp, - env: None, - arguments: vec![], - }) + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + + async move { + let venv = Self::ensure_venv(delegate.as_ref()).await?; + let pip_path = venv.join(BINARY_DIR).join("pip3"); + ensure!( + util::command::new_command(pip_path.as_path()) + .arg("install") + .arg("python-lsp-server[all]") + .arg("--upgrade") + .output() + .await? + .status + .success(), + "python-lsp-server[all] installation failed" + ); + ensure!( + util::command::new_command(pip_path) + .arg("install") + .arg("pylsp-mypy") + .arg("--upgrade") + .output() + .await? + .status + .success(), + "pylsp-mypy installation failed" + ); + let pylsp = venv.join(BINARY_DIR).join("pylsp"); + ensure!( + delegate.which(pylsp.as_os_str()).await.is_some(), + "pylsp installation was incomplete" + ); + Ok(LanguageServerBinary { + path: pylsp, + env: None, + arguments: vec![], + }) + } } async fn cached_server_binary( @@ -2229,57 +2250,67 @@ impl LspInstaller for BasedPyrightLspAdapter { } } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(Self::SERVER_PATH); - let latest_version = latest_version.to_string(); + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(Self::SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::SERVER_NAME.as_ref(), latest_version.as_str())], ) .await?; - let env = delegate.shell_env().await; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: Some(env), - arguments: vec![server_path.into(), "--stdio".into()], - }) + let env = delegate.shell_env().await; + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: Some(env), + arguments: vec![server_path.into(), "--stdio".into()], + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(Self::SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::SERVER_NAME.as_ref(), - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - let env = delegate.shell_env().await; - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: Some(env), - arguments: vec![server_path.into(), "--stdio".into()], - }) + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(Self::SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::SERVER_NAME.as_ref(), + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + let env = delegate.shell_env().await; + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: Some(env), + arguments: vec![server_path.into(), "--stdio".into()], + }) + } } } @@ -2566,89 +2597,95 @@ impl LspInstaller for RuffLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: GitHubLspBinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let GitHubLspBinaryVersion { - name, - url, - digest: expected_digest, - } = latest_version; - let destination_path = container_dir.join(format!("ruff-{name}")); - let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path - .join(Self::build_asset_name()?.0) - .join("ruff"), - AssetKind::Zip => destination_path.clone().join("ruff.exe"), - }; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); - let binary = LanguageServerBinary { - path: server_path.clone(), - env: None, - arguments: vec!["server".into()], - }; + async move { + let GitHubLspBinaryVersion { + name, + url, + digest: expected_digest, + } = latest_version; + let destination_path = container_dir.join(format!("ruff-{name}")); + let server_path = match Self::GITHUB_ASSET_KIND { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path + .join(Self::build_asset_name()?.0) + .join("ruff"), + AssetKind::Zip => destination_path.clone().join("ruff.exe"), + }; - let metadata_path = destination_path.with_extension("metadata"); - let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) - .await - .ok(); - if let Some(metadata) = metadata { - let validity_check = async || { - delegate - .try_exec(LanguageServerBinary { - path: server_path.clone(), - arguments: vec!["--version".into()], - env: None, - }) - .await - .inspect_err(|err| { - log::warn!("Unable to run {server_path:?} asset, redownloading: {err:#}",) - }) + let binary = LanguageServerBinary { + path: server_path.clone(), + env: None, + arguments: vec!["server".into()], }; - if let (Some(actual_digest), Some(expected_digest)) = - (&metadata.digest, &expected_digest) - { - if actual_digest == expected_digest { - if validity_check().await.is_ok() { - return Ok(binary); + + let metadata_path = destination_path.with_extension("metadata"); + let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) + .await + .ok(); + if let Some(metadata) = metadata { + let validity_check = async || { + delegate + .try_exec(LanguageServerBinary { + path: server_path.clone(), + arguments: vec!["--version".into()], + env: None, + }) + .await + .inspect_err(|err| { + log::warn!( + "Unable to run {server_path:?} asset, redownloading: {err:#}", + ) + }) + }; + if let (Some(actual_digest), Some(expected_digest)) = + (&metadata.digest, &expected_digest) + { + if actual_digest == expected_digest { + if validity_check().await.is_ok() { + return Ok(binary); + } + } else { + log::info!( + "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" + ); } - } else { - log::info!( - "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" - ); + } else if validity_check().await.is_ok() { + return Ok(binary); } - } else if validity_check().await.is_ok() { - return Ok(binary); } - } - download_server_binary( - &*delegate.http_client(), - &url, - expected_digest.as_deref(), - &destination_path, - Self::GITHUB_ASSET_KIND, - ) - .await?; - make_file_executable(&server_path).await?; - remove_matching(&container_dir, |path| path != destination_path).await; - GithubBinaryMetadata::write_to_file( - &GithubBinaryMetadata { - metadata_version: 1, - digest: expected_digest, - }, - &metadata_path, - ) - .await?; + download_server_binary( + &*delegate.http_client(), + &url, + expected_digest.as_deref(), + &destination_path, + Self::GITHUB_ASSET_KIND, + ) + .await?; + make_file_executable(&server_path).await?; + remove_matching(&container_dir, |path| path != destination_path).await; + GithubBinaryMetadata::write_to_file( + &GithubBinaryMetadata { + metadata_version: 1, + digest: expected_digest, + }, + &metadata_path, + ) + .await?; - Ok(LanguageServerBinary { - path: server_path, - env: None, - arguments: vec!["server".into()], - }) + Ok(LanguageServerBinary { + path: server_path, + env: None, + arguments: vec!["server".into()], + }) + } } async fn cached_server_binary( diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 57d86ea91f342a88ef2a527895664a65d7b62ace..de219d30928eed8a261be594276e853185a8d986 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -19,6 +19,7 @@ use smallvec::SmallVec; use smol::fs::{self}; use std::cmp::Reverse; use std::fmt::Display; +use std::future::Future; use std::ops::Range; use std::{ borrow::Cow, @@ -729,87 +730,93 @@ impl LspInstaller for RustLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, version: GitHubLspBinaryVersion, container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let GitHubLspBinaryVersion { - name, - url, - digest: expected_digest, - } = version; - let destination_path = container_dir.join(format!("rust-analyzer-{name}")); - let server_path = match Self::GITHUB_ASSET_KIND { - AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place. - AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe - }; + delegate: &Arc, + ) -> impl Send + Future> + use<> { + let delegate = delegate.clone(); - let binary = LanguageServerBinary { - path: server_path.clone(), - env: None, - arguments: Default::default(), - }; + async move { + let GitHubLspBinaryVersion { + name, + url, + digest: expected_digest, + } = version; + let destination_path = container_dir.join(format!("rust-analyzer-{name}")); + let server_path = match Self::GITHUB_ASSET_KIND { + AssetKind::TarGz | AssetKind::TarBz2 | AssetKind::Gz => destination_path.clone(), // Tar and gzip extract in place. + AssetKind::Zip => destination_path.clone().join("rust-analyzer.exe"), // zip contains a .exe + }; - let metadata_path = destination_path.with_extension("metadata"); - let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) - .await - .ok(); - if let Some(metadata) = metadata { - let validity_check = async || { - delegate - .try_exec(LanguageServerBinary { - path: server_path.clone(), - arguments: vec!["--version".into()], - env: None, - }) - .await - .inspect_err(|err| { - log::warn!("Unable to run {server_path:?} asset, redownloading: {err:#}",) - }) + let binary = LanguageServerBinary { + path: server_path.clone(), + env: None, + arguments: Default::default(), }; - if let (Some(actual_digest), Some(expected_digest)) = - (&metadata.digest, &expected_digest) - { - if actual_digest == expected_digest { - if validity_check().await.is_ok() { - return Ok(binary); + + let metadata_path = destination_path.with_extension("metadata"); + let metadata = GithubBinaryMetadata::read_from_file(&metadata_path) + .await + .ok(); + if let Some(metadata) = metadata { + let validity_check = async || { + delegate + .try_exec(LanguageServerBinary { + path: server_path.clone(), + arguments: vec!["--version".into()], + env: None, + }) + .await + .inspect_err(|err| { + log::warn!( + "Unable to run {server_path:?} asset, redownloading: {err:#}", + ) + }) + }; + if let (Some(actual_digest), Some(expected_digest)) = + (&metadata.digest, &expected_digest) + { + if actual_digest == expected_digest { + if validity_check().await.is_ok() { + return Ok(binary); + } + } else { + log::info!( + "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" + ); } - } else { - log::info!( - "SHA-256 mismatch for {destination_path:?} asset, downloading new asset. Expected: {expected_digest}, Got: {actual_digest}" - ); + } else if validity_check().await.is_ok() { + return Ok(binary); } - } else if validity_check().await.is_ok() { - return Ok(binary); } - } - download_server_binary( - &*delegate.http_client(), - &url, - expected_digest.as_deref(), - &destination_path, - Self::GITHUB_ASSET_KIND, - ) - .await?; - make_file_executable(&server_path).await?; - remove_matching(&container_dir, |path| path != destination_path).await; - GithubBinaryMetadata::write_to_file( - &GithubBinaryMetadata { - metadata_version: 1, - digest: expected_digest, - }, - &metadata_path, - ) - .await?; + download_server_binary( + &*delegate.http_client(), + &url, + expected_digest.as_deref(), + &destination_path, + Self::GITHUB_ASSET_KIND, + ) + .await?; + make_file_executable(&server_path).await?; + remove_matching(&container_dir, |path| path != destination_path).await; + GithubBinaryMetadata::write_to_file( + &GithubBinaryMetadata { + metadata_version: 1, + digest: expected_digest, + }, + &metadata_path, + ) + .await?; - Ok(LanguageServerBinary { - path: server_path, - env: None, - arguments: Default::default(), - }) + Ok(LanguageServerBinary { + path: server_path, + env: None, + arguments: Default::default(), + }) + } } async fn cached_server_binary( diff --git a/crates/languages/src/tailwind.rs b/crates/languages/src/tailwind.rs index c78790b74c81c9a7fce89425f4499d41f343189e..41fa248a935aeaf41a48a182fe4559ea4364c8e8 100644 --- a/crates/languages/src/tailwind.rs +++ b/crates/languages/src/tailwind.rs @@ -10,6 +10,7 @@ use semver::Version; use serde_json::{Value, json}; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -69,55 +70,63 @@ impl LspInstaller for TailwindLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(SERVER_PATH); - let latest_version = latest_version.to_string(); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } } diff --git a/crates/languages/src/tailwindcss.rs b/crates/languages/src/tailwindcss.rs index aa310fac3f57477b9c0ef85f24f51e619a893c87..dcc9e8bf4ef6d5957f7360990ba31f2bc16adc2e 100644 --- a/crates/languages/src/tailwindcss.rs +++ b/crates/languages/src/tailwindcss.rs @@ -9,6 +9,7 @@ use semver::Version; use serde_json::json; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -65,55 +66,63 @@ impl LspInstaller for TailwindCssLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(SERVER_PATH); - let latest_version = latest_version.to_string(); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } } diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index a83e36270d2ca1b91bca8f15725021939226e83a..d6889d8cbb8ce62c879aaaa6f2383bd8603ce39d 100644 --- a/crates/languages/src/typescript.rs +++ b/crates/languages/src/typescript.rs @@ -18,6 +18,7 @@ use smol::lock::RwLock; use std::{ borrow::Cow, ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::{Arc, LazyLock}, }; @@ -669,76 +670,80 @@ impl LspInstaller for TypeScriptLspAdapter { }) } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(Self::NEW_SERVER_PATH); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let typescript_version = version.typescript_version.clone(); + let server_version = version.server_version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(Self::NEW_SERVER_PATH); + + if node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&typescript_version), + ) + .await + { + return None; + } - if self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(&version.typescript_version), - ) - .await - { - return None; - } + if node + .should_install_npm_package( + Self::SERVER_PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&server_version), + ) + .await + { + return None; + } - if self - .node - .should_install_npm_package( - Self::SERVER_PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(&version.server_version), - ) - .await - { - return None; + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: typescript_server_binary_arguments(&server_path), + }) } - - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: typescript_server_binary_arguments(&server_path), - }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(Self::NEW_SERVER_PATH); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + + async move { + let server_path = container_dir.join(Self::NEW_SERVER_PATH); + let typescript_version = latest_version.typescript_version.to_string(); + let server_version = latest_version.server_version.to_string(); - self.node - .npm_install_packages( + node.npm_install_packages( &container_dir, &[ - ( - Self::PACKAGE_NAME, - &latest_version.typescript_version.to_string(), - ), - ( - Self::SERVER_PACKAGE_NAME, - &latest_version.server_version.to_string(), - ), + (Self::PACKAGE_NAME, typescript_version.as_str()), + (Self::SERVER_PACKAGE_NAME, server_version.as_str()), ], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: typescript_server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: typescript_server_binary_arguments(&server_path), + }) + } } async fn cached_server_binary( diff --git a/crates/languages/src/vtsls.rs b/crates/languages/src/vtsls.rs index 23434b81a98589e57a769bc8eba42e55e5445623..4bc4401ff3046ed6422ef9854ade7f4394e6562e 100644 --- a/crates/languages/src/vtsls.rs +++ b/crates/languages/src/vtsls.rs @@ -15,6 +15,7 @@ use serde_json::json; use settings::update_settings_file; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::{Arc, LazyLock}, }; @@ -123,54 +124,56 @@ impl LspInstaller for VtslsLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(Self::SERVER_PATH); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + + async move { + let server_path = container_dir.join(Self::SERVER_PATH); + + let typescript_version = latest_version.typescript_version.to_string(); + let server_version = latest_version.server_version.to_string(); + + let mut packages_to_install = Vec::new(); + + if node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&latest_version.server_version), + ) + .await + { + packages_to_install.push((Self::PACKAGE_NAME, server_version.as_str())); + } - let typescript_version = latest_version.typescript_version.to_string(); - let server_version = latest_version.server_version.to_string(); + if node + .should_install_npm_package( + Self::TYPESCRIPT_PACKAGE_NAME, + &container_dir.join(Self::TYPESCRIPT_TSDK_PATH), + &container_dir, + VersionStrategy::Latest(&latest_version.typescript_version), + ) + .await + { + packages_to_install + .push((Self::TYPESCRIPT_PACKAGE_NAME, typescript_version.as_str())); + } - let mut packages_to_install = Vec::new(); + node.npm_install_packages(&container_dir, &packages_to_install) + .await?; - if self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - &container_dir, - VersionStrategy::Latest(&latest_version.server_version), - ) - .await - { - packages_to_install.push((Self::PACKAGE_NAME, server_version.as_str())); + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: typescript_server_binary_arguments(&server_path), + }) } - - if self - .node - .should_install_npm_package( - Self::TYPESCRIPT_PACKAGE_NAME, - &container_dir.join(Self::TYPESCRIPT_TSDK_PATH), - &container_dir, - VersionStrategy::Latest(&latest_version.typescript_version), - ) - .await - { - packages_to_install.push((Self::TYPESCRIPT_PACKAGE_NAME, typescript_version.as_str())); - } - - self.node - .npm_install_packages(&container_dir, &packages_to_install) - .await?; - - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: typescript_server_binary_arguments(&server_path), - }) } async fn cached_server_binary( diff --git a/crates/languages/src/yaml.rs b/crates/languages/src/yaml.rs index e8bad8eb2059b02486fa2b7d7e14479dc57a23d6..22781acf25a2cdad777adb25a18122bc69da4a45 100644 --- a/crates/languages/src/yaml.rs +++ b/crates/languages/src/yaml.rs @@ -12,6 +12,7 @@ use serde_json::Value; use settings::{Settings, SettingsLocation}; use std::{ ffi::OsString, + future::Future, path::{Path, PathBuf}, sync::Arc, }; @@ -65,54 +66,63 @@ impl LspInstaller for YamlLspAdapter { }) } - async fn fetch_server_binary( + fn fetch_server_binary( &self, latest_version: Self::BinaryVersion, container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - let server_path = container_dir.join(SERVER_PATH); + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); - self.node - .npm_install_packages( + async move { + let server_path = container_dir.join(SERVER_PATH); + let latest_version = latest_version.to_string(); + + node.npm_install_packages( &container_dir, - &[(Self::PACKAGE_NAME, &latest_version.to_string())], + &[(Self::PACKAGE_NAME, latest_version.as_str())], ) .await?; - Ok(LanguageServerBinary { - path: self.node.binary_path().await?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + Ok(LanguageServerBinary { + path: node.binary_path().await?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } - async fn check_if_version_installed( + fn check_if_version_installed( &self, version: &Self::BinaryVersion, container_dir: &PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let server_path = container_dir.join(SERVER_PATH); - - let should_install_language_server = self - .node - .should_install_npm_package( - Self::PACKAGE_NAME, - &server_path, - container_dir, - VersionStrategy::Latest(version), - ) - .await; - - if should_install_language_server { - None - } else { - Some(LanguageServerBinary { - path: self.node.binary_path().await.ok()?, - env: None, - arguments: server_binary_arguments(&server_path), - }) + _: &Arc, + ) -> impl Send + Future> + use<> { + let node = self.node.clone(); + let version = version.clone(); + let container_dir = container_dir.clone(); + + async move { + let server_path = container_dir.join(SERVER_PATH); + + let should_install_language_server = node + .should_install_npm_package( + Self::PACKAGE_NAME, + &server_path, + &container_dir, + VersionStrategy::Latest(&version), + ) + .await; + + if should_install_language_server { + None + } else { + Some(LanguageServerBinary { + path: node.binary_path().await.ok()?, + env: None, + arguments: server_binary_arguments(&server_path), + }) + } } } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 85229cfdcdeb34d08a2e5b09448c1f2d21f5c99a..55bf8c66c2ac0c1bc8b2fc96c0c0a6daa4d85c95 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -14349,13 +14349,13 @@ impl LspInstaller for SshLspAdapter { anyhow::bail!("SshLspAdapter does not support fetch_latest_server_version") } - async fn fetch_server_binary( + fn fetch_server_binary( &self, _: (), _: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Result { - anyhow::bail!("SshLspAdapter does not support fetch_server_binary") + _: &Arc, + ) -> impl Send + Future> + use<> { + async { anyhow::bail!("SshLspAdapter does not support fetch_server_binary") } } }