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