1use std::ops::Range;
32
33use crate::bracket_tree::{self, BracketItem};
34use crate::buffer::Buffer;
35use crate::patch::Patch;
36use crate::sum_tree::SumTree;
37
38#[cfg(any(test, debug_assertions))]
44thread_local! {
45 pub(crate) static ENCLOSING_WALKS: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
46}
47
48pub(crate) fn is_bracket_byte(c: u8) -> bool {
51 matches!(c, b'(' | b')' | b'[' | b']' | b'{' | b'}')
52}
53
54#[derive(Clone, Debug, Default, PartialEq, Eq)]
62pub struct BracketConfig {
63 pub line_comments: Vec<String>,
66 pub string_delims: Vec<u8>,
69 pub char_delim: Option<u8>,
72}
73
74impl BracketConfig {
75 #[must_use]
78 pub fn is_active(&self) -> bool {
79 !self.line_comments.is_empty() || !self.string_delims.is_empty() || self.char_delim.is_some()
80 }
81}
82
83#[derive(Clone, Copy, PartialEq, Eq)]
87enum Skip {
88 Code,
89 LineComment,
90 Str(u8),
92 Char(u8),
94}
95
96pub(crate) struct SkipContext<'a> {
104 bytes: &'a [u8],
105 i: usize,
106 cfg: &'a BracketConfig,
107 state: Skip,
108 escaped: bool,
110}
111
112impl<'a> SkipContext<'a> {
113 pub(crate) fn new(bytes: &'a [u8], cfg: &'a BracketConfig) -> Self {
114 Self { bytes, i: 0, cfg, state: Skip::Code, escaped: false }
115 }
116
117 fn step(&mut self, b: u8) -> bool {
120 if b == b'\n' {
122 let was_skip = self.state != Skip::Code;
123 self.state = Skip::Code;
124 self.escaped = false;
125 return was_skip; }
127 match self.state {
128 Skip::LineComment => true,
129 Skip::Str(delim) | Skip::Char(delim) => {
130 if self.escaped {
131 self.escaped = false;
132 } else if b == b'\\' {
133 self.escaped = true;
134 } else if b == delim {
135 self.state = Skip::Code;
136 }
137 true
138 }
139 Skip::Code => {
140 let opens_comment = self.cfg.line_comments.iter().any(|m| {
144 let mb = m.as_bytes();
145 mb.first() == Some(&b) && self.bytes[self.i..].starts_with(mb)
146 });
147 if opens_comment {
148 self.state = Skip::LineComment;
149 return true;
150 }
151 if self.cfg.string_delims.contains(&b) {
152 self.state = Skip::Str(b);
153 return true;
154 }
155 if Some(b) == self.cfg.char_delim {
156 self.state = Skip::Char(b);
157 return true;
158 }
159 false
160 }
161 }
162 }
163}
164
165impl Iterator for SkipContext<'_> {
166 type Item = (u32, u8, bool);
168
169 fn next(&mut self) -> Option<Self::Item> {
170 let b = *self.bytes.get(self.i)?;
171 let off = self.i as u32;
172 let skip = self.step(b);
173 self.i += 1;
174 Some((off, b, skip))
175 }
176}
177
178#[derive(Copy, Clone, PartialEq, Eq, Debug)]
181pub struct Bracket {
182 pub offset: u32,
184 pub open: bool,
186 pub depth: u32,
188 pub partner: Option<u32>,
190}
191
192#[derive(Clone, Debug, Default)]
201pub struct Brackets {
202 tree: SumTree<BracketItem>,
203}
204
205impl Brackets {
206 #[must_use]
210 pub fn match_text(text: &str) -> Self {
211 Self { tree: bracket_tree::tree_from_text(text) }
212 }
213
214 #[must_use]
218 pub fn match_text_with(text: &str, cfg: &BracketConfig) -> Self {
219 if !cfg.is_active() {
220 return Self::match_text(text);
221 }
222 Self { tree: bracket_tree::tree_from_text_with(text, cfg) }
223 }
224
225 #[must_use]
228 pub fn all(&self) -> Vec<Bracket> {
229 bracket_tree::derive_all(&self.tree)
230 }
231
232 #[must_use]
234 pub fn at(&self, offset: u32) -> Option<Bracket> {
235 bracket_tree::at(&self.tree, offset)
236 }
237
238 #[must_use]
246 pub fn foldable_partner(&self, offset: u32) -> Option<u32> {
247 bracket_tree::foldable_partner(&self.tree, offset)
248 }
249
250 #[must_use]
255 pub fn in_range(&self, range: Range<u32>) -> Vec<Bracket> {
256 self.in_range_iter(range).collect()
257 }
258
259 pub fn in_range_iter(&self, range: Range<u32>) -> impl Iterator<Item = Bracket> + '_ {
264 bracket_tree::in_range(&self.tree, range.start, range.end)
265 }
266
267 #[must_use]
272 pub fn active_pair(&self, caret: u32) -> Option<(u32, u32)> {
273 bracket_tree::active_pair(&self.tree, caret)
274 }
275
276 #[must_use]
280 pub fn enclosing_pair(&self, caret: u32) -> Option<(u32, u32)> {
281 bracket_tree::enclosing_pairs(&self.tree, caret).into_iter().next()
282 }
283
284 #[must_use]
287 pub fn innermost_enclosing_where(&self, caret: u32, pred: impl Fn(u32, u32) -> bool) -> Option<u32> {
288 bracket_tree::enclosing_pairs(&self.tree, caret)
289 .into_iter()
290 .find(|&(o, c)| pred(o, c))
291 .map(|(o, _)| o)
292 }
293
294 #[must_use]
298 pub fn enclosing_pair_of_range(&self, start: u32, end: u32) -> Option<(u32, u32)> {
299 bracket_tree::enclosing_pairs(&self.tree, start).into_iter().find(|&(_, close)| end <= close)
302 }
303
304 #[must_use]
309 pub fn enclosing_or_touching(&self, caret: u32) -> Vec<(u32, u32)> {
310 #[cfg(any(test, debug_assertions))]
311 ENCLOSING_WALKS.with(|c| c.set(c.get() + 1));
312 bracket_tree::enclosing_or_touching_pairs(&self.tree, caret)
313 }
314
315 pub fn apply_edit(&mut self, patch: &Patch, buffer: &Buffer, cfg: &BracketConfig) -> Range<u32> {
338 let (tree, region) = bracket_tree::apply_edit(&self.tree, patch, buffer, cfg);
339 self.tree = tree;
340 region
341 }
342}
343
344#[cfg(test)]
345mod tests {
346 use super::*;
347 use crate::coords::Point;
348 use crate::transaction::{apply, EditOp};
349
350 fn bat(b: &Brackets, o: u32) -> Bracket {
351 b.at(o).expect("a bracket at that offset")
352 }
353
354 fn offs(b: &Brackets) -> Vec<u32> {
356 b.all().iter().map(|x| x.offset).collect()
357 }
358
359 fn cfg() -> BracketConfig {
360 BracketConfig {
361 line_comments: vec!["//".into()],
362 string_delims: vec![b'"'],
363 char_delim: None,
364 }
365 }
366
367 #[test]
368 fn inactive_config_is_identical_to_plain_match() {
369 let text = "(a)//(b)\n\"(\"[]";
370 assert_eq!(
371 Brackets::match_text_with(text, &BracketConfig::default()).all(),
372 Brackets::match_text(text).all(),
373 );
374 }
375
376 #[test]
377 fn skips_brackets_in_line_comments_and_resets_at_newline() {
378 let b = Brackets::match_text_with("(a) // (b)\n(c)", &cfg());
380 assert_eq!(offs(&b), vec![0, 2, 11, 13], "the comment's ( ) at 7,9 are skipped");
381 }
382
383 #[test]
384 fn skips_brackets_in_strings() {
385 let b = Brackets::match_text_with("\"(\"(", &cfg());
387 assert_eq!(offs(&b), vec![3]);
388 }
389
390 #[test]
391 fn precedence_is_positional_not_a_fixed_layer_order() {
392 let b = Brackets::match_text_with("\"//\"()", &cfg());
396 assert_eq!(offs(&b), vec![4, 5]);
397 }
398
399 #[test]
400 fn a_backslash_escapes_the_closing_quote() {
401 let b = Brackets::match_text_with("\"\\\")\"(", &cfg());
404 assert_eq!(offs(&b), vec![5]);
405 }
406
407 #[test]
408 fn char_delim_is_opt_in() {
409 let c = BracketConfig { char_delim: Some(b'\''), ..Default::default() };
410 assert_eq!(offs(&Brackets::match_text_with("'(')", &c)), vec![3]);
412 assert_eq!(offs(&Brackets::match_text_with("'(')", &BracketConfig::default())), vec![1, 3]);
414 }
415
416 #[test]
417 fn matches_nested_pairs_with_depth() {
418 let b = Brackets::match_text("(a[b]{c})"); assert_eq!((bat(&b, 0).partner, bat(&b, 0).depth), (Some(8), 0));
420 assert_eq!((bat(&b, 2).partner, bat(&b, 2).depth), (Some(4), 1));
421 assert_eq!((bat(&b, 5).partner, bat(&b, 5).depth), (Some(7), 1));
422 assert_eq!((bat(&b, 8).partner, bat(&b, 8).depth), (Some(0), 0));
423 }
424
425 #[test]
426 fn flags_unmatched_brackets() {
427 let b = Brackets::match_text("(]"); assert_eq!(bat(&b, 0).partner, None);
429 assert_eq!(bat(&b, 1).partner, None);
430 }
431
432 #[test]
433 fn active_pair_prefers_the_bracket_left_of_the_caret() {
434 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); }
442
443 #[test]
444 fn enclosing_pair_is_the_innermost_containing_the_caret() {
445 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); }
452
453 #[test]
459 fn enclosing_walk_matches_a_brute_scan_with_siblings() {
460 let b = Brackets::match_text("([1][2]{a(b)c}[3])xy");
463 let brute_enclosing = |caret: u32| {
465 b.all()
466 .iter()
467 .filter_map(|br| br.partner.map(|p| (br.offset, p)).filter(|_| br.open))
468 .filter(|&(o, c)| o < caret && caret <= c)
469 .min_by_key(|&(o, c)| c - o)
470 };
471 for caret in 0..=20 {
472 assert_eq!(b.enclosing_pair(caret), brute_enclosing(caret), "enclosing_pair({caret})");
473 }
474 assert_eq!(b.enclosing_pair(10), Some((9, 11)));
477 assert_eq!(b.enclosing_pair(14), Some((0, 17)));
479 let mut touch = b.enclosing_or_touching(9); touch.sort_unstable();
482 assert!(touch.contains(&(9, 11)), "the pair opening at the caret is touched");
483 assert!(touch.contains(&(7, 13)) && touch.contains(&(0, 17)), "and its enclosers");
484 let after_close = b.enclosing_or_touching(12); assert!(after_close.contains(&(9, 11)), "the pair closing at caret-1 is touched");
486 assert_eq!(b.enclosing_pair_of_range(10, 11), Some((9, 11))); assert_eq!(b.enclosing_pair_of_range(9, 12), Some((7, 13))); }
490
491 #[test]
492 fn active_pair_skips_unmatched_brackets() {
493 let b = Brackets::match_text("(]"); assert_eq!(b.active_pair(1), None); assert_eq!(b.active_pair(2), None);
496 }
497
498 #[test]
499 fn quotes_are_not_brackets() {
500 let b = Brackets::match_text("\"()\""); assert_eq!(b.all().len(), 2);
502 assert_eq!(bat(&b, 1).partner, Some(2));
503 }
504
505 #[test]
506 fn in_range_boundaries() {
507 let b = Brackets::match_text("(a[b]{c})"); let offsets = |s: &[Bracket]| s.iter().map(|x| x.offset).collect::<Vec<_>>();
509 assert!(b.in_range(0..0).is_empty()); assert!(b.in_range(3..3).is_empty());
511 assert_eq!(b.in_range(0..9), b.all()); assert_eq!(b.in_range(0..u32::MAX), b.all());
513 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)]
518 let inverted = b.in_range(5..2);
519 assert!(inverted.is_empty()); }
521
522 fn assert_oracle(b: &Brackets, buf: &Buffer, ctx: &str) {
529 let oracle = Brackets::match_text(&buf.text());
530 assert_eq!(b.all(), oracle.all(), "brackets diverged from a scratch rebuild: {ctx}");
531 }
532
533 fn edit(b: &mut Brackets, buf: &mut Buffer, ops: Vec<EditOp>) -> Range<u32> {
536 let committed = apply(buf, ops).expect("test edits are disjoint and in-bounds");
537 b.apply_edit(committed.patch(), buf, &BracketConfig::default())
538 }
539
540 #[test]
548 fn incremental_matches_scratch_after_random_edits() {
549 let seed: u64 = 0x5EED_0BAD_F00D_2026;
550 let mut s = seed;
551 let mut rng = move || {
552 s ^= s << 13;
554 s ^= s >> 7;
555 s ^= s << 17;
556 s
557 };
558 let mut buf = Buffer::new(
559 "fn main() {\n let v = vec![1, (2), [3]];\n if (a[i]) { b(c[d]); }\n}\n",
560 )
561 .expect("fixture loads");
562 let mut b = Brackets::match_text(&buf.text());
563 const POOL: &[u8] = b"(){}[](){}[]\n\nab ";
564 for i in 0..500 {
565 let len = buf.len();
566 let kind = rng() % 12;
567 let ops = if kind < 5 {
568 let at = (rng() % (u64::from(len) + 1)) as u32;
569 let n = 1 + (rng() % 4) as usize;
570 let text: String =
571 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
572 vec![EditOp::insert(at, text)]
573 } else if kind < 7 {
574 let a = (rng() % (u64::from(len) + 1)) as u32;
575 let e = (a + 1 + (rng() % 12) as u32).min(len);
576 vec![EditOp::delete(a..e)]
577 } else if kind < 9 {
578 let a = (rng() % (u64::from(len) + 1)) as u32;
579 let e = (a + (rng() % 6) as u32).min(len);
580 let n = 1 + (rng() % 5) as usize;
581 let text: String =
582 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
583 vec![EditOp::new(a..e, text)]
584 } else if kind == 9 {
585 let a = (rng() % (u64::from(len) / 2 + 1)) as u32;
586 let c = (len / 2 + (rng() % (u64::from(len) / 2 + 1)) as u32).min(len);
587 vec![EditOp::insert(a, "("), EditOp::insert(c, ")")]
588 } else {
589 let n = 2 + (rng() % 3) as usize; let mut offs: Vec<u32> =
594 (0..n).map(|_| (rng() % (u64::from(len) + 1)) as u32).collect();
595 offs.sort_unstable();
596 offs.dedup();
597 offs.into_iter().map(|o| EditOp::insert(o, "z")).collect()
598 };
599 let committed = apply(&mut buf, ops.clone()).expect("generated ops are disjoint");
600 b.apply_edit(committed.patch(), &buf, &BracketConfig::default());
601 let ctx = format!("edit {i} (seed {seed:#x}): {ops:?}");
602 assert_oracle(&b, &buf, &ctx);
603 }
604 }
605
606 #[test]
614 fn incremental_matches_scratch_with_comment_awareness() {
615 let cfg = BracketConfig {
616 line_comments: vec!["//".into()],
617 string_delims: vec![b'"'],
618 char_delim: None,
619 };
620 let seed: u64 = 0xC0FF_EE00_2026_1234;
621 let mut s = seed;
622 let mut rng = move || {
623 s ^= s << 13;
624 s ^= s >> 7;
625 s ^= s << 17;
626 s
627 };
628 let mut buf = Buffer::new("fn f() { // (a)\n let s = \"([{\";\n g([1]);\n}\n")
629 .expect("fixture loads");
630 let mut b = Brackets::match_text_with(&buf.text(), &cfg);
631 const POOL: &[u8] = b"(){}[]//\"\"\\\n ab";
633 for i in 0..600 {
634 let len = buf.len();
635 let kind = rng() % 12;
636 let ops = if kind < 5 {
637 let at = (rng() % (u64::from(len) + 1)) as u32;
638 let n = 1 + (rng() % 4) as usize;
639 let text: String =
640 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
641 vec![EditOp::insert(at, text)]
642 } else if kind < 7 {
643 let a = (rng() % (u64::from(len) + 1)) as u32;
644 let e = (a + 1 + (rng() % 10) as u32).min(len);
645 vec![EditOp::delete(a..e)]
646 } else if kind < 9 {
647 let a = (rng() % (u64::from(len) + 1)) as u32;
648 let e = (a + (rng() % 6) as u32).min(len);
649 let n = 1 + (rng() % 5) as usize;
650 let text: String =
651 (0..n).map(|_| POOL[(rng() % POOL.len() as u64) as usize] as char).collect();
652 vec![EditOp::new(a..e, text)]
653 } else {
654 let n = 2 + (rng() % 3) as usize;
657 let mut offs: Vec<u32> =
658 (0..n).map(|_| (rng() % (u64::from(len) + 1)) as u32).collect();
659 offs.sort_unstable();
660 offs.dedup();
661 offs.into_iter()
662 .map(|o| {
663 let byte = POOL[(rng() % POOL.len() as u64) as usize];
664 EditOp::insert(o, (byte as char).to_string())
665 })
666 .collect()
667 };
668 let committed = apply(&mut buf, ops.clone()).expect("generated ops are disjoint");
669 b.apply_edit(committed.patch(), &buf, &cfg);
670 let oracle = Brackets::match_text_with(&buf.text(), &cfg);
671 assert_eq!(
672 b.all(),
673 oracle.all(),
674 "aware brackets diverged from a scratch rebuild at edit {i} (seed {seed:#x}): {ops:?}",
675 );
676 }
677 }
678
679 #[test]
684 fn prefix_opener_repoints_into_region() {
685 let mut buf = Buffer::new("{\nx\n}").expect("fixture loads");
688 let mut b = Brackets::match_text(&buf.text());
689 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}")]); assert_oracle(&b, &buf, "line 1 x → }");
691 assert_eq!(bat(&b, 0).partner, Some(2));
692 assert_eq!(bat(&b, 2).partner, Some(0));
693 assert_eq!((bat(&b, 4).partner, bat(&b, 4).depth), (None, 0));
694 }
695
696 #[test]
697 fn converged_shape_different_identity() {
698 let mut buf = Buffer::new("{\nx\n}").expect("fixture loads");
702 let mut b = Brackets::match_text(&buf.text());
703 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}{")]); assert_oracle(&b, &buf, "line 1 x → }{");
705 assert_eq!(bat(&b, 0).partner, Some(2));
706 assert_eq!(bat(&b, 3).partner, Some(5));
707 assert_eq!(bat(&b, 5).partner, Some(3));
708 }
709
710 #[test]
711 fn converged_shape_crosses_into_the_suffix() {
712 let mut buf = Buffer::new("{\nx\nq\n}").expect("fixture loads");
715 let mut b = Brackets::match_text(&buf.text());
716 edit(&mut b, &mut buf, vec![EditOp::new(2..3, "}{")]); assert_oracle(&b, &buf, "line 1 x → }{ with a clean row before the closer");
718 assert_eq!(bat(&b, 0).partner, Some(2));
719 assert_eq!(bat(&b, 3).partner, Some(7));
720 assert_eq!(bat(&b, 7).partner, Some(3));
721 }
722
723 #[test]
724 fn crossing_repairs_mixed_seed_and_region_entries() {
725 let mut buf = Buffer::new("{\n(\nb\n)}").expect("fixture loads");
728 let mut b = Brackets::match_text(&buf.text());
729 edit(&mut b, &mut buf, vec![EditOp::new(2..3, ")(")]); assert_oracle(&b, &buf, "mixed carried stack");
731 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));
734 assert_eq!(bat(&b, 0).partner, Some(8)); assert_eq!(bat(&b, 8).partner, Some(0));
736 }
737
738 #[test]
739 fn pair_spanning_edit_shifts_the_closer() {
740 let mut buf = Buffer::new("{\naaa\nzzz\n}").expect("fixture loads");
744 let mut b = Brackets::match_text(&buf.text());
745 edit(&mut b, &mut buf, vec![EditOp::new(2..5, "aaaaa")]); assert_oracle(&b, &buf, "interior growth inside a multi-line pair");
747 assert_eq!(bat(&b, 0).partner, Some(12)); assert_eq!(bat(&b, 12).partner, Some(0)); }
750
751 #[test]
752 fn seed_leftover_cleared_to_none() {
753 let mut buf = Buffer::new("{\n}").expect("fixture loads");
756 let mut b = Brackets::match_text(&buf.text());
757 edit(&mut b, &mut buf, vec![EditOp::delete(2..3)]); assert_oracle(&b, &buf, "closer deleted");
759 assert_eq!(b.all().len(), 1);
760 assert_eq!((bat(&b, 0).partner, bat(&b, 0).open), (None, true));
761 }
762
763 #[test]
767 fn keystroke_deep_in_2000_lines() {
768 let doc = vec!["fn f() { g(a[i], (b)); }"; 2000].join("\n");
769 let mut buf = Buffer::new(&doc).expect("fixture loads");
770 let mut b = Brackets::match_text(&buf.text());
771 let target = buf.point_to_offset(Point::new(1000, 3));
772 edit(&mut b, &mut buf, vec![EditOp::insert(target, "x")]);
773 assert_oracle(&b, &buf, "keystroke at line 1000");
774 }
775
776 #[test]
777 fn balanced_pair_insert_deep_in_a_document() {
778 let doc = vec!["fn f() { g(a[i], (b)); }"; 2000].join("\n");
779 let mut buf = Buffer::new(&doc).expect("fixture loads");
780 let mut b = Brackets::match_text(&buf.text());
781 let target = buf.point_to_offset(Point::new(1000, 8));
782 edit(&mut b, &mut buf, vec![EditOp::insert(target, "()")]);
783 assert_oracle(&b, &buf, "balanced pair at line 1000");
784 }
785
786 #[test]
787 fn structural_edit_after_many_sibling_blocks() {
788 const BLOCKS: usize = 20;
792 let mut text = String::new();
793 for _ in 0..BLOCKS {
794 text.push_str("{[[[[[[[[[[]]]]]]]]]]}\n");
795 }
796 let tail = text.len() as u32;
797 text.push_str("tail");
798 let mut buf = Buffer::new(&text).expect("fixture loads");
799 let mut b = Brackets::match_text(&buf.text());
800 edit(&mut b, &mut buf, vec![EditOp::insert(tail, "\n")]);
801 assert_oracle(&b, &buf, "newline after N closed sibling blocks");
802 }
803
804 #[test]
805 fn structure_neutral_insert_at_document_start() {
806 let mut lines = vec!["alpha", "beta"]; lines.extend(std::iter::repeat_n("x(y[z]) {w}", 1998));
808 let doc = lines.join("\n");
809 let mut buf = Buffer::new(&doc).expect("fixture loads");
810 let mut b = Brackets::match_text(&buf.text());
811 let region = edit(&mut b, &mut buf, vec![EditOp::insert(0, "q")]);
812 assert_eq!(region, 0..0, "a structure-neutral insert reconciles nothing");
813 assert_oracle(&b, &buf, "letter at offset 0");
814 }
815
816 #[test]
817 fn scattered_multicursor_typing_is_structure_neutral() {
818 let doc = vec!["fn f() { g(a[i], (b)); }"; 500].join("\n");
822 let mut buf = Buffer::new(&doc).expect("fixture loads");
823 let mut b = Brackets::match_text(&buf.text());
824 let inserts: Vec<EditOp> = (0..500)
825 .step_by(50)
826 .map(|row| EditOp::insert(buf.point_to_offset(Point::new(row, 0)), "x"))
827 .collect();
828 let region = edit(&mut b, &mut buf, inserts);
829 assert_eq!(region, 0..0, "scattered structure-neutral edits reconcile nothing");
830 assert_oracle(&b, &buf, "scattered multi-cursor letter inserts");
831 }
832
833 #[test]
834 fn unbalanced_opener_at_the_top() {
835 let doc = vec!["(x)"; 100].join("\n");
839 let mut buf = Buffer::new(&doc).expect("fixture loads");
840 let mut b = Brackets::match_text(&buf.text());
841 edit(&mut b, &mut buf, vec![EditOp::insert(0, "{")]);
842 assert_oracle(&b, &buf, "unbalanced opener at the top");
843 assert_eq!((bat(&b, 0).partner, bat(&b, 0).open), (None, true));
844 }
845
846 #[test]
849 fn empty_document_first_insert_and_full_delete() {
850 let mut buf = Buffer::new("").expect("empty loads");
851 let mut b = Brackets::match_text(&buf.text());
852 assert_eq!(b.apply_edit(&Patch::new(), &buf, &BracketConfig::default()), 0..0);
854 edit(&mut b, &mut buf, vec![EditOp::insert(0, "({\n[")]);
855 assert_oracle(&b, &buf, "first insert into an empty document");
856 let len = buf.len();
857 edit(&mut b, &mut buf, vec![EditOp::delete(0..len)]);
858 assert_oracle(&b, &buf, "delete everything");
859 assert!(b.all().is_empty());
860 }
861
862 #[test]
863 fn edit_at_eof_appends() {
864 let mut buf = Buffer::new("(a\n[b").expect("fixture loads");
865 let mut b = Brackets::match_text(&buf.text());
866 let len = buf.len();
867 edit(&mut b, &mut buf, vec![EditOp::insert(len, "])")]); assert_oracle(&b, &buf, "append at EOF");
869 assert_eq!(bat(&b, 0).partner, Some(6)); assert_eq!(bat(&b, 3).partner, Some(5)); }
872
873 #[test]
874 fn whole_document_replace() {
875 let mut buf = Buffer::new("(a)\n[b]\n{c}").expect("fixture loads");
876 let mut b = Brackets::match_text(&buf.text());
877 let len = buf.len();
878 edit(&mut b, &mut buf, vec![EditOp::new(0..len, "{new\n(doc)\n}")]);
879 assert_oracle(&b, &buf, "whole-document replace");
880 let len = buf.len();
881 edit(&mut b, &mut buf, vec![EditOp::new(0..len, "[]")]);
882 assert_oracle(&b, &buf, "whole-document replace, fewer lines");
883 }
884
885 #[test]
886 fn edit_ending_exactly_on_a_line_boundary() {
887 let mut buf = Buffer::new("(a)\n[b]\n{c}").expect("fixture loads");
888 let mut b = Brackets::match_text(&buf.text());
889 let target = buf.point_to_offset(Point::new(1, 0));
890 edit(&mut b, &mut buf, vec![EditOp::insert(target, "(q)\n")]);
891 assert_oracle(&b, &buf, "insert ending with a newline");
892 let a = buf.point_to_offset(Point::new(1, 0));
893 let e = buf.point_to_offset(Point::new(2, 0));
894 edit(&mut b, &mut buf, vec![EditOp::delete(a..e)]);
895 assert_oracle(&b, &buf, "delete ending at a line start");
896 }
897
898 #[test]
899 fn multi_edit_transaction_with_scattered_inserts() {
900 let mut buf = Buffer::new("aaa\nbbb\nccc\nddd\neee").expect("fixture loads");
902 let mut b = Brackets::match_text(&buf.text());
903 let p1 = buf.point_to_offset(Point::new(0, 1));
904 let p2 = buf.point_to_offset(Point::new(4, 1));
905 edit(&mut b, &mut buf, vec![EditOp::insert(p1, "("), EditOp::insert(p2, ")")]);
906 assert_oracle(&b, &buf, "scattered two-insert transaction");
907 assert_eq!(bat(&b, 1).partner, Some(18)); }
909}