1use std::fs;
2
3use zed_extension_api::{
4 self as zed, Architecture, DownloadedFileType, GithubReleaseOptions, Os, Result,
5 settings::LspSettings,
6};
7
8use crate::language_servers::util;
9
10pub(crate) struct BufLsp {
11 cached_binary_path: Option<String>,
12}
13
14impl BufLsp {
15 pub(crate) const SERVER_NAME: &str = "buf";
16
17 pub(crate) fn new() -> Self {
18 BufLsp {
19 cached_binary_path: None,
20 }
21 }
22
23 pub(crate) fn language_server_binary(
24 &mut self,
25 worktree: &zed::Worktree,
26 ) -> Result<zed::Command> {
27 let binary_settings = LspSettings::for_worktree(Self::SERVER_NAME, worktree)
28 .ok()
29 .and_then(|lsp_settings| lsp_settings.binary);
30
31 let args = binary_settings
32 .as_ref()
33 .and_then(|binary_settings| binary_settings.arguments.clone())
34 .unwrap_or_else(|| ["lsp", "serve"].map(ToOwned::to_owned).into());
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: Default::default(),
41 });
42 } else if let Some(path) = self.cached_binary_path.clone() {
43 return Ok(zed::Command {
44 command: path,
45 args,
46 env: Default::default(),
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: Default::default(),
54 });
55 }
56
57 let latest_release = zed::latest_github_release(
58 "bufbuild/buf",
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) => "Darwin-arm64",
69 (Os::Mac, Architecture::X8664) => "Darwin-x86_64",
70 (Os::Linux, Architecture::Aarch64) => "Linux-aarch64",
71 (Os::Linux, Architecture::X8664) => "Linux-x86_64",
72 (Os::Windows, Architecture::Aarch64) => "Windows-arm64.exe",
73 (Os::Windows, Architecture::X8664) => "Windows-x86_64.exe",
74 _ => {
75 return Err("Platform and architecture not supported by buf CLI".to_string());
76 }
77 };
78
79 let release_name = format!("buf-{release_suffix}");
80
81 let version_dir = format!("{}-{}", Self::SERVER_NAME, latest_release.version);
82 fs::create_dir_all(&version_dir).map_err(|_| "Could not create directory")?;
83
84 let binary_path = format!("{version_dir}/buf");
85
86 let download_target = latest_release
87 .assets
88 .into_iter()
89 .find(|asset| asset.name == release_name)
90 .ok_or_else(|| {
91 format!(
92 "Could not find asset with name {} in buf CLI release",
93 &release_name
94 )
95 })?;
96
97 zed::download_file(
98 &download_target.download_url,
99 &binary_path,
100 DownloadedFileType::Uncompressed,
101 )?;
102 zed::make_file_executable(&binary_path)?;
103
104 util::remove_outdated_versions(Self::SERVER_NAME, &version_dir)?;
105
106 self.cached_binary_path = Some(binary_path.clone());
107
108 Ok(zed::Command {
109 command: binary_path,
110 args,
111 env: Default::default(),
112 })
113 }
114}