@konomanoasa/tree-sitter-yaml 0.3.0 → 0.5.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 +2 -2
- package/README.md +4 -5
- package/grammar.js +478 -267
- package/package.json +5 -5
- package/src/grammar.json +3230 -1995
- package/src/node-types.json +96 -2
- package/src/parser.c +62335 -164747
- package/src/scanner.c +1230 -1202
- package/test/bindings.test.rs +376 -13
- package/tree-sitter.json +1 -1
package/test/bindings.test.rs
CHANGED
|
@@ -166,6 +166,382 @@ fn parses_valid_source() {
|
|
|
166
166
|
Query::new(&language, grammar::HIGHLIGHTS_QUERY).unwrap();
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
#[test]
|
|
170
|
+
#[ignore = "Run with --release --ignored --nocapture to measure incremental reuse."]
|
|
171
|
+
fn profile_incremental_reuse() {
|
|
172
|
+
use std::collections::BTreeMap;
|
|
173
|
+
use std::sync::{Arc, Mutex};
|
|
174
|
+
|
|
175
|
+
for rows in [1200, 4800] {
|
|
176
|
+
for (case, prefix, entry, indentation_edit) in [
|
|
177
|
+
("flat", "", "k%: value\n", false),
|
|
178
|
+
("indented", "", " k%: value\n", false),
|
|
179
|
+
("commented", "", "k%: value # comment\n", false),
|
|
180
|
+
(
|
|
181
|
+
"nested",
|
|
182
|
+
"services:\n",
|
|
183
|
+
" k%:\n image: value\n ports:\n - 80\n",
|
|
184
|
+
false,
|
|
185
|
+
),
|
|
186
|
+
("block-values", "", "k%: |\n value\n", false),
|
|
187
|
+
("flow-values", "", "k%: [value]\n", false),
|
|
188
|
+
(
|
|
189
|
+
"plain-prefix",
|
|
190
|
+
"first: text\n continuation\n",
|
|
191
|
+
"k%: value\n",
|
|
192
|
+
true,
|
|
193
|
+
),
|
|
194
|
+
(
|
|
195
|
+
"quote-prefix",
|
|
196
|
+
"first: \"text\n continuation\"\n",
|
|
197
|
+
"k%: value\n",
|
|
198
|
+
true,
|
|
199
|
+
),
|
|
200
|
+
("block-prefix", "first: |\n body\n", "k%: value\n", true),
|
|
201
|
+
("flat-indent", "", "k%: value\n", true),
|
|
202
|
+
] {
|
|
203
|
+
if std::env::var("YAML_PROFILE_CASE")
|
|
204
|
+
.is_ok_and(|selected| selected != case)
|
|
205
|
+
{
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
let source = format!(
|
|
209
|
+
"{prefix}{}",
|
|
210
|
+
(0..rows)
|
|
211
|
+
.map(|i| entry.replace('%', &format!("{i:04}")))
|
|
212
|
+
.collect::<String>()
|
|
213
|
+
);
|
|
214
|
+
for (row, target) in [
|
|
215
|
+
(0, "key"),
|
|
216
|
+
(0, "value"),
|
|
217
|
+
(1, "value"),
|
|
218
|
+
(rows / 2, "value"),
|
|
219
|
+
(rows - 2, "value"),
|
|
220
|
+
] {
|
|
221
|
+
if indentation_edit && (row != 1 || target != "value") {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
let start = if indentation_edit {
|
|
225
|
+
source.find('\n').unwrap() + 1
|
|
226
|
+
} else {
|
|
227
|
+
let key = source.find(&format!("k{row:04}")).unwrap();
|
|
228
|
+
if target == "key" {
|
|
229
|
+
key
|
|
230
|
+
} else {
|
|
231
|
+
key + source[key..].find("value").unwrap()
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
let mut parser = parser();
|
|
235
|
+
let mut old = parser.parse(&source, None).unwrap();
|
|
236
|
+
let edited = edit_source(
|
|
237
|
+
source.as_bytes(),
|
|
238
|
+
&mut old,
|
|
239
|
+
start,
|
|
240
|
+
usize::from(!indentation_edit),
|
|
241
|
+
if indentation_edit { b" " } else { b"V" },
|
|
242
|
+
);
|
|
243
|
+
let fresh = parser.parse(&edited, None).unwrap();
|
|
244
|
+
assert!(
|
|
245
|
+
!fresh.root_node().has_error(),
|
|
246
|
+
"{case}/{rows}/{row}/{target}"
|
|
247
|
+
);
|
|
248
|
+
let result = parser.parse(&edited, Some(&old)).unwrap();
|
|
249
|
+
assert_eq!(
|
|
250
|
+
snapshot(result.root_node()),
|
|
251
|
+
snapshot(fresh.root_node()),
|
|
252
|
+
"{case}/{rows}/{row}/{target}"
|
|
253
|
+
);
|
|
254
|
+
let mut timings = [Vec::new(), Vec::new()];
|
|
255
|
+
for iteration in 0..17 {
|
|
256
|
+
for (index, previous) in [None, Some(&old)].into_iter().enumerate() {
|
|
257
|
+
let before = Instant::now();
|
|
258
|
+
let tree = parser.parse(&edited, previous).unwrap();
|
|
259
|
+
let elapsed = before.elapsed().as_secs_f64() * 1000.0;
|
|
260
|
+
std::hint::black_box(&tree);
|
|
261
|
+
if iteration >= 2 {
|
|
262
|
+
timings[index].push(elapsed);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for samples in &mut timings {
|
|
267
|
+
samples.sort_by(f64::total_cmp);
|
|
268
|
+
}
|
|
269
|
+
let counts = Arc::new(Mutex::new(BTreeMap::<String, usize>::new()));
|
|
270
|
+
let recorded = counts.clone();
|
|
271
|
+
parser.set_logger(Some(Box::new(move |kind, message| {
|
|
272
|
+
if kind == tree_sitter::LogType::Parse {
|
|
273
|
+
let event = if message.starts_with("reuse_node ")
|
|
274
|
+
|| message.starts_with("cant_reuse_node_is_fragile ")
|
|
275
|
+
{
|
|
276
|
+
message.split(",").next().unwrap()
|
|
277
|
+
} else {
|
|
278
|
+
message.split(' ').next().unwrap()
|
|
279
|
+
};
|
|
280
|
+
*recorded
|
|
281
|
+
.lock()
|
|
282
|
+
.unwrap()
|
|
283
|
+
.entry(event.to_owned())
|
|
284
|
+
.or_default() += 1;
|
|
285
|
+
}
|
|
286
|
+
})));
|
|
287
|
+
parser.parse(&edited, Some(&old)).unwrap();
|
|
288
|
+
parser.set_logger(None);
|
|
289
|
+
println!(
|
|
290
|
+
"{case},{rows},{row},{target},fresh_ms={:.3},incremental_ms={:.3},events={:?}",
|
|
291
|
+
timings[0][7],
|
|
292
|
+
timings[1][7],
|
|
293
|
+
counts.lock().unwrap()
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#[test]
|
|
301
|
+
fn completed_scalar_edits_reuse_following_entries() {
|
|
302
|
+
use std::sync::{
|
|
303
|
+
Arc,
|
|
304
|
+
atomic::{AtomicUsize, Ordering},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
for prefix in [
|
|
308
|
+
"first: text\n continuation\n",
|
|
309
|
+
"first: 'text\n continuation'\n",
|
|
310
|
+
"first: \"text\n continuation\"\n",
|
|
311
|
+
"first: |\n body\n",
|
|
312
|
+
"first: >\n body\n",
|
|
313
|
+
] {
|
|
314
|
+
for rows in [32, 512] {
|
|
315
|
+
let source = format!(
|
|
316
|
+
"{prefix}{}",
|
|
317
|
+
(0..rows)
|
|
318
|
+
.map(|i| format!("k{i}: value\n"))
|
|
319
|
+
.collect::<String>()
|
|
320
|
+
);
|
|
321
|
+
let mut parser = parser();
|
|
322
|
+
let mut old = parser.parse(&source, None).unwrap();
|
|
323
|
+
let edited = edit_source(
|
|
324
|
+
source.as_bytes(),
|
|
325
|
+
&mut old,
|
|
326
|
+
source.find('\n').unwrap() + 1,
|
|
327
|
+
0,
|
|
328
|
+
b" ",
|
|
329
|
+
);
|
|
330
|
+
let calls = Arc::new(AtomicUsize::new(0));
|
|
331
|
+
let recorded = calls.clone();
|
|
332
|
+
parser.set_logger(Some(Box::new(move |kind, message| {
|
|
333
|
+
if kind == tree_sitter::LogType::Parse
|
|
334
|
+
&& message.starts_with("lex_external ")
|
|
335
|
+
{
|
|
336
|
+
recorded.fetch_add(1, Ordering::Relaxed);
|
|
337
|
+
}
|
|
338
|
+
})));
|
|
339
|
+
let incremental = parser.parse(&edited, Some(&old)).unwrap();
|
|
340
|
+
parser.set_logger(None);
|
|
341
|
+
let count = calls.load(Ordering::Relaxed);
|
|
342
|
+
assert!(
|
|
343
|
+
(1..=64).contains(&count),
|
|
344
|
+
"Relexed unchanged entries after {prefix:?}, {rows} rows: {count} scanner calls"
|
|
345
|
+
);
|
|
346
|
+
let fresh = parser.parse(&edited, None).unwrap();
|
|
347
|
+
assert_eq!(
|
|
348
|
+
snapshot(incremental.root_node()),
|
|
349
|
+
snapshot(fresh.root_node())
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
#[test]
|
|
356
|
+
fn completed_construct_edits_reuse_following_entries() {
|
|
357
|
+
use std::sync::{
|
|
358
|
+
Arc,
|
|
359
|
+
atomic::{AtomicUsize, Ordering},
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
for (before, after, row) in [
|
|
363
|
+
("z: 0\na: # c\n", "z: 0\na: v # c\n", "k%: value\n"),
|
|
364
|
+
("z: 0\na: v # c\n", "z: 0\na: # c\n", "k%: value\n"),
|
|
365
|
+
("z: 0\na: x\n", "z: 0\na: !t x\n", "k%: value\n"),
|
|
366
|
+
("z: 0\na: !t x\n", "z: 0\na: !!t x\n", "k%: value\n"),
|
|
367
|
+
("z: 0\na: !t :x\n", "z: 0\na: !t x\n", "k%: value\n"),
|
|
368
|
+
("z: 0\na: !t[b]\n", "z: 0\na: !t [b]\n", "k%: value\n"),
|
|
369
|
+
("z: 0\na:\n b: c\n", "z: 0\na:\nb: c\n", "k%: value\n"),
|
|
370
|
+
("- z\n- [a: b]\n", "- z\n- [a]\n", "- k%\n"),
|
|
371
|
+
(
|
|
372
|
+
"a: 1\n...\n%TAG !e! tag:x:\n---\n",
|
|
373
|
+
"a: 1\n...\n%TAGX !e! tag:x:\n---\n",
|
|
374
|
+
"k%: value\n",
|
|
375
|
+
),
|
|
376
|
+
("x:\n- a\n- b\n", "x:\n- a\n- b: c\n", "- v%\n"),
|
|
377
|
+
("z: 0\nk: \"v\"\n", "z: 0\nk: v\n", "# c%\n"),
|
|
378
|
+
("z: 0\nk: [a]\n", "z: 0\nk: a\n", "# c%\n"),
|
|
379
|
+
] {
|
|
380
|
+
let start = before
|
|
381
|
+
.bytes()
|
|
382
|
+
.zip(after.bytes())
|
|
383
|
+
.take_while(|(a, b)| a == b)
|
|
384
|
+
.count();
|
|
385
|
+
let end = before[start..]
|
|
386
|
+
.bytes()
|
|
387
|
+
.rev()
|
|
388
|
+
.zip(after[start..].bytes().rev())
|
|
389
|
+
.take_while(|(a, b)| a == b)
|
|
390
|
+
.count();
|
|
391
|
+
for rows in [32, 512] {
|
|
392
|
+
let source = format!(
|
|
393
|
+
"{before}{}",
|
|
394
|
+
(0..rows)
|
|
395
|
+
.map(|i| row.replace('%', &i.to_string()))
|
|
396
|
+
.collect::<String>()
|
|
397
|
+
);
|
|
398
|
+
let mut parser = parser();
|
|
399
|
+
let mut old = parser.parse(&source, None).unwrap();
|
|
400
|
+
let edited = edit_source(
|
|
401
|
+
source.as_bytes(),
|
|
402
|
+
&mut old,
|
|
403
|
+
start,
|
|
404
|
+
before.len() - start - end,
|
|
405
|
+
&after.as_bytes()[start..after.len() - end],
|
|
406
|
+
);
|
|
407
|
+
let calls = Arc::new(AtomicUsize::new(0));
|
|
408
|
+
let recorded = calls.clone();
|
|
409
|
+
parser.set_logger(Some(Box::new(move |kind, message| {
|
|
410
|
+
if kind == tree_sitter::LogType::Parse
|
|
411
|
+
&& message.starts_with("lex_external ")
|
|
412
|
+
{
|
|
413
|
+
recorded.fetch_add(1, Ordering::Relaxed);
|
|
414
|
+
}
|
|
415
|
+
})));
|
|
416
|
+
let incremental = parser.parse(&edited, Some(&old)).unwrap();
|
|
417
|
+
parser.set_logger(None);
|
|
418
|
+
let count = calls.load(Ordering::Relaxed);
|
|
419
|
+
assert!(
|
|
420
|
+
(1..=64).contains(&count),
|
|
421
|
+
"Relexed unchanged entries after {before:?} -> {after:?}, {rows} rows: {count} scanner calls"
|
|
422
|
+
);
|
|
423
|
+
let fresh = parser.parse(&edited, None).unwrap();
|
|
424
|
+
assert_eq!(
|
|
425
|
+
snapshot(incremental.root_node()),
|
|
426
|
+
snapshot(fresh.root_node())
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
#[test]
|
|
433
|
+
fn mapping_layout_is_structural_between_pairs_and_at_eof() {
|
|
434
|
+
for (source, expected) in [
|
|
435
|
+
(
|
|
436
|
+
"a: b # one\n # two\nc: d\n \t",
|
|
437
|
+
&[
|
|
438
|
+
("comment", 5..10, "block_mapping", false),
|
|
439
|
+
("line_prefix", 11..13, "block_mapping", false),
|
|
440
|
+
("comment", 13..18, "block_mapping", false),
|
|
441
|
+
("line_prefix", 24..26, "block_mapping", false),
|
|
442
|
+
][..],
|
|
443
|
+
),
|
|
444
|
+
(
|
|
445
|
+
"a: # one\n # two\nc:\n \t",
|
|
446
|
+
&[
|
|
447
|
+
("comment", 3..8, "block_mapping", false),
|
|
448
|
+
("line_prefix", 9..11, "block_mapping", false),
|
|
449
|
+
("comment", 11..16, "block_mapping", false),
|
|
450
|
+
("line_prefix", 20..22, "block_mapping", false),
|
|
451
|
+
],
|
|
452
|
+
),
|
|
453
|
+
(
|
|
454
|
+
"? a\n# one\nb:\n\n# two",
|
|
455
|
+
&[
|
|
456
|
+
("comment", 4..9, "block_mapping", false),
|
|
457
|
+
("comment", 14..19, "block_mapping", false),
|
|
458
|
+
],
|
|
459
|
+
),
|
|
460
|
+
(
|
|
461
|
+
"# zero\n--- a:\n# one\n",
|
|
462
|
+
&[
|
|
463
|
+
("comment", 0..6, "stream", true),
|
|
464
|
+
("comment", 14..19, "block_mapping", false),
|
|
465
|
+
],
|
|
466
|
+
),
|
|
467
|
+
] {
|
|
468
|
+
let tree = parser().parse(source, None).unwrap();
|
|
469
|
+
assert!(!tree.root_node().has_error());
|
|
470
|
+
let mut layout = Vec::new();
|
|
471
|
+
let mut pending = vec![tree.root_node()];
|
|
472
|
+
while let Some(node) = pending.pop() {
|
|
473
|
+
if matches!(node.kind(), "comment" | "line_prefix") {
|
|
474
|
+
layout.push((
|
|
475
|
+
node.kind(),
|
|
476
|
+
node.byte_range(),
|
|
477
|
+
node.parent().unwrap().kind(),
|
|
478
|
+
node.is_extra(),
|
|
479
|
+
));
|
|
480
|
+
}
|
|
481
|
+
let mut cursor = node.walk();
|
|
482
|
+
pending.extend(node.children(&mut cursor));
|
|
483
|
+
}
|
|
484
|
+
layout.sort_by_key(|(_, range, _, _)| range.start);
|
|
485
|
+
assert_eq!(layout, expected, "{source:?}");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
#[test]
|
|
490
|
+
fn mapping_layout_reuses_unchanged_indentation_and_comments() {
|
|
491
|
+
use std::sync::{Arc, Mutex};
|
|
492
|
+
|
|
493
|
+
for (prefix, indent, suffix) in [
|
|
494
|
+
("", " ", "\n"),
|
|
495
|
+
("", "", " # comment\n"),
|
|
496
|
+
("", " ", "\n # comment\n\n"),
|
|
497
|
+
("root:\n", " ", " # comment\n"),
|
|
498
|
+
] {
|
|
499
|
+
for rows in [32, 512] {
|
|
500
|
+
let source = format!(
|
|
501
|
+
"{prefix}{}",
|
|
502
|
+
(0..rows)
|
|
503
|
+
.map(|i| format!("{indent}k{i}: value{suffix}"))
|
|
504
|
+
.collect::<String>()
|
|
505
|
+
);
|
|
506
|
+
for row in [1, rows / 2, rows - 2] {
|
|
507
|
+
let mut parser = parser();
|
|
508
|
+
let mut old = parser.parse(&source, None).unwrap();
|
|
509
|
+
assert!(!old.root_node().has_error());
|
|
510
|
+
let key = format!("k{row}: ");
|
|
511
|
+
let start = source.find(&key).unwrap() + key.len();
|
|
512
|
+
let edited = edit_source(source.as_bytes(), &mut old, start, 1, b"V");
|
|
513
|
+
let counts = Arc::new(Mutex::new([0, 0]));
|
|
514
|
+
let recorded = counts.clone();
|
|
515
|
+
parser.set_logger(Some(Box::new(move |kind, message| {
|
|
516
|
+
if kind == tree_sitter::LogType::Parse {
|
|
517
|
+
let mut counts = recorded.lock().unwrap();
|
|
518
|
+
if message.starts_with("lex_external ") {
|
|
519
|
+
counts[0] += 1;
|
|
520
|
+
}
|
|
521
|
+
if message == "no_lookahead_after_non_terminal_extra" {
|
|
522
|
+
counts[1] += 1;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
})));
|
|
526
|
+
let incremental = parser.parse(&edited, Some(&old)).unwrap();
|
|
527
|
+
parser.set_logger(None);
|
|
528
|
+
let fresh = parser.parse(&edited, None).unwrap();
|
|
529
|
+
assert!(!fresh.root_node().has_error());
|
|
530
|
+
assert_eq!(
|
|
531
|
+
snapshot(incremental.root_node()),
|
|
532
|
+
snapshot(fresh.root_node())
|
|
533
|
+
);
|
|
534
|
+
let counts = *counts.lock().unwrap();
|
|
535
|
+
assert!(
|
|
536
|
+
(1..=32).contains(&counts[0]),
|
|
537
|
+
"{prefix:?}/{indent:?}/{suffix:?}/{rows}/{row}: {counts:?}"
|
|
538
|
+
);
|
|
539
|
+
assert_eq!(counts[1], 0);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
169
545
|
#[test]
|
|
170
546
|
fn utf16_uses_original_byte_ranges() {
|
|
171
547
|
for big_endian in [false, true] {
|
|
@@ -234,19 +610,6 @@ fn utf16_unpaired_surrogates_have_exact_decoding_issue_ranges() {
|
|
|
234
610
|
assert_eq!(tree.root_node().end_byte(), 8);
|
|
235
611
|
}
|
|
236
612
|
|
|
237
|
-
#[test]
|
|
238
|
-
fn leading_bom_is_removed_by_input_layer_without_shifting_ranges() {
|
|
239
|
-
let tree = parser().parse("\u{feff}a", None).unwrap();
|
|
240
|
-
let scalar = tree
|
|
241
|
-
.root_node()
|
|
242
|
-
.named_child(0)
|
|
243
|
-
.unwrap()
|
|
244
|
-
.child_by_field_name("content")
|
|
245
|
-
.unwrap();
|
|
246
|
-
assert_eq!(scalar.byte_range(), 3..4);
|
|
247
|
-
assert_eq!(tree.root_node().end_byte(), 4);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
613
|
#[test]
|
|
251
614
|
fn deeply_nested_flow_nodes_and_expired_keys_keep_their_structure() {
|
|
252
615
|
let depth = 4096;
|