emmet.rs

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