on master help sign in
Add language support for sway (#6023)
f69bb41Sophie Dankel
book/src/generated/lang-support.md+1
@@ -125,6 +125,7 @@
125| sshclientconfig | ✓ | | | |
125| sshclientconfig | ✓ | | | |
126| starlark | ✓ | ✓ | | |
126| starlark | ✓ | ✓ | | |
127| svelte | ✓ | | | `svelteserver` |
127| svelte | ✓ | | | `svelteserver` |
128+| sway | ✓ | ✓ | ✓ | `forc` |
128| swift | ✓ | | | `sourcekit-lsp` |
129| swift | ✓ | | | `sourcekit-lsp` |
129| tablegen | ✓ | ✓ | ✓ | |
130| tablegen | ✓ | ✓ | ✓ | |
130| task | ✓ | | | |
131| task | ✓ | | | |
languages.toml+14
@@ -52,6 +52,20 @@ args = { attachCommands = [ "platform select remote-gdb-server", "platform conne
52name = "rust"
52name = "rust"
53source = { git = "https://github.com/tree-sitter/tree-sitter-rust", rev = "0431a2c60828731f27491ee9fdefe25e250ce9c9" }
53source = { git = "https://github.com/tree-sitter/tree-sitter-rust", rev = "0431a2c60828731f27491ee9fdefe25e250ce9c9" }
54
54
55+[[language]]
56+name = "sway"
57+scope = "source.sway"
58+injection-regex = "sway"
59+file-types = ["sw"]
60+language-server = { command = "forc", args = ["lsp"] }
61+roots = ["Forc.toml", "Forc.lock"]
62+indent = { tab-width = 4, unit = " " }
63+comment-token = "//"
64+
65+[[grammar]]
66+name = "sway"
67+source = { git = "https://github.com/FuelLabs/tree-sitter-sway", rev = "e491a005ee1d310f4c138bf215afd44cfebf959c" }
68+
55[[language]]
69[[language]]
56name = "toml"
70name = "toml"
57scope = "source.toml"
71scope = "source.toml"
runtime/queries/sway/highlights.scmnew file+336
@@ -0,0 +1,336 @@
1+; -------
2+; Tree-Sitter doesn't allow overrides in regards to captures,
3+; though it is possible to affect the child node of a captured
4+; node. Thus, the approach here is to flip the order so that
5+; overrides are unnecessary.
6+; -------
7+
8+; -------
9+; Types
10+; -------
11+
12+; ---
13+; Primitives
14+; ---
15+
16+(escape_sequence) @constant.character.escape
17+(primitive_type) @type.builtin
18+(boolean_literal) @constant.builtin.boolean
19+(integer_literal) @constant.numeric.integer
20+(float_literal) @constant.numeric.float
21+(char_literal) @constant.character
22+[
23+ (string_literal)
24+ (raw_string_literal)
25+] @string
26+[
27+ (line_comment)
28+ (block_comment)
29+] @comment
30+
31+; ---
32+; Extraneous
33+; ---
34+
35+(self) @variable.builtin
36+(enum_variant (identifier) @type.enum.variant)
37+
38+(field_initializer
39+ (field_identifier) @variable.other.member)
40+(shorthand_field_initializer
41+ (identifier) @variable.other.member)
42+(shorthand_field_identifier) @variable.other.member
43+
44+(loop_label
45+ "'" @label
46+ (identifier) @label)
47+
48+; ---
49+; Punctuation
50+; ---
51+
52+[
53+ "::"
54+ "."
55+ ";"
56+ ","
57+] @punctuation.delimiter
58+
59+[
60+ "("
61+ ")"
62+ "["
63+ "]"
64+ "{"
65+ "}"
66+ "#"
67+] @punctuation.bracket
68+(type_arguments
69+ [
70+ "<"
71+ ">"
72+ ] @punctuation.bracket)
73+(type_parameters
74+ [
75+ "<"
76+ ">"
77+ ] @punctuation.bracket)
78+(closure_parameters
79+ "|" @punctuation.bracket)
80+
81+; ---
82+; Variables
83+; ---
84+
85+(let_declaration
86+ pattern: [
87+ ((identifier) @variable)
88+ ((tuple_pattern
89+ (identifier) @variable))
90+ ])
91+
92+; It needs to be anonymous to not conflict with `call_expression` further below.
93+(_
94+ value: (field_expression
95+ value: (identifier)? @variable
96+ field: (field_identifier) @variable.other.member))
97+
98+(parameter
99+ pattern: (identifier) @variable.parameter)
100+(closure_parameters
101+ (identifier) @variable.parameter)
102+
103+; -------
104+; Keywords
105+; -------
106+
107+(for_expression
108+ "for" @keyword.control.repeat)
109+((identifier) @keyword.control
110+ (#match? @keyword.control "^yield$"))
111+
112+"in" @keyword.control
113+
114+[
115+ "match"
116+ "if"
117+ "else"
118+] @keyword.control.conditional
119+
120+[
121+ "while"
122+] @keyword.control.repeat
123+
124+[
125+ "break"
126+ "continue"
127+ "return"
128+] @keyword.control.return
129+
130+[
131+ "contract"
132+ "script"
133+ "predicate"
134+] @keyword.other
135+
136+"use" @keyword.control.import
137+(dep_item "dep" @keyword.control.import !body)
138+(use_as_clause "as" @keyword.control.import)
139+
140+(type_cast_expression "as" @keyword.operator)
141+
142+[
143+ "as"
144+ "pub"
145+ "dep"
146+
147+ "abi"
148+ "impl"
149+ "where"
150+ "trait"
151+ "for"
152+] @keyword
153+
154+[
155+ "struct"
156+ "enum"
157+ "storage"
158+ "configurable"
159+] @keyword.storage.type
160+
161+"let" @keyword.storage
162+"fn" @keyword.function
163+"abi" @keyword.function
164+
165+(mutable_specifier) @keyword.storage.modifier.mut
166+
167+(reference_type "&" @keyword.storage.modifier.ref)
168+(self_parameter "&" @keyword.storage.modifier.ref)
169+
170+[
171+ "const"
172+ "ref"
173+ "deref"
174+ "move"
175+] @keyword.storage.modifier
176+
177+; TODO: variable.mut to highlight mutable identifiers via locals.scm
178+
179+; -------
180+; Guess Other Types
181+; -------
182+
183+((identifier) @constant
184+ (#match? @constant "^[A-Z][A-Z\\d_]*$"))
185+
186+; ---
187+; PascalCase identifiers in call_expressions (e.g. `Ok()`)
188+; are assumed to be enum constructors.
189+; ---
190+
191+(call_expression
192+ function: [
193+ ((identifier) @type.enum.variant
194+ (#match? @type.enum.variant "^[A-Z]"))
195+ (scoped_identifier
196+ name: ((identifier) @type.enum.variant
197+ (#match? @type.enum.variant "^[A-Z]")))
198+ ])
199+
200+; ---
201+; Assume that types in match arms are enums and not
202+; tuple structs. Same for `if let` expressions.
203+; ---
204+
205+(match_pattern
206+ (scoped_identifier
207+ name: (identifier) @constructor))
208+(tuple_struct_pattern
209+ type: [
210+ ((identifier) @constructor)
211+ (scoped_identifier
212+ name: (identifier) @constructor)
213+ ])
214+(struct_pattern
215+ type: [
216+ ((type_identifier) @constructor)
217+ (scoped_type_identifier
218+ name: (type_identifier) @constructor)
219+ ])
220+
221+; ---
222+; Other PascalCase identifiers are assumed to be structs.
223+; ---
224+
225+((identifier) @type
226+ (#match? @type "^[A-Z]"))
227+
228+; -------
229+; Functions
230+; -------
231+
232+(call_expression
233+ function: [
234+ ((identifier) @function)
235+ (scoped_identifier
236+ name: (identifier) @function)
237+ (field_expression
238+ field: (field_identifier) @function)
239+ ])
240+(generic_function
241+ function: [
242+ ((identifier) @function)
243+ (scoped_identifier
244+ name: (identifier) @function)
245+ (field_expression
246+ field: (field_identifier) @function.method)
247+ ])
248+
249+(function_item
250+ name: (identifier) @function)
251+
252+(function_signature_item
253+ name: (identifier) @function)
254+
255+; -------
256+; Operators
257+; -------
258+
259+[
260+ "*"
261+ "'"
262+ "->"
263+ "=>"
264+ "<="
265+ "="
266+ "=="
267+ "!"
268+ "!="
269+ "%"
270+ "%="
271+ "&"
272+ "&="
273+ "&&"
274+ "|"
275+ "|="
276+ "||"
277+ "^"
278+ "^="
279+ "*"
280+ "*="
281+ "-"
282+ "-="
283+ "+"
284+ "+="
285+ "/"
286+ "/="
287+ ">"
288+ "<"
289+ ">="
290+ ">>"
291+ "<<"
292+ ">>="
293+ "<<="
294+ "@"
295+ ".."
296+ "..="
297+ "'"
298+] @operator
299+
300+; -------
301+; Paths
302+; -------
303+
304+(use_declaration
305+ argument: (identifier) @namespace)
306+(use_wildcard
307+ (identifier) @namespace)
308+(dep_item
309+ name: (identifier) @namespace)
310+(scoped_use_list
311+ path: (identifier)? @namespace)
312+(use_list
313+ (identifier) @namespace)
314+(use_as_clause
315+ path: (identifier)? @namespace
316+ alias: (identifier) @namespace)
317+
318+; ---
319+; Remaining Paths
320+; ---
321+
322+(scoped_identifier
323+ path: (identifier)? @namespace
324+ name: (identifier) @namespace)
325+(scoped_type_identifier
326+ path: (identifier) @namespace)
327+
328+; -------
329+; Remaining Identifiers
330+; -------
331+
332+"?" @special
333+
334+(type_identifier) @type
335+(identifier) @variable
336+(field_identifier) @variable.other.member
runtime/queries/sway/indents.scmnew file+71
@@ -0,0 +1,71 @@
1+[
2+ (use_list)
3+ (block)
4+ (match_block)
5+ (arguments)
6+ (parameters)
7+ (declaration_list)
8+ (field_declaration_list)
9+ (field_initializer_list)
10+ (struct_pattern)
11+ (tuple_pattern)
12+ (unit_expression)
13+ (enum_variant_list)
14+ (call_expression)
15+ (binary_expression)
16+ (field_expression)
17+ (tuple_expression)
18+ (array_expression)
19+ (where_clause)
20+
21+ (token_tree)
22+] @indent
23+
24+[
25+ "}"
26+ "]"
27+ ")"
28+] @outdent
29+
30+; Indent the right side of assignments.
31+; The #not-same-line? predicate is required to prevent an extra indent for e.g.
32+; an else-clause where the previous if-clause starts on the same line as the assignment.
33+(assignment_expression
34+ .
35+ (_) @expr-start
36+ right: (_) @indent
37+ (#not-same-line? @indent @expr-start)
38+ (#set! "scope" "all")
39+)
40+(compound_assignment_expr
41+ .
42+ (_) @expr-start
43+ right: (_) @indent
44+ (#not-same-line? @indent @expr-start)
45+ (#set! "scope" "all")
46+)
47+(let_declaration
48+ .
49+ (_) @expr-start
50+ value: (_) @indent
51+ alternative: (_)? @indent
52+ (#not-same-line? @indent @expr-start)
53+ (#set! "scope" "all")
54+)
55+(if_expression
56+ .
57+ (_) @expr-start
58+ condition: (_) @indent
59+ (#not-same-line? @indent @expr-start)
60+ (#set! "scope" "all")
61+)
62+
63+; Some field expressions where the left part is a multiline expression are not
64+; indented by cargo fmt.
65+; Because this multiline expression might be nested in an arbitrary number of
66+; field expressions, this can only be matched using a Regex.
67+(field_expression
68+ value: (_) @val
69+ "." @outdent
70+ (#match? @val "(\\A[^\\n\\r]+\\([\\t ]*(\\n|\\r).*)|(\\A[^\\n\\r]*\\{[\\t ]*(\\n|\\r))")
71+)
runtime/queries/sway/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+([(line_comment) (block_comment)] @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/sway/locals.scmnew file+17
@@ -0,0 +1,17 @@
1+; Scopes
2+
3+[
4+ (function_item)
5+ (closure_expression)
6+ (block)
7+] @local.scope
8+
9+; Definitions
10+
11+(parameter
12+ (identifier) @local.definition)
13+
14+(closure_parameters (identifier) @local.definition)
15+
16+; References
17+(identifier) @local.reference
runtime/queries/sway/textobjects.scmnew file+52
@@ -0,0 +1,52 @@
1+(function_item
2+ body: (_) @function.inside) @function.around(closure_expression body: (_) @function.inside) @function.around
3+
4+(struct_item
5+ body: (_) @class.inside) @class.around
6+
7+(enum_item
8+ body: (_) @class.inside) @class.around
9+
10+(trait_item
11+ body: (_) @class.inside) @class.around
12+
13+(impl_item
14+ body: (_) @class.inside) @class.around
15+
16+(parameters
17+ ((_) @parameter.inside . ","? @parameter.around) @parameter.around)
18+
19+(type_parameters
20+ ((_) @parameter.inside . ","? @parameter.around) @parameter.around)
21+
22+(type_arguments
23+ ((_) @parameter.inside . ","? @parameter.around) @parameter.around)
24+
25+(closure_parameters
26+ ((_) @parameter.inside . ","? @parameter.around) @parameter.around)
27+
28+(arguments
29+ ((_) @parameter.inside . ","? @parameter.around) @parameter.around)
30+
31+[
32+ (line_comment)
33+ (block_comment)
34+] @comment.inside
35+
36+(line_comment)+ @comment.around
37+
38+(block_comment) @comment.around
39+
40+(; #[test]
41+ (attribute_item
42+ (attribute
43+ (identifier) @_test_attribute))
44+ ; allow other attributes like #[should_panic] and comments
45+ [
46+ (attribute_item)
47+ (line_comment)
48+ ]*
49+ ; the test function
50+ (function_item
51+ body: (_) @test.inside) @test.around
52+ (#eq? @_test_attribute "test"))