intelephense.rs

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