Skip to content

Commit

Permalink
svelte: Migrate to tree-sitter-grammars/tree-sitter-svelte (#17529)
Browse files Browse the repository at this point in the history
> [!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 zed-extensions/svelte#1 it was
suggested that we swap to this repository for Svelte grammars

- Closes #17310
- Closes #10893
- Closes #12833
- Closes zed-extensions/svelte#1
- Closes #14943
- Closes zed-extensions/svelte#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 <[email protected]>
  • Loading branch information
AlbertMarashi and maxdeviant authored Sep 17, 2024
1 parent 27f0995 commit accff82
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 110 deletions.
23 changes: 18 additions & 5 deletions docs/src/languages/svelte.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

<!--
TBD: Rewrite Svelte docs so it doesn't begin with a json block assuming you know what inlayHints are.
-->
## 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:<click|preventDefault|stopPropagation>`
"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": {
Expand Down
4 changes: 2 additions & 2 deletions extensions/svelte/extension.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
7 changes: 7 additions & 0 deletions extensions/svelte/languages/svelte/brackets.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
("<" @open ">" @close)
("{" @open "}" @close)
("'" @open "'" @close)
("\"" @open "\"" @close)
("(" @open ")" @close)
; ("[" @open "]" @close)
; ("`" @open "`" @close)
12 changes: 6 additions & 6 deletions extensions/svelte/languages/svelte/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
121 changes: 89 additions & 32 deletions extensions/svelte/languages/svelte/highlights.scm
Original file line number Diff line number Diff line change
@@ -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
142 changes: 77 additions & 65 deletions extensions/svelte/languages/svelte/injections.scm
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit accff82

Please sign in to comment.