html.rs

  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);