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

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

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

View file

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

View file

@ -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")
; )
; 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
(raw_text) @content
(#set! "language" "css"))
(start_tag
(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
((style_element
(start_tag
(attribute
(attribute_name) @_name)*)
(raw_text) @content)
(#not-eq? @_name "lang")
(#set! "language" "css"))
; 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")
)
; 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
((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"))
((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

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