1use std::ops::Range;
27
28use crate::bracket_tree::{self, BracketItem};
29use crate::buffer::Buffer;
30use crate::patch::Patch;
31use crate::sum_tree::SumTree;
32
33#[cfg(any(test, debug_assertions))]
39thread_local! {
40 pub(crate) static ENCLOSING_WALKS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
41}
42
43pub(crate) fn is_bracket_byte(c: u8) -> bool {
46 matches!(c, b'(' | b')' | b'[' | b']' | b'{' | b'}')
47}
48
49#[derive(Copy, Clone, PartialEq, Eq, Debug)]
52pub struct Bracket {
53 pub offset: u32,
55 pub open: bool,
57 pub depth: u32,
59 pub partner: Option<u32>,
61}
62
63#[derive(Clone, Debug, Default)]
72pub struct Brackets {
73 tree: SumTree<BracketItem>,
74}
75
76impl Brackets {
77 #[must_use]
81 pub fn match_text(text: &str) -> Self {
82 Self { tree: bracket_tree::tree_from_text(text) }
83 }
84
85 #[must_use]
88 pub fn all(&self) -> Vec<Bracket> {
89 bracket_tree::derive_all(&self.tree)
90 }
91
92 #[must_use]
94 pub fn at(&self, offset: u32) -> Option<Bracket> {
95 bracket_tree::at(&self.tree, offset)
96 }
97
98 #[must_use]
106 pub fn foldable_partner(&self, offset: u32) -> Option<u32> {
107 bracket_tree::foldable_partner(&self.tree, offset)
108 }
109
110 #[must_use]
115 pub fn in_range(&self, range: Range<u32>) -> Vec<Bracket> {
116 self.in_range_iter(range).collect()
117 }
118
119 pub fn in_range_iter(&self, range: Range<u32>) -> impl Iterator<Item = Bracket> + '_ {
124 bracket_tree::in_range(&self.tree, range.start, range.end)
125 }
126
127 #[must_use]
132 pub fn active_pair(&self, caret: u32) -> Option<(u32, u32)> {
133 bracket_tree::active_pair(&self.tree, caret)
134 }
135
136 #[must_use]
140 pub fn enclosing_pair(&self, caret: u32) -> Option<(u32, u32)> {
141 bracket_tree::enclosing_pairs(&self.tree, caret).into_iter().next()
142 }
143
144 #[must_use]
147 pub fn innermost_enclosing_where(&self, caret: u32, pred: impl Fn(u32, u32) -> bool) -> Option<u32> {
148 bracket_tree::enclosing_pairs(&self.tree, caret)
149 .into_iter()
150 .find(|&(o, c)| pred(o, c))
151 .map(|(o, _)| o)
152 }
153
154 #[must_use]
158 pub fn enclosing_pair_of_range(&self, start: u32, end: u32) -> Option<(u32, u32)> {
159 bracket_tree::enclosing_pairs(&self.tree, start).into_iter().find(|&(_, close)| end <= close)
162 }
163
164 #[must_use]
169 pub fn enclosing_or_touching(&self, caret: u32) -> Vec<(u32, u32)> {
170 #[cfg(any(test, debug_assertions))]
171 ENCLOSING_WALKS.with(|c| c.set(c.get() + 1));
172 bracket_tree::enclosing_or_touching_pairs(&self.tree, caret)
173 }
174
175 pub fn apply_edit(&mut self, patch: &Patch, buffer: &Buffer) -> Range<u32> {
198 let (tree, region) = bracket_tree::apply_edit(&self.tree, patch, buffer);
199 self.tree = tree;
200 region
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::coords::Point;
208 use crate::transaction::{apply, EditOp};
209
210 fn bat(b: &Brackets, o: u32) -> Bracket {
211 b.at(o).expect("a bracket at that offset")
212 }
213
214 #[test]
215 fn matches_nested_pairs_with_depth() {
216 let b = Brackets::match_text("(a[b]{c})"); assert_eq!((bat(&b, 0).partner, bat(&b, 0).depth), (Some(8), 0));
218 assert_eq!((bat(&b, 2).partner, bat(&b, 2).depth), (Some(4), 1));
219 assert_eq!((bat(&b, 5).partner, bat(&b, 5).depth), (Some(7), 1));
220 assert_eq!((bat(&b, 8).partner, bat(&b, 8).depth), (Some(0), 0));
221 }
222
223 #[test]
224 fn flags_unmatched_brackets() {
225 let b = Brackets::match_text("(]"); assert_eq!(bat(&b, 0).partner, None);
227 assert_eq!(bat(&b, 1).partner, None);
228 }
229
230 #[test]
231 fn active_pair_prefers_the_bracket_left_of_the_caret() {
232 let b = Brackets::match_text("(){}"); assert_eq!(b.active_pair(0), Some((0, 1))); assert_eq!(b.active_pair(1), Some((0, 1))); assert_eq!(b.active_pair(2), Some((1, 0))); assert_eq!(b.active_pair(3), Some((2, 3))); assert_eq!(b.active_pair(4), Some((3, 2))); assert_eq!(Brackets::match_text("x").active_pair(1), None); }
240
241 #[test]
242 fn enclosing_pair_is_the_innermost_containing_the_caret() {
243 let b = Brackets::match_text("(a[b]{c})"); assert_eq!(b.enclosing_pair(1), Some((0, 8))); assert_eq!(b.enclosing_pair(3), Some((2, 4))); assert_eq!(b.enclosing_pair(6), Some((5, 7))); assert_eq!(b.enclosing_pair(0), None); assert_eq!(b.enclosing_pair(9), None); }
250
251 #[test]
257 fn enclosing_walk_matches_a_brute_scan_with_siblings() {
258 let b = Brackets::match_text("([1][2]{a(b)c}[3])xy");
261 let brute_enclosing = |caret: u32| {
263 b.all()
264 .iter()
265 .filter_map(|br| br.partner.map(|p| (br.offset, p)).filter(|_| br.open))
266 .filter(|&(o, c)| o < caret && caret <= c)
267 .min_by_key(|&(o, c)| c - o)
268 };
269 for caret in 0..=20 {
270 assert_eq!(b.enclosing_pair(caret), brute_enclosing(caret), "enclosing_pair({caret})");
271 }
272 assert_eq!(b.enclosing_pair(10), Some((9, 11)));
275 assert_eq!(b.enclosing_pair(14), Some((0, 17)));
277 let mut touch = b.enclosing_or_touching(9); touch.sort_unstable();
280 assert!(touch.contains(&(9, 11)), "the pair opening at the caret is touched");
281 assert!(touch.contains(&(7, 13)) && touch.contains(&(0, 17)), "and its enclosers");
282 let after_close = b.enclosing_or_touching(12); assert!(after_close.contains(&(9, 11)), "the pair closing at caret-1 is touched");
284 assert_eq!(b.enclosing_pair_of_range(10, 11), Some((9, 11))); assert_eq!(b.enclosing_pair_of_range(9, 12), Some((7, 13))); }
288
289 #[test]
290 fn active_pair_skips_unmatched_brackets() {
291 let b = Brackets::match_text("(]"); assert_eq!(b.active_pair(1), None); assert_eq!(b.active_pair(2), None);
294 }
295
296 #[test]
297 fn quotes_are_not_brackets() {
298 let b = Brackets::match_text("\"()\""); assert_eq!(b.all().len(), 2);
300 assert_eq!(bat(&b, 1).partner, Some(2));
301 }
302
303 #[test]
304 fn in_range_boundaries() {
305 let b = Brackets::match_text("(a[b]{c})"); let offsets = |s: &[Bracket]| s.iter().map(|x| x.offset).collect::<Vec<_>>();
307 assert!(b.in_range(0..0).is_empty()); assert!(b.in_range(3..3).is_empty());
309 assert_eq!(b.in_range(0..9), b.all()); assert_eq!(b.in_range(0..u32::MAX), b.all());
311 assert_eq!(offsets(&b.in_range(2..5)), vec![2, 4]); assert_eq!(offsets(&b.in_range(2..6)), vec![2, 4, 5]); assert_eq!(offsets(&b.in_range(8..9)), vec![8]); assert_eq!(offsets(&b.in_range(1..2)), Vec::<u32>::new()); #[allow(clippy::reversed_empty_ranges)]
316 let inverted = b.in_range(5..2);
317 assert!(inverted.is_empty()); }
319
320 fn assert_oracle(b: &Brackets, buf: &Buffer, ctx: &str) {
327 let oracle = Brackets::match_text(&buf.text());
328 assert_eq!(b.all(), oracle.all(), "brackets diverged from a scratch rebuild: {ctx}");
329 }
330
331 fn edit(b: &mut Brackets, buf: &mut Buffer, ops: Vec<EditOp>) -> Range<u32> {
334 let committed = apply(buf, ops).expect("test edits are disjoint and in-bounds");
335 b.apply_edit(committed.patch(), buf)
336 }
337
338 #[test]
346 fn incremental_matches_scratch_after_random_edits() {
347 let seed: u64 = 0x5EED_0BAD_F00D_2026;
348 let mut s = seed;
349 let mut rng = move || {
350 s ^= s << 13;
352 s ^= s >> 7;
353 s ^= s << 17;
354 s
355 };
356 let mut buf = Buffer::new(
357 "fn main() {\n let v = vec![1, (2), [3]];\n if (a[i]) { b(c[d]); }\n}\n",
358 )
359 .expect("fixture loads");
360 let mut b = Brackets::match_text(&buf.text());
361 const POOL: &[u8] = b"(){}[](){}[]\n\nab ";
362 for i in 0..500 {
363 let len = buf.len();
364 let kind = rng() % 12;
365 let ops = if kind < 5 {
366 let at = (rng() % (u64::from(len) + 1)) as u32;
367 let n = 1 + (rng() % 4) as usize;
368 let text: String =
369 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
370 vec![EditOp::insert(at, text)]
371 } else if kind < 7 {
372 let a = (rng() % (u64::from(len) + 1)) as u32;
373 let e = (a + 1 + (rng() % 12) as u32).min(len);
374 vec![EditOp::delete(a..e)]
375 } else if kind < 9 {
376 let a = (rng() % (u64::from(len) + 1)) as u32;
377 let e = (a + (rng() % 6) as u32).min(len);
378 let n = 1 + (rng() % 5) as usize;
379 let text: String =
380 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
381 vec![EditOp::new(a..e, text)]
382 } else if kind == 9 {
383 let a = (rng() % (u64::from(len) / 2 + 1)) as u32;
384 let c = (len / 2 + (rng() % (u64::from(len) / 2 + 1)) as u32).min(len);
385 vec![EditOp::insert(a, "("), EditOp::insert(c, ")")]
386 } else {
387 let n = 2 + (rng() % 3) as usize; let mut offs: Vec<u32> =
392 (0..n).map(|_| (rng() % (u64::from(len) + 1)) as u32).collect();
393 offs.sort_unstable();
394 offs.dedup();
395 offs.into_iter().map(|o| EditOp::insert(o, "z")).collect()
396 };
397 let committed = apply(&mut buf, ops.clone()).expect("generated ops are disjoint");
398 b.apply_edit(committed.patch(), &buf);
399 let ctx = format!("edit {i} (seed {seed:#x}): {ops:?}");
400 assert_oracle(&b, &buf, &ctx);
401 }
402 }
403
404 #[test]
409 fn prefix_opener_repoints_into_region() {
410 let mut buf = Buffer::new("{\nx\n}").expect("fixture loads");
413 let mut b = Brackets::match_text(&buf.text());
414 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}")]); assert_oracle(&b, &buf, "line 1 x → }");
416 assert_eq!(bat(&b, 0).partner, Some(2));
417 assert_eq!(bat(&b, 2).partner, Some(0));
418 assert_eq!((bat(&b, 4).partner, bat(&b, 4).depth), (None, 0));
419 }
420
421 #[test]
422 fn converged_shape_different_identity() {
423 let mut buf = Buffer::new("{\nx\n}").expect("fixture loads");
427 let mut b = Brackets::match_text(&buf.text());
428 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}{")]); assert_oracle(&b, &buf, "line 1 x → }{");
430 assert_eq!(bat(&b, 0).partner, Some(2));
431 assert_eq!(bat(&b, 3).partner, Some(5));
432 assert_eq!(bat(&b, 5).partner, Some(3));
433 }
434
435 #[test]
436 fn converged_shape_crosses_into_the_suffix() {
437 let mut buf = Buffer::new("{\nx\nq\n}").expect("fixture loads");
440 let mut b = Brackets::match_text(&buf.text());
441 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}{")]); assert_oracle(&b, &buf, "line 1 x → }{ with a clean row before the closer");
443 assert_eq!(bat(&b, 0).partner, Some(2));
444 assert_eq!(bat(&b, 3).partner, Some(7));
445 assert_eq!(bat(&b, 7).partner, Some(3));
446 }
447
448 #[test]
449 fn crossing_repairs_mixed_seed_and_region_entries() {
450 let mut buf = Buffer::new("{\n(\nb\n)}").expect("fixture loads");
453 let mut b = Brackets::match_text(&buf.text());
454 edit(&mut b, &mut buf, vec![EditOp::new(2..3, ")(")]); assert_oracle(&b, &buf, "mixed carried stack");
456 assert_eq!((bat(&b, 2).partner, bat(&b, 2).depth), (None, 0)); assert_eq!(bat(&b, 3).partner, Some(7)); assert_eq!(bat(&b, 7).partner, Some(3));
459 assert_eq!(bat(&b, 0).partner, Some(8)); assert_eq!(bat(&b, 8).partner, Some(0));
461 }
462
463 #[test]
464 fn pair_spanning_edit_shifts_the_closer() {
465 let mut buf = Buffer::new("{\naaa\nzzz\n}").expect("fixture loads");
469 let mut b = Brackets::match_text(&buf.text());
470 edit(&mut b, &mut buf, vec![EditOp::new(2..5, "aaaaa")]); assert_oracle(&b, &buf, "interior growth inside a multi-line pair");
472 assert_eq!(bat(&b, 0).partner, Some(12)); assert_eq!(bat(&b, 12).partner, Some(0)); }
475
476 #[test]
477 fn seed_leftover_cleared_to_none() {
478 let mut buf = Buffer::new("{\n}").expect("fixture loads");
481 let mut b = Brackets::match_text(&buf.text());
482 edit(&mut b, &mut buf, vec![EditOp::delete(2..3)]); assert_oracle(&b, &buf, "closer deleted");
484 assert_eq!(b.all().len(), 1);
485 assert_eq!((bat(&b, 0).partner, bat(&b, 0).open), (None, true));
486 }
487
488 #[test]
492 fn keystroke_deep_in_2000_lines() {
493 let doc = vec!["fn f() { g(a[i], (b)); }"; 2000].join("\n");
494 let mut buf = Buffer::new(&doc).expect("fixture loads");
495 let mut b = Brackets::match_text(&buf.text());
496 let target = buf.point_to_offset(Point::new(1000, 3));
497 edit(&mut b, &mut buf, vec![EditOp::insert(target, "x")]);
498 assert_oracle(&b, &buf, "keystroke at line 1000");
499 }
500
501 #[test]
502 fn balanced_pair_insert_deep_in_a_document() {
503 let doc = vec!["fn f() { g(a[i], (b)); }"; 2000].join("\n");
504 let mut buf = Buffer::new(&doc).expect("fixture loads");
505 let mut b = Brackets::match_text(&buf.text());
506 let target = buf.point_to_offset(Point::new(1000, 8));
507 edit(&mut b, &mut buf, vec![EditOp::insert(target, "()")]);
508 assert_oracle(&b, &buf, "balanced pair at line 1000");
509 }
510
511 #[test]
512 fn structural_edit_after_many_sibling_blocks() {
513 const BLOCKS: usize = 20;
517 let mut text = String::new();
518 for _ in 0..BLOCKS {
519 text.push_str("{[[[[[[[[[[]]]]]]]]]]}\n");
520 }
521 let tail = text.len() as u32;
522 text.push_str("tail");
523 let mut buf = Buffer::new(&text).expect("fixture loads");
524 let mut b = Brackets::match_text(&buf.text());
525 edit(&mut b, &mut buf, vec![EditOp::insert(tail, "\n")]);
526 assert_oracle(&b, &buf, "newline after N closed sibling blocks");
527 }
528
529 #[test]
530 fn structure_neutral_insert_at_document_start() {
531 let mut lines = vec!["alpha", "beta"]; lines.extend(std::iter::repeat_n("x(y[z]) {w}", 1998));
533 let doc = lines.join("\n");
534 let mut buf = Buffer::new(&doc).expect("fixture loads");
535 let mut b = Brackets::match_text(&buf.text());
536 let region = edit(&mut b, &mut buf, vec![EditOp::insert(0, "q")]);
537 assert_eq!(region, 0..0, "a structure-neutral insert reconciles nothing");
538 assert_oracle(&b, &buf, "letter at offset 0");
539 }
540
541 #[test]
542 fn scattered_multicursor_typing_is_structure_neutral() {
543 let doc = vec!["fn f() { g(a[i], (b)); }"; 500].join("\n");
547 let mut buf = Buffer::new(&doc).expect("fixture loads");
548 let mut b = Brackets::match_text(&buf.text());
549 let inserts: Vec<EditOp> = (0..500)
550 .step_by(50)
551 .map(|row| EditOp::insert(buf.point_to_offset(Point::new(row, 0)), "x"))
552 .collect();
553 let region = edit(&mut b, &mut buf, inserts);
554 assert_eq!(region, 0..0, "scattered structure-neutral edits reconcile nothing");
555 assert_oracle(&b, &buf, "scattered multi-cursor letter inserts");
556 }
557
558 #[test]
559 fn unbalanced_opener_at_the_top() {
560 let doc = vec!["(x)"; 100].join("\n");
564 let mut buf = Buffer::new(&doc).expect("fixture loads");
565 let mut b = Brackets::match_text(&buf.text());
566 edit(&mut b, &mut buf, vec![EditOp::insert(0, "{")]);
567 assert_oracle(&b, &buf, "unbalanced opener at the top");
568 assert_eq!((bat(&b, 0).partner, bat(&b, 0).open), (None, true));
569 }
570
571 #[test]
574 fn empty_document_first_insert_and_full_delete() {
575 let mut buf = Buffer::new("").expect("empty loads");
576 let mut b = Brackets::match_text(&buf.text());
577 assert_eq!(b.apply_edit(&Patch::new(), &buf), 0..0);
579 edit(&mut b, &mut buf, vec![EditOp::insert(0, "({\n[")]);
580 assert_oracle(&b, &buf, "first insert into an empty document");
581 let len = buf.len();
582 edit(&mut b, &mut buf, vec![EditOp::delete(0..len)]);
583 assert_oracle(&b, &buf, "delete everything");
584 assert!(b.all().is_empty());
585 }
586
587 #[test]
588 fn edit_at_eof_appends() {
589 let mut buf = Buffer::new("(a\n[b").expect("fixture loads");
590 let mut b = Brackets::match_text(&buf.text());
591 let len = buf.len();
592 edit(&mut b, &mut buf, vec![EditOp::insert(len, "])")]); assert_oracle(&b, &buf, "append at EOF");
594 assert_eq!(bat(&b, 0).partner, Some(6)); assert_eq!(bat(&b, 3).partner, Some(5)); }
597
598 #[test]
599 fn whole_document_replace() {
600 let mut buf = Buffer::new("(a)\n[b]\n{c}").expect("fixture loads");
601 let mut b = Brackets::match_text(&buf.text());
602 let len = buf.len();
603 edit(&mut b, &mut buf, vec![EditOp::new(0..len, "{new\n(doc)\n}")]);
604 assert_oracle(&b, &buf, "whole-document replace");
605 let len = buf.len();
606 edit(&mut b, &mut buf, vec![EditOp::new(0..len, "[]")]);
607 assert_oracle(&b, &buf, "whole-document replace, fewer lines");
608 }
609
610 #[test]
611 fn edit_ending_exactly_on_a_line_boundary() {
612 let mut buf = Buffer::new("(a)\n[b]\n{c}").expect("fixture loads");
613 let mut b = Brackets::match_text(&buf.text());
614 let target = buf.point_to_offset(Point::new(1, 0));
615 edit(&mut b, &mut buf, vec![EditOp::insert(target, "(q)\n")]);
616 assert_oracle(&b, &buf, "insert ending with a newline");
617 let a = buf.point_to_offset(Point::new(1, 0));
618 let e = buf.point_to_offset(Point::new(2, 0));
619 edit(&mut b, &mut buf, vec![EditOp::delete(a..e)]);
620 assert_oracle(&b, &buf, "delete ending at a line start");
621 }
622
623 #[test]
624 fn multi_edit_transaction_with_scattered_inserts() {
625 let mut buf = Buffer::new("aaa\nbbb\nccc\nddd\neee").expect("fixture loads");
627 let mut b = Brackets::match_text(&buf.text());
628 let p1 = buf.point_to_offset(Point::new(0, 1));
629 let p2 = buf.point_to_offset(Point::new(4, 1));
630 edit(&mut b, &mut buf, vec![EditOp::insert(p1, "("), EditOp::insert(p2, ")")]);
631 assert_oracle(&b, &buf, "scattered two-insert transaction");
632 assert_eq!(bat(&b, 1).partner, Some(18)); }
634}