protols.rs

  1use zed_extension_api::{
  2    self as zed, Architecture, DownloadedFileType, GithubReleaseOptions, Os, Result,
  3    settings::LspSettings,
  4};
  5
  6use crate::language_servers::util;
  7
  8pub(crate) struct ProtoLs {
  9    cached_binary_path: Option<String>,
 10}
 11
 12impl ProtoLs {
 13    pub(crate) const SERVER_NAME: &str = "protols";
 14
 15    pub(crate) fn new() -> Self {
 16        ProtoLs {
 17            cached_binary_path: None,
 18        }
 19    }
 20
 21    pub(crate) fn language_server_binary(
 22        &mut self,
 23        worktree: &zed::Worktree,
 24    ) -> Result<zed::Command> {
 25        let binary_settings = LspSettings::for_worktree(Self::SERVER_NAME, worktree)
 26            .ok()
 27            .and_then(|lsp_settings| lsp_settings.binary);
 28
 29        let args = binary_settings
 30            .as_ref()
 31            .and_then(|binary_settings| binary_settings.arguments.clone())
 32            .unwrap_or_default();
 33
 34        let env = worktree.shell_env();
 35
 36        if let Some(path) = binary_settings.and_then(|binary_settings| binary_settings.path) {
 37            return Ok(zed::Command {
 38                command: path,
 39                args,
 40                env,
 41            });
 42        } else if let Some(path) = self.cached_binary_path.clone() {
 43            return Ok(zed::Command {
 44                command: path,
 45                args,
 46                env,
 47            });
 48        } else if let Some(path) = worktree.which(Self::SERVER_NAME) {
 49            self.cached_binary_path = Some(path.clone());
 50            return Ok(zed::Command {
 51                command: path,
 52                args,
 53                env,
 54            });
 55        }
 56
 57        let latest_release = zed::latest_github_release(
 58            "coder3101/protols",
 59            GithubReleaseOptions {
 60                require_assets: true,
 61                pre_release: false,
 62            },
 63        )?;
 64
 65        let (os, arch) = zed::current_platform();
 66
 67        let release_suffix = match (os, arch) {
 68            (Os::Mac, Architecture::Aarch64) => "aarch64-apple-darwin.tar.gz",
 69            (Os::Mac, Architecture::X8664) => "x86_64-apple-darwin.tar.gz",
 70            (Os::Linux, Architecture::Aarch64) => "aarch64-unknown-linux-gnu.tar.gz",
 71            (Os::Linux, Architecture::X8664) => "x86_64-unknown-linux-gnu.tar.gz",
 72            (Os::Windows, Architecture::X8664) => "x86_64-pc-windows-msvc.zip",
 73            _ => {
 74                return Err("Platform and architecture not supported by Protols".to_string());
 75            }
 76        };
 77
 78        let release_name = format!("protols-{release_suffix}");
 79
 80        let file_type = if os == Os::Windows {
 81            DownloadedFileType::Zip
 82        } else {
 83            DownloadedFileType::GzipTar
 84        };
 85
 86        let version_dir = format!("{}-{}", Self::SERVER_NAME, latest_release.version);
 87        let binary_path = format!("{version_dir}/protols");
 88
 89        let download_target = latest_release
 90            .assets
 91            .into_iter()
 92            .find(|asset| asset.name == release_name)
 93            .ok_or_else(|| {
 94                format!(
 95                    "Could not find asset with name {} in Protols release",
 96                    &release_name
 97                )
 98            })?;
 99
100        zed::download_file(&download_target.download_url, &version_dir, file_type)?;
101        zed::make_file_executable(&binary_path)?;
102
103        util::remove_outdated_versions(Self::SERVER_NAME, &version_dir)?;
104
105        self.cached_binary_path = Some(binary_path.clone());
106
107        Ok(zed::Command {
108            command: binary_path,
109            args,
110            env,
111        })
112    }
113}