diff --git a/Cargo.lock b/Cargo.lock index 3a1b2e1edf..8a1f8c161c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5429,7 +5429,6 @@ dependencies = [ "tree-sitter-cpp", "tree-sitter-css", "tree-sitter-elixir", - "tree-sitter-elm", "tree-sitter-embedded-template", "tree-sitter-glsl", "tree-sitter-go", @@ -10343,15 +10342,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-elm" -version = "5.6.4" -source = "git+https://github.com/elm-tooling/tree-sitter-elm?rev=692c50c0b961364c40299e73c1306aecb5d20f40#692c50c0b961364c40299e73c1306aecb5d20f40" -dependencies = [ - "cc", - "tree-sitter", -] - [[package]] name = "tree-sitter-embedded-template" version = "0.20.0" @@ -12587,6 +12577,13 @@ dependencies = [ "zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "zed_elm" +version = "0.0.1" +dependencies = [ + "zed_extension_api 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "zed_emmet" version = "0.0.2" diff --git a/Cargo.toml b/Cargo.toml index 368071f608..9d9e629aa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -104,6 +104,7 @@ members = [ "extensions/clojure", "extensions/csharp", "extensions/dart", + "extensions/elm", "extensions/emmet", "extensions/erlang", "extensions/gleam", @@ -312,7 +313,6 @@ tree-sitter-c = "0.20.1" 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-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "a2861e88a730287a60c11ea9299c033c7d076e30" } -tree-sitter-elm = { git = "https://github.com/elm-tooling/tree-sitter-elm", rev = "692c50c0b961364c40299e73c1306aecb5d20f40" } tree-sitter-embedded-template = "0.20.0" tree-sitter-glsl = { git = "https://github.com/theHamsta/tree-sitter-glsl", rev = "2a56fb7bc8bb03a1892b4741279dd0a8758b7fb3" } tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } diff --git a/crates/extensions_ui/src/extension_suggest.rs b/crates/extensions_ui/src/extension_suggest.rs index f2d4ab14e4..65175456de 100644 --- a/crates/extensions_ui/src/extension_suggest.rs +++ b/crates/extensions_ui/src/extension_suggest.rs @@ -26,6 +26,7 @@ fn suggested_extensions() -> &'static HashMap<&'static str, Arc> { ("dart", "dart"), ("dockerfile", "Dockerfile"), ("elisp", "el"), + ("elm", "elm"), ("erlang", "erl"), ("erlang", "hrl"), ("fish", "fish"), diff --git a/crates/languages/Cargo.toml b/crates/languages/Cargo.toml index e8322a1a87..52cbe8fd68 100644 --- a/crates/languages/Cargo.toml +++ b/crates/languages/Cargo.toml @@ -41,7 +41,6 @@ tree-sitter-c.workspace = true tree-sitter-cpp.workspace = true tree-sitter-css.workspace = true tree-sitter-elixir.workspace = true -tree-sitter-elm.workspace = true tree-sitter-embedded-template.workspace = true tree-sitter-glsl.workspace = true tree-sitter-go.workspace = true diff --git a/crates/languages/src/lib.rs b/crates/languages/src/lib.rs index cd80d5122f..f03c30c951 100644 --- a/crates/languages/src/lib.rs +++ b/crates/languages/src/lib.rs @@ -16,7 +16,6 @@ mod c; mod css; mod deno; mod elixir; -mod elm; mod go; mod json; mod lua; @@ -59,7 +58,6 @@ pub fn init( ("cpp", tree_sitter_cpp::language()), ("css", tree_sitter_css::language()), ("elixir", tree_sitter_elixir::language()), - ("elm", tree_sitter_elm::language()), ( "embedded_template", tree_sitter_embedded_template::language(), @@ -288,10 +286,6 @@ pub fn init( "yaml", vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime.clone()))] ); - language!( - "elm", - vec![Arc::new(elm::ElmLspAdapter::new(node_runtime.clone()))] - ); language!("glsl"); language!("nix"); language!("nu", vec![Arc::new(nu::NuLanguageServer {})]); diff --git a/extensions/elm/Cargo.toml b/extensions/elm/Cargo.toml new file mode 100644 index 0000000000..8d702c2790 --- /dev/null +++ b/extensions/elm/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "zed_elm" +version = "0.0.1" +edition = "2021" +publish = false +license = "Apache-2.0" + +[lints] +workspace = true + +[lib] +path = "src/elm.rs" +crate-type = ["cdylib"] + +[dependencies] +zed_extension_api = "0.0.6" diff --git a/extensions/elm/LICENSE-APACHE b/extensions/elm/LICENSE-APACHE new file mode 120000 index 0000000000..1cd601d0a3 --- /dev/null +++ b/extensions/elm/LICENSE-APACHE @@ -0,0 +1 @@ +../../LICENSE-APACHE \ No newline at end of file diff --git a/extensions/elm/extension.toml b/extensions/elm/extension.toml new file mode 100644 index 0000000000..0c77cf6478 --- /dev/null +++ b/extensions/elm/extension.toml @@ -0,0 +1,15 @@ +id = "elm" +name = "Elm" +description = "Elm support." +version = "0.0.1" +schema_version = 1 +authors = ["Quinn Wilton ", "Andrey Kuzmin "] +repository = "https://github.com/zed-industries/zed" + +[language_servers.elm-language-server] +name = "elm-language-server" +language = "Elm" + +[grammars.elm] +repository = "https://github.com/elm-tooling/tree-sitter-elm" +commit = "09dbf221d7491dc8d8839616b27c21b9c025c457" diff --git a/crates/languages/src/elm/config.toml b/extensions/elm/languages/elm/config.toml similarity index 97% rename from crates/languages/src/elm/config.toml rename to extensions/elm/languages/elm/config.toml index 96fb2989ad..a142035cc0 100644 --- a/crates/languages/src/elm/config.toml +++ b/extensions/elm/languages/elm/config.toml @@ -10,3 +10,4 @@ brackets = [ { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, ] +tab_size = 2 diff --git a/crates/languages/src/elm/highlights.scm b/extensions/elm/languages/elm/highlights.scm similarity index 100% rename from crates/languages/src/elm/highlights.scm rename to extensions/elm/languages/elm/highlights.scm diff --git a/crates/languages/src/elm/injections.scm b/extensions/elm/languages/elm/injections.scm similarity index 100% rename from crates/languages/src/elm/injections.scm rename to extensions/elm/languages/elm/injections.scm diff --git a/crates/languages/src/elm/outline.scm b/extensions/elm/languages/elm/outline.scm similarity index 100% rename from crates/languages/src/elm/outline.scm rename to extensions/elm/languages/elm/outline.scm diff --git a/extensions/elm/src/elm.rs b/extensions/elm/src/elm.rs new file mode 100644 index 0000000000..9f8899bfe2 --- /dev/null +++ b/extensions/elm/src/elm.rs @@ -0,0 +1,113 @@ +use std::{env, fs}; +use zed::{ + serde_json::{self, Value}, + settings::LspSettings, +}; +use zed_extension_api::{self as zed, Result}; + +const SERVER_PATH: &str = "node_modules/@elm-tooling/elm-language-server/out/node/index.js"; +const PACKAGE_NAME: &str = "@elm-tooling/elm-language-server"; + +struct ElmExtension { + did_find_server: bool, +} + +impl ElmExtension { + fn server_exists(&self) -> bool { + fs::metadata(SERVER_PATH).map_or(false, |stat| stat.is_file()) + } + + fn server_script_path(&mut self, server_id: &zed::LanguageServerId) -> Result { + let server_exists = self.server_exists(); + if self.did_find_server && server_exists { + return Ok(SERVER_PATH.to_string()); + } + + zed::set_language_server_installation_status( + &server_id, + &zed::LanguageServerInstallationStatus::CheckingForUpdate, + ); + let version = zed::npm_package_latest_version(PACKAGE_NAME)?; + + if !server_exists + || zed::npm_package_installed_version(PACKAGE_NAME)?.as_ref() != Some(&version) + { + zed::set_language_server_installation_status( + &server_id, + &zed::LanguageServerInstallationStatus::Downloading, + ); + let result = zed::npm_install_package(PACKAGE_NAME, &version); + match result { + Ok(()) => { + if !self.server_exists() { + Err(format!( + "installed package '{PACKAGE_NAME}' did not contain expected path '{SERVER_PATH}'", + ))?; + } + } + Err(error) => { + if !self.server_exists() { + Err(error)?; + } + } + } + } + + self.did_find_server = true; + Ok(SERVER_PATH.to_string()) + } +} + +impl zed::Extension for ElmExtension { + fn new() -> Self { + Self { + did_find_server: false, + } + } + + fn language_server_command( + &mut self, + server_id: &zed::LanguageServerId, + _worktree: &zed::Worktree, + ) -> Result { + let server_path = self.server_script_path(server_id)?; + Ok(zed::Command { + command: zed::node_binary_path()?, + args: vec![ + env::current_dir() + .unwrap() + .join(&server_path) + .to_string_lossy() + .to_string(), + "--stdio".to_string(), + ], + env: Default::default(), + }) + } + + fn language_server_workspace_configuration( + &mut self, + server_id: &zed::LanguageServerId, + worktree: &zed::Worktree, + ) -> Result> { + // elm-language-server expects workspace didChangeConfiguration notification + // params to be the same as lsp initialization_options + let initialization_options = LspSettings::for_worktree(server_id.as_ref(), worktree)? + .initialization_options + .clone() + .unwrap_or_default(); + + Ok(Some(match initialization_options.clone().as_object_mut() { + Some(op) => { + // elm-language-server requests workspace configuration + // for the `elmLS` section, so we have to nest + // another copy of initialization_options there + op.insert("elmLS".into(), initialization_options); + serde_json::to_value(op).unwrap_or_default() + } + None => initialization_options, + })) + } +} + +zed::register_extension!(ElmExtension);