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 <elliott.codes@gmail.com>
This commit is contained in:
Albert Marashi 2024-09-18 01:32:25 +09:30 committed by GitHub
parent 27f09957c2
commit accff826ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 271 additions and 113 deletions

View file

@ -2,16 +2,29 @@
Svelte support is available through the [Svelte extension](https://github.com/zed-industries/zed/tree/main/extensions/svelte). 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) - Language Server: [sveltejs/language-tools](https://github.com/sveltejs/language-tools)
<!-- ## Extra theme styling configuration
TBD: Rewrite Svelte docs so it doesn't begin with a json block assuming you know what inlayHints are.
--> 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:<click|preventDefault|stopPropagation>`
"attribute.special": {
"color": "#00ff00"
}
}
```
## Inlay Hints ## Inlay Hints
Zed sets the following initialization options for inlay Hints: Zed sets the following initialization options for inlay hints:
```json ```json
"inlayHints": { "inlayHints": {

View file

@ -11,5 +11,5 @@ name = "Svelte Language Server"
language = "Svelte" language = "Svelte"
[grammars.svelte] [grammars.svelte]
repository = "https://github.com/Himujjal/tree-sitter-svelte" repository = "https://github.com/tree-sitter-grammars/tree-sitter-svelte"
commit = "b08d070e303d2a385d6d0ab3add500f8fa514443" commit = "3f06f705410683adb17d146b5eca28c62fe81ba6"

View file

@ -0,0 +1,7 @@
("<" @open ">" @close)
("{" @open "}" @close)
("'" @open "'" @close)
("\"" @open "\"" @close)
("(" @open ")" @close)
; ("[" @open "]" @close)
; ("`" @open "`" @close)

View file

@ -2,16 +2,16 @@ name = "Svelte"
grammar = "svelte" grammar = "svelte"
path_suffixes = ["svelte"] path_suffixes = ["svelte"]
block_comment = ["<!-- ", " -->"] block_comment = ["<!-- ", " -->"]
autoclose_before = ";:.,=}])>" autoclose_before = ":\"'}]>"
brackets = [ brackets = [
{ start = "{", end = "}", close = true, newline = true }, { 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 = true, newline = true }, { start = "(", end = ")", close = true, newline = true },
{ start = "<", end = ">", close = false, newline = true, not_in = ["string", "comment"] }, { start = "!--", end = " --", close = true, newline = true },
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] }, { start = "\"", end = "\"", close = true, newline = true, not_in = ["string"] },
{ start = "'", end = "'", close = true, newline = false, not_in = ["string", "comment"] }, { start = "'", end = "'", close = true, newline = true, not_in = ["string"] },
{ start = "`", end = "`", close = true, newline = false, not_in = ["string"] }, { start = "`", end = "`", close = true, newline = true, not_in = ["string"] },
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
] ]
scope_opt_in_language_servers = ["tailwindcss-language-server"] scope_opt_in_language_servers = ["tailwindcss-language-server"]
prettier_parser_name = "svelte" prettier_parser_name = "svelte"

View file

@ -1,50 +1,107 @@
; Special identifiers
;--------------------
; Treat capitalized tag names as constructors and types ; comments
((tag_name) @type
(#match? @type "^[A-Z]"))
; Regular (lowercase) tag names
((tag_name) @tag
(#match? @tag "^[a-z]"))
; TODO:
(attribute_name) @property
(erroneous_end_tag_name) @keyword
(comment) @comment (comment) @comment
[ ; property attribute
(attribute_value) (attribute_directive) @attribute.function
(quoted_attribute_value) (attribute_identifier) @attribute
] @string (attribute_modifier) @attribute.special
[ ; Style component attributes as @property
(text) (start_tag
(raw_text_expr) (
(raw_text_each) (tag_name) @_tag_name
] @none (#match? @_tag_name "^[A-Z]")
)
(attribute
(attribute_name
(attribute_identifier) @tag.property
)
)
)
[ (self_closing_tag
(special_block_keyword) (
(then) (tag_name) @_tag_name
(as) (#match? @_tag_name "^[A-Z]")
] @keyword )
(attribute
(attribute_name
(attribute_identifier) @tag.property
)
)
)
[
"{"
"}"
] @punctuation.bracket
"=" @operator ; 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]")
)
[ [
"<" "<"
">" ">"
"</" "</"
"/>" "/>"
] @tag.punctuation.bracket
[
"{"
"}"
] @punctuation.bracket
[
"|"
] @punctuation.delimiter
[
"@"
"#" "#"
":" ":"
"/" "/"
"@" ] @tag.punctuation.special
] @tag.delimiter
"=" @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

View file

@ -1,74 +1,86 @@
; injections.scm ; ; injections.scm
; -------------- ; ; --------------
; match script tags without a lang tag ; Match script tags with a lang attribute
((script_element (script_element
(start_tag (start_tag
(attribute (attribute
(attribute_name) @_name)*) (attribute_name) @_attr_name
(raw_text) @content) (#eq? @_attr_name "lang")
(#not-eq? @_name "lang") (quoted_attribute_value
(#set! "language" "javascript")) (attribute_value) @language
)
)
)
(raw_text) @content
)
; match javascript ; Match script tags without a lang attribute
((script_element (script_element
(start_tag (start_tag
(attribute (attribute
(attribute_name) @_name (attribute_name) @_attr_name
(quoted_attribute_value (attribute_value) @_value))) )*
(raw_text) @content) )
(#eq? @_name "lang") (raw_text) @content
(#eq? @_value "js") (#not-any-of? @_attr_name "lang")
(#set! "language" "javascript")) (#set! language "javascript")
)
; match typescript ; Match the contents of the script's generics="T extends string" as typescript code
((script_element ;
(start_tag ; Disabled for the time-being because tree-sitter is treating the generics
(attribute ; attribute as a top-level typescript statement, where `T extends string` is
(attribute_name) @_name ; not a valid top-level typescript statement.
(quoted_attribute_value (attribute_value) @_value))) ;
(raw_text) @content) ; (script_element
(#eq? @_name "lang") ; (start_tag
(#eq? @_value "ts") ; (attribute
(#set! "language" "typescript")) ; (attribute_name) @_attr_name
; (#eq? @_attr_name "generics")
; (quoted_attribute_value
; (attribute_value) @content
; )
; )
; )
; (#set! language "typescript")
; )
; 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 style tags with a lang attribute
(style_element (style_element
(raw_text) @content (start_tag
(#set! "language" "css")) (attribute
(attribute_name) @_attr_name
(#eq? @_attr_name "lang")
(quoted_attribute_value
(attribute_value) @language
)
)
)
(raw_text) @content
)
; match style tags without a lang tag ; Match style tags without a lang attribute
((style_element (style_element
(start_tag (start_tag
(attribute (attribute
(attribute_name) @_name)*) (attribute_name) @_attr_name
(raw_text) @content) )*
(#not-eq? @_name "lang") )
(#set! "language" "css")) (raw_text) @content
(#not-any-of? @_attr_name "lang")
(#set! language "css")
)
; 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 scss ; Downstream TODO: Style highlighting for `style:background="red"` and `style="background: red"` strings
((style_element ; Downstream TODO: Style component comments as markdown
(start_tag
(attribute
(attribute_name) @_name
(quoted_attribute_value (attribute_value) @_value)))
(raw_text) @content)
(#eq? @_name "lang")
(#eq? @_value "scss")
(#set! "language" "scss"))
((raw_text_expr) @content
(#set! "language" "javascript"))
((raw_text_each) @content
(#set! "language" "javascript"))

View file

@ -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