@@ -743,6 +743,9 @@
"Elixir": {
"language_servers": ["elixir-ls", "!next-ls", "!lexical", "..."]
},
+ "Erlang": {
+ "language_servers": ["erlang-ls", "!elp", "..."]
+ },
"Go": {
"code_actions_on_format": {
"source.organizeImports": true
@@ -1,12 +1,18 @@
+use std::fs;
+
use zed_extension_api::{self as zed, LanguageServerId, Result};
-pub struct ErlangLanguagePlatform;
+pub struct ErlangLanguagePlatform {
+ cached_binary_path: Option<String>,
+}
impl ErlangLanguagePlatform {
pub const LANGUAGE_SERVER_ID: &'static str = "elp";
pub fn new() -> Self {
- Self
+ Self {
+ cached_binary_path: None,
+ }
}
pub fn language_server_command(
@@ -23,11 +29,84 @@ impl ErlangLanguagePlatform {
fn language_server_binary_path(
&mut self,
- _language_server_id: &LanguageServerId,
+ language_server_id: &LanguageServerId,
worktree: &zed::Worktree,
) -> Result<String> {
- worktree
- .which("elp")
- .ok_or_else(|| "elp must be installed and available on your $PATH".to_string())
+ if let Some(path) = worktree.which("elp") {
+ return Ok(path);
+ }
+
+ if let Some(path) = &self.cached_binary_path {
+ if fs::metadata(path).map_or(false, |stat| stat.is_file()) {
+ return Ok(path.clone());
+ }
+ }
+
+ zed::set_language_server_installation_status(
+ &language_server_id,
+ &zed::LanguageServerInstallationStatus::CheckingForUpdate,
+ );
+ let release = zed::latest_github_release(
+ "WhatsApp/erlang-language-platform",
+ zed::GithubReleaseOptions {
+ require_assets: true,
+ pre_release: false,
+ },
+ )?;
+
+ let (platform, arch) = zed::current_platform();
+ let asset_name = {
+ let otp_version = "26.2";
+ let (os, os_target) = match platform {
+ zed::Os::Mac => ("macos", "apple-darwin"),
+ zed::Os::Linux => ("linux", "unknown-linux-gnu"),
+ zed::Os::Windows => return Err(format!("unsupported platform: {platform:?}")),
+ };
+
+ format!(
+ "elp-{os}-{arch}-{os_target}-otp-{otp_version}.tar.gz",
+ arch = match arch {
+ zed::Architecture::Aarch64 => "aarch64",
+ zed::Architecture::X8664 => "x86_64",
+ zed::Architecture::X86 =>
+ return Err(format!("unsupported architecture: {arch:?}")),
+ },
+ )
+ };
+
+ let asset = release
+ .assets
+ .iter()
+ .find(|asset| asset.name == asset_name)
+ .ok_or_else(|| format!("no asset found matching {:?}", asset_name))?;
+
+ let version_dir = format!("elp-{}", release.version);
+ let binary_path = format!("{version_dir}/elp");
+
+ if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) {
+ zed::set_language_server_installation_status(
+ &language_server_id,
+ &zed::LanguageServerInstallationStatus::Downloading,
+ );
+
+ zed::download_file(
+ &asset.download_url,
+ &version_dir,
+ zed::DownloadedFileType::GzipTar,
+ )
+ .map_err(|e| format!("failed to download file: {e}"))?;
+
+ let entries =
+ fs::read_dir(".").map_err(|e| format!("failed to list working directory {e}"))?;
+ for entry in entries {
+ let entry = entry.map_err(|e| format!("failed to load directory entry {e}"))?;
+ if entry.file_name().to_str() != Some(&version_dir) {
+ fs::remove_dir_all(&entry.path()).ok();
+ }
+ }
+ }
+
+ self.cached_binary_path = Some(binary_path.clone());
+ Ok(binary_path)
}
}