1use std::fs;
2use zed_extension_api::{self as zed, Result};
3
4struct GleamExtension {
5 cached_binary_path: Option<String>,
6}
7
8impl GleamExtension {
9 fn language_server_binary_path(&mut self, config: zed::LanguageServerConfig) -> Result<String> {
10 if let Some(path) = &self.cached_binary_path {
11 if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
12 return Ok(path.clone());
13 }
14 }
15
16 zed::set_language_server_installation_status(
17 &config.name,
18 &zed::LanguageServerInstallationStatus::CheckingForUpdate,
19 );
20 let release = zed::latest_github_release(
21 "gleam-lang/gleam",
22 zed::GithubReleaseOptions {
23 require_assets: true,
24 pre_release: false,
25 },
26 )?;
27
28 let (platform, arch) = zed::current_platform();
29 let asset_name = format!(
30 "gleam-{version}-{arch}-{os}.tar.gz",
31 version = release.version,
32 arch = match arch {
33 zed::Architecture::Aarch64 => "aarch64",
34 zed::Architecture::X86 => "x86",
35 zed::Architecture::X8664 => "x86_64",
36 },
37 os = match platform {
38 zed::Os::Mac => "apple-darwin",
39 zed::Os::Linux => "unknown-linux-musl",
40 zed::Os::Windows => "pc-windows-msvc",
41 },
42 );
43
44 let asset = release
45 .assets
46 .iter()
47 .find(|asset| asset.name == asset_name)
48 .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
49
50 let version_dir = format!("gleam-{}", release.version);
51 let binary_path = format!("{version_dir}/gleam");
52
53 if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
54 zed::set_language_server_installation_status(
55 &config.name,
56 &zed::LanguageServerInstallationStatus::Downloading,
57 );
58
59 zed::download_file(
60 &asset.download_url,
61 &version_dir,
62 zed::DownloadedFileType::GzipTar,
63 )
64 .map_err(|e| format!("failed to download file: {e}"))?;
65
66 let entries =
67 fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
68 for entry in entries {
69 let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
70 if entry.file_name().to_str() != Some(&version_dir) {
71 fs::remove_dir_all(&entry.path()).ok();
72 }
73 }
74 }
75
76 self.cached_binary_path = Some(binary_path.clone());
77 Ok(binary_path)
78 }
79}
80
81impl zed::Extension for GleamExtension {
82 fn new() -> Self {
83 Self {
84 cached_binary_path: None,
85 }
86 }
87
88 fn language_server_command(
89 &mut self,
90 config: zed::LanguageServerConfig,
91 _worktree: &zed::Worktree,
92 ) -> Result<zed::Command> {
93 Ok(zed::Command {
94 command: self.language_server_binary_path(config)?,
95 args: vec!["lsp".to_string()],
96 env: Default::default(),
97 })
98 }
99}
100
101zed::register_extension!(GleamExtension);