html.rs

 1use std::{env, fs};
 2use zed::settings::LspSettings;
 3use zed_extension_api::{self as zed, LanguageServerId, Result};
 4
 5const SERVER_PATH: &str =
 6    "node_modules/@zed-industries/vscode-langservers-extracted/bin/vscode-html-language-server";
 7const PACKAGE_NAME: &str = "@zed-industries/vscode-langservers-extracted";
 8
 9struct HtmlExtension {
10    did_find_server: bool,
11}
12
13impl HtmlExtension {
14    fn server_exists(&self) -> bool {
15        fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file())
16    }
17
18    fn server_script_path(&mut self, language_server_id: &LanguageServerId) -> Result<String> {
19        let server_exists = self.server_exists();
20        if self.did_find_server && server_exists {
21            return Ok(SERVER_PATH.to_string());
22        }
23
24        zed::set_language_server_installation_status(
25            language_server_id,
26            &zed::LanguageServerInstallationStatus::CheckingForUpdate,
27        );
28        let version = zed::npm_package_latest_version(PACKAGE_NAME)?;
29
30        if !server_exists
31            || zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version)
32        {
33            zed::set_language_server_installation_status(
34                language_server_id,
35                &zed::LanguageServerInstallationStatus::Downloading,
36            );
37            let result = zed::npm_install_package(PACKAGE_NAME, &version);
38            match result {
39                Ok(()) => {
40                    if !self.server_exists() {
41                        Err(format!(
42                            "installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'",
43                        ))?;
44                    }
45                }
46                Err(error) => {
47                    if !self.server_exists() {
48                        Err(error)?;
49                    }
50                }
51            }
52        }
53
54        self.did_find_server = true;
55        Ok(SERVER_PATH.to_string())
56    }
57}
58
59impl zed::Extension for HtmlExtension {
60    fn new() -> Self {
61        Self {
62            did_find_server: false,
63        }
64    }
65
66    fn language_server_command(
67        &mut self,
68        language_server_id: &LanguageServerId,
69        _worktree: &zed::Worktree,
70    ) -> Result<zed::Command> {
71        let server_path = self.server_script_path(language_server_id)?;
72        Ok(zed::Command {
73            command: zed::node_binary_path()?,
74            args: vec![
75                env::current_dir()
76                    .unwrap()
77                    .join(&server_path)
78                    .to_string_lossy()
79                    .to_string(),
80                "--stdio".to_string(),
81            ],
82            env: Default::default(),
83        })
84    }
85
86    fn language_server_workspace_configuration(
87        &mut self,
88        server_id: &LanguageServerId,
89        worktree: &zed::Worktree,
90    ) -> Result<Option<zed::serde_json::Value>> {
91        let settings = LspSettings::for_worktree(server_id.as_ref(), worktree)
92            .ok()
93            .and_then(|lsp_settings| lsp_settings.settings.clone())
94            .unwrap_or_default();
95        Ok(Some(settings))
96    }
97}
98
99zed::register_extension!(HtmlExtension);