svelte.rs

  1use std::{env, fs};
  2use zed_extension_api::{self as zed, serde_json, Result};
  3
  4struct SvelteExtension {
  5    did_find_server: bool,
  6}
  7
  8const SERVER_PATH: &str = "node_modules/svelte-language-server/bin/server.js";
  9const PACKAGE_NAME: &str = "svelte-language-server";
 10
 11impl SvelteExtension {
 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, 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            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                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 SvelteExtension {
 58    fn new() -> Self {
 59        Self {
 60            did_find_server: false,
 61        }
 62    }
 63
 64    fn language_server_command(
 65        &mut self,
 66        id: &zed::LanguageServerId,
 67        _: &zed::Worktree,
 68    ) -> Result<zed::Command> {
 69        let server_path = self.server_script_path(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    fn language_server_initialization_options(
 85        &mut self,
 86        _: &zed::LanguageServerId,
 87        _: &zed::Worktree,
 88    ) -> Result<Option<serde_json::Value>> {
 89        let config = serde_json::json!({
 90          "inlayHints": {
 91            "parameterNames": {
 92              "enabled": "all",
 93              "suppressWhenArgumentMatchesName": false
 94            },
 95            "parameterTypes": {
 96              "enabled": true
 97            },
 98            "variableTypes": {
 99              "enabled": true,
100              "suppressWhenTypeMatchesName": false
101            },
102            "propertyDeclarationTypes": {
103              "enabled": true
104            },
105            "functionLikeReturnTypes": {
106              "enabled": true
107            },
108            "enumMemberValues": {
109              "enabled": true
110            }
111          }
112        });
113
114        Ok(Some(serde_json::json!({
115            "provideFormatter": true,
116            "configuration": {
117                "typescript": config,
118                "javascript": config
119            }
120        })))
121    }
122}
123
124zed::register_extension!(SvelteExtension);