From 482dc483069caee5135156f5d8c05981deef671f Mon Sep 17 00:00:00 2001 From: Albert Marashi Date: Wed, 18 Sep 2024 01:32:25 +0930 Subject: [PATCH] svelte: Migrate to `tree-sitter-grammars/tree-sitter-svelte` (#17529) > [!NOTE] > The https://github.com/tree-sitter-grammars/tree-sitter-svelte repository seems to be more well maintained, with higher quality code, and as per https://github.com/zed-extensions/svelte/issues/1 it was suggested that we swap to this repository for Svelte grammars - Closes https://github.com/zed-industries/zed/issues/17310 - Closes https://github.com/zed-industries/zed/issues/10893 - Closes https://github.com/zed-industries/zed/issues/12833 - Closes https://github.com/zed-extensions/svelte/issues/1 - Closes https://github.com/zed-industries/zed/issues/14943 - Closes https://github.com/zed-extensions/svelte/issues/2 - Added: buffer/file symbol outlines for `.svelte` (`outlines.scm`) - Improved: Attribute directives & modifiers in `.svelte` files can be styled independently. - Fixed: issue where svelte expression inside quotes failed parsing - Improved: Svelte components in Markup are styled differently from tags. - Added: Support for Svelte 5 syntax (`{#snippet children()}`, `{@render foo()`) - Change: Svelte now using [tree-sitter-grammars/tree-sitter-svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) for language highlighting - Added: Support for typescript syntax in svelte expressions ![image](https://github.com/user-attachments/assets/49d199ee-7550-49a7-912d-070cf691b029) ![image](https://github.com/user-attachments/assets/848ac5b6-62da-4c42-8e24-b7023504f8af) Release Notes: - N/A --- **tree-sitter-grammar things to improve** - [ ] snippet functions aren't being treated as JS code - [ ] we should be able to detect @component comments and treat them as markdown - [x] `foo:bar` style/class/prop directives - [x] `--foo="..."` var fields - [ ] snippet/if blocks's children may need to be indented a little further Will implement some of the rest of these in a separate PR --------- Co-authored-by: Marshall Bowers --- docs/src/languages/svelte.md | 23 ++- extensions/svelte/extension.toml | 4 +- .../svelte/languages/svelte/brackets.scm | 7 + .../svelte/languages/svelte/config.toml | 12 +- .../svelte/languages/svelte/highlights.scm | 121 +++++++++++---- .../svelte/languages/svelte/injections.scm | 142 ++++++++++-------- .../svelte/languages/svelte/outline.scm | 69 +++++++++ 7 files changed, 268 insertions(+), 110 deletions(-) create mode 100644 extensions/svelte/languages/svelte/brackets.scm create mode 100644 extensions/svelte/languages/svelte/outline.scm diff --git a/docs/src/languages/svelte.md b/docs/src/languages/svelte.md index 1c6fd49b3e25ff..157a57d43e1b8f 100644 --- a/docs/src/languages/svelte.md +++ b/docs/src/languages/svelte.md @@ -2,16 +2,29 @@ Svelte support is available through the [Svelte extension](https://github.com/zed-industries/zed/tree/main/extensions/svelte). -- Tree Sitter: [Himujjal/tree-sitter-svelte](https://github.com/Himujjal/tree-sitter-svelte) +- Tree Sitter: [tree-sitter-grammars/tree-sitter-svelte](https://github.com/tree-sitter-grammars/tree-sitter-svelte) - Language Server: [sveltejs/language-tools](https://github.com/sveltejs/language-tools) - +## Extra theme styling configuration + +You can modify how certain styles such as directives and modifiers appear in attributes: + +```json +"syntax": { + // Styling for directives (e.g., `class:foo` or `on:click`) (the `on` or `class` part of the attribute). + "attribute.function": { + "color": "#ff0000" + }, + // Styling for modifiers at the end of attributes, e.g. `on:` + "attribute.special": { + "color": "#00ff00" + } +} +``` ## Inlay Hints -Zed sets the following initialization options for inlay Hints: +Zed sets the following initialization options for inlay hints: ```json "inlayHints": { diff --git a/extensions/svelte/extension.toml b/extensions/svelte/extension.toml index 9ca1d6c5daafdb..694fdec2a67c56 100644 --- a/extensions/svelte/extension.toml +++ b/extensions/svelte/extension.toml @@ -11,5 +11,5 @@ name = "Svelte Language Server" language = "Svelte" [grammars.svelte] -repository = "https://github.com/Himujjal/tree-sitter-svelte" -commit = "b08d070e303d2a385d6d0ab3add500f8fa514443" +repository = "https://github.com/tree-sitter-grammars/tree-sitter-svelte" +commit = "3f06f705410683adb17d146b5eca28c62fe81ba6" diff --git a/extensions/svelte/languages/svelte/brackets.scm b/extensions/svelte/languages/svelte/brackets.scm new file mode 100644 index 00000000000000..deb34f80a806ff --- /dev/null +++ b/extensions/svelte/languages/svelte/brackets.scm @@ -0,0 +1,7 @@ +("<" @open ">" @close) +("{" @open "}" @close) +("'" @open "'" @close) +("\"" @open "\"" @close) +("(" @open ")" @close) +; ("[" @open "]" @close) +; ("`" @open "`" @close) diff --git a/extensions/svelte/languages/svelte/config.toml b/extensions/svelte/languages/svelte/config.toml index 3bab2f29430813..4db968be8c8f64 100644 --- a/extensions/svelte/languages/svelte/config.toml +++ b/extensions/svelte/languages/svelte/config.toml @@ -2,16 +2,16 @@ name = "Svelte" grammar = "svelte" path_suffixes = ["svelte"] block_comment = [""] -autoclose_before = ";:.,=}])>" +autoclose_before = ":\"'}]>" brackets = [ { start = "{", end = "}", close = true, newline = true }, + { start = "<", end = ">", close = true, newline = true, not_in = ["string"] }, { start = "[", end = "]", close = true, newline = true }, { start = "(", end = ")", close = true, newline = true }, - { start = "<", end = ">", close = false, newline = true, not_in = ["string", "comment"] }, - { start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, - { start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, - { start = "`", end = "`", close = true, newline = false, not_in = ["string"] }, - { start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] }, + { start = "!--", end = " --", close = true, newline = true }, + { start = "\"", end = "\"", close = true, newline = true, not_in = ["string"] }, + { start = "'", end = "'", close = true, newline = true, not_in = ["string"] }, + { start = "`", end = "`", close = true, newline = true, not_in = ["string"] }, ] scope_opt_in_language_servers = ["tailwindcss-language-server"] prettier_parser_name = "svelte" diff --git a/extensions/svelte/languages/svelte/highlights.scm b/extensions/svelte/languages/svelte/highlights.scm index 4e317489d832fa..01d8bb8db6531b 100755 --- a/extensions/svelte/languages/svelte/highlights.scm +++ b/extensions/svelte/languages/svelte/highlights.scm @@ -1,50 +1,107 @@ -; Special identifiers -;-------------------- -; Treat capitalized tag names as constructors and types -((tag_name) @type - (#match? @type "^[A-Z]")) +; comments +(comment) @comment -; Regular (lowercase) tag names -((tag_name) @tag - (#match? @tag "^[a-z]")) +; property attribute +(attribute_directive) @attribute.function +(attribute_identifier) @attribute +(attribute_modifier) @attribute.special -; TODO: -(attribute_name) @property -(erroneous_end_tag_name) @keyword -(comment) @comment +; Style component attributes as @property +(start_tag + ( + (tag_name) @_tag_name + (#match? @_tag_name "^[A-Z]") + ) + (attribute + (attribute_name + (attribute_identifier) @tag.property + ) + ) +) -[ - (attribute_value) - (quoted_attribute_value) -] @string +(self_closing_tag + ( + (tag_name) @_tag_name + (#match? @_tag_name "^[A-Z]") + ) + (attribute + (attribute_name + (attribute_identifier) @tag.property + ) + ) +) -[ - (text) - (raw_text_expr) - (raw_text_each) -] @none + +; style elements starting with lowercase letters as tags +( + (tag_name) @tag + (#match? @tag "^[a-z]") +) + +; style elements starting with uppercase letters as components (types) +; Also valid might be to treat them as constructors +( + (tag_name) @tag @tag.component.type.constructor + (#match? @tag "^[A-Z]") +) [ - (special_block_keyword) - (then) - (as) -] @keyword + "<" + ">" + "" +] @tag.punctuation.bracket + [ "{" "}" ] @punctuation.bracket -"=" @operator +[ + "|" +] @punctuation.delimiter + [ - "<" - ">" - "" + "@" "#" ":" "/" - "@" -] @tag.delimiter +] @tag.punctuation.special + +"=" @operator + + +; Treating (if, each, ...) as a keyword inside of blocks +; like {#if ...} or {#each ...} +(block_start_tag + tag: _ @tag.keyword +) + +(block_tag + tag: _ @tag.keyword +) + +(block_end_tag + tag: _ @tag.keyword +) + +(expression_tag + tag: _ @tag.keyword +) + +; Style quoted string attribute values +(quoted_attribute_value) @string + + +; Highlight the `as` keyword in each blocks +(each_start + ("as") @tag.keyword +) + + +; Highlight the snippet name as a function +; (e.g. {#snippet foo(bar)} +(snippet_name) @function diff --git a/extensions/svelte/languages/svelte/injections.scm b/extensions/svelte/languages/svelte/injections.scm index 24f9425803e2d4..73d2b9abb1bb6d 100755 --- a/extensions/svelte/languages/svelte/injections.scm +++ b/extensions/svelte/languages/svelte/injections.scm @@ -1,74 +1,86 @@ -; injections.scm -; -------------- +; ; injections.scm +; ; -------------- -; match script tags without a lang tag -((script_element - (start_tag - (attribute - (attribute_name) @_name)*) - (raw_text) @content) - (#not-eq? @_name "lang") - (#set! "language" "javascript")) +; Match script tags with a lang attribute +(script_element + (start_tag + (attribute + (attribute_name) @_attr_name + (#eq? @_attr_name "lang") + (quoted_attribute_value + (attribute_value) @language + ) + ) + ) + (raw_text) @content +) -; match javascript -((script_element - (start_tag - (attribute - (attribute_name) @_name - (quoted_attribute_value (attribute_value) @_value))) - (raw_text) @content) - (#eq? @_name "lang") - (#eq? @_value "js") - (#set! "language" "javascript")) +; Match script tags without a lang attribute +(script_element + (start_tag + (attribute + (attribute_name) @_attr_name + )* + ) + (raw_text) @content + (#not-any-of? @_attr_name "lang") + (#set! language "javascript") +) -; match typescript -((script_element - (start_tag - (attribute - (attribute_name) @_name - (quoted_attribute_value (attribute_value) @_value))) - (raw_text) @content) - (#eq? @_name "lang") - (#eq? @_value "ts") - (#set! "language" "typescript")) +; Match the contents of the script's generics="T extends string" as typescript code +; +; Disabled for the time-being because tree-sitter is treating the generics +; attribute as a top-level typescript statement, where `T extends string` is +; not a valid top-level typescript statement. +; +; (script_element +; (start_tag +; (attribute +; (attribute_name) @_attr_name +; (#eq? @_attr_name "generics") +; (quoted_attribute_value +; (attribute_value) @content +; ) +; ) +; ) +; (#set! language "typescript") +; ) -(style_element - (raw_text) @content - (#set! "language" "css")) -; match style tags without a lang tag -((style_element - (start_tag - (attribute - (attribute_name) @_name)*) - (raw_text) @content) - (#not-eq? @_name "lang") - (#set! "language" "css")) +; Mark everything as typescript because it's +; a more generic superset of javascript +; Not sure if it's possible to somehow refer to the +; script's language attribute here. +((svelte_raw_text) @content + (#set! "language" "ts") +) -; match css -((style_element - (start_tag - (attribute - (attribute_name) @_name - (quoted_attribute_value (attribute_value) @_value))) - (raw_text) @content) - (#eq? @_name "lang") - (#eq? @_value "css") - (#set! "language" "css")) +; Match style tags with a lang attribute +(style_element + (start_tag + (attribute + (attribute_name) @_attr_name + (#eq? @_attr_name "lang") + (quoted_attribute_value + (attribute_value) @language + ) + ) + ) + (raw_text) @content +) -; match scss -((style_element - (start_tag - (attribute - (attribute_name) @_name - (quoted_attribute_value (attribute_value) @_value))) - (raw_text) @content) - (#eq? @_name "lang") - (#eq? @_value "scss") - (#set! "language" "scss")) +; Match style tags without a lang attribute +(style_element + (start_tag + (attribute + (attribute_name) @_attr_name + )* + ) + (raw_text) @content + (#not-any-of? @_attr_name "lang") + (#set! language "css") +) -((raw_text_expr) @content - (#set! "language" "javascript")) -((raw_text_each) @content - (#set! "language" "javascript")) +; Downstream TODO: Style highlighting for `style:background="red"` and `style="background: red"` strings +; Downstream TODO: Style component comments as markdown diff --git a/extensions/svelte/languages/svelte/outline.scm b/extensions/svelte/languages/svelte/outline.scm new file mode 100644 index 00000000000000..8242ada2433de2 --- /dev/null +++ b/extensions/svelte/languages/svelte/outline.scm @@ -0,0 +1,69 @@ + +(script_element + (start_tag) @name + (raw_text) @context @item +) + +(script_element + (end_tag) @name @item +) + +(style_element + (start_tag) @name + (raw_text) @context +) @item + + +(document) @item + +(comment) @annotation + +(if_statement + (if_start) @name +) @item + +(else_block + (else_start) @name +) @item + +(else_if_block + (else_if_start) @name +) @item + +(element + (start_tag) @name +) @item + +(element + (self_closing_tag) @name +) @item + + +; (if_end) @name @item + +(each_statement + (each_start) @name +) @item + + +(snippet_statement + (snippet_start) @name +) @item + +(snippet_end) @name @item + +(html_tag) @name @item + +(const_tag) @name @item + +(await_statement + (await_start) @name +) @item + +(then_block + (then_start) @name +) @item + +(catch_block + (catch_start) @name +) @item