tree-sitter-sed 0.8.0 → 0.10.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/Cargo.toml CHANGED
@@ -10,7 +10,7 @@ name = "tree-sitter-sed"
10
10
  publish = false
11
11
  readme = "README.md"
12
12
  repository = "https://github.com/konomanoasa/tree-sitter-sed"
13
- version = "0.8.0"
13
+ version = "0.10.0"
14
14
 
15
15
  build = "bindings/rust/build.rs"
16
16
  include = [
@@ -67,4 +67,506 @@ mod tests {
67
67
  let sed_ere = tree_sitter::Language::new(super::LANGUAGE_ERE);
68
68
  assert_ne!(sed, sed_ere);
69
69
  }
70
+
71
+ fn issue_signatures(tree: &tree_sitter::Tree) -> Vec<(String, String, tree_sitter::Range)> {
72
+ let mut issues = Vec::new();
73
+ let mut nodes = vec![tree.root_node()];
74
+ while let Some(node) = nodes.pop() {
75
+ if node.kind() == "syntax_issue" {
76
+ let outcome = node.named_child(0).expect("issue must have an outcome");
77
+ let reason = outcome.named_child(0).expect("issue must have a reason");
78
+ issues.push((
79
+ outcome.kind().to_owned(),
80
+ reason.kind().to_owned(),
81
+ node.range(),
82
+ ));
83
+ }
84
+ for index in (0..node.named_child_count()).rev() {
85
+ nodes.push(
86
+ node.named_child(index as u32)
87
+ .expect("named child must exist"),
88
+ );
89
+ }
90
+ }
91
+ issues
92
+ }
93
+
94
+ #[test]
95
+ fn invalid_byte_issues_preserve_classification_and_ranges_through_edits() {
96
+ use tree_sitter::{InputEdit, Point, Range};
97
+
98
+ for (language, mode) in [(super::LANGUAGE, "BRE"), (super::LANGUAGE_ERE, "ERE")] {
99
+ let mut parser = tree_sitter::Parser::new();
100
+ parser
101
+ .set_language(&language.into())
102
+ .expect("generated grammar must load");
103
+ for (name, source, start, end, row, column, reason) in [
104
+ (
105
+ "first function",
106
+ &b"p\nq\n"[..],
107
+ 0,
108
+ 1,
109
+ 0,
110
+ 0,
111
+ "unknown_function",
112
+ ),
113
+ (
114
+ "function after newline",
115
+ &b"p\np\nq\n"[..],
116
+ 2,
117
+ 3,
118
+ 1,
119
+ 0,
120
+ "unknown_function",
121
+ ),
122
+ (
123
+ "substitute delimiter",
124
+ &b"p\ns///\nq\n"[..],
125
+ 3,
126
+ 6,
127
+ 1,
128
+ 1,
129
+ "invalid_delimiter",
130
+ ),
131
+ (
132
+ "translate delimiter",
133
+ &b"p\ny///\nq\n"[..],
134
+ 3,
135
+ 6,
136
+ 1,
137
+ 1,
138
+ "invalid_delimiter",
139
+ ),
140
+ (
141
+ "address delimiter",
142
+ &b"p\n\\##p\nq\n"[..],
143
+ 3,
144
+ 5,
145
+ 1,
146
+ 1,
147
+ "invalid_delimiter",
148
+ ),
149
+ (
150
+ "trailing command text",
151
+ &b"p\np\nq\n"[..],
152
+ 3,
153
+ 3,
154
+ 1,
155
+ 1,
156
+ "unexpected_command_text",
157
+ ),
158
+ ] {
159
+ for invalid_byte in [0, 0xff] {
160
+ let mut damaged = source.to_vec();
161
+ damaged.splice(start..end, [invalid_byte]);
162
+ let mut tree = parser
163
+ .parse(source, None)
164
+ .expect("parser must return a tree");
165
+ assert!(!tree.root_node().has_error(), "{mode}, {name}");
166
+ assert!(issue_signatures(&tree).is_empty(), "{mode}, {name}");
167
+ for damage in [true, false, true] {
168
+ let (next, old_length, new_length) = if damage {
169
+ (damaged.as_slice(), end - start, 1)
170
+ } else {
171
+ (source, 1, end - start)
172
+ };
173
+ tree.edit(&InputEdit {
174
+ start_byte: start,
175
+ old_end_byte: start + old_length,
176
+ new_end_byte: start + new_length,
177
+ start_position: Point::new(row, column),
178
+ old_end_position: Point::new(row, column + old_length),
179
+ new_end_position: Point::new(row, column + new_length),
180
+ });
181
+ let incremental =
182
+ parser.parse(next, Some(&tree)).expect("incremental tree");
183
+ let fresh = parser.parse(next, None).expect("fresh tree");
184
+ let mut expected = Vec::new();
185
+ if damage {
186
+ let range = Range {
187
+ start_byte: start,
188
+ end_byte: start + 1,
189
+ start_point: Point::new(row, column),
190
+ end_point: Point::new(row, column + 1),
191
+ };
192
+ expected.push((
193
+ "nonconforming_syntax".to_owned(),
194
+ reason.to_owned(),
195
+ range,
196
+ ));
197
+ if invalid_byte == 0 && reason == "unexpected_command_text" {
198
+ expected.push((
199
+ "nonconforming_syntax".to_owned(),
200
+ "nul_character".to_owned(),
201
+ range,
202
+ ));
203
+ }
204
+ }
205
+ let context =
206
+ format!("{mode}, {name}, byte {invalid_byte:#x}, damage {damage}");
207
+ assert_eq!(issue_signatures(&fresh), expected, "fresh: {context}");
208
+ assert_eq!(
209
+ issue_signatures(&incremental),
210
+ expected,
211
+ "incremental: {context}"
212
+ );
213
+ if !damage {
214
+ assert!(!incremental.root_node().has_error(), "{context}");
215
+ assert_eq!(
216
+ incremental.root_node().to_sexp(),
217
+ fresh.root_node().to_sexp(),
218
+ "{context}"
219
+ );
220
+ }
221
+ tree = incremental;
222
+ }
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ #[test]
229
+ fn invalid_bytes_inside_intervals_and_escapes_remain_visible_through_edits() {
230
+ use tree_sitter::{InputEdit, Point, Range};
231
+
232
+ for (language, mode, interval, count_start) in [
233
+ (super::LANGUAGE, "BRE", &b"/a\\{1,2\\}/p\n"[..], 6),
234
+ (super::LANGUAGE_ERE, "ERE", &b"/a{1,2}/p\n"[..], 5),
235
+ ] {
236
+ let mut parser = tree_sitter::Parser::new();
237
+ parser
238
+ .set_language(&language.into())
239
+ .expect("generated grammar must load");
240
+ for (name, source, start, prefix, reasons) in [
241
+ (
242
+ "malformed interval",
243
+ interval,
244
+ count_start,
245
+ &b"x"[..],
246
+ &[
247
+ ("undefined_syntax", "malformed_interval", 0, 2),
248
+ (
249
+ "invalid_syntax",
250
+ "invalid_regular_expression_character",
251
+ 1,
252
+ 2,
253
+ ),
254
+ ][..],
255
+ ),
256
+ (
257
+ "escaped invalid character",
258
+ &b"/\\*/p\n"[..],
259
+ 2,
260
+ &b""[..],
261
+ &[(
262
+ "invalid_syntax",
263
+ "invalid_regular_expression_character",
264
+ 0,
265
+ 1,
266
+ )][..],
267
+ ),
268
+ ] {
269
+ for invalid_byte in [0, 0xff] {
270
+ let replacement = [prefix, &[invalid_byte]].concat();
271
+ let mut damaged = source.to_vec();
272
+ damaged.splice(start..start + 1, replacement.iter().copied());
273
+ let expected: Vec<_> = reasons
274
+ .iter()
275
+ .map(|&(outcome, reason, first, last)| {
276
+ (
277
+ outcome.to_owned(),
278
+ reason.to_owned(),
279
+ Range {
280
+ start_byte: start + first,
281
+ end_byte: start + last,
282
+ start_point: Point::new(0, start + first),
283
+ end_point: Point::new(0, start + last),
284
+ },
285
+ )
286
+ })
287
+ .collect();
288
+ let mut tree = parser.parse(source, None).expect("initial tree");
289
+ for damage in [true, false, true] {
290
+ let (next, old_length, new_length) = if damage {
291
+ (damaged.as_slice(), 1, replacement.len())
292
+ } else {
293
+ (source, replacement.len(), 1)
294
+ };
295
+ tree.edit(&InputEdit {
296
+ start_byte: start,
297
+ old_end_byte: start + old_length,
298
+ new_end_byte: start + new_length,
299
+ start_position: Point::new(0, start),
300
+ old_end_position: Point::new(0, start + old_length),
301
+ new_end_position: Point::new(0, start + new_length),
302
+ });
303
+ let incremental = parser.parse(next, Some(&tree)).expect("edited tree");
304
+ let fresh = parser.parse(next, None).expect("fresh tree");
305
+ let wanted = if damage { expected.as_slice() } else { &[] };
306
+ let context =
307
+ format!("{mode}, {name}, byte {invalid_byte:#x}, damage {damage}");
308
+ for parsed in [&fresh, &incremental] {
309
+ assert_eq!(issue_signatures(parsed), wanted, "{context}");
310
+ if damage && name == "malformed interval" {
311
+ let mut nodes = vec![parsed.root_node()];
312
+ let mut nested = false;
313
+ while let Some(node) = nodes.pop() {
314
+ if node.kind() == "malformed_interval" {
315
+ nested = node.to_sexp().contains(
316
+ "(syntax_issue (invalid_syntax (invalid_regular_expression_character",
317
+ );
318
+ }
319
+ for index in 0..node.named_child_count() {
320
+ nodes.push(node.named_child(index as u32).unwrap());
321
+ }
322
+ }
323
+ assert!(
324
+ nested,
325
+ "{context}: invalid byte must belong to the interval issue"
326
+ );
327
+ }
328
+ }
329
+ if !damage {
330
+ assert!(!incremental.root_node().has_error(), "{context}");
331
+ assert_eq!(
332
+ incremental.root_node().to_sexp(),
333
+ fresh.root_node().to_sexp(),
334
+ "{context}"
335
+ );
336
+ }
337
+ tree = incremental;
338
+ }
339
+ }
340
+ }
341
+ }
342
+ }
343
+
344
+ #[test]
345
+ fn literal_ranges_remain_stable_when_edits_split_multibyte_delimiters() {
346
+ use tree_sitter::{InputEdit, Point};
347
+
348
+ for (language, mode) in [(super::LANGUAGE, "BRE"), (super::LANGUAGE_ERE, "ERE")] {
349
+ let mut parser = tree_sitter::Parser::new();
350
+ parser
351
+ .set_language(&language.into())
352
+ .expect("generated grammar must load");
353
+ for (source, edited, positions, literal, expected) in [
354
+ (
355
+ "séaébé",
356
+ "séaébèé",
357
+ [8, 9, 11],
358
+ "replacement_literal",
359
+ &[(6, 9)][..],
360
+ ),
361
+ (
362
+ "yéaébé",
363
+ "yéaébèé",
364
+ [8, 9, 11],
365
+ "translation_literal",
366
+ &[(3, 4), (6, 9)][..],
367
+ ),
368
+ (
369
+ "s🙂a🙂b🙂",
370
+ "s🙂a🙂b🙁🙂",
371
+ [14, 15, 19],
372
+ "replacement_literal",
373
+ &[(10, 15)][..],
374
+ ),
375
+ (
376
+ "y🙂a🙂b🙂",
377
+ "y🙂a🙂b🙁🙂",
378
+ [14, 15, 19],
379
+ "translation_literal",
380
+ &[(5, 6), (10, 15)][..],
381
+ ),
382
+ ] {
383
+ let [start, old_end, new_end] = positions;
384
+ assert_eq!(&source.as_bytes()[..start], &edited.as_bytes()[..start]);
385
+ assert_eq!(&source.as_bytes()[old_end..], &edited.as_bytes()[new_end..]);
386
+ let mut tree = parser.parse(source, None).expect("initial tree");
387
+ assert!(!tree.root_node().has_error());
388
+ assert!(issue_signatures(&tree).is_empty());
389
+ tree.edit(&InputEdit {
390
+ start_byte: start,
391
+ old_end_byte: old_end,
392
+ new_end_byte: new_end,
393
+ start_position: Point::new(0, start),
394
+ old_end_position: Point::new(0, old_end),
395
+ new_end_position: Point::new(0, new_end),
396
+ });
397
+ let incremental = parser.parse(edited, Some(&tree)).expect("edited tree");
398
+ let fresh = parser.parse(edited, None).expect("fresh tree");
399
+ for (parse_kind, parsed) in [("fresh", &fresh), ("incremental", &incremental)] {
400
+ let context = format!("{mode}, {parse_kind}, {source:?} -> {edited:?}");
401
+ assert!(!parsed.root_node().has_error(), "{context}");
402
+ assert!(issue_signatures(parsed).is_empty(), "{context}");
403
+ let mut ranges = Vec::new();
404
+ let mut nodes = vec![parsed.root_node()];
405
+ while let Some(node) = nodes.pop() {
406
+ if node.kind() == literal {
407
+ ranges.push((node.start_byte(), node.end_byte()));
408
+ assert_eq!(node.start_position(), Point::new(0, node.start_byte()));
409
+ assert_eq!(node.end_position(), Point::new(0, node.end_byte()));
410
+ }
411
+ for index in (0..node.named_child_count()).rev() {
412
+ nodes.push(node.named_child(index as u32).unwrap());
413
+ }
414
+ }
415
+ assert_eq!(ranges, expected, "{context}");
416
+ }
417
+ }
418
+ }
419
+ }
420
+
421
+ #[test]
422
+ fn completed_groups_replace_recovery_after_multibyte_delimiter_edits() {
423
+ use tree_sitter::{InputEdit, Point};
424
+
425
+ for (language, source, edited, edit_start, group_kind, boundaries) in [
426
+ (
427
+ super::LANGUAGE,
428
+ r"sé\(é",
429
+ r"sé\(è\)éé",
430
+ 6,
431
+ "nondupl_bre",
432
+ [3, 5, 7, 9],
433
+ ),
434
+ (
435
+ super::LANGUAGE_ERE,
436
+ "sé(é",
437
+ "sé(è)éé",
438
+ 5,
439
+ "ere_expression",
440
+ [3, 4, 6, 7],
441
+ ),
442
+ (
443
+ super::LANGUAGE,
444
+ r"s🙂\(🙂",
445
+ r"s🙂\(🙁\)🙂🙂",
446
+ 10,
447
+ "nondupl_bre",
448
+ [5, 7, 11, 13],
449
+ ),
450
+ (
451
+ super::LANGUAGE_ERE,
452
+ "s🙂(🙂",
453
+ "s🙂(🙁)🙂🙂",
454
+ 9,
455
+ "ere_expression",
456
+ [5, 6, 10, 11],
457
+ ),
458
+ ] {
459
+ let mut parser = tree_sitter::Parser::new();
460
+ parser.set_language(&language.into()).unwrap();
461
+ let mut tree = parser.parse(source, None).unwrap();
462
+ assert!(!issue_signatures(&tree).is_empty());
463
+ assert_eq!(
464
+ &source.as_bytes()[..edit_start],
465
+ &edited.as_bytes()[..edit_start]
466
+ );
467
+ tree.edit(&InputEdit {
468
+ start_byte: edit_start,
469
+ old_end_byte: source.len(),
470
+ new_end_byte: edited.len(),
471
+ start_position: Point::new(0, edit_start),
472
+ old_end_position: Point::new(0, source.len()),
473
+ new_end_position: Point::new(0, edited.len()),
474
+ });
475
+ let incremental = parser.parse(edited, Some(&tree)).unwrap();
476
+ let fresh = parser.parse(edited, None).unwrap();
477
+ for (kind, parsed) in [("fresh", &fresh), ("incremental", &incremental)] {
478
+ let context = format!("{kind}: {source:?} -> {edited:?}");
479
+ assert!(!parsed.root_node().has_error(), "{context}");
480
+ assert!(issue_signatures(parsed).is_empty(), "{context}");
481
+ let mut nodes = vec![parsed.root_node()];
482
+ let mut groups = Vec::new();
483
+ while let Some(node) = nodes.pop() {
484
+ if node.kind() == group_kind && node.child_by_field_name("opening").is_some() {
485
+ groups.push(node);
486
+ }
487
+ for index in (0..node.named_child_count()).rev() {
488
+ nodes.push(node.named_child(index as u32).unwrap());
489
+ }
490
+ }
491
+ assert_eq!(groups.len(), 1, "{context}");
492
+ let [start, content, closing, end] = boundaries;
493
+ let group = groups[0];
494
+ assert_eq!(group.byte_range(), start..end, "{context}");
495
+ for (field, range) in [
496
+ ("opening", start..content),
497
+ ("expression", content..closing),
498
+ ("closing", closing..end),
499
+ ] {
500
+ let child = group.child_by_field_name(field).unwrap();
501
+ assert_eq!(child.byte_range(), range, "{field}: {context}");
502
+ assert_eq!(child.start_position(), Point::new(0, range.start));
503
+ assert_eq!(child.end_position(), Point::new(0, range.end));
504
+ }
505
+ }
506
+ assert_eq!(
507
+ incremental.root_node().to_sexp(),
508
+ fresh.root_node().to_sexp()
509
+ );
510
+ }
511
+ }
512
+
513
+ #[test]
514
+ fn balanced_regex_groups_preserve_depth_across_the_16_bit_boundary() {
515
+ for (language, mode, opening, closing, opening_kind, closing_kind) in [
516
+ (
517
+ super::LANGUAGE,
518
+ "BRE",
519
+ "\\(",
520
+ "\\)",
521
+ "back_open_parenthesis",
522
+ "back_close_parenthesis",
523
+ ),
524
+ (
525
+ super::LANGUAGE_ERE,
526
+ "ERE",
527
+ "(",
528
+ ")",
529
+ "open_parenthesis",
530
+ "close_parenthesis",
531
+ ),
532
+ ] {
533
+ let mut parser = tree_sitter::Parser::new();
534
+ parser
535
+ .set_language(&language.into())
536
+ .expect("generated grammar must load");
537
+ for depth in [65_535, 65_536] {
538
+ let source = format!("/{}a{}/p\n", opening.repeat(depth), closing.repeat(depth));
539
+ let tree = parser
540
+ .parse(&source, None)
541
+ .expect("parser must return a tree");
542
+ let root = tree.root_node();
543
+ assert_eq!(root.kind(), "script", "{mode}, depth {depth}");
544
+ assert_eq!(root.byte_range(), 0..source.len(), "{mode}, depth {depth}");
545
+ assert!(!root.has_error(), "{mode}, depth {depth}");
546
+
547
+ let mut cursor = root.walk();
548
+ let mut openings = 0;
549
+ let mut closings = 0;
550
+ 'walk: loop {
551
+ let node = cursor.node();
552
+ assert_ne!(node.kind(), "syntax_issue", "{mode}, depth {depth}");
553
+ if node.kind() == opening_kind {
554
+ openings += 1;
555
+ } else if node.kind() == closing_kind {
556
+ closings += 1;
557
+ }
558
+ if cursor.goto_first_child() {
559
+ continue;
560
+ }
561
+ while !cursor.goto_next_sibling() {
562
+ if !cursor.goto_parent() {
563
+ break 'walk;
564
+ }
565
+ }
566
+ }
567
+ assert_eq!(openings, depth, "{mode}, depth {depth}");
568
+ assert_eq!(closings, depth, "{mode}, depth {depth}");
569
+ }
570
+ }
571
+ }
70
572
  }
package/common/dsl.js CHANGED
@@ -36,4 +36,4 @@ function issueNode($, id) {
36
36
  return alias($[issueRuleName(id)], $.syntax_issue);
37
37
  }
38
38
 
39
- module.exports = { defineIssueRules, issueField, issueNode, namedExternal };
39
+ export { defineIssueRules, issueField, issueNode, namedExternal };