diff --git a/src/language_servers/csharp_ls.rs b/src/language_servers/csharp_ls.rs index 3149e2d..cd5e7b4 100644 --- a/src/language_servers/csharp_ls.rs +++ b/src/language_servers/csharp_ls.rs @@ -62,20 +62,25 @@ impl CsharpLs { &zed::LanguageServerInstallationStatus::CheckingForUpdate, ); - let version = self.nuget.get_latest_version(PACKAGE_ID)?; - let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, version); + let version_dir = if let Ok(version) = self.nuget.get_latest_version(PACKAGE_ID) { + let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, version); - if Self::find_dll(&version_dir).is_err() { - zed::set_language_server_installation_status( - language_server_id, - &zed::LanguageServerInstallationStatus::Downloading, - ); + if Self::find_dll(&version_dir).is_err() { + zed::set_language_server_installation_status( + language_server_id, + &zed::LanguageServerInstallationStatus::Downloading, + ); - self.nuget - .download_and_extract(PACKAGE_ID, &version, &version_dir)?; + self.nuget + .download_and_extract(PACKAGE_ID, &version, &version_dir)?; - util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; - } + util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; + } + + version_dir + } else { + util::find_offline_version(Self::LANGUAGE_SERVER_ID)? + }; let dll_path = Self::find_dll(&version_dir)?; let command = Self::dotnet_exec(worktree, &dll_path, binary_args)?; diff --git a/src/language_servers/omnisharp.rs b/src/language_servers/omnisharp.rs index c5bf69e..cc3ec0b 100644 --- a/src/language_servers/omnisharp.rs +++ b/src/language_servers/omnisharp.rs @@ -1,5 +1,5 @@ use std::fs; -use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Result}; +use zed_extension_api::{self as zed, settings::LspSettings, LanguageServerId, Os, Result}; use crate::language_servers::util; @@ -60,68 +60,77 @@ impl Omnisharp { language_server_id, &zed::LanguageServerInstallationStatus::CheckingForUpdate, ); - let release = zed::latest_github_release( + let (platform, arch) = zed::current_platform(); + let binary_path = if let Ok(release) = zed::latest_github_release( "OmniSharp/omnisharp-roslyn", zed::GithubReleaseOptions { require_assets: true, pre_release: false, }, - )?; + ) { + let asset_name = format!( + "omnisharp-{os}-{arch}-net6.0.{extension}", + os = match platform { + zed::Os::Mac => "osx", + zed::Os::Linux => "linux", + zed::Os::Windows => "win", + }, + arch = match arch { + zed::Architecture::Aarch64 => "arm64", + zed::Architecture::X86 => "x86", + zed::Architecture::X8664 => "x64", + }, + extension = match platform { + zed::Os::Mac | zed::Os::Linux => "tar.gz", + zed::Os::Windows => "zip", + } + ); - let (platform, arch) = zed::current_platform(); - let asset_name = format!( - "omnisharp-{os}-{arch}-net6.0.{extension}", - os = match platform { - zed::Os::Mac => "osx", - zed::Os::Linux => "linux", - zed::Os::Windows => "win", - }, - arch = match arch { - zed::Architecture::Aarch64 => "arm64", - zed::Architecture::X86 => "x86", - zed::Architecture::X8664 => "x64", - }, - extension = match platform { - zed::Os::Mac | zed::Os::Linux => "tar.gz", - zed::Os::Windows => "zip", + let asset = release + .assets + .iter() + .find(|asset| asset.name == asset_name) + .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?; + + let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, release.version); + let binary_path = Self::get_binary_path(&version_dir, platform); + + if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) { + zed::set_language_server_installation_status( + language_server_id, + &zed::LanguageServerInstallationStatus::Downloading, + ); + + zed::download_file( + &asset.download_url, + &version_dir, + match platform { + zed::Os::Mac | zed::Os::Linux => zed::DownloadedFileType::GzipTar, + zed::Os::Windows => zed::DownloadedFileType::Zip, + }, + ) + .map_err(|e| format!("failed to download file: {e}"))?; + + util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; } - ); - - let asset = release - .assets - .iter() - .find(|asset| asset.name == asset_name) - .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?; - let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, release.version); - let binary_path = match platform { - zed::Os::Windows => format!("{version_dir}/OmniSharp.exe"), - _ => format!("{version_dir}/OmniSharp"), + binary_path + } else { + let version_dir = util::find_offline_version(Self::LANGUAGE_SERVER_ID)?; + Self::get_binary_path(&version_dir, platform) }; - if !fs::metadata(&binary_path).is_ok_and(|stat| stat.is_file()) { - zed::set_language_server_installation_status( - language_server_id, - &zed::LanguageServerInstallationStatus::Downloading, - ); - - zed::download_file( - &asset.download_url, - &version_dir, - match platform { - zed::Os::Mac | zed::Os::Linux => zed::DownloadedFileType::GzipTar, - zed::Os::Windows => zed::DownloadedFileType::Zip, - }, - ) - .map_err(|e| format!("failed to download file: {e}"))?; - - util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; - } - self.cached_binary_path = Some(binary_path.clone()); Ok(OmnisharpBinary { path: binary_path, args: binary_args, }) } + + fn get_binary_path(version_dir: &str, platform: Os) -> String { + match platform { + zed::Os::Windows => format!("{version_dir}/OmniSharp.exe"), + _ => format!("{version_dir}/OmniSharp"), + } + } } diff --git a/src/language_servers/roslyn.rs b/src/language_servers/roslyn.rs index b6f413f..767df59 100644 --- a/src/language_servers/roslyn.rs +++ b/src/language_servers/roslyn.rs @@ -64,23 +64,28 @@ impl Roslyn { }; let package_id = format!("{PACKAGE_PREFIX}.{rid}"); - let version = self.nuget.get_latest_version(&package_id)?; - let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, version); + let version_dir = if let Ok(version) = self.nuget.get_latest_version(&package_id) { + let version_dir = format!("{}-{}", Self::LANGUAGE_SERVER_ID, version); - let already_installed = Self::find_server_path(rid, &version_dir) - .is_ok_and(|sp| fs::metadata(sp.as_str()).is_ok_and(|stat| stat.is_file())); + let already_installed = Self::find_server_path(rid, &version_dir) + .is_ok_and(|sp| fs::metadata(sp.as_str()).is_ok_and(|stat| stat.is_file())); - if !already_installed { - zed::set_language_server_installation_status( - language_server_id, - &zed::LanguageServerInstallationStatus::Downloading, - ); + if !already_installed { + zed::set_language_server_installation_status( + language_server_id, + &zed::LanguageServerInstallationStatus::Downloading, + ); - self.nuget - .download_and_extract(&package_id, &version, &version_dir)?; + self.nuget + .download_and_extract(&package_id, &version, &version_dir)?; - util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; - } + util::remove_outdated_versions(Self::LANGUAGE_SERVER_ID, &version_dir)?; + } + + version_dir + } else { + util::find_offline_version(Self::LANGUAGE_SERVER_ID)? + }; let server_path = Self::find_server_path(rid, &version_dir)?; if let ServerPath::Exe(ref path) = server_path { diff --git a/src/language_servers/util.rs b/src/language_servers/util.rs index b9a294c..9bdd0fc 100644 --- a/src/language_servers/util.rs +++ b/src/language_servers/util.rs @@ -23,3 +23,16 @@ pub(super) fn remove_outdated_versions( } Ok(()) } + +pub(super) fn find_offline_version(language_server_id: &'static str) -> Result { + let entries = fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?; + for entry in entries { + let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?; + if let Ok(name) = entry.file_name().into_string() { + if name.starts_with(language_server_id) { + return Ok(name); + } + } + } + Err(format!("couldn't find installed language server version; try connecting to the internet")) +}