From 6121bfc5a4a03d934d90729434e532d258377792 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 2 Apr 2024 13:47:03 -0400 Subject: [PATCH] Extract Clojure support into an extension (#10088) This PR extracts Clojure support into an extension and removes the built-in Clojure support from Zed. Release Notes: - Removed built-in support for Clojure, in favor of making it available as an extension. The Clojure extension will be suggested for download when you open a `.clj` or other Clojure-related files. --------- Co-authored-by: Max --- Cargo.lock | 17 +-- Cargo.toml | 2 +- .../src/wasm_host/wit/since_v0_0_4.rs | 2 + crates/extensions_ui/src/extension_suggest.rs | 5 + crates/languages/Cargo.toml | 1 - crates/languages/src/clojure.rs | 141 ------------------ crates/languages/src/lib.rs | 3 - extensions/clojure/Cargo.toml | 16 ++ extensions/clojure/extension.toml | 15 ++ .../clojure/languages}/clojure/brackets.scm | 0 .../clojure/languages}/clojure/config.toml | 0 .../clojure/languages}/clojure/highlights.scm | 0 .../clojure/languages}/clojure/indents.scm | 0 .../clojure/languages}/clojure/outline.scm | 0 extensions/clojure/src/clojure.rs | 110 ++++++++++++++ typos.toml | 3 +- 16 files changed, 158 insertions(+), 157 deletions(-) delete mode 100644 crates/languages/src/clojure.rs create mode 100644 extensions/clojure/Cargo.toml create mode 100644 extensions/clojure/extension.toml rename {crates/languages/src => extensions/clojure/languages}/clojure/brackets.scm (100%) rename {crates/languages/src => extensions/clojure/languages}/clojure/config.toml (100%) rename {crates/languages/src => extensions/clojure/languages}/clojure/highlights.scm (100%) rename {crates/languages/src => extensions/clojure/languages}/clojure/indents.scm (100%) rename {crates/languages/src => extensions/clojure/languages}/clojure/outline.scm (100%) create mode 100644 extensions/clojure/src/clojure.rs diff --git a/Cargo.lock b/Cargo.lock index f1bfc1575e..dadccd45d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5461,7 +5461,6 @@ dependencies = [ "tree-sitter", "tree-sitter-bash", "tree-sitter-c", - "tree-sitter-clojure", "tree-sitter-cpp", "tree-sitter-css", "tree-sitter-dart", @@ -10388,15 +10387,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-clojure" -version = "0.0.9" -source = "git+https://github.com/prcastro/tree-sitter-clojure?branch=update-ts#38b4f8d264248b2fd09575fbce66f7c22e8929d5" -dependencies = [ - "cc", - "tree-sitter", -] - [[package]] name = "tree-sitter-cpp" version = "0.20.0" @@ -12672,6 +12662,13 @@ dependencies = [ "zed_extension_api 0.0.4", ] +[[package]] +name = "zed_clojure" +version = "0.0.1" +dependencies = [ + "zed_extension_api 0.0.4", +] + [[package]] name = "zed_csharp" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index be0f7f54f3..72079ff004 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -100,6 +100,7 @@ members = [ "crates/zed_actions", "extensions/astro", + "extensions/clojure", "extensions/csharp", "extensions/erlang", "extensions/gleam", @@ -302,7 +303,6 @@ tower-http = "0.4.4" tree-sitter = { version = "0.20", features = ["wasm"] } tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash", rev = "7331995b19b8f8aba2d5e26deb51d2195c18bc94" } tree-sitter-c = "0.20.1" -tree-sitter-clojure = { git = "https://github.com/prcastro/tree-sitter-clojure", branch = "update-ts" } tree-sitter-cpp = { git = "https://github.com/tree-sitter/tree-sitter-cpp", rev = "f44509141e7e483323d2ec178f2d2e6c0fc041c1" } tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" } tree-sitter-dart = { git = "https://github.com/agent3bood/tree-sitter-dart", rev = "48934e3bf757a9b78f17bdfaa3e2b4284656fdc7" } diff --git a/crates/extension/src/wasm_host/wit/since_v0_0_4.rs b/crates/extension/src/wasm_host/wit/since_v0_0_4.rs index 726fda0850..d168681b5f 100644 --- a/crates/extension/src/wasm_host/wit/since_v0_0_4.rs +++ b/crates/extension/src/wasm_host/wit/since_v0_0_4.rs @@ -258,6 +258,8 @@ impl ExtensionImports for WasmState { let unzip_status = std::process::Command::new("unzip") .current_dir(&extension_work_dir) + .arg("-d") + .arg(&destination_path) .arg(&zip_path) .output()? .status; diff --git a/crates/extensions_ui/src/extension_suggest.rs b/crates/extensions_ui/src/extension_suggest.rs index 757a0652d7..281e8c0c6d 100644 --- a/crates/extensions_ui/src/extension_suggest.rs +++ b/crates/extensions_ui/src/extension_suggest.rs @@ -16,6 +16,11 @@ fn suggested_extensions() -> &'static HashMap<&'static str, Arc> { [ ("astro", "astro"), ("beancount", "beancount"), + ("clojure", "bb"), + ("clojure", "clj"), + ("clojure", "cljc"), + ("clojure", "cljs"), + ("clojure", "edn"), ("csharp", "cs"), ("dockerfile", "Dockerfile"), ("elisp", "el"), diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index 5f48a7f1b1..9299c0fd6b 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -38,7 +38,6 @@ task.workspace = true toml.workspace = true tree-sitter-bash.workspace = true tree-sitter-c.workspace = true -tree-sitter-clojure.workspace = true tree-sitter-cpp.workspace = true tree-sitter-css.workspace = true tree-sitter-dart.workspace = true diff --git a/crates/languages/src/clojure.rs b/crates/languages/src/clojure.rs deleted file mode 100644 index 6fae33636c..0000000000 --- a/crates/languages/src/clojure.rs +++ /dev/null @@ -1,141 +0,0 @@ -use anyhow::{anyhow, bail, Context, Result}; -use async_trait::async_trait; -pub use language::*; -use lsp::LanguageServerBinary; -use smol::fs::{self, File}; -use std::{any::Any, env::consts, path::PathBuf}; -use util::{ - fs::remove_matching, - github::{latest_github_release, GitHubLspBinaryVersion}, -}; - -#[derive(Copy, Clone)] -pub struct ClojureLspAdapter; - -#[async_trait(?Send)] -impl super::LspAdapter for ClojureLspAdapter { - fn name(&self) -> LanguageServerName { - LanguageServerName("clojure-lsp".into()) - } - - async fn fetch_latest_server_version( - &self, - delegate: &dyn LspAdapterDelegate, - ) -> Result> { - let release = latest_github_release( - "clojure-lsp/clojure-lsp", - true, - false, - delegate.http_client(), - ) - .await?; - let os = match consts::OS { - "macos" => "macos", - "linux" => "linux", - "windows" => "windows", - other => bail!("Running on unsupported os: {other}"), - }; - let platform = match consts::ARCH { - "x86_64" => "amd64", - "aarch64" => "aarch64", - other => bail!("Running on unsupported platform: {other}"), - }; - let asset_name = format!("clojure-lsp-native-{os}-{platform}.zip"); - let asset = release - .assets - .iter() - .find(|asset| asset.name == asset_name) - .ok_or_else(|| anyhow!("no asset found matching {:?}", asset_name))?; - let version = GitHubLspBinaryVersion { - name: release.tag_name.clone(), - url: asset.browser_download_url.clone(), - }; - Ok(Box::new(version) as Box<_>) - } - - async fn fetch_server_binary( - &self, - version: Box, - container_dir: PathBuf, - delegate: &dyn LspAdapterDelegate, - ) -> Result { - let version = version.downcast::().unwrap(); - let zip_path = container_dir.join(format!("clojure-lsp_{}.zip", version.name)); - let folder_path = container_dir.join("bin"); - let binary_path = folder_path.join("clojure-lsp"); - - if fs::metadata(&binary_path).await.is_err() { - let mut response = delegate - .http_client() - .get(&version.url, Default::default(), true) - .await - .context("error downloading release")?; - let mut file = File::create(&zip_path) - .await - .with_context(|| format!("failed to create file {}", zip_path.display()))?; - if !response.status().is_success() { - return Err(anyhow!( - "download failed with status {}", - response.status().to_string() - ))?; - } - futures::io::copy(response.body_mut(), &mut file).await?; - - fs::create_dir_all(&folder_path) - .await - .with_context(|| format!("failed to create directory {}", folder_path.display()))?; - - let unzip_status = smol::process::Command::new("unzip") - .arg(&zip_path) - .arg("-d") - .arg(&folder_path) - .output() - .await? - .status; - if !unzip_status.success() { - return Err(anyhow!("failed to unzip elixir-ls archive"))?; - } - - remove_matching(&container_dir, |entry| entry != folder_path).await; - } - - Ok(LanguageServerBinary { - path: binary_path, - env: None, - arguments: vec![], - }) - } - - async fn cached_server_binary( - &self, - container_dir: PathBuf, - _: &dyn LspAdapterDelegate, - ) -> Option { - let binary_path = container_dir.join("bin").join("clojure-lsp"); - if binary_path.exists() { - Some(LanguageServerBinary { - path: binary_path, - env: None, - arguments: vec![], - }) - } else { - None - } - } - - async fn installation_test_binary( - &self, - container_dir: PathBuf, - ) -> Option { - let binary_path = container_dir.join("bin").join("clojure-lsp"); - if binary_path.exists() { - Some(LanguageServerBinary { - path: binary_path, - env: None, - arguments: vec!["--version".into()], - }) - } else { - None - } - } -} diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index ebf4738936..8cb5af6c25 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -12,7 +12,6 @@ use crate::{elixir::elixir_task_context, rust::RustContextProvider}; use self::{deno::DenoSettings, elixir::ElixirSettings}; mod c; -mod clojure; mod css; mod dart; mod deno; @@ -58,7 +57,6 @@ pub fn init( languages.register_native_grammars([ ("bash", tree_sitter_bash::language()), ("c", tree_sitter_c::language()), - ("clojure", tree_sitter_clojure::language()), ("cpp", tree_sitter_cpp::language()), ("css", tree_sitter_css::language()), ("elixir", tree_sitter_elixir::language()), @@ -158,7 +156,6 @@ pub fn init( } language!("bash"); language!("c", vec![Arc::new(c::CLspAdapter) as Arc]); - language!("clojure", vec![Arc::new(clojure::ClojureLspAdapter)]); language!("cpp", vec![Arc::new(c::CLspAdapter)]); language!( "css", diff --git a/extensions/clojure/Cargo.toml b/extensions/clojure/Cargo.toml new file mode 100644 index 0000000000..607cf78f98 --- /dev/null +++ b/extensions/clojure/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zed_clojure" +version = "0.0.1" +edition = "2021" +publish = false +license = "Apache-2.0" + +[lints] +workspace = true + +[lib] +path = "src/clojure.rs" +crate-type = ["cdylib"] + +[dependencies] +zed_extension_api = "0.0.4" diff --git a/extensions/clojure/extension.toml b/extensions/clojure/extension.toml new file mode 100644 index 0000000000..c43683f654 --- /dev/null +++ b/extensions/clojure/extension.toml @@ -0,0 +1,15 @@ +id = "clojure" +name = "Clojure" +description = "Clojure support." +version = "0.0.1" +schema_version = 1 +authors = ["Paulo Roberto de Oliveira Castro "] +repository = "https://github.com/zed-industries/zed" + +[language_servers.clojure-lsp] +name = "clojure-lsp" +language = "Clojure" + +[grammars.clojure] +repository = "https://github.com/prcastro/tree-sitter-clojure" +commit = "38b4f8d264248b2fd09575fbce66f7c22e8929d5" diff --git a/crates/languages/src/clojure/brackets.scm b/extensions/clojure/languages/clojure/brackets.scm similarity index 100% rename from crates/languages/src/clojure/brackets.scm rename to extensions/clojure/languages/clojure/brackets.scm diff --git a/crates/languages/src/clojure/config.toml b/extensions/clojure/languages/clojure/config.toml similarity index 100% rename from crates/languages/src/clojure/config.toml rename to extensions/clojure/languages/clojure/config.toml diff --git a/crates/languages/src/clojure/highlights.scm b/extensions/clojure/languages/clojure/highlights.scm similarity index 100% rename from crates/languages/src/clojure/highlights.scm rename to extensions/clojure/languages/clojure/highlights.scm diff --git a/crates/languages/src/clojure/indents.scm b/extensions/clojure/languages/clojure/indents.scm similarity index 100% rename from crates/languages/src/clojure/indents.scm rename to extensions/clojure/languages/clojure/indents.scm diff --git a/crates/languages/src/clojure/outline.scm b/extensions/clojure/languages/clojure/outline.scm similarity index 100% rename from crates/languages/src/clojure/outline.scm rename to extensions/clojure/languages/clojure/outline.scm diff --git a/extensions/clojure/src/clojure.rs b/extensions/clojure/src/clojure.rs new file mode 100644 index 0000000000..93eb69227b --- /dev/null +++ b/extensions/clojure/src/clojure.rs @@ -0,0 +1,110 @@ +use std::fs; +use zed_extension_api::{self as zed, Result}; + +struct ClojureExtension { + cached_binary_path: Option, +} + +impl ClojureExtension { + fn language_server_binary_path( + &mut self, + config: zed::LanguageServerConfig, + worktree: &zed::Worktree, + ) -> Result { + if let Some(path) = &self.cached_binary_path { + if fs::metadata(path).map_or(false, |stat| stat.is_file()) { + return Ok(path.clone()); + } + } + + if let Some(path) = worktree.which("clojure-lsp") { + self.cached_binary_path = Some(path.clone()); + return Ok(path); + } + + zed::set_language_server_installation_status( + &config.name, + &zed::LanguageServerInstallationStatus::CheckingForUpdate, + ); + let release = zed::latest_github_release( + "clojure-lsp/clojure-lsp", + zed::GithubReleaseOptions { + require_assets: true, + pre_release: false, + }, + )?; + + let (platform, arch) = zed::current_platform(); + let asset_name = format!( + "clojure-lsp-native-{os}-{arch}.zip", + os = match platform { + zed::Os::Mac => "macos", + zed::Os::Linux => "linux", + zed::Os::Windows => "windows", + }, + arch = match arch { + zed::Architecture::Aarch64 => "aarch64", + zed::Architecture::X8664 => "amd64", + 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!("clojure-lsp-{}", release.version); + let binary_path = format!("{version_dir}/clojure-lsp"); + + if !fs::metadata(&binary_path).map_or(false, |stat| stat.is_file()) { + zed::set_language_server_installation_status( + &config.name, + &zed::LanguageServerInstallationStatus::Downloading, + ); + + zed::download_file( + &asset.download_url, + &version_dir, + zed::DownloadedFileType::Zip, + ) + .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) + } +} + +impl zed::Extension for ClojureExtension { + fn new() -> Self { + Self { + cached_binary_path: None, + } + } + + fn language_server_command( + &mut self, + config: zed::LanguageServerConfig, + worktree: &zed::Worktree, + ) -> Result { + Ok(zed::Command { + command: self.language_server_binary_path(config, worktree)?, + args: Vec::new(), + env: Default::default(), + }) + } +} + +zed::register_extension!(ClojureExtension); diff --git a/typos.toml b/typos.toml index 66a78307dd..23dd801729 100644 --- a/typos.toml +++ b/typos.toml @@ -7,6 +7,7 @@ extend-exclude = [ "crates/languages/src/glsl/", # File suffixes aren't typos "assets/icons/file_icons/file_types.json", + "crates/extensions_ui/src/extension_suggest.rs", # Not our typos "crates/live_kit_server/", # Vim makes heavy use of partial typing tables @@ -15,7 +16,7 @@ extend-exclude = [ "crates/file_finder/src/file_finder_tests.rs", "crates/editor/src/editor_tests.rs", # Clojure uses .edn filename extension, which is not a misspelling of "end" - "crates/languages/src/clojure/config.toml", + "extensions/clojure/languages/clojure/config.toml", # Windows likes it's abbreviations "crates/gpui/src/platform/windows/", ]