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