tree-sitter-aspx 0.1.3 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/grammar.js +137 -24
- package/package.json +3 -3
- package/prebuilds/linux-x64/tree-sitter-aspx.node +0 -0
- package/prebuilds/win32-x64/tree-sitter-aspx.node +0 -0
- package/queries/highlights.scm +2 -0
- package/queries/injections.scm +37 -8
- package/src/grammar.json +498 -122
- package/src/node-types.json +246 -25
- package/src/parser.c +8385 -3824
- package/src/scanner.c +860 -58
- package/tree-sitter.json +2 -2
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ cargo add tree-sitter-aspx # Rust
|
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
22
|
-
The grammar parses directives, server comments, HTML comments, code nuggets, `<script>` and `<style>` elements, and tags with attributes. It does not validate HTML. Void tags (`br`, `img`, `meta`, and the other HTML void elements) do not swallow the following markup. An end tag closes the
|
|
22
|
+
The grammar parses directives, server comments, HTML comments, code nuggets, `<script>` and `<style>` elements, and tags with attributes. It does not validate HTML. Void tags (`br`, `img`, `meta`, and the other HTML void elements) do not swallow the following markup. End tags that HTML lets a page leave out are implied, as a browser would: an open `li`, `p`, `td`, `tr`, `option`, `dt`, `dd`, or other element with an optional end tag ends at the start tag that closes it (a sibling `li`, a block element after a `p`, and so on), at an ancestor's end tag, or at the end of the file. An end tag that names an element further out closes the elements inside it, and an end tag whose name matches no open element closes nothing and stays in the tree as an `end_tag`, as a browser ignores it. The scanner keeps the names of the innermost 64 open elements; deeper than that, end tags pair with start tags by position. A `<` followed by whitespace starts no tag, so `if a < b` is text, as browsers and ASP.NET read it. `<script>` and `<style>` bodies are raw text, so `<` inside JavaScript or CSS is not a tag, but a nugget or server comment inside a body is still parsed as one. Nuggets and server comments may also stand in for attributes in a tag, as in `<option <%= selected %>>`. `<![CDATA[ ]]>` sections and `<?xml ?>` declarations parse as `cdata_section` and `processing_instruction` nodes whose nuggets are still parsed, and self-closing `<script ... />` and `<style />` tags are `self_closing_element` nodes.
|
|
23
23
|
|
|
24
24
|
The C# and VB.NET inside a page are exposed as `c_sharp_code` and `vb_code` nodes, with injection queries for editors. See [docs/EMBEDDED_CODE.md](docs/EMBEDDED_CODE.md).
|
|
25
25
|
|
package/grammar.js
CHANGED
|
@@ -56,8 +56,9 @@ export default grammar({
|
|
|
56
56
|
|
|
57
57
|
// Reserved names (`col`, `script`, `Language`, `WebHandler`) must not match
|
|
58
58
|
// a prefix of a longer name (`Columns`, `asp:ScriptManager`). Keywords are
|
|
59
|
-
// recognized only against this word token,
|
|
60
|
-
//
|
|
59
|
+
// recognized only against this word token, which directive names use. Other
|
|
60
|
+
// tag names come from the scanner, which reads the whole name and leaves
|
|
61
|
+
// the reserved ones to the grammar.
|
|
61
62
|
word: ($) => $._name,
|
|
62
63
|
|
|
63
64
|
externals: ($) => [
|
|
@@ -71,7 +72,14 @@ export default grammar({
|
|
|
71
72
|
$._script_vb,
|
|
72
73
|
$.style_text,
|
|
73
74
|
$.expression_text,
|
|
74
|
-
$.
|
|
75
|
+
$._handler_csharp,
|
|
76
|
+
$._handler_vb,
|
|
77
|
+
$._start_tag_name,
|
|
78
|
+
$._end_tag_name,
|
|
79
|
+
$._implicit_end_tag,
|
|
80
|
+
$._self_closing_delimiter,
|
|
81
|
+
$._stray_end_tag_name,
|
|
82
|
+
$._script_tag_probe,
|
|
75
83
|
],
|
|
76
84
|
|
|
77
85
|
rules: {
|
|
@@ -86,9 +94,14 @@ export default grammar({
|
|
|
86
94
|
handler_file: ($) =>
|
|
87
95
|
seq(
|
|
88
96
|
$.handler_directive,
|
|
89
|
-
repeat(choice($.
|
|
97
|
+
repeat(choice($.directive, $.server_comment, $.script_element, $.handler_code)),
|
|
90
98
|
),
|
|
91
99
|
|
|
100
|
+
// The code of a handler file, labeled with the directive's language like
|
|
101
|
+
// nugget code so consumers count and inject it the same way.
|
|
102
|
+
handler_code: ($) =>
|
|
103
|
+
choice(alias($._handler_csharp, $.c_sharp_code), alias($._handler_vb, $.vb_code)),
|
|
104
|
+
|
|
92
105
|
_top: ($) => choice($._content, $.end_tag),
|
|
93
106
|
|
|
94
107
|
_content: ($) =>
|
|
@@ -103,33 +116,40 @@ export default grammar({
|
|
|
103
116
|
$.self_closing_element,
|
|
104
117
|
$.element,
|
|
105
118
|
$.doctype,
|
|
119
|
+
$.cdata_section,
|
|
120
|
+
$.processing_instruction,
|
|
121
|
+
alias($.stray_end_tag, $.end_tag),
|
|
106
122
|
$.text,
|
|
107
123
|
),
|
|
108
124
|
|
|
109
|
-
directive: ($) =>
|
|
125
|
+
directive: ($) =>
|
|
126
|
+
seq('<%@', field('name', $.directive_name), repeat($._directive_attribute), '%>'),
|
|
110
127
|
|
|
111
128
|
handler_directive: ($) =>
|
|
112
129
|
seq(
|
|
113
130
|
'<%@',
|
|
114
131
|
field('name', alias(choice($.web_handler_name, $.web_service_name), $.directive_name)),
|
|
115
|
-
repeat($.
|
|
132
|
+
repeat($._directive_attribute),
|
|
116
133
|
'%>',
|
|
117
134
|
),
|
|
118
135
|
|
|
119
136
|
_name: (_) => /[A-Za-z_][A-Za-z0-9_.:-]*/,
|
|
120
137
|
directive_name: ($) => $._name,
|
|
121
|
-
web_handler_name: (_) =>
|
|
122
|
-
web_service_name: (_) =>
|
|
138
|
+
web_handler_name: (_) => reserved('WebHandler'),
|
|
139
|
+
web_service_name: (_) => reserved('WebService'),
|
|
123
140
|
|
|
124
141
|
// `Language` is its own attribute so the value is a language_value node
|
|
125
|
-
// and the scanner can record C# vs VB for later code tokens.
|
|
142
|
+
// and the scanner can record C# vs VB for later code tokens. On an
|
|
143
|
+
// element it is an ordinary attribute; the scanner reads a server
|
|
144
|
+
// script's `language` itself, before the attributes are parsed.
|
|
126
145
|
language_attribute: ($) =>
|
|
127
146
|
seq(
|
|
128
147
|
field('name', alias($.language_name, $.attribute_name)),
|
|
129
148
|
'=',
|
|
130
149
|
choice(
|
|
131
|
-
seq('"', field('value', $._language_value), '"'),
|
|
132
|
-
seq("'", field('value', $._language_value), "'"),
|
|
150
|
+
seq('"', optional(field('value', $._language_value)), '"'),
|
|
151
|
+
seq("'", optional(field('value', $._language_value)), "'"),
|
|
152
|
+
field('value', $._language_value),
|
|
133
153
|
),
|
|
134
154
|
),
|
|
135
155
|
|
|
@@ -138,7 +158,7 @@ export default grammar({
|
|
|
138
158
|
_language_value: ($) =>
|
|
139
159
|
alias(choice($._lang_csharp, $._lang_vb, $._lang_other), $.language_value),
|
|
140
160
|
|
|
141
|
-
|
|
161
|
+
_directive_attribute: ($) => choice($.language_attribute, $.attribute),
|
|
142
162
|
|
|
143
163
|
attribute: ($) =>
|
|
144
164
|
seq(
|
|
@@ -185,7 +205,10 @@ export default grammar({
|
|
|
185
205
|
|
|
186
206
|
unquoted_attribute_value: (_) => /[^"'=<>\s`]+/,
|
|
187
207
|
|
|
188
|
-
|
|
208
|
+
// Any run of two or more dashes before `%>` closes the comment, as in
|
|
209
|
+
// `<%-- note ---%>`; the scanner stops the text before that run.
|
|
210
|
+
server_comment: ($) =>
|
|
211
|
+
seq('<%--', optional($.server_comment_text), alias(token(/-*--%>/), '--%>')),
|
|
189
212
|
|
|
190
213
|
// Nuggets inside an HTML comment still execute, so they stay real nodes.
|
|
191
214
|
html_comment: ($) =>
|
|
@@ -226,15 +249,36 @@ export default grammar({
|
|
|
226
249
|
expression_builder: ($) =>
|
|
227
250
|
seq('<%$', optional(alias($.expression_text, $.expression_value)), '%>'),
|
|
228
251
|
|
|
252
|
+
// Script and style bodies are raw text up to their end tag, except that
|
|
253
|
+
// nuggets, server comments, and directives inside them are still parsed. A body
|
|
254
|
+
// split by a nugget is several code or style_text chunks; the injection
|
|
255
|
+
// query combines them.
|
|
229
256
|
script_element: ($) =>
|
|
230
257
|
seq(
|
|
231
258
|
alias($.script_start_tag, $.start_tag),
|
|
232
|
-
|
|
259
|
+
repeat(
|
|
260
|
+
choice(
|
|
261
|
+
alias($._script_csharp, $.c_sharp_code),
|
|
262
|
+
alias($._script_vb, $.vb_code),
|
|
263
|
+
$._nugget,
|
|
264
|
+
$.server_comment,
|
|
265
|
+
$.directive,
|
|
266
|
+
),
|
|
267
|
+
),
|
|
233
268
|
alias($.script_end_tag, $.end_tag),
|
|
234
269
|
),
|
|
235
270
|
|
|
271
|
+
// The probe is a zero-width scanner token that reads the tag's `runat`
|
|
272
|
+
// and `language` ahead of the grammar, so a server script's body is
|
|
273
|
+
// labeled with its own language.
|
|
236
274
|
script_start_tag: ($) =>
|
|
237
|
-
seq(
|
|
275
|
+
seq(
|
|
276
|
+
'<',
|
|
277
|
+
field('name', alias($.script_tag_name, $.tag_name)),
|
|
278
|
+
$._script_tag_probe,
|
|
279
|
+
repeat($._tag_item),
|
|
280
|
+
'>',
|
|
281
|
+
),
|
|
238
282
|
|
|
239
283
|
script_end_tag: ($) => seq('</', field('name', alias($.script_tag_name, $.tag_name)), '>'),
|
|
240
284
|
|
|
@@ -243,12 +287,12 @@ export default grammar({
|
|
|
243
287
|
style_element: ($) =>
|
|
244
288
|
seq(
|
|
245
289
|
alias($.style_start_tag, $.start_tag),
|
|
246
|
-
|
|
290
|
+
repeat(choice($.style_text, $._nugget, $.server_comment, $.directive)),
|
|
247
291
|
alias($.style_end_tag, $.end_tag),
|
|
248
292
|
),
|
|
249
293
|
|
|
250
294
|
style_start_tag: ($) =>
|
|
251
|
-
seq('<', field('name', alias($.style_tag_name, $.tag_name)), repeat($.
|
|
295
|
+
seq('<', field('name', alias($.style_tag_name, $.tag_name)), repeat($._tag_item), '>'),
|
|
252
296
|
|
|
253
297
|
style_end_tag: ($) => seq('</', field('name', alias($.style_tag_name, $.tag_name)), '>'),
|
|
254
298
|
|
|
@@ -258,17 +302,48 @@ export default grammar({
|
|
|
258
302
|
seq(
|
|
259
303
|
'<',
|
|
260
304
|
field('name', alias($.void_tag_name, $.tag_name)),
|
|
261
|
-
repeat($.
|
|
305
|
+
repeat($._tag_item),
|
|
262
306
|
choice('/>', '>'),
|
|
263
307
|
),
|
|
264
308
|
|
|
265
309
|
void_tag_name: (_) => choice(...VOID_TAGS.map((name) => reserved(name))),
|
|
266
310
|
|
|
267
|
-
|
|
311
|
+
// `<script src="..." />` and `<style />` close themselves in XHTML-era
|
|
312
|
+
// pages instead of opening a raw-text body. Other names go through the
|
|
313
|
+
// scanner's open-element stack, so their `/>` is the scanner's token.
|
|
314
|
+
self_closing_element: ($) =>
|
|
315
|
+
choice(
|
|
316
|
+
seq(
|
|
317
|
+
'<',
|
|
318
|
+
field('name', $._tag_name),
|
|
319
|
+
repeat($._tag_item),
|
|
320
|
+
alias($._self_closing_delimiter, '/>'),
|
|
321
|
+
),
|
|
322
|
+
seq(
|
|
323
|
+
'<',
|
|
324
|
+
field('name', alias($.script_tag_name, $.tag_name)),
|
|
325
|
+
$._script_tag_probe,
|
|
326
|
+
repeat($._tag_item),
|
|
327
|
+
'/>',
|
|
328
|
+
),
|
|
329
|
+
seq('<', field('name', alias($.style_tag_name, $.tag_name)), repeat($._tag_item), '/>'),
|
|
330
|
+
),
|
|
331
|
+
|
|
332
|
+
// An element ends at its end tag or, for HTML's optional end tags (`li`,
|
|
333
|
+
// `p`, `td`, `option`, and the rest), where the scanner finds it implied:
|
|
334
|
+
// at a sibling or other start tag that closes it, at an ancestor's end
|
|
335
|
+
// tag, or at the end of the file.
|
|
336
|
+
element: ($) => seq($.start_tag, repeat($._content), choice($.end_tag, $._implicit_end_tag)),
|
|
337
|
+
|
|
338
|
+
start_tag: ($) => seq('<', field('name', $._tag_name), repeat($._tag_item), '>'),
|
|
268
339
|
|
|
269
|
-
|
|
340
|
+
// A nugget or server comment can stand in for an attribute, as in
|
|
341
|
+
// `<option value="1" <%= selected ? "selected" : "" %>>`.
|
|
342
|
+
_tag_item: ($) => choice($.attribute, $._nugget, $.server_comment),
|
|
270
343
|
|
|
271
|
-
|
|
344
|
+
// Start and end tag names come from the scanner, which keeps the stack of
|
|
345
|
+
// open elements that implied end tags are decided from.
|
|
346
|
+
_tag_name: ($) => alias($._start_tag_name, $.tag_name),
|
|
272
347
|
|
|
273
348
|
end_tag: ($) =>
|
|
274
349
|
seq(
|
|
@@ -279,18 +354,56 @@ export default grammar({
|
|
|
279
354
|
alias($.script_tag_name, $.tag_name),
|
|
280
355
|
alias($.style_tag_name, $.tag_name),
|
|
281
356
|
alias($.void_tag_name, $.tag_name),
|
|
282
|
-
$.tag_name,
|
|
357
|
+
alias($._end_tag_name, $.tag_name),
|
|
283
358
|
),
|
|
284
359
|
),
|
|
285
360
|
'>',
|
|
286
361
|
),
|
|
287
362
|
|
|
288
|
-
|
|
363
|
+
// An end tag that names no open element closes nothing, as a browser
|
|
364
|
+
// treats it, so `<p><table></table></p>` keeps its parent open.
|
|
365
|
+
stray_end_tag: ($) => seq('</', field('name', alias($._stray_end_tag_name, $.tag_name)), '>'),
|
|
289
366
|
|
|
290
367
|
doctype: ($) => seq($.doctype_token, optional(alias(/[^>]+/, $.doctype_declaration)), '>'),
|
|
291
368
|
|
|
292
369
|
doctype_token: (_) => token(prec(RESERVED, /<![Dd][Oo][Cc][Tt][Yy][Pp][Ee]/)),
|
|
293
370
|
|
|
294
|
-
|
|
371
|
+
// `<![CDATA[ ... ]]>` and `<?xml ... ?>` appear in XHTML-era pages and
|
|
372
|
+
// .asmx files. ASP.NET still runs nuggets inside them, as in an RSS feed's
|
|
373
|
+
// `<![CDATA[<%# Eval("Body") %>]]>`, so nuggets stay real nodes and the
|
|
374
|
+
// rest is text.
|
|
375
|
+
cdata_section: ($) =>
|
|
376
|
+
seq(
|
|
377
|
+
'<![CDATA[',
|
|
378
|
+
repeat(
|
|
379
|
+
choice(
|
|
380
|
+
$._nugget,
|
|
381
|
+
$.server_comment,
|
|
382
|
+
alias(/[^\]<]+/, $.text),
|
|
383
|
+
alias(']', $.text),
|
|
384
|
+
alias('<', $.text),
|
|
385
|
+
),
|
|
386
|
+
),
|
|
387
|
+
']]>',
|
|
388
|
+
),
|
|
389
|
+
|
|
390
|
+
processing_instruction: ($) =>
|
|
391
|
+
seq(
|
|
392
|
+
'<?',
|
|
393
|
+
repeat(
|
|
394
|
+
choice(
|
|
395
|
+
$._nugget,
|
|
396
|
+
$.server_comment,
|
|
397
|
+
alias(/[^?<]+/, $.text),
|
|
398
|
+
alias('?', $.text),
|
|
399
|
+
alias('<', $.text),
|
|
400
|
+
),
|
|
401
|
+
),
|
|
402
|
+
'?>',
|
|
403
|
+
),
|
|
404
|
+
|
|
405
|
+
// A `<` followed by whitespace starts no tag, as browsers and ASP.NET's
|
|
406
|
+
// own tag pattern read it, so `a < b` is text rather than an element.
|
|
407
|
+
text: (_) => token(choice(/[^<\s][^<]*/, /<\s[^<]*/)),
|
|
295
408
|
},
|
|
296
409
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tree-sitter-aspx",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Tree-sitter grammar for ASP.NET Web Forms markup",
|
|
5
5
|
"main": "bindings/node",
|
|
6
6
|
"types": "bindings/node",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"node-gyp": "13.1.0",
|
|
24
|
-
"oxfmt": "0.
|
|
25
|
-
"oxlint": "1.
|
|
24
|
+
"oxfmt": "0.75.0",
|
|
25
|
+
"oxlint": "1.90.0",
|
|
26
26
|
"tree-sitter": "0.25.1",
|
|
27
27
|
"tree-sitter-cli": "0.27.1"
|
|
28
28
|
},
|
|
Binary file
|
|
Binary file
|
package/queries/highlights.scm
CHANGED
package/queries/injections.scm
CHANGED
|
@@ -9,9 +9,22 @@
|
|
|
9
9
|
;
|
|
10
10
|
; A <script> body uses the same code node. Without runat="server" the body
|
|
11
11
|
; is client script, so the query below replaces the page language with
|
|
12
|
-
; JavaScript. With runat="server" the page language
|
|
12
|
+
; JavaScript. With runat="server" the page language, or the script's own
|
|
13
|
+
; language attribute, stands. ASP.NET compares the value without regard to
|
|
14
|
+
; case and accepts it unquoted, so runat=SERVER and runat=server count too.
|
|
15
|
+
; The pattern needs whitespace before runat and a non-name character after
|
|
16
|
+
; server, so text such as src="x.ashx?runat=server" or runat="serverless"
|
|
17
|
+
; does not match. It uses only syntax that Rust, ECMAScript, and Vim regex
|
|
18
|
+
; read the same way.
|
|
13
19
|
; Expression builders (`<%$ ... %>`) are not C# or VB and are not injected.
|
|
14
20
|
; <style> bodies inject as CSS.
|
|
21
|
+
;
|
|
22
|
+
; A nugget inside a <script> or <style> body splits the body into several
|
|
23
|
+
; code or style_text chunks. Those patterns set injection.combined so the
|
|
24
|
+
; chunks are parsed together with the nuggets left out; the nuggets
|
|
25
|
+
; themselves are injected by the patterns above. Combined injection joins
|
|
26
|
+
; every match of a pattern in the page, so all client script bodies form one
|
|
27
|
+
; JavaScript document, and likewise for server scripts and styles.
|
|
15
28
|
|
|
16
29
|
((statement_block
|
|
17
30
|
(c_sharp_code) @injection.content)
|
|
@@ -47,24 +60,40 @@
|
|
|
47
60
|
(vb_code) @injection.content)
|
|
48
61
|
(#set! injection.language "vb"))
|
|
49
62
|
|
|
63
|
+
; Handler files (.ashx, .asmx) are a directive plus code. The code pieces
|
|
64
|
+
; between any <script> elements and server comments form one document.
|
|
65
|
+
((handler_code
|
|
66
|
+
(c_sharp_code) @injection.content)
|
|
67
|
+
(#set! injection.language "c_sharp")
|
|
68
|
+
(#set! injection.combined))
|
|
69
|
+
|
|
70
|
+
((handler_code
|
|
71
|
+
(vb_code) @injection.content)
|
|
72
|
+
(#set! injection.language "vb")
|
|
73
|
+
(#set! injection.combined))
|
|
74
|
+
|
|
50
75
|
((script_element
|
|
51
76
|
(start_tag) @_tag
|
|
52
77
|
(c_sharp_code) @injection.content)
|
|
53
|
-
(#match? @_tag "[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"'][Ss]
|
|
54
|
-
(#set! injection.language "c_sharp")
|
|
78
|
+
(#match? @_tag "\\s[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"']*[Ss][Ee][Rr][Vv][Ee][Rr][^A-Za-z0-9_:.-]")
|
|
79
|
+
(#set! injection.language "c_sharp")
|
|
80
|
+
(#set! injection.combined))
|
|
55
81
|
|
|
56
82
|
((script_element
|
|
57
83
|
(start_tag) @_tag
|
|
58
84
|
(vb_code) @injection.content)
|
|
59
|
-
(#match? @_tag "[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"'][Ss]
|
|
60
|
-
(#set! injection.language "vb")
|
|
85
|
+
(#match? @_tag "\\s[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"']*[Ss][Ee][Rr][Vv][Ee][Rr][^A-Za-z0-9_:.-]")
|
|
86
|
+
(#set! injection.language "vb")
|
|
87
|
+
(#set! injection.combined))
|
|
61
88
|
|
|
62
89
|
((script_element
|
|
63
90
|
(start_tag) @_tag
|
|
64
91
|
[(c_sharp_code) (vb_code)] @injection.content)
|
|
65
|
-
(#not-match? @_tag "[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"'][Ss]
|
|
66
|
-
(#set! injection.language "javascript")
|
|
92
|
+
(#not-match? @_tag "\\s[Rr][Uu][Nn][Aa][Tt]\\s*=\\s*[\"']*[Ss][Ee][Rr][Vv][Ee][Rr][^A-Za-z0-9_:.-]")
|
|
93
|
+
(#set! injection.language "javascript")
|
|
94
|
+
(#set! injection.combined))
|
|
67
95
|
|
|
68
96
|
((style_element
|
|
69
97
|
(style_text) @injection.content)
|
|
70
|
-
(#set! injection.language "css")
|
|
98
|
+
(#set! injection.language "css")
|
|
99
|
+
(#set! injection.combined))
|