1use super::*;
2use gpui::{AppContext, Context, TestAppContext};
3use language::{Buffer, Rope};
4use parking_lot::RwLock;
5use rand::prelude::*;
6use settings::SettingsStore;
7use std::env;
8use util::test::sample_text;
9
10#[ctor::ctor]
11fn init_logger() {
12 if std::env::var("RUST_LOG").is_ok() {
13 env_logger::init();
14 }
15}
16
17#[gpui::test]
18fn test_singleton(cx: &mut AppContext) {
19 let buffer = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
20 let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
21
22 let snapshot = multibuffer.read(cx).snapshot(cx);
23 assert_eq!(snapshot.text(), buffer.read(cx).text());
24
25 assert_eq!(
26 snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
27 (0..buffer.read(cx).row_count())
28 .map(Some)
29 .collect::<Vec<_>>()
30 );
31
32 buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx));
33 let snapshot = multibuffer.read(cx).snapshot(cx);
34
35 assert_eq!(snapshot.text(), buffer.read(cx).text());
36 assert_eq!(
37 snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
38 (0..buffer.read(cx).row_count())
39 .map(Some)
40 .collect::<Vec<_>>()
41 );
42}
43
44#[gpui::test]
45fn test_remote(cx: &mut AppContext) {
46 let host_buffer = cx.new_model(|cx| Buffer::local("a", cx));
47 let guest_buffer = cx.new_model(|cx| {
48 let state = host_buffer.read(cx).to_proto(cx);
49 let ops = cx
50 .background_executor()
51 .block(host_buffer.read(cx).serialize_ops(None, cx));
52 let mut buffer = Buffer::from_proto(1, Capability::ReadWrite, state, None).unwrap();
53 buffer.apply_ops(
54 ops.into_iter()
55 .map(|op| language::proto::deserialize_operation(op).unwrap()),
56 cx,
57 );
58 buffer
59 });
60 let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
61 let snapshot = multibuffer.read(cx).snapshot(cx);
62 assert_eq!(snapshot.text(), "a");
63
64 guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx));
65 let snapshot = multibuffer.read(cx).snapshot(cx);
66 assert_eq!(snapshot.text(), "ab");
67
68 guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx));
69 let snapshot = multibuffer.read(cx).snapshot(cx);
70 assert_eq!(snapshot.text(), "abc");
71}
72
73#[gpui::test]
74fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
75 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
76 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
77 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
78
79 let events = Arc::new(RwLock::new(Vec::<Event>::new()));
80 multibuffer.update(cx, |_, cx| {
81 let events = events.clone();
82 cx.subscribe(&multibuffer, move |_, _, event, _| {
83 if let Event::Edited { .. } = event {
84 events.write().push(event.clone())
85 }
86 })
87 .detach();
88 });
89
90 let subscription = multibuffer.update(cx, |multibuffer, cx| {
91 let subscription = multibuffer.subscribe();
92 multibuffer.push_excerpts(
93 buffer_1.clone(),
94 [ExcerptRange {
95 context: Point::new(1, 2)..Point::new(2, 5),
96 primary: None,
97 }],
98 cx,
99 );
100 assert_eq!(
101 subscription.consume().into_inner(),
102 [Edit {
103 old: 0..0,
104 new: 0..10
105 }]
106 );
107
108 multibuffer.push_excerpts(
109 buffer_1.clone(),
110 [ExcerptRange {
111 context: Point::new(3, 3)..Point::new(4, 4),
112 primary: None,
113 }],
114 cx,
115 );
116 multibuffer.push_excerpts(
117 buffer_2.clone(),
118 [ExcerptRange {
119 context: Point::new(3, 1)..Point::new(3, 3),
120 primary: None,
121 }],
122 cx,
123 );
124 assert_eq!(
125 subscription.consume().into_inner(),
126 [Edit {
127 old: 10..10,
128 new: 10..22
129 }]
130 );
131
132 subscription
133 });
134
135 // Adding excerpts emits an edited event.
136 assert_eq!(
137 events.read().as_slice(),
138 &[
139 Event::Edited {
140 singleton_buffer_edited: false,
141 edited_buffer: None,
142 },
143 Event::Edited {
144 singleton_buffer_edited: false,
145 edited_buffer: None,
146 },
147 Event::Edited {
148 singleton_buffer_edited: false,
149 edited_buffer: None,
150 }
151 ]
152 );
153
154 let snapshot = multibuffer.read(cx).snapshot(cx);
155 assert_eq!(
156 snapshot.text(),
157 concat!(
158 "bbbb\n", // Preserve newlines
159 "ccccc\n", //
160 "ddd\n", //
161 "eeee\n", //
162 "jj" //
163 )
164 );
165 assert_eq!(
166 snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
167 [Some(1), Some(2), Some(3), Some(4), Some(3)]
168 );
169 assert_eq!(
170 snapshot.buffer_rows(MultiBufferRow(2)).collect::<Vec<_>>(),
171 [Some(3), Some(4), Some(3)]
172 );
173 assert_eq!(
174 snapshot.buffer_rows(MultiBufferRow(4)).collect::<Vec<_>>(),
175 [Some(3)]
176 );
177 assert_eq!(
178 snapshot.buffer_rows(MultiBufferRow(5)).collect::<Vec<_>>(),
179 []
180 );
181
182 assert_eq!(
183 boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
184 &[
185 (MultiBufferRow(0), "bbbb\nccccc".to_string(), true),
186 (MultiBufferRow(2), "ddd\neeee".to_string(), false),
187 (MultiBufferRow(4), "jj".to_string(), true),
188 ]
189 );
190 assert_eq!(
191 boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
192 &[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)]
193 );
194 assert_eq!(
195 boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
196 &[]
197 );
198 assert_eq!(
199 boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot),
200 &[]
201 );
202 assert_eq!(
203 boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
204 &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
205 );
206 assert_eq!(
207 boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
208 &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
209 );
210 assert_eq!(
211 boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
212 &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
213 );
214 assert_eq!(
215 boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
216 &[(MultiBufferRow(4), "jj".to_string(), true)]
217 );
218 assert_eq!(
219 boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
220 &[]
221 );
222
223 buffer_1.update(cx, |buffer, cx| {
224 let text = "\n";
225 buffer.edit(
226 [
227 (Point::new(0, 0)..Point::new(0, 0), text),
228 (Point::new(2, 1)..Point::new(2, 3), text),
229 ],
230 None,
231 cx,
232 );
233 });
234
235 let snapshot = multibuffer.read(cx).snapshot(cx);
236 assert_eq!(
237 snapshot.text(),
238 concat!(
239 "bbbb\n", // Preserve newlines
240 "c\n", //
241 "cc\n", //
242 "ddd\n", //
243 "eeee\n", //
244 "jj" //
245 )
246 );
247
248 assert_eq!(
249 subscription.consume().into_inner(),
250 [Edit {
251 old: 6..8,
252 new: 6..7
253 }]
254 );
255
256 let snapshot = multibuffer.read(cx).snapshot(cx);
257 assert_eq!(
258 snapshot.clip_point(Point::new(0, 5), Bias::Left),
259 Point::new(0, 4)
260 );
261 assert_eq!(
262 snapshot.clip_point(Point::new(0, 5), Bias::Right),
263 Point::new(0, 4)
264 );
265 assert_eq!(
266 snapshot.clip_point(Point::new(5, 1), Bias::Right),
267 Point::new(5, 1)
268 );
269 assert_eq!(
270 snapshot.clip_point(Point::new(5, 2), Bias::Right),
271 Point::new(5, 2)
272 );
273 assert_eq!(
274 snapshot.clip_point(Point::new(5, 3), Bias::Right),
275 Point::new(5, 2)
276 );
277
278 let snapshot = multibuffer.update(cx, |multibuffer, cx| {
279 let (buffer_2_excerpt_id, _) = multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone();
280 multibuffer.remove_excerpts([buffer_2_excerpt_id], cx);
281 multibuffer.snapshot(cx)
282 });
283
284 assert_eq!(
285 snapshot.text(),
286 concat!(
287 "bbbb\n", // Preserve newlines
288 "c\n", //
289 "cc\n", //
290 "ddd\n", //
291 "eeee", //
292 )
293 );
294
295 fn boundaries_in_range(
296 range: Range<Point>,
297 snapshot: &MultiBufferSnapshot,
298 ) -> Vec<(MultiBufferRow, String, bool)> {
299 snapshot
300 .excerpt_boundaries_in_range(range)
301 .filter_map(|boundary| {
302 let starts_new_buffer = boundary.starts_new_buffer();
303 boundary.next.map(|next| {
304 (
305 boundary.row,
306 next.buffer
307 .text_for_range(next.range.context)
308 .collect::<String>(),
309 starts_new_buffer,
310 )
311 })
312 })
313 .collect::<Vec<_>>()
314 }
315}
316
317#[gpui::test]
318fn test_excerpt_events(cx: &mut AppContext) {
319 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
320 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(10, 3, 'm'), cx));
321
322 let leader_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
323 let follower_multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
324 let follower_edit_event_count = Arc::new(RwLock::new(0));
325
326 follower_multibuffer.update(cx, |_, cx| {
327 let follower_edit_event_count = follower_edit_event_count.clone();
328 cx.subscribe(
329 &leader_multibuffer,
330 move |follower, _, event, cx| match event.clone() {
331 Event::ExcerptsAdded {
332 buffer,
333 predecessor,
334 excerpts,
335 } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
336 Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
337 Event::Edited { .. } => {
338 *follower_edit_event_count.write() += 1;
339 }
340 _ => {}
341 },
342 )
343 .detach();
344 });
345
346 leader_multibuffer.update(cx, |leader, cx| {
347 leader.push_excerpts(
348 buffer_1.clone(),
349 [
350 ExcerptRange {
351 context: 0..8,
352 primary: None,
353 },
354 ExcerptRange {
355 context: 12..16,
356 primary: None,
357 },
358 ],
359 cx,
360 );
361 leader.insert_excerpts_after(
362 leader.excerpt_ids()[0],
363 buffer_2.clone(),
364 [
365 ExcerptRange {
366 context: 0..5,
367 primary: None,
368 },
369 ExcerptRange {
370 context: 10..15,
371 primary: None,
372 },
373 ],
374 cx,
375 )
376 });
377 assert_eq!(
378 leader_multibuffer.read(cx).snapshot(cx).text(),
379 follower_multibuffer.read(cx).snapshot(cx).text(),
380 );
381 assert_eq!(*follower_edit_event_count.read(), 2);
382
383 leader_multibuffer.update(cx, |leader, cx| {
384 let excerpt_ids = leader.excerpt_ids();
385 leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
386 });
387 assert_eq!(
388 leader_multibuffer.read(cx).snapshot(cx).text(),
389 follower_multibuffer.read(cx).snapshot(cx).text(),
390 );
391 assert_eq!(*follower_edit_event_count.read(), 3);
392
393 // Removing an empty set of excerpts is a noop.
394 leader_multibuffer.update(cx, |leader, cx| {
395 leader.remove_excerpts([], cx);
396 });
397 assert_eq!(
398 leader_multibuffer.read(cx).snapshot(cx).text(),
399 follower_multibuffer.read(cx).snapshot(cx).text(),
400 );
401 assert_eq!(*follower_edit_event_count.read(), 3);
402
403 // Adding an empty set of excerpts is a noop.
404 leader_multibuffer.update(cx, |leader, cx| {
405 leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
406 });
407 assert_eq!(
408 leader_multibuffer.read(cx).snapshot(cx).text(),
409 follower_multibuffer.read(cx).snapshot(cx).text(),
410 );
411 assert_eq!(*follower_edit_event_count.read(), 3);
412
413 leader_multibuffer.update(cx, |leader, cx| {
414 leader.clear(cx);
415 });
416 assert_eq!(
417 leader_multibuffer.read(cx).snapshot(cx).text(),
418 follower_multibuffer.read(cx).snapshot(cx).text(),
419 );
420 assert_eq!(*follower_edit_event_count.read(), 4);
421}
422
423#[gpui::test]
424fn test_expand_excerpts(cx: &mut AppContext) {
425 let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
426 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
427
428 multibuffer.update(cx, |multibuffer, cx| {
429 multibuffer.push_excerpts_with_context_lines(
430 buffer.clone(),
431 vec![
432 // Note that in this test, this first excerpt
433 // does not contain a new line
434 Point::new(3, 2)..Point::new(3, 3),
435 Point::new(7, 1)..Point::new(7, 3),
436 Point::new(15, 0)..Point::new(15, 0),
437 ],
438 1,
439 cx,
440 )
441 });
442
443 let snapshot = multibuffer.read(cx).snapshot(cx);
444
445 assert_eq!(
446 snapshot.text(),
447 concat!(
448 "ccc\n", //
449 "ddd\n", //
450 "eee", //
451 "\n", // End of excerpt
452 "ggg\n", //
453 "hhh\n", //
454 "iii", //
455 "\n", // End of excerpt
456 "ooo\n", //
457 "ppp\n", //
458 "qqq", // End of excerpt
459 )
460 );
461 drop(snapshot);
462
463 multibuffer.update(cx, |multibuffer, cx| {
464 multibuffer.expand_excerpts(
465 multibuffer.excerpt_ids(),
466 1,
467 ExpandExcerptDirection::UpAndDown,
468 cx,
469 )
470 });
471
472 let snapshot = multibuffer.read(cx).snapshot(cx);
473
474 // Expanding context lines causes the line containing 'fff' to appear in two different excerpts.
475 // We don't attempt to merge them, because removing the excerpt could create inconsistency with other layers
476 // that are tracking excerpt ids.
477 assert_eq!(
478 snapshot.text(),
479 concat!(
480 "bbb\n", //
481 "ccc\n", //
482 "ddd\n", //
483 "eee\n", //
484 "fff\n", // End of excerpt
485 "fff\n", //
486 "ggg\n", //
487 "hhh\n", //
488 "iii\n", //
489 "jjj\n", // End of excerpt
490 "nnn\n", //
491 "ooo\n", //
492 "ppp\n", //
493 "qqq\n", //
494 "rrr", // End of excerpt
495 )
496 );
497}
498
499#[gpui::test]
500fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
501 let buffer = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
502 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
503 let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
504 multibuffer.push_excerpts_with_context_lines(
505 buffer.clone(),
506 vec![
507 // Note that in this test, this first excerpt
508 // does contain a new line
509 Point::new(3, 2)..Point::new(4, 2),
510 Point::new(7, 1)..Point::new(7, 3),
511 Point::new(15, 0)..Point::new(15, 0),
512 ],
513 2,
514 cx,
515 )
516 });
517
518 let snapshot = multibuffer.read(cx).snapshot(cx);
519 assert_eq!(
520 snapshot.text(),
521 concat!(
522 "bbb\n", // Preserve newlines
523 "ccc\n", //
524 "ddd\n", //
525 "eee\n", //
526 "fff\n", //
527 "ggg\n", //
528 "hhh\n", //
529 "iii\n", //
530 "jjj\n", //
531 "nnn\n", //
532 "ooo\n", //
533 "ppp\n", //
534 "qqq\n", //
535 "rrr", //
536 )
537 );
538
539 assert_eq!(
540 anchor_ranges
541 .iter()
542 .map(|range| range.to_point(&snapshot))
543 .collect::<Vec<_>>(),
544 vec![
545 Point::new(2, 2)..Point::new(3, 2),
546 Point::new(6, 1)..Point::new(6, 3),
547 Point::new(11, 0)..Point::new(11, 0)
548 ]
549 );
550}
551
552#[gpui::test(iterations = 100)]
553async fn test_push_multiple_excerpts_with_context_lines(cx: &mut TestAppContext) {
554 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
555 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(15, 4, 'a'), cx));
556 let snapshot_1 = buffer_1.update(cx, |buffer, _| buffer.snapshot());
557 let snapshot_2 = buffer_2.update(cx, |buffer, _| buffer.snapshot());
558 let ranges_1 = vec![
559 snapshot_1.anchor_before(Point::new(3, 2))..snapshot_1.anchor_before(Point::new(4, 2)),
560 snapshot_1.anchor_before(Point::new(7, 1))..snapshot_1.anchor_before(Point::new(7, 3)),
561 snapshot_1.anchor_before(Point::new(15, 0))..snapshot_1.anchor_before(Point::new(15, 0)),
562 ];
563 let ranges_2 = vec![
564 snapshot_2.anchor_before(Point::new(2, 1))..snapshot_2.anchor_before(Point::new(3, 1)),
565 snapshot_2.anchor_before(Point::new(10, 0))..snapshot_2.anchor_before(Point::new(10, 2)),
566 ];
567
568 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
569 let anchor_ranges = multibuffer
570 .update(cx, |multibuffer, cx| {
571 multibuffer.push_multiple_excerpts_with_context_lines(
572 vec![(buffer_1.clone(), ranges_1), (buffer_2.clone(), ranges_2)],
573 2,
574 cx,
575 )
576 })
577 .await;
578
579 let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
580 assert_eq!(
581 snapshot.text(),
582 concat!(
583 "bbb\n", // buffer_1
584 "ccc\n", //
585 "ddd\n", // <-- excerpt 1
586 "eee\n", // <-- excerpt 1
587 "fff\n", //
588 "ggg\n", //
589 "hhh\n", // <-- excerpt 2
590 "iii\n", //
591 "jjj\n", //
592 //
593 "nnn\n", //
594 "ooo\n", //
595 "ppp\n", // <-- excerpt 3
596 "qqq\n", //
597 "rrr\n", //
598 //
599 "aaaa\n", // buffer 2
600 "bbbb\n", //
601 "cccc\n", // <-- excerpt 4
602 "dddd\n", // <-- excerpt 4
603 "eeee\n", //
604 "ffff\n", //
605 //
606 "iiii\n", //
607 "jjjj\n", //
608 "kkkk\n", // <-- excerpt 5
609 "llll\n", //
610 "mmmm", //
611 )
612 );
613
614 assert_eq!(
615 anchor_ranges
616 .iter()
617 .map(|range| range.to_point(&snapshot))
618 .collect::<Vec<_>>(),
619 vec![
620 Point::new(2, 2)..Point::new(3, 2),
621 Point::new(6, 1)..Point::new(6, 3),
622 Point::new(11, 0)..Point::new(11, 0),
623 Point::new(16, 1)..Point::new(17, 1),
624 Point::new(22, 0)..Point::new(22, 2)
625 ]
626 );
627}
628
629#[gpui::test]
630fn test_empty_multibuffer(cx: &mut AppContext) {
631 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
632
633 let snapshot = multibuffer.read(cx).snapshot(cx);
634 assert_eq!(snapshot.text(), "");
635 assert_eq!(
636 snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
637 &[Some(0)]
638 );
639 assert_eq!(
640 snapshot.buffer_rows(MultiBufferRow(1)).collect::<Vec<_>>(),
641 &[]
642 );
643}
644
645#[gpui::test]
646fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
647 let buffer = cx.new_model(|cx| Buffer::local("abcd", cx));
648 let multibuffer = cx.new_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
649 let old_snapshot = multibuffer.read(cx).snapshot(cx);
650 buffer.update(cx, |buffer, cx| {
651 buffer.edit([(0..0, "X")], None, cx);
652 buffer.edit([(5..5, "Y")], None, cx);
653 });
654 let new_snapshot = multibuffer.read(cx).snapshot(cx);
655
656 assert_eq!(old_snapshot.text(), "abcd");
657 assert_eq!(new_snapshot.text(), "XabcdY");
658
659 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
660 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
661 assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
662 assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
663}
664
665#[gpui::test]
666fn test_multibuffer_anchors(cx: &mut AppContext) {
667 let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
668 let buffer_2 = cx.new_model(|cx| Buffer::local("efghi", cx));
669 let multibuffer = cx.new_model(|cx| {
670 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
671 multibuffer.push_excerpts(
672 buffer_1.clone(),
673 [ExcerptRange {
674 context: 0..4,
675 primary: None,
676 }],
677 cx,
678 );
679 multibuffer.push_excerpts(
680 buffer_2.clone(),
681 [ExcerptRange {
682 context: 0..5,
683 primary: None,
684 }],
685 cx,
686 );
687 multibuffer
688 });
689 let old_snapshot = multibuffer.read(cx).snapshot(cx);
690
691 assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0);
692 assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0);
693 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
694 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
695 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
696 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
697
698 buffer_1.update(cx, |buffer, cx| {
699 buffer.edit([(0..0, "W")], None, cx);
700 buffer.edit([(5..5, "X")], None, cx);
701 });
702 buffer_2.update(cx, |buffer, cx| {
703 buffer.edit([(0..0, "Y")], None, cx);
704 buffer.edit([(6..6, "Z")], None, cx);
705 });
706 let new_snapshot = multibuffer.read(cx).snapshot(cx);
707
708 assert_eq!(old_snapshot.text(), "abcd\nefghi");
709 assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ");
710
711 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
712 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
713 assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2);
714 assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
715 assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
716 assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
717 assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7);
718 assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8);
719 assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13);
720 assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
721}
722
723#[gpui::test]
724fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
725 let buffer_1 = cx.new_model(|cx| Buffer::local("abcd", cx));
726 let buffer_2 = cx.new_model(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx));
727 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
728
729 // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
730 // Add an excerpt from buffer 1 that spans this new insertion.
731 buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
732 let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
733 multibuffer
734 .push_excerpts(
735 buffer_1.clone(),
736 [ExcerptRange {
737 context: 0..7,
738 primary: None,
739 }],
740 cx,
741 )
742 .pop()
743 .unwrap()
744 });
745
746 let snapshot_1 = multibuffer.read(cx).snapshot(cx);
747 assert_eq!(snapshot_1.text(), "abcd123");
748
749 // Replace the buffer 1 excerpt with new excerpts from buffer 2.
750 let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
751 multibuffer.remove_excerpts([excerpt_id_1], cx);
752 let mut ids = multibuffer
753 .push_excerpts(
754 buffer_2.clone(),
755 [
756 ExcerptRange {
757 context: 0..4,
758 primary: None,
759 },
760 ExcerptRange {
761 context: 6..10,
762 primary: None,
763 },
764 ExcerptRange {
765 context: 12..16,
766 primary: None,
767 },
768 ],
769 cx,
770 )
771 .into_iter();
772 (ids.next().unwrap(), ids.next().unwrap())
773 });
774 let snapshot_2 = multibuffer.read(cx).snapshot(cx);
775 assert_eq!(snapshot_2.text(), "ABCD\nGHIJ\nMNOP");
776
777 // The old excerpt id doesn't get reused.
778 assert_ne!(excerpt_id_2, excerpt_id_1);
779
780 // Resolve some anchors from the previous snapshot in the new snapshot.
781 // The current excerpts are from a different buffer, so we don't attempt to
782 // resolve the old text anchor in the new buffer.
783 assert_eq!(
784 snapshot_2.summary_for_anchor::<usize>(&snapshot_1.anchor_before(2)),
785 0
786 );
787 assert_eq!(
788 snapshot_2.summaries_for_anchors::<usize, _>(&[
789 snapshot_1.anchor_before(2),
790 snapshot_1.anchor_after(3)
791 ]),
792 vec![0, 0]
793 );
794
795 // Refresh anchors from the old snapshot. The return value indicates that both
796 // anchors lost their original excerpt.
797 let refresh =
798 snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]);
799 assert_eq!(
800 refresh,
801 &[
802 (0, snapshot_2.anchor_before(0), false),
803 (1, snapshot_2.anchor_after(0), false),
804 ]
805 );
806
807 // Replace the middle excerpt with a smaller excerpt in buffer 2,
808 // that intersects the old excerpt.
809 let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| {
810 multibuffer.remove_excerpts([excerpt_id_3], cx);
811 multibuffer
812 .insert_excerpts_after(
813 excerpt_id_2,
814 buffer_2.clone(),
815 [ExcerptRange {
816 context: 5..8,
817 primary: None,
818 }],
819 cx,
820 )
821 .pop()
822 .unwrap()
823 });
824
825 let snapshot_3 = multibuffer.read(cx).snapshot(cx);
826 assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP");
827 assert_ne!(excerpt_id_5, excerpt_id_3);
828
829 // Resolve some anchors from the previous snapshot in the new snapshot.
830 // The third anchor can't be resolved, since its excerpt has been removed,
831 // so it resolves to the same position as its predecessor.
832 let anchors = [
833 snapshot_2.anchor_before(0),
834 snapshot_2.anchor_after(2),
835 snapshot_2.anchor_after(6),
836 snapshot_2.anchor_after(14),
837 ];
838 assert_eq!(
839 snapshot_3.summaries_for_anchors::<usize, _>(&anchors),
840 &[0, 2, 9, 13]
841 );
842
843 let new_anchors = snapshot_3.refresh_anchors(&anchors);
844 assert_eq!(
845 new_anchors.iter().map(|a| (a.0, a.2)).collect::<Vec<_>>(),
846 &[(0, true), (1, true), (2, true), (3, true)]
847 );
848 assert_eq!(
849 snapshot_3.summaries_for_anchors::<usize, _>(new_anchors.iter().map(|a| &a.1)),
850 &[0, 2, 7, 13]
851 );
852}
853
854#[gpui::test(iterations = 100)]
855fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
856 let operations = env::var("OPERATIONS")
857 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
858 .unwrap_or(10);
859
860 let mut buffers: Vec<Model<Buffer>> = Vec::new();
861 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
862 let mut excerpt_ids = Vec::<ExcerptId>::new();
863 let mut expected_excerpts = Vec::<(Model<Buffer>, Range<text::Anchor>)>::new();
864 let mut anchors = Vec::new();
865 let mut old_versions = Vec::new();
866
867 for _ in 0..operations {
868 match rng.gen_range(0..100) {
869 0..=14 if !buffers.is_empty() => {
870 let buffer = buffers.choose(&mut rng).unwrap();
871 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
872 }
873 15..=19 if !expected_excerpts.is_empty() => {
874 multibuffer.update(cx, |multibuffer, cx| {
875 let ids = multibuffer.excerpt_ids();
876 let mut excerpts = HashSet::default();
877 for _ in 0..rng.gen_range(0..ids.len()) {
878 excerpts.extend(ids.choose(&mut rng).copied());
879 }
880
881 let line_count = rng.gen_range(0..5);
882
883 let excerpt_ixs = excerpts
884 .iter()
885 .map(|id| excerpt_ids.iter().position(|i| i == id).unwrap())
886 .collect::<Vec<_>>();
887 log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
888 multibuffer.expand_excerpts(
889 excerpts.iter().cloned(),
890 line_count,
891 ExpandExcerptDirection::UpAndDown,
892 cx,
893 );
894
895 if line_count > 0 {
896 for id in excerpts {
897 let excerpt_ix = excerpt_ids.iter().position(|&i| i == id).unwrap();
898 let (buffer, range) = &mut expected_excerpts[excerpt_ix];
899 let snapshot = buffer.read(cx).snapshot();
900 let mut point_range = range.to_point(&snapshot);
901 point_range.start =
902 Point::new(point_range.start.row.saturating_sub(line_count), 0);
903 point_range.end = snapshot.clip_point(
904 Point::new(point_range.end.row + line_count, 0),
905 Bias::Left,
906 );
907 point_range.end.column = snapshot.line_len(point_range.end.row);
908 *range = snapshot.anchor_before(point_range.start)
909 ..snapshot.anchor_after(point_range.end);
910 }
911 }
912 });
913 }
914 20..=29 if !expected_excerpts.is_empty() => {
915 let mut ids_to_remove = vec![];
916 for _ in 0..rng.gen_range(1..=3) {
917 if expected_excerpts.is_empty() {
918 break;
919 }
920
921 let ix = rng.gen_range(0..expected_excerpts.len());
922 ids_to_remove.push(excerpt_ids.remove(ix));
923 let (buffer, range) = expected_excerpts.remove(ix);
924 let buffer = buffer.read(cx);
925 log::info!(
926 "Removing excerpt {}: {:?}",
927 ix,
928 buffer
929 .text_for_range(range.to_offset(buffer))
930 .collect::<String>(),
931 );
932 }
933 let snapshot = multibuffer.read(cx).read(cx);
934 ids_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
935 drop(snapshot);
936 multibuffer.update(cx, |multibuffer, cx| {
937 multibuffer.remove_excerpts(ids_to_remove, cx)
938 });
939 }
940 30..=39 if !expected_excerpts.is_empty() => {
941 let multibuffer = multibuffer.read(cx).read(cx);
942 let offset =
943 multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left);
944 let bias = if rng.gen() { Bias::Left } else { Bias::Right };
945 log::info!("Creating anchor at {} with bias {:?}", offset, bias);
946 anchors.push(multibuffer.anchor_at(offset, bias));
947 anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
948 }
949 40..=44 if !anchors.is_empty() => {
950 let multibuffer = multibuffer.read(cx).read(cx);
951 let prev_len = anchors.len();
952 anchors = multibuffer
953 .refresh_anchors(&anchors)
954 .into_iter()
955 .map(|a| a.1)
956 .collect();
957
958 // Ensure the newly-refreshed anchors point to a valid excerpt and don't
959 // overshoot its boundaries.
960 assert_eq!(anchors.len(), prev_len);
961 for anchor in &anchors {
962 if anchor.excerpt_id == ExcerptId::min()
963 || anchor.excerpt_id == ExcerptId::max()
964 {
965 continue;
966 }
967
968 let excerpt = multibuffer.excerpt(anchor.excerpt_id).unwrap();
969 assert_eq!(excerpt.id, anchor.excerpt_id);
970 assert!(excerpt.contains(anchor));
971 }
972 }
973 _ => {
974 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
975 let base_text = util::RandomCharIter::new(&mut rng)
976 .take(25)
977 .collect::<String>();
978
979 buffers.push(cx.new_model(|cx| Buffer::local(base_text, cx)));
980 buffers.last().unwrap()
981 } else {
982 buffers.choose(&mut rng).unwrap()
983 };
984
985 let buffer = buffer_handle.read(cx);
986 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
987 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
988 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
989 let prev_excerpt_ix = rng.gen_range(0..=expected_excerpts.len());
990 let prev_excerpt_id = excerpt_ids
991 .get(prev_excerpt_ix)
992 .cloned()
993 .unwrap_or_else(ExcerptId::max);
994 let excerpt_ix = (prev_excerpt_ix + 1).min(expected_excerpts.len());
995
996 log::info!(
997 "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}",
998 excerpt_ix,
999 expected_excerpts.len(),
1000 buffer_handle.read(cx).remote_id(),
1001 buffer.text(),
1002 start_ix..end_ix,
1003 &buffer.text()[start_ix..end_ix]
1004 );
1005
1006 let excerpt_id = multibuffer.update(cx, |multibuffer, cx| {
1007 multibuffer
1008 .insert_excerpts_after(
1009 prev_excerpt_id,
1010 buffer_handle.clone(),
1011 [ExcerptRange {
1012 context: start_ix..end_ix,
1013 primary: None,
1014 }],
1015 cx,
1016 )
1017 .pop()
1018 .unwrap()
1019 });
1020
1021 excerpt_ids.insert(excerpt_ix, excerpt_id);
1022 expected_excerpts.insert(excerpt_ix, (buffer_handle.clone(), anchor_range));
1023 }
1024 }
1025
1026 if rng.gen_bool(0.3) {
1027 multibuffer.update(cx, |multibuffer, cx| {
1028 old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
1029 })
1030 }
1031
1032 let snapshot = multibuffer.read(cx).snapshot(cx);
1033
1034 let mut excerpt_starts = Vec::new();
1035 let mut expected_text = String::new();
1036 let mut expected_buffer_rows = Vec::new();
1037 for (buffer, range) in &expected_excerpts {
1038 let buffer = buffer.read(cx);
1039 let buffer_range = range.to_offset(buffer);
1040
1041 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1042 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1043 expected_text.push('\n');
1044
1045 let buffer_row_range = buffer.offset_to_point(buffer_range.start).row
1046 ..=buffer.offset_to_point(buffer_range.end).row;
1047 for row in buffer_row_range {
1048 expected_buffer_rows.push(Some(row));
1049 }
1050 }
1051 // Remove final trailing newline.
1052 if !expected_excerpts.is_empty() {
1053 expected_text.pop();
1054 }
1055
1056 // Always report one buffer row
1057 if expected_buffer_rows.is_empty() {
1058 expected_buffer_rows.push(Some(0));
1059 }
1060
1061 assert_eq!(snapshot.text(), expected_text);
1062 log::info!("MultiBuffer text: {:?}", expected_text);
1063
1064 assert_eq!(
1065 snapshot.buffer_rows(MultiBufferRow(0)).collect::<Vec<_>>(),
1066 expected_buffer_rows,
1067 );
1068
1069 for _ in 0..5 {
1070 let start_row = rng.gen_range(0..=expected_buffer_rows.len());
1071 assert_eq!(
1072 snapshot
1073 .buffer_rows(MultiBufferRow(start_row as u32))
1074 .collect::<Vec<_>>(),
1075 &expected_buffer_rows[start_row..],
1076 "buffer_rows({})",
1077 start_row
1078 );
1079 }
1080
1081 assert_eq!(
1082 snapshot.widest_line_number(),
1083 expected_buffer_rows.into_iter().flatten().max().unwrap() + 1
1084 );
1085
1086 let mut excerpt_starts = excerpt_starts.into_iter();
1087 for (buffer, range) in &expected_excerpts {
1088 let buffer = buffer.read(cx);
1089 let buffer_id = buffer.remote_id();
1090 let buffer_range = range.to_offset(buffer);
1091 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1092 let buffer_start_point_utf16 =
1093 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1094
1095 let excerpt_start = excerpt_starts.next().unwrap();
1096 let mut offset = excerpt_start.len;
1097 let mut buffer_offset = buffer_range.start;
1098 let mut point = excerpt_start.lines;
1099 let mut buffer_point = buffer_start_point;
1100 let mut point_utf16 = excerpt_start.lines_utf16();
1101 let mut buffer_point_utf16 = buffer_start_point_utf16;
1102 for ch in buffer
1103 .snapshot()
1104 .chunks(buffer_range.clone(), false)
1105 .flat_map(|c| c.text.chars())
1106 {
1107 for _ in 0..ch.len_utf8() {
1108 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1109 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1110 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1111 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1112 assert_eq!(
1113 left_offset,
1114 excerpt_start.len + (buffer_left_offset - buffer_range.start),
1115 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1116 offset,
1117 buffer_id,
1118 buffer_offset,
1119 );
1120 assert_eq!(
1121 right_offset,
1122 excerpt_start.len + (buffer_right_offset - buffer_range.start),
1123 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1124 offset,
1125 buffer_id,
1126 buffer_offset,
1127 );
1128
1129 let left_point = snapshot.clip_point(point, Bias::Left);
1130 let right_point = snapshot.clip_point(point, Bias::Right);
1131 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1132 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1133 assert_eq!(
1134 left_point,
1135 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1136 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1137 point,
1138 buffer_id,
1139 buffer_point,
1140 );
1141 assert_eq!(
1142 right_point,
1143 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1144 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1145 point,
1146 buffer_id,
1147 buffer_point,
1148 );
1149
1150 assert_eq!(
1151 snapshot.point_to_offset(left_point),
1152 left_offset,
1153 "point_to_offset({:?})",
1154 left_point,
1155 );
1156 assert_eq!(
1157 snapshot.offset_to_point(left_offset),
1158 left_point,
1159 "offset_to_point({:?})",
1160 left_offset,
1161 );
1162
1163 offset += 1;
1164 buffer_offset += 1;
1165 if ch == '\n' {
1166 point += Point::new(1, 0);
1167 buffer_point += Point::new(1, 0);
1168 } else {
1169 point += Point::new(0, 1);
1170 buffer_point += Point::new(0, 1);
1171 }
1172 }
1173
1174 for _ in 0..ch.len_utf16() {
1175 let left_point_utf16 =
1176 snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Left);
1177 let right_point_utf16 =
1178 snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Right);
1179 let buffer_left_point_utf16 =
1180 buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Left);
1181 let buffer_right_point_utf16 =
1182 buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Right);
1183 assert_eq!(
1184 left_point_utf16,
1185 excerpt_start.lines_utf16()
1186 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1187 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1188 point_utf16,
1189 buffer_id,
1190 buffer_point_utf16,
1191 );
1192 assert_eq!(
1193 right_point_utf16,
1194 excerpt_start.lines_utf16()
1195 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1196 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1197 point_utf16,
1198 buffer_id,
1199 buffer_point_utf16,
1200 );
1201
1202 if ch == '\n' {
1203 point_utf16 += PointUtf16::new(1, 0);
1204 buffer_point_utf16 += PointUtf16::new(1, 0);
1205 } else {
1206 point_utf16 += PointUtf16::new(0, 1);
1207 buffer_point_utf16 += PointUtf16::new(0, 1);
1208 }
1209 }
1210 }
1211 }
1212
1213 for (row, line) in expected_text.split('\n').enumerate() {
1214 assert_eq!(
1215 snapshot.line_len(MultiBufferRow(row as u32)),
1216 line.len() as u32,
1217 "line_len({}).",
1218 row
1219 );
1220 }
1221
1222 let text_rope = Rope::from(expected_text.as_str());
1223 for _ in 0..10 {
1224 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
1225 let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1226
1227 let text_for_range = snapshot
1228 .text_for_range(start_ix..end_ix)
1229 .collect::<String>();
1230 assert_eq!(
1231 text_for_range,
1232 &expected_text[start_ix..end_ix],
1233 "incorrect text for range {:?}",
1234 start_ix..end_ix
1235 );
1236
1237 let snapshot = multibuffer.read(cx).snapshot(cx);
1238 let excerpted_buffer_ranges = snapshot.range_to_buffer_ranges(start_ix..end_ix);
1239 let excerpted_buffers_text = excerpted_buffer_ranges
1240 .iter()
1241 .map(|(excerpt, buffer_range)| {
1242 excerpt
1243 .buffer()
1244 .text_for_range(buffer_range.clone())
1245 .collect::<String>()
1246 })
1247 .collect::<Vec<_>>()
1248 .join("\n");
1249 assert_eq!(excerpted_buffers_text, text_for_range);
1250 if !expected_excerpts.is_empty() {
1251 assert!(!excerpted_buffer_ranges.is_empty());
1252 }
1253
1254 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1255 assert_eq!(
1256 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1257 expected_summary,
1258 "incorrect summary for range {:?}",
1259 start_ix..end_ix
1260 );
1261 }
1262
1263 // Anchor resolution
1264 let summaries = snapshot.summaries_for_anchors::<usize, _>(&anchors);
1265 assert_eq!(anchors.len(), summaries.len());
1266 for (anchor, resolved_offset) in anchors.iter().zip(summaries) {
1267 assert!(resolved_offset <= snapshot.len());
1268 assert_eq!(
1269 snapshot.summary_for_anchor::<usize>(anchor),
1270 resolved_offset
1271 );
1272 }
1273
1274 for _ in 0..10 {
1275 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
1276 assert_eq!(
1277 snapshot.reversed_chars_at(end_ix).collect::<String>(),
1278 expected_text[..end_ix].chars().rev().collect::<String>(),
1279 );
1280 }
1281
1282 for _ in 0..10 {
1283 let end_ix = rng.gen_range(0..=text_rope.len());
1284 let start_ix = rng.gen_range(0..=end_ix);
1285 assert_eq!(
1286 snapshot
1287 .bytes_in_range(start_ix..end_ix)
1288 .flatten()
1289 .copied()
1290 .collect::<Vec<_>>(),
1291 expected_text.as_bytes()[start_ix..end_ix].to_vec(),
1292 "bytes_in_range({:?})",
1293 start_ix..end_ix,
1294 );
1295 }
1296 }
1297
1298 let snapshot = multibuffer.read(cx).snapshot(cx);
1299 for (old_snapshot, subscription) in old_versions {
1300 let edits = subscription.consume().into_inner();
1301
1302 log::info!(
1303 "applying subscription edits to old text: {:?}: {:?}",
1304 old_snapshot.text(),
1305 edits,
1306 );
1307
1308 let mut text = old_snapshot.text();
1309 for edit in edits {
1310 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1311 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1312 }
1313 assert_eq!(text.to_string(), snapshot.text());
1314 }
1315}
1316
1317#[gpui::test]
1318fn test_history(cx: &mut AppContext) {
1319 let test_settings = SettingsStore::test(cx);
1320 cx.set_global(test_settings);
1321 let group_interval: Duration = Duration::from_millis(1);
1322 let buffer_1 = cx.new_model(|cx| {
1323 let mut buf = Buffer::local("1234", cx);
1324 buf.set_group_interval(group_interval);
1325 buf
1326 });
1327 let buffer_2 = cx.new_model(|cx| {
1328 let mut buf = Buffer::local("5678", cx);
1329 buf.set_group_interval(group_interval);
1330 buf
1331 });
1332 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1333 multibuffer.update(cx, |this, _| {
1334 this.history.group_interval = group_interval;
1335 });
1336 multibuffer.update(cx, |multibuffer, cx| {
1337 multibuffer.push_excerpts(
1338 buffer_1.clone(),
1339 [ExcerptRange {
1340 context: 0..buffer_1.read(cx).len(),
1341 primary: None,
1342 }],
1343 cx,
1344 );
1345 multibuffer.push_excerpts(
1346 buffer_2.clone(),
1347 [ExcerptRange {
1348 context: 0..buffer_2.read(cx).len(),
1349 primary: None,
1350 }],
1351 cx,
1352 );
1353 });
1354
1355 let mut now = Instant::now();
1356
1357 multibuffer.update(cx, |multibuffer, cx| {
1358 let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
1359 multibuffer.edit(
1360 [
1361 (Point::new(0, 0)..Point::new(0, 0), "A"),
1362 (Point::new(1, 0)..Point::new(1, 0), "A"),
1363 ],
1364 None,
1365 cx,
1366 );
1367 multibuffer.edit(
1368 [
1369 (Point::new(0, 1)..Point::new(0, 1), "B"),
1370 (Point::new(1, 1)..Point::new(1, 1), "B"),
1371 ],
1372 None,
1373 cx,
1374 );
1375 multibuffer.end_transaction_at(now, cx);
1376 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1377
1378 // Verify edited ranges for transaction 1
1379 assert_eq!(
1380 multibuffer.edited_ranges_for_transaction(transaction_1, cx),
1381 &[
1382 Point::new(0, 0)..Point::new(0, 2),
1383 Point::new(1, 0)..Point::new(1, 2)
1384 ]
1385 );
1386
1387 // Edit buffer 1 through the multibuffer
1388 now += 2 * group_interval;
1389 multibuffer.start_transaction_at(now, cx);
1390 multibuffer.edit([(2..2, "C")], None, cx);
1391 multibuffer.end_transaction_at(now, cx);
1392 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
1393
1394 // Edit buffer 1 independently
1395 buffer_1.update(cx, |buffer_1, cx| {
1396 buffer_1.start_transaction_at(now);
1397 buffer_1.edit([(3..3, "D")], None, cx);
1398 buffer_1.end_transaction_at(now, cx);
1399
1400 now += 2 * group_interval;
1401 buffer_1.start_transaction_at(now);
1402 buffer_1.edit([(4..4, "E")], None, cx);
1403 buffer_1.end_transaction_at(now, cx);
1404 });
1405 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
1406
1407 // An undo in the multibuffer undoes the multibuffer transaction
1408 // and also any individual buffer edits that have occurred since
1409 // that transaction.
1410 multibuffer.undo(cx);
1411 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1412
1413 multibuffer.undo(cx);
1414 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1415
1416 multibuffer.redo(cx);
1417 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1418
1419 multibuffer.redo(cx);
1420 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
1421
1422 // Undo buffer 2 independently.
1423 buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx));
1424 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678");
1425
1426 // An undo in the multibuffer undoes the components of the
1427 // the last multibuffer transaction that are not already undone.
1428 multibuffer.undo(cx);
1429 assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678");
1430
1431 multibuffer.undo(cx);
1432 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1433
1434 multibuffer.redo(cx);
1435 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1436
1437 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
1438 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
1439
1440 // Redo stack gets cleared after an edit.
1441 now += 2 * group_interval;
1442 multibuffer.start_transaction_at(now, cx);
1443 multibuffer.edit([(0..0, "X")], None, cx);
1444 multibuffer.end_transaction_at(now, cx);
1445 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1446 multibuffer.redo(cx);
1447 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1448 multibuffer.undo(cx);
1449 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
1450 multibuffer.undo(cx);
1451 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1452
1453 // Transactions can be grouped manually.
1454 multibuffer.redo(cx);
1455 multibuffer.redo(cx);
1456 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1457 multibuffer.group_until_transaction(transaction_1, cx);
1458 multibuffer.undo(cx);
1459 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1460 multibuffer.redo(cx);
1461 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1462 });
1463}
1464
1465#[gpui::test]
1466fn test_excerpts_in_ranges_no_ranges(cx: &mut AppContext) {
1467 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1468 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1469 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1470 multibuffer.update(cx, |multibuffer, cx| {
1471 multibuffer.push_excerpts(
1472 buffer_1.clone(),
1473 [ExcerptRange {
1474 context: 0..buffer_1.read(cx).len(),
1475 primary: None,
1476 }],
1477 cx,
1478 );
1479 multibuffer.push_excerpts(
1480 buffer_2.clone(),
1481 [ExcerptRange {
1482 context: 0..buffer_2.read(cx).len(),
1483 primary: None,
1484 }],
1485 cx,
1486 );
1487 });
1488
1489 let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
1490
1491 let mut excerpts = snapshot.excerpts_in_ranges(iter::from_fn(|| None));
1492
1493 assert!(excerpts.next().is_none());
1494}
1495
1496fn validate_excerpts(
1497 actual: &[(ExcerptId, BufferId, Range<Anchor>)],
1498 expected: &Vec<(ExcerptId, BufferId, Range<Anchor>)>,
1499) {
1500 assert_eq!(actual.len(), expected.len());
1501
1502 actual
1503 .iter()
1504 .zip(expected)
1505 .map(|(actual, expected)| {
1506 assert_eq!(actual.0, expected.0);
1507 assert_eq!(actual.1, expected.1);
1508 assert_eq!(actual.2.start, expected.2.start);
1509 assert_eq!(actual.2.end, expected.2.end);
1510 })
1511 .collect_vec();
1512}
1513
1514fn map_range_from_excerpt(
1515 snapshot: &MultiBufferSnapshot,
1516 excerpt_id: ExcerptId,
1517 excerpt_buffer: &BufferSnapshot,
1518 range: Range<usize>,
1519) -> Range<Anchor> {
1520 snapshot
1521 .anchor_in_excerpt(excerpt_id, excerpt_buffer.anchor_before(range.start))
1522 .unwrap()
1523 ..snapshot
1524 .anchor_in_excerpt(excerpt_id, excerpt_buffer.anchor_after(range.end))
1525 .unwrap()
1526}
1527
1528fn make_expected_excerpt_info(
1529 snapshot: &MultiBufferSnapshot,
1530 cx: &mut AppContext,
1531 excerpt_id: ExcerptId,
1532 buffer: &Model<Buffer>,
1533 range: Range<usize>,
1534) -> (ExcerptId, BufferId, Range<Anchor>) {
1535 (
1536 excerpt_id,
1537 buffer.read(cx).remote_id(),
1538 map_range_from_excerpt(snapshot, excerpt_id, &buffer.read(cx).snapshot(), range),
1539 )
1540}
1541
1542#[gpui::test]
1543fn test_excerpts_in_ranges_range_inside_the_excerpt(cx: &mut AppContext) {
1544 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1545 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1546 let buffer_len = buffer_1.read(cx).len();
1547 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1548 let mut expected_excerpt_id = ExcerptId(0);
1549
1550 multibuffer.update(cx, |multibuffer, cx| {
1551 expected_excerpt_id = multibuffer.push_excerpts(
1552 buffer_1.clone(),
1553 [ExcerptRange {
1554 context: 0..buffer_1.read(cx).len(),
1555 primary: None,
1556 }],
1557 cx,
1558 )[0];
1559 multibuffer.push_excerpts(
1560 buffer_2.clone(),
1561 [ExcerptRange {
1562 context: 0..buffer_2.read(cx).len(),
1563 primary: None,
1564 }],
1565 cx,
1566 );
1567 });
1568
1569 let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
1570
1571 let range = snapshot
1572 .anchor_in_excerpt(expected_excerpt_id, buffer_1.read(cx).anchor_before(1))
1573 .unwrap()
1574 ..snapshot
1575 .anchor_in_excerpt(
1576 expected_excerpt_id,
1577 buffer_1.read(cx).anchor_after(buffer_len / 2),
1578 )
1579 .unwrap();
1580
1581 let expected_excerpts = vec![make_expected_excerpt_info(
1582 &snapshot,
1583 cx,
1584 expected_excerpt_id,
1585 &buffer_1,
1586 1..(buffer_len / 2),
1587 )];
1588
1589 let excerpts = snapshot
1590 .excerpts_in_ranges(vec![range.clone()].into_iter())
1591 .map(|(excerpt_id, buffer, actual_range)| {
1592 (
1593 excerpt_id,
1594 buffer.remote_id(),
1595 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1596 )
1597 })
1598 .collect_vec();
1599
1600 validate_excerpts(&excerpts, &expected_excerpts);
1601}
1602
1603#[gpui::test]
1604fn test_excerpts_in_ranges_range_crosses_excerpts_boundary(cx: &mut AppContext) {
1605 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1606 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1607 let buffer_len = buffer_1.read(cx).len();
1608 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1609 let mut excerpt_1_id = ExcerptId(0);
1610 let mut excerpt_2_id = ExcerptId(0);
1611
1612 multibuffer.update(cx, |multibuffer, cx| {
1613 excerpt_1_id = multibuffer.push_excerpts(
1614 buffer_1.clone(),
1615 [ExcerptRange {
1616 context: 0..buffer_1.read(cx).len(),
1617 primary: None,
1618 }],
1619 cx,
1620 )[0];
1621 excerpt_2_id = multibuffer.push_excerpts(
1622 buffer_2.clone(),
1623 [ExcerptRange {
1624 context: 0..buffer_2.read(cx).len(),
1625 primary: None,
1626 }],
1627 cx,
1628 )[0];
1629 });
1630
1631 let snapshot = multibuffer.read(cx).snapshot(cx);
1632
1633 let expected_range = snapshot
1634 .anchor_in_excerpt(
1635 excerpt_1_id,
1636 buffer_1.read(cx).anchor_before(buffer_len / 2),
1637 )
1638 .unwrap()
1639 ..snapshot
1640 .anchor_in_excerpt(excerpt_2_id, buffer_2.read(cx).anchor_after(buffer_len / 2))
1641 .unwrap();
1642
1643 let expected_excerpts = vec![
1644 make_expected_excerpt_info(
1645 &snapshot,
1646 cx,
1647 excerpt_1_id,
1648 &buffer_1,
1649 (buffer_len / 2)..buffer_len,
1650 ),
1651 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, 0..buffer_len / 2),
1652 ];
1653
1654 let excerpts = snapshot
1655 .excerpts_in_ranges(vec![expected_range.clone()].into_iter())
1656 .map(|(excerpt_id, buffer, actual_range)| {
1657 (
1658 excerpt_id,
1659 buffer.remote_id(),
1660 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1661 )
1662 })
1663 .collect_vec();
1664
1665 validate_excerpts(&excerpts, &expected_excerpts);
1666}
1667
1668#[gpui::test]
1669fn test_excerpts_in_ranges_range_encloses_excerpt(cx: &mut AppContext) {
1670 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1671 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1672 let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'r'), cx));
1673 let buffer_len = buffer_1.read(cx).len();
1674 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1675 let mut excerpt_1_id = ExcerptId(0);
1676 let mut excerpt_2_id = ExcerptId(0);
1677 let mut excerpt_3_id = ExcerptId(0);
1678
1679 multibuffer.update(cx, |multibuffer, cx| {
1680 excerpt_1_id = multibuffer.push_excerpts(
1681 buffer_1.clone(),
1682 [ExcerptRange {
1683 context: 0..buffer_1.read(cx).len(),
1684 primary: None,
1685 }],
1686 cx,
1687 )[0];
1688 excerpt_2_id = multibuffer.push_excerpts(
1689 buffer_2.clone(),
1690 [ExcerptRange {
1691 context: 0..buffer_2.read(cx).len(),
1692 primary: None,
1693 }],
1694 cx,
1695 )[0];
1696 excerpt_3_id = multibuffer.push_excerpts(
1697 buffer_3.clone(),
1698 [ExcerptRange {
1699 context: 0..buffer_3.read(cx).len(),
1700 primary: None,
1701 }],
1702 cx,
1703 )[0];
1704 });
1705
1706 let snapshot = multibuffer.read(cx).snapshot(cx);
1707
1708 let expected_range = snapshot
1709 .anchor_in_excerpt(
1710 excerpt_1_id,
1711 buffer_1.read(cx).anchor_before(buffer_len / 2),
1712 )
1713 .unwrap()
1714 ..snapshot
1715 .anchor_in_excerpt(excerpt_3_id, buffer_3.read(cx).anchor_after(buffer_len / 2))
1716 .unwrap();
1717
1718 let expected_excerpts = vec![
1719 make_expected_excerpt_info(
1720 &snapshot,
1721 cx,
1722 excerpt_1_id,
1723 &buffer_1,
1724 (buffer_len / 2)..buffer_len,
1725 ),
1726 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, 0..buffer_len),
1727 make_expected_excerpt_info(&snapshot, cx, excerpt_3_id, &buffer_3, 0..buffer_len / 2),
1728 ];
1729
1730 let excerpts = snapshot
1731 .excerpts_in_ranges(vec![expected_range.clone()].into_iter())
1732 .map(|(excerpt_id, buffer, actual_range)| {
1733 (
1734 excerpt_id,
1735 buffer.remote_id(),
1736 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1737 )
1738 })
1739 .collect_vec();
1740
1741 validate_excerpts(&excerpts, &expected_excerpts);
1742}
1743
1744#[gpui::test]
1745fn test_excerpts_in_ranges_multiple_ranges(cx: &mut AppContext) {
1746 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1747 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1748 let buffer_len = buffer_1.read(cx).len();
1749 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1750 let mut excerpt_1_id = ExcerptId(0);
1751 let mut excerpt_2_id = ExcerptId(0);
1752
1753 multibuffer.update(cx, |multibuffer, cx| {
1754 excerpt_1_id = multibuffer.push_excerpts(
1755 buffer_1.clone(),
1756 [ExcerptRange {
1757 context: 0..buffer_1.read(cx).len(),
1758 primary: None,
1759 }],
1760 cx,
1761 )[0];
1762 excerpt_2_id = multibuffer.push_excerpts(
1763 buffer_2.clone(),
1764 [ExcerptRange {
1765 context: 0..buffer_2.read(cx).len(),
1766 primary: None,
1767 }],
1768 cx,
1769 )[0];
1770 });
1771
1772 let snapshot = multibuffer.read(cx).snapshot(cx);
1773
1774 let ranges = vec![
1775 1..(buffer_len / 4),
1776 (buffer_len / 3)..(buffer_len / 2),
1777 (buffer_len / 4 * 3)..(buffer_len),
1778 ];
1779
1780 let expected_excerpts = ranges
1781 .iter()
1782 .map(|range| {
1783 make_expected_excerpt_info(&snapshot, cx, excerpt_1_id, &buffer_1, range.clone())
1784 })
1785 .collect_vec();
1786
1787 let ranges = ranges.into_iter().map(|range| {
1788 map_range_from_excerpt(
1789 &snapshot,
1790 excerpt_1_id,
1791 &buffer_1.read(cx).snapshot(),
1792 range,
1793 )
1794 });
1795
1796 let excerpts = snapshot
1797 .excerpts_in_ranges(ranges)
1798 .map(|(excerpt_id, buffer, actual_range)| {
1799 (
1800 excerpt_id,
1801 buffer.remote_id(),
1802 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1803 )
1804 })
1805 .collect_vec();
1806
1807 validate_excerpts(&excerpts, &expected_excerpts);
1808}
1809
1810#[gpui::test]
1811fn test_excerpts_in_ranges_range_ends_at_excerpt_end(cx: &mut AppContext) {
1812 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1813 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1814 let buffer_len = buffer_1.read(cx).len();
1815 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1816 let mut excerpt_1_id = ExcerptId(0);
1817 let mut excerpt_2_id = ExcerptId(0);
1818
1819 multibuffer.update(cx, |multibuffer, cx| {
1820 excerpt_1_id = multibuffer.push_excerpts(
1821 buffer_1.clone(),
1822 [ExcerptRange {
1823 context: 0..buffer_1.read(cx).len(),
1824 primary: None,
1825 }],
1826 cx,
1827 )[0];
1828 excerpt_2_id = multibuffer.push_excerpts(
1829 buffer_2.clone(),
1830 [ExcerptRange {
1831 context: 0..buffer_2.read(cx).len(),
1832 primary: None,
1833 }],
1834 cx,
1835 )[0];
1836 });
1837
1838 let snapshot = multibuffer.read(cx).snapshot(cx);
1839
1840 let ranges = [0..buffer_len, (buffer_len / 3)..(buffer_len / 2)];
1841
1842 let expected_excerpts = vec![
1843 make_expected_excerpt_info(&snapshot, cx, excerpt_1_id, &buffer_1, ranges[0].clone()),
1844 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, ranges[1].clone()),
1845 ];
1846
1847 let ranges = [
1848 map_range_from_excerpt(
1849 &snapshot,
1850 excerpt_1_id,
1851 &buffer_1.read(cx).snapshot(),
1852 ranges[0].clone(),
1853 ),
1854 map_range_from_excerpt(
1855 &snapshot,
1856 excerpt_2_id,
1857 &buffer_2.read(cx).snapshot(),
1858 ranges[1].clone(),
1859 ),
1860 ];
1861
1862 let excerpts = snapshot
1863 .excerpts_in_ranges(ranges.into_iter())
1864 .map(|(excerpt_id, buffer, actual_range)| {
1865 (
1866 excerpt_id,
1867 buffer.remote_id(),
1868 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1869 )
1870 })
1871 .collect_vec();
1872
1873 validate_excerpts(&excerpts, &expected_excerpts);
1874}
1875
1876#[gpui::test]
1877fn test_split_ranges(cx: &mut AppContext) {
1878 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1879 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1880 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1881 multibuffer.update(cx, |multibuffer, cx| {
1882 multibuffer.push_excerpts(
1883 buffer_1.clone(),
1884 [ExcerptRange {
1885 context: 0..buffer_1.read(cx).len(),
1886 primary: None,
1887 }],
1888 cx,
1889 );
1890 multibuffer.push_excerpts(
1891 buffer_2.clone(),
1892 [ExcerptRange {
1893 context: 0..buffer_2.read(cx).len(),
1894 primary: None,
1895 }],
1896 cx,
1897 );
1898 });
1899
1900 let snapshot = multibuffer.read(cx).snapshot(cx);
1901
1902 let buffer_1_len = buffer_1.read(cx).len();
1903 let buffer_2_len = buffer_2.read(cx).len();
1904 let buffer_1_midpoint = buffer_1_len / 2;
1905 let buffer_2_start = buffer_1_len + '\n'.len_utf8();
1906 let buffer_2_midpoint = buffer_2_start + buffer_2_len / 2;
1907 let total_len = buffer_2_start + buffer_2_len;
1908
1909 let input_ranges = [
1910 0..buffer_1_midpoint,
1911 buffer_1_midpoint..buffer_2_midpoint,
1912 buffer_2_midpoint..total_len,
1913 ]
1914 .map(|range| snapshot.anchor_before(range.start)..snapshot.anchor_after(range.end));
1915
1916 let actual_ranges = snapshot
1917 .split_ranges(input_ranges.into_iter())
1918 .map(|range| range.to_offset(&snapshot))
1919 .collect::<Vec<_>>();
1920
1921 let expected_ranges = vec![
1922 0..buffer_1_midpoint,
1923 buffer_1_midpoint..buffer_1_len,
1924 buffer_2_start..buffer_2_midpoint,
1925 buffer_2_midpoint..total_len,
1926 ];
1927
1928 assert_eq!(actual_ranges, expected_ranges);
1929}
1930
1931#[gpui::test]
1932fn test_split_ranges_single_range_spanning_three_excerpts(cx: &mut AppContext) {
1933 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1934 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1935 let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'm'), cx));
1936 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1937 multibuffer.update(cx, |multibuffer, cx| {
1938 multibuffer.push_excerpts(
1939 buffer_1.clone(),
1940 [ExcerptRange {
1941 context: 0..buffer_1.read(cx).len(),
1942 primary: None,
1943 }],
1944 cx,
1945 );
1946 multibuffer.push_excerpts(
1947 buffer_2.clone(),
1948 [ExcerptRange {
1949 context: 0..buffer_2.read(cx).len(),
1950 primary: None,
1951 }],
1952 cx,
1953 );
1954 multibuffer.push_excerpts(
1955 buffer_3.clone(),
1956 [ExcerptRange {
1957 context: 0..buffer_3.read(cx).len(),
1958 primary: None,
1959 }],
1960 cx,
1961 );
1962 });
1963
1964 let snapshot = multibuffer.read(cx).snapshot(cx);
1965
1966 let buffer_1_len = buffer_1.read(cx).len();
1967 let buffer_2_len = buffer_2.read(cx).len();
1968 let buffer_3_len = buffer_3.read(cx).len();
1969 let buffer_2_start = buffer_1_len + '\n'.len_utf8();
1970 let buffer_3_start = buffer_2_start + buffer_2_len + '\n'.len_utf8();
1971 let buffer_1_midpoint = buffer_1_len / 2;
1972 let buffer_3_midpoint = buffer_3_start + buffer_3_len / 2;
1973
1974 let input_range =
1975 snapshot.anchor_before(buffer_1_midpoint)..snapshot.anchor_after(buffer_3_midpoint);
1976
1977 let actual_ranges = snapshot
1978 .split_ranges(std::iter::once(input_range))
1979 .map(|range| range.to_offset(&snapshot))
1980 .collect::<Vec<_>>();
1981
1982 let expected_ranges = vec![
1983 buffer_1_midpoint..buffer_1_len,
1984 buffer_2_start..buffer_2_start + buffer_2_len,
1985 buffer_3_start..buffer_3_midpoint,
1986 ];
1987
1988 assert_eq!(actual_ranges, expected_ranges);
1989}