1use std::{env, fs, path::PathBuf};
2use zed_extension_api::{self as zed, Result};
3
4const PACKAGE_NAME: &str = "vscode-language-server";
5
6struct HtmlExtension {
7 path: Option<PathBuf>,
8}
9
10impl HtmlExtension {
11 fn server_script_path(&self, language_server_id: &zed::LanguageServerId) -> Result<PathBuf> {
12 if let Some(path) = self.path.as_ref() {
13 if fs::metadata(path).map_or(false, |stat| stat.is_dir()) {
14 return Ok(path.clone());
15 }
16 }
17
18 zed::set_language_server_installation_status(
19 language_server_id,
20 &zed::LanguageServerInstallationStatus::CheckingForUpdate,
21 );
22 let release = zed::latest_github_release(
23 "zed-industries/vscode-langservers-extracted",
24 zed::GithubReleaseOptions {
25 require_assets: true,
26 pre_release: false,
27 },
28 )?;
29
30 let asset_name = "vscode-language-server.tar.gz";
31
32 let asset = release
33 .assets
34 .iter()
35 .find(|asset| asset.name == asset_name)
36 .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
37 let version_dir = format!("{}-{}", PACKAGE_NAME, release.version);
38 if !fs::metadata(&version_dir).map_or(false, |stat| stat.is_dir()) {
39 zed::set_language_server_installation_status(
40 &language_server_id,
41 &zed::LanguageServerInstallationStatus::Downloading,
42 );
43
44 zed::download_file(
45 &asset.download_url,
46 &version_dir,
47 zed::DownloadedFileType::GzipTar,
48 )
49 .map_err(|e| format!("failed to download file: {e}"))?;
50
51 let entries =
52 fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
53 for entry in entries {
54 let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
55 if entry.file_name().to_str() != Some(&version_dir) {
56 fs::remove_dir_all(&entry.path()).ok();
57 }
58 }
59 }
60 Ok(PathBuf::from(version_dir)
61 .join("bin")
62 .join("vscode-html-language-server"))
63 }
64}
65
66impl zed::Extension for HtmlExtension {
67 fn new() -> Self {
68 Self { path: None }
69 }
70
71 fn language_server_command(
72 &mut self,
73 language_server_id: &zed::LanguageServerId,
74 _worktree: &zed::Worktree,
75 ) -> Result<zed::Command> {
76 let path = match &self.path {
77 Some(path) => path,
78 None => {
79 let path = self.server_script_path(language_server_id)?;
80 self.path = Some(path);
81 self.path.as_ref().unwrap()
82 }
83 };
84
85 Ok(zed::Command {
86 command: zed::node_binary_path()?,
87 args: vec![
88 env::current_dir()
89 .unwrap()
90 .join(path)
91 .to_string_lossy()
92 .to_string(),
93 "--stdio".to_string(),
94 ],
95 env: Default::default(),
96 })
97 }
98}
99
100zed::register_extension!(HtmlExtension);