diff --git a/Cargo.lock b/Cargo.lock index df97ab9c51..4636404dcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7423,6 +7423,15 @@ dependencies = [ "tree-sitter", ] +[[package]] +name = "tree-sitter-heex" +version = "0.0.1" +source = "git+https://github.com/phoenixframework/tree-sitter-heex?rev=2e1348c3cf2c9323e87c2744796cf3f3868aa82a#2e1348c3cf2c9323e87c2744796cf3f3868aa82a" +dependencies = [ + "cc", + "tree-sitter", +] + [[package]] name = "tree-sitter-html" version = "0.19.0" @@ -8876,6 +8885,7 @@ dependencies = [ "tree-sitter-elixir", "tree-sitter-embedded-template", "tree-sitter-go", + "tree-sitter-heex", "tree-sitter-html", "tree-sitter-json 0.20.0", "tree-sitter-lua", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 2a7274990b..c30c30f17d 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -109,6 +109,7 @@ tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "4ba9dab6e2602960d95b2b625f3386c27e08084e" } tree-sitter-embedded-template = "0.20.0" tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" } +tree-sitter-heex = { git = "https://github.com/phoenixframework/tree-sitter-heex", rev = "2e1348c3cf2c9323e87c2744796cf3f3868aa82a" } tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" } tree-sitter-rust = "0.20.3" tree-sitter-markdown = { git = "https://github.com/MDeiml/tree-sitter-markdown", rev = "330ecab87a3e3a7211ac69bbadc19eabecdb1cca" } diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index a27bc781b9..44e144e89b 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -34,110 +34,109 @@ mod yaml; struct LanguageDir; pub fn init(languages: Arc, node_runtime: Arc) { - fn adapter_arc(adapter: impl LspAdapter) -> Arc { - Arc::new(adapter) - } + let language = |name, grammar, adapters| { + languages.register(name, load_config(name), grammar, adapters, load_queries) + }; - let languages_list = [ - ( - "c", - tree_sitter_c::language(), - vec![adapter_arc(c::CLspAdapter)], - ), - ( - "cpp", - tree_sitter_cpp::language(), - vec![adapter_arc(c::CLspAdapter)], - ), - ("css", tree_sitter_css::language(), vec![]), - ( - "elixir", - tree_sitter_elixir::language(), - vec![adapter_arc(elixir::ElixirLspAdapter)], - ), - ( - "go", - tree_sitter_go::language(), - vec![adapter_arc(go::GoLspAdapter)], - ), - ( - "json", - tree_sitter_json::language(), - vec![adapter_arc(json::JsonLspAdapter::new( - node_runtime.clone(), - languages.clone(), - ))], - ), - ("markdown", tree_sitter_markdown::language(), vec![]), - ( - "python", - tree_sitter_python::language(), - vec![adapter_arc(python::PythonLspAdapter::new( - node_runtime.clone(), - ))], - ), - ( - "rust", - tree_sitter_rust::language(), - vec![adapter_arc(rust::RustLspAdapter)], - ), - ("toml", tree_sitter_toml::language(), vec![]), - ( - "tsx", - tree_sitter_typescript::language_tsx(), - vec![ - adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), - adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())), - ], - ), - ( - "typescript", - tree_sitter_typescript::language_typescript(), - vec![ - adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), - adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())), - ], - ), - ( - "javascript", - tree_sitter_typescript::language_tsx(), - vec![ - adapter_arc(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), - adapter_arc(typescript::EsLintLspAdapter::new(node_runtime.clone())), - ], - ), - ( - "html", - tree_sitter_html::language(), - vec![adapter_arc(html::HtmlLspAdapter::new(node_runtime.clone()))], - ), - ( - "ruby", - tree_sitter_ruby::language(), - vec![adapter_arc(ruby::RubyLanguageServer)], - ), - ( - "erb", - tree_sitter_embedded_template::language(), - vec![adapter_arc(ruby::RubyLanguageServer)], - ), - ("scheme", tree_sitter_scheme::language(), vec![]), - ("racket", tree_sitter_racket::language(), vec![]), - ( - "lua", - tree_sitter_lua::language(), - vec![adapter_arc(lua::LuaLspAdapter)], - ), - ( - "yaml", - tree_sitter_yaml::language(), - vec![adapter_arc(yaml::YamlLspAdapter::new(node_runtime))], - ), - ]; - - for (name, grammar, lsp_adapters) in languages_list { - languages.register(name, load_config(name), grammar, lsp_adapters, load_queries); - } + language( + "c", + tree_sitter_c::language(), + vec![Arc::new(c::CLspAdapter) as Arc], + ); + language( + "cpp", + tree_sitter_cpp::language(), + vec![Arc::new(c::CLspAdapter)], + ); + language("css", tree_sitter_css::language(), vec![]); + language( + "elixir", + tree_sitter_elixir::language(), + vec![Arc::new(elixir::ElixirLspAdapter)], + ); + language( + "go", + tree_sitter_go::language(), + vec![Arc::new(go::GoLspAdapter)], + ); + language( + "heex", + tree_sitter_heex::language(), + vec![Arc::new(elixir::ElixirLspAdapter)], + ); + language( + "json", + tree_sitter_json::language(), + vec![Arc::new(json::JsonLspAdapter::new( + node_runtime.clone(), + languages.clone(), + ))], + ); + language("markdown", tree_sitter_markdown::language(), vec![]); + language( + "python", + tree_sitter_python::language(), + vec![Arc::new(python::PythonLspAdapter::new( + node_runtime.clone(), + ))], + ); + language( + "rust", + tree_sitter_rust::language(), + vec![Arc::new(rust::RustLspAdapter)], + ); + language("toml", tree_sitter_toml::language(), vec![]); + language( + "tsx", + tree_sitter_typescript::language_tsx(), + vec![ + Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), + Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + ], + ); + language( + "typescript", + tree_sitter_typescript::language_typescript(), + vec![ + Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), + Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + ], + ); + language( + "javascript", + tree_sitter_typescript::language_tsx(), + vec![ + Arc::new(typescript::TypeScriptLspAdapter::new(node_runtime.clone())), + Arc::new(typescript::EsLintLspAdapter::new(node_runtime.clone())), + ], + ); + language( + "html", + tree_sitter_html::language(), + vec![Arc::new(html::HtmlLspAdapter::new(node_runtime.clone()))], + ); + language( + "ruby", + tree_sitter_ruby::language(), + vec![Arc::new(ruby::RubyLanguageServer)], + ); + language( + "erb", + tree_sitter_embedded_template::language(), + vec![Arc::new(ruby::RubyLanguageServer)], + ); + language("scheme", tree_sitter_scheme::language(), vec![]); + language("racket", tree_sitter_racket::language(), vec![]); + language( + "lua", + tree_sitter_lua::language(), + vec![Arc::new(lua::LuaLspAdapter)], + ); + language( + "yaml", + tree_sitter_yaml::language(), + vec![Arc::new(yaml::YamlLspAdapter::new(node_runtime))], + ); } #[cfg(any(test, feature = "test-support"))] diff --git a/crates/zed/src/languages/elixir/injections.scm b/crates/zed/src/languages/elixir/injections.scm new file mode 100644 index 0000000000..4de229f104 --- /dev/null +++ b/crates/zed/src/languages/elixir/injections.scm @@ -0,0 +1,7 @@ +; Phoenix HTML template + +((sigil + (sigil_name) @_sigil_name + (quoted_content) @content) + (#eq? @_sigil_name "H") + (#set! language "heex")) diff --git a/crates/zed/src/languages/erb/highlights.scm b/crates/zed/src/languages/erb/highlights.scm index 91b21d081f..0bf76a7d49 100644 --- a/crates/zed/src/languages/erb/highlights.scm +++ b/crates/zed/src/languages/erb/highlights.scm @@ -9,4 +9,4 @@ "%>" "-%>" "_%>" -] @keyword \ No newline at end of file +] @keyword diff --git a/crates/zed/src/languages/heex/config.toml b/crates/zed/src/languages/heex/config.toml new file mode 100644 index 0000000000..fafd75dc8d --- /dev/null +++ b/crates/zed/src/languages/heex/config.toml @@ -0,0 +1,7 @@ +name = "HEEX" +path_suffixes = ["heex"] +autoclose_before = ">})" +brackets = [ + { start = "<", end = ">", close = true, newline = true }, +] +block_comment = ["<%#", "%>"] diff --git a/crates/zed/src/languages/heex/highlights.scm b/crates/zed/src/languages/heex/highlights.scm new file mode 100644 index 0000000000..fa88acd4d9 --- /dev/null +++ b/crates/zed/src/languages/heex/highlights.scm @@ -0,0 +1,54 @@ +; HEEx delimiters +[ + "%>" + "--%>" + "-->" + "/>" + "" + "{" + "}" +] @punctuation.bracket + +; HEEx operators are highlighted as such +"=" @operator + +; HEEx inherits the DOCTYPE tag from HTML +(doctype) @constant + +(comment) @comment + +; HEEx tags and slots are highlighted as HTML +[ + (tag_name) + (slot_name) +] @tag + +; HEEx attributes are highlighted as HTML attributes +(attribute_name) @attribute + +; HEEx special attributes are highlighted as keywords +(special_attribute_name) @keyword + +[ + (attribute_value) + (quoted_attribute_value) +] @string + +; HEEx components are highlighted as Elixir modules and functions +(component_name + [ + (module) @module + (function) @function + "." @punctuation.delimiter + ]) diff --git a/crates/zed/src/languages/heex/injections.scm b/crates/zed/src/languages/heex/injections.scm new file mode 100644 index 0000000000..0d4977b28a --- /dev/null +++ b/crates/zed/src/languages/heex/injections.scm @@ -0,0 +1,13 @@ +((directive (partial_expression_value) @content) + (#set! language "elixir") + (#set! include-children) + (#set! combined)) + +; Regular expression_values do not need to be combined +((directive (expression_value) @content) + (#set! language "elixir")) + +; expressions live within HTML tags, and do not need to be combined +; +((expression (expression_value) @content) + (#set! language "elixir"))