@konomanoasa/tree-sitter-query 0.1.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.
@@ -0,0 +1,518 @@
1
+ use konomanoasa_tree_sitter_query as grammar;
2
+ use tree_sitter::{
3
+ Parser, Point, Query, QueryCursor, QueryErrorKind, Range, StreamingIterator,
4
+ };
5
+
6
+ fn parser() -> Parser {
7
+ let mut parser = Parser::new();
8
+ parser.set_language(&grammar::LANGUAGE.into()).unwrap();
9
+ parser
10
+ }
11
+
12
+ #[test]
13
+ fn parses_valid_source() {
14
+ let source = "(identifier) @name\n";
15
+ let language = grammar::LANGUAGE.into();
16
+ let mut parser = Parser::new();
17
+ parser.set_language(&language).unwrap();
18
+ let tree = parser.parse(source, None).unwrap();
19
+ let root = tree.root_node();
20
+ assert_eq!(root.kind(), "query");
21
+ assert_eq!(root.byte_range(), 0..source.len());
22
+ assert!(!root.has_error());
23
+ assert!(grammar::NODE_TYPES.contains("\"query\""));
24
+ Query::new(&language, grammar::HIGHLIGHTS_QUERY).unwrap();
25
+ }
26
+
27
+ #[test]
28
+ fn preserves_comment_text() {
29
+ let mut parser = parser();
30
+ let source = "; query\n";
31
+ let tree = parser.parse(source, None).unwrap();
32
+ let root = tree.root_node();
33
+ assert!(!root.has_error());
34
+ assert_eq!(root.kind(), "query");
35
+ assert_eq!(root.to_sexp(), "(query (comment))");
36
+ assert_eq!(root.named_child(0).unwrap().byte_range(), 0..7);
37
+ }
38
+
39
+ #[test]
40
+ fn missing_node_forms_follow_public_query_api_syntax() {
41
+ let language = grammar::LANGUAGE.into();
42
+ let mut parser = parser();
43
+ for (source, valid) in [
44
+ ("(MISSING)", true),
45
+ ("(MISSING ; comment\n)", true),
46
+ ("(MISSING identifier)", true),
47
+ ("(MISSING pattern body: (_))", true),
48
+ ("(MISSING (_))", false),
49
+ ("(MISSING . (_))", false),
50
+ ("(MISSING !body)", false),
51
+ ("(MISSING (#custom?))", false),
52
+ ("(MISSING identifier: (_))", false),
53
+ ("(MISSING \"(\" @x)", false),
54
+ ("(MISSING/identifier)", false),
55
+ ] {
56
+ let query = Query::new(&language, source);
57
+ if valid {
58
+ assert!(query.is_ok(), "{source:?}: {query:?}");
59
+ } else {
60
+ assert_eq!(
61
+ query.unwrap_err().kind,
62
+ QueryErrorKind::Syntax,
63
+ "{source:?}"
64
+ );
65
+ }
66
+ let tree = parser.parse(source, None).unwrap();
67
+ assert_eq!(tree.root_node().has_error(), !valid, "{source:?}");
68
+ }
69
+ }
70
+
71
+ #[test]
72
+ fn field_constraints_and_call_anchors_follow_public_query_api_syntax() {
73
+ let language = grammar::LANGUAGE.into();
74
+ let mut parser = parser();
75
+ for (source, valid) in [
76
+ ("(node_pattern . !node (pattern))", false),
77
+ ("(node_pattern !node . (pattern))", true),
78
+ ("(node_pattern (pattern) . !node)", false),
79
+ ("(node_pattern (pattern) !node .)", true),
80
+ ("(node_pattern _: (_))", false),
81
+ ("body: (node_pattern)", true),
82
+ ("((identifier) subtype: (identifier))", true),
83
+ ("[(identifier) node: (identifier)]", true),
84
+ ("node: node: (identifier)", true),
85
+ ("(node_pattern node: node: (identifier))", true),
86
+ ("(node_pattern . (#custom?))", true),
87
+ ("(node_pattern . (#custom!))", true),
88
+ ("(node_pattern (#custom?) .)", false),
89
+ ("(node_pattern (#custom!) .)", false),
90
+ ("(node_pattern ((#custom?)) .)", false),
91
+ ("(node_pattern [(#custom?)] .)", false),
92
+ ("(node_pattern [((#custom?))] .)", false),
93
+ ("(node_pattern ((#custom?) (pattern)?) .)", true),
94
+ ("(node_pattern [(#custom?) (pattern)] .)", true),
95
+ ("(node_pattern (pattern) ((#custom?)) .)", true),
96
+ ("(node_pattern . (#custom?) !node)", true),
97
+ ("(node_pattern . (#custom?) . (pattern))", true),
98
+ ("(node_pattern (pattern) . (#custom?) .)", true),
99
+ ("((#custom?) . (identifier))", true),
100
+ ("((identifier) . (#custom?))", true),
101
+ ("((#custom?) . (#other?))", true),
102
+ ] {
103
+ let query = Query::new(&language, source);
104
+ if valid {
105
+ assert!(query.is_ok(), "{source:?}: {query:?}");
106
+ } else {
107
+ assert_eq!(
108
+ query.unwrap_err().kind,
109
+ QueryErrorKind::Syntax,
110
+ "{source:?}"
111
+ );
112
+ }
113
+ assert_eq!(
114
+ parser.parse(source, None).unwrap().root_node().has_error(),
115
+ !valid,
116
+ "{source:?}"
117
+ );
118
+ }
119
+
120
+ let source = "(capture name: body: (node_pattern))";
121
+ assert_eq!(
122
+ Query::new(&language, source).unwrap_err().kind,
123
+ QueryErrorKind::Structure
124
+ );
125
+ assert!(!parser.parse(source, None).unwrap().root_node().has_error());
126
+ }
127
+
128
+ #[test]
129
+ fn adjacent_bare_wildcards_follow_public_query_api_pattern_and_capture_counts()
130
+ {
131
+ let language = grammar::LANGUAGE.into();
132
+ let mut parser = parser();
133
+ let target = "(x) (y)";
134
+ let tree = parser.parse(target, None).unwrap();
135
+ for (source, expected_patterns, expected_captures) in [
136
+ ("__ @hit", 2, 11),
137
+ ("_ _ @hit", 2, 11),
138
+ ("_body: (node_pattern) @hit", 2, 2),
139
+ ("_ body: (node_pattern) @hit", 2, 2),
140
+ ] {
141
+ let query = Query::new(&language, source).unwrap();
142
+ assert_eq!(query.pattern_count(), expected_patterns, "{source:?}");
143
+ let mut cursor = QueryCursor::new();
144
+ let mut matches =
145
+ cursor.matches(&query, tree.root_node(), target.as_bytes());
146
+ let mut captures = 0;
147
+ while let Some(item) = matches.next() {
148
+ for capture in item.captures() {
149
+ assert_eq!(item.pattern_index, 1, "{source:?}");
150
+ assert_eq!(query.capture_names()[capture.index as usize], "hit");
151
+ captures += 1;
152
+ }
153
+ }
154
+ assert_eq!(captures, expected_captures, "{source:?}");
155
+ let syntax = parser.parse(source, None).unwrap();
156
+ assert!(!syntax.root_node().has_error(), "{source:?}");
157
+ assert_eq!(syntax.root_node().named_child_count(), expected_patterns);
158
+ }
159
+ }
160
+
161
+ #[test]
162
+ fn bare_wildcards_split_field_prefixes_but_identifier_contexts_preserve_ranges()
163
+ {
164
+ let language = grammar::LANGUAGE.into();
165
+ let query = Query::new(&language, "[(wildcard) (identifier)] @leaf").unwrap();
166
+ let mut parser = parser();
167
+ for (source, expected) in [
168
+ (
169
+ "__ @x",
170
+ vec![("wildcard", 0..1), ("wildcard", 1..2), ("identifier", 4..5)],
171
+ ),
172
+ (
173
+ "_field: (x)",
174
+ vec![
175
+ ("wildcard", 0..1),
176
+ ("identifier", 1..6),
177
+ ("identifier", 9..10),
178
+ ],
179
+ ),
180
+ (
181
+ "(_x !_field) @_cap",
182
+ vec![
183
+ ("identifier", 1..3),
184
+ ("identifier", 5..11),
185
+ ("identifier", 14..18),
186
+ ],
187
+ ),
188
+ (
189
+ "(#_call? _arg @_cap)",
190
+ vec![
191
+ ("identifier", 2..7),
192
+ ("identifier", 9..13),
193
+ ("identifier", 15..19),
194
+ ],
195
+ ),
196
+ (
197
+ "(MISSING _type) (_super/_sub)",
198
+ vec![
199
+ ("identifier", 9..14),
200
+ ("identifier", 17..23),
201
+ ("identifier", 24..28),
202
+ ],
203
+ ),
204
+ ] {
205
+ let tree = parser.parse(source, None).unwrap();
206
+ assert!(!tree.root_node().has_error(), "{source:?}");
207
+ let mut cursor = QueryCursor::new();
208
+ let mut matches =
209
+ cursor.matches(&query, tree.root_node(), source.as_bytes());
210
+ let mut actual = Vec::new();
211
+ while let Some(item) = matches.next() {
212
+ let node = item.captures()[0].node;
213
+ assert_eq!(node.child_count(), 0);
214
+ actual.push((node.kind(), node.byte_range()));
215
+ }
216
+ assert_eq!(actual, expected, "{source:?}");
217
+ }
218
+ }
219
+
220
+ #[test]
221
+ fn chained_fields_preserve_names_separators_and_postfix_ranges() {
222
+ let tree = parser().parse("left: right: (_)+ @x", None).unwrap();
223
+ let root = tree.root_node();
224
+ assert!(!root.has_error());
225
+ assert_eq!(root.named_child_count(), 1);
226
+ let mut target = root.named_child(0).unwrap();
227
+ for (range, name_range, separator_range) in
228
+ [(0..20, 0..4, 4..5), (6..20, 6..11, 11..12)]
229
+ {
230
+ assert_eq!(target.kind(), "field_constraint");
231
+ assert_eq!(target.byte_range(), range);
232
+ assert_eq!(target.child_count(), 3);
233
+ assert_eq!(
234
+ target.child_by_field_name("name").unwrap().byte_range(),
235
+ name_range
236
+ );
237
+ let separator = target.child(1).unwrap();
238
+ assert_eq!(separator.kind(), ":");
239
+ assert!(!separator.is_named());
240
+ assert_eq!(separator.byte_range(), separator_range);
241
+ target = target.child_by_field_name("pattern").unwrap();
242
+ }
243
+ assert_eq!(target.kind(), "pattern");
244
+ assert_eq!(target.byte_range(), 13..20);
245
+ for (field, kind, range) in [
246
+ ("body", "node_pattern", 13..16),
247
+ ("quantifier", "quantifier", 16..17),
248
+ ("capture", "capture", 18..20),
249
+ ] {
250
+ let child = target.child_by_field_name(field).unwrap();
251
+ assert_eq!(child.kind(), kind);
252
+ assert_eq!(child.byte_range(), range);
253
+ }
254
+ }
255
+
256
+ #[test]
257
+ fn string_leaves_preserve_literal_bytes_and_escape_spelling() {
258
+ let language = grammar::LANGUAGE.into();
259
+ let mut parser = parser();
260
+ for (source, expected) in [
261
+ (
262
+ "\"日\\\"本\"",
263
+ vec![
264
+ ("\"", 0..1),
265
+ ("string_content", 1..4),
266
+ ("escape_sequence", 4..6),
267
+ ("string_content", 6..9),
268
+ ("\"", 9..10),
269
+ ],
270
+ ),
271
+ (
272
+ "\"a\0b\0\0c\"",
273
+ vec![("\"", 0..1), ("string_content", 1..7), ("\"", 7..8)],
274
+ ),
275
+ (
276
+ "\"\0\"",
277
+ vec![("\"", 0..1), ("string_content", 1..2), ("\"", 2..3)],
278
+ ),
279
+ (
280
+ "\"a\\\0b\"",
281
+ vec![
282
+ ("\"", 0..1),
283
+ ("string_content", 1..2),
284
+ ("escape_sequence", 2..4),
285
+ ("string_content", 4..5),
286
+ ("\"", 5..6),
287
+ ],
288
+ ),
289
+ (
290
+ "\"\\\0\"",
291
+ vec![("\"", 0..1), ("escape_sequence", 1..3), ("\"", 3..4)],
292
+ ),
293
+ ] {
294
+ assert!(Query::new(&language, &format!("(#custom? {source})")).is_ok());
295
+ let tree = parser.parse(source, None).unwrap();
296
+ assert!(!tree.root_node().has_error(), "{source:?}");
297
+ let string = tree
298
+ .root_node()
299
+ .named_child(0)
300
+ .unwrap()
301
+ .child_by_field_name("body")
302
+ .unwrap();
303
+ let mut cursor = string.walk();
304
+ let actual: Vec<_> = string
305
+ .children(&mut cursor)
306
+ .map(|node| {
307
+ assert_eq!(node.child_count(), 0);
308
+ (node.kind(), node.byte_range())
309
+ })
310
+ .collect();
311
+ assert_eq!(actual, expected, "{source:?}");
312
+ }
313
+ for source in ["\"a\0", "\"\\\0", "\"a\\", "\"\0\n\""] {
314
+ assert!(
315
+ parser.parse(source, None).unwrap().root_node().has_error(),
316
+ "{source:?}"
317
+ );
318
+ }
319
+ }
320
+
321
+ #[test]
322
+ fn carriage_returns_inside_comments_preserve_the_following_pattern() {
323
+ let language = grammar::LANGUAGE.into();
324
+ let mut parser = parser();
325
+ let source = ";a\rb\r\n(_)";
326
+ assert!(Query::new(&language, source).is_ok());
327
+ let tree = parser.parse(source, None).unwrap();
328
+ let root = tree.root_node();
329
+ assert!(!root.has_error());
330
+ assert_eq!(
331
+ root.to_sexp(),
332
+ "(query (comment) (pattern body: (node_pattern node: (wildcard))))"
333
+ );
334
+ assert_eq!(root.named_child(0).unwrap().byte_range(), 0..4);
335
+ assert_eq!(root.named_child(1).unwrap().byte_range(), 6..9);
336
+ }
337
+
338
+ #[test]
339
+ fn highlight_captures_use_leaf_ranges_even_during_recovery() {
340
+ let language = grammar::LANGUAGE.into();
341
+ let query = Query::new(&language, grammar::HIGHLIGHTS_QUERY).unwrap();
342
+ let mut parser = parser();
343
+ for (source, has_error) in [
344
+ ("(node field: (_) @name)", false),
345
+ ("((node . child: (_)+ @x !absent) . [(other)* _?])", false),
346
+ (
347
+ "(MISSING) (MISSING identifier) (MISSING \";\") (ERROR)",
348
+ false,
349
+ ),
350
+ ("(expression/call) (expression/\"()\")", false),
351
+ ("(node _field: (_) !_) (_/node) (MISSING type/node)", false),
352
+ ("(MISSING node field: (_)) (MISSING _ MISSING: (_))", false),
353
+ ("left: right: (node)+ @x [field: (_)]", false),
354
+ ("((#first?) . body: (node . (#check?)) . (#last!))", false),
355
+ ("(node _: (_))", true),
356
+ ("(node . !field (child))", true),
357
+ ("(MISSING _) (MISSING MISSING)", false),
358
+ ("(#custom? \"a\0b\\\0c\")", false),
359
+ (
360
+ "((node) @x (#custom? @x \"日\\\"本\" local) (#set! key value)) ; text",
361
+ false,
362
+ ),
363
+ ("(node) @", true),
364
+ ("(node", true),
365
+ ("(#eq? @x \"unfinished", true),
366
+ (")]", true),
367
+ ] {
368
+ let tree = parser.parse(source, None).unwrap();
369
+ assert_eq!(tree.root_node().has_error(), has_error, "{source:?}");
370
+ let mut cursor = QueryCursor::new();
371
+ let mut matches =
372
+ cursor.matches(&query, tree.root_node(), source.as_bytes());
373
+ let mut count = 0;
374
+ while let Some(item) = matches.next() {
375
+ for capture in item.captures() {
376
+ count += 1;
377
+ assert_eq!(
378
+ capture.node.child_count(),
379
+ 0,
380
+ "{source:?}: {}",
381
+ capture.node.kind()
382
+ );
383
+ assert!(capture.node.end_byte() <= source.len());
384
+ if capture.node.is_missing() {
385
+ assert!(capture.node.byte_range().is_empty());
386
+ }
387
+ }
388
+ }
389
+ assert!(count > 0, "{source:?}: no highlight captures");
390
+ }
391
+ }
392
+
393
+ #[test]
394
+ fn public_children_preserve_fields_delimiters_and_source_order() {
395
+ let source = "(node . child: (_)+ @x !absent)";
396
+ let tree = parser().parse(source, None).unwrap();
397
+ assert!(!tree.root_node().has_error());
398
+ let mut cursor = tree.walk();
399
+ let mut depth = 0;
400
+ let mut actual = Vec::new();
401
+ 'walk: loop {
402
+ let node = cursor.node();
403
+ actual.push((
404
+ depth,
405
+ node.kind(),
406
+ cursor.field_name(),
407
+ node.is_named(),
408
+ node.byte_range(),
409
+ ));
410
+ assert_eq!(
411
+ node.start_position(),
412
+ Point {
413
+ row: 0,
414
+ column: node.start_byte()
415
+ }
416
+ );
417
+ assert_eq!(
418
+ node.end_position(),
419
+ Point {
420
+ row: 0,
421
+ column: node.end_byte()
422
+ }
423
+ );
424
+ if cursor.goto_first_child() {
425
+ depth += 1;
426
+ continue;
427
+ }
428
+ while !cursor.goto_next_sibling() {
429
+ if !cursor.goto_parent() {
430
+ break 'walk;
431
+ }
432
+ depth -= 1;
433
+ }
434
+ }
435
+ assert_eq!(
436
+ actual,
437
+ vec![
438
+ (0, "query", None, true, 0..31),
439
+ (1, "pattern", None, true, 0..31),
440
+ (2, "node_pattern", Some("body"), true, 0..31),
441
+ (3, "(", None, false, 0..1),
442
+ (3, "identifier", Some("node"), true, 1..5),
443
+ (3, "anchor", None, true, 6..7),
444
+ (3, "field_constraint", None, true, 8..22),
445
+ (4, "identifier", Some("name"), true, 8..13),
446
+ (4, ":", None, false, 13..14),
447
+ (4, "pattern", Some("pattern"), true, 15..22),
448
+ (5, "node_pattern", Some("body"), true, 15..18),
449
+ (6, "(", None, false, 15..16),
450
+ (6, "wildcard", Some("node"), true, 16..17),
451
+ (6, ")", None, false, 17..18),
452
+ (5, "quantifier", Some("quantifier"), true, 18..19),
453
+ (5, "capture", Some("capture"), true, 20..22),
454
+ (6, "@", None, false, 20..21),
455
+ (6, "identifier", Some("name"), true, 21..22),
456
+ (3, "negated_field", None, true, 23..30),
457
+ (4, "!", None, false, 23..24),
458
+ (4, "identifier", Some("name"), true, 24..30),
459
+ (3, ")", None, false, 30..31),
460
+ ]
461
+ );
462
+ }
463
+
464
+ #[test]
465
+ fn bom_is_ignored_only_once_at_source_byte_zero() {
466
+ let mut parser = parser();
467
+ let source = "\u{feff}(node)\r\n";
468
+ let tree = parser.parse(source, None).unwrap();
469
+ assert!(!tree.root_node().has_error());
470
+ let pattern = tree.root_node().named_child(0).unwrap();
471
+ assert_eq!(pattern.byte_range(), 3..9);
472
+ assert_eq!(pattern.start_position(), Point { row: 0, column: 3 });
473
+ assert_eq!(tree.root_node().end_position(), Point { row: 1, column: 0 });
474
+ for invalid in [
475
+ " \u{feff}(node)",
476
+ "\u{feff}\u{feff}(node)",
477
+ "(node)\u{feff}",
478
+ ] {
479
+ assert!(
480
+ parser.parse(invalid, None).unwrap().root_node().has_error(),
481
+ "{invalid:?}"
482
+ );
483
+ }
484
+ let embedded = "x\u{feff}(node)";
485
+ parser
486
+ .set_included_ranges(&[Range {
487
+ start_byte: 1,
488
+ end_byte: embedded.len(),
489
+ start_point: Point { row: 0, column: 1 },
490
+ end_point: Point {
491
+ row: 0,
492
+ column: embedded.len(),
493
+ },
494
+ }])
495
+ .unwrap();
496
+ assert!(
497
+ parser
498
+ .parse(embedded, None)
499
+ .unwrap()
500
+ .root_node()
501
+ .has_error()
502
+ );
503
+ }
504
+
505
+ #[test]
506
+ fn crlf_and_multibyte_comment_ranges_keep_source_coordinates() {
507
+ let mut parser = parser();
508
+ let source = "; 日\r\n(node)";
509
+ let tree = parser.parse(source, None).unwrap();
510
+ let root = tree.root_node();
511
+ assert!(!root.has_error());
512
+ let comment = root.named_child(0).unwrap();
513
+ assert_eq!(comment.byte_range(), 0..5);
514
+ assert_eq!(comment.end_position(), Point { row: 0, column: 5 });
515
+ let pattern = root.named_child(1).unwrap();
516
+ assert_eq!(pattern.byte_range(), 7..13);
517
+ assert_eq!(pattern.start_position(), Point { row: 1, column: 0 });
518
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://tree-sitter.github.io/tree-sitter/assets/schemas/config.schema.json",
3
+ "grammars": [
4
+ {
5
+ "name": "query",
6
+ "title": "Tree-sitter Query",
7
+ "scope": "source.query",
8
+ "path": ".",
9
+ "file-types": ["scm"],
10
+ "injection-regex": "^query$",
11
+ "highlights": "queries/highlights.scm"
12
+ }
13
+ ],
14
+ "metadata": {
15
+ "version": "0.1.0",
16
+ "license": "MIT",
17
+ "description": "Tree-sitter grammar for Tree-sitter Query.",
18
+ "links": {
19
+ "repository": "https://github.com/konomanoasa/tree-sitter-query"
20
+ }
21
+ },
22
+ "bindings": {
23
+ "c": false,
24
+ "go": false,
25
+ "node": false,
26
+ "python": false,
27
+ "rust": true,
28
+ "swift": false
29
+ }
30
+ }