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 excerpted_buffer_ranges = multibuffer
1238 .read(cx)
1239 .range_to_buffer_ranges(start_ix..end_ix, cx);
1240 let excerpted_buffers_text = excerpted_buffer_ranges
1241 .iter()
1242 .map(|(buffer, buffer_range, _)| {
1243 buffer
1244 .read(cx)
1245 .text_for_range(buffer_range.clone())
1246 .collect::<String>()
1247 })
1248 .collect::<Vec<_>>()
1249 .join("\n");
1250 assert_eq!(excerpted_buffers_text, text_for_range);
1251 if !expected_excerpts.is_empty() {
1252 assert!(!excerpted_buffer_ranges.is_empty());
1253 }
1254
1255 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1256 assert_eq!(
1257 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1258 expected_summary,
1259 "incorrect summary for range {:?}",
1260 start_ix..end_ix
1261 );
1262 }
1263
1264 // Anchor resolution
1265 let summaries = snapshot.summaries_for_anchors::<usize, _>(&anchors);
1266 assert_eq!(anchors.len(), summaries.len());
1267 for (anchor, resolved_offset) in anchors.iter().zip(summaries) {
1268 assert!(resolved_offset <= snapshot.len());
1269 assert_eq!(
1270 snapshot.summary_for_anchor::<usize>(anchor),
1271 resolved_offset
1272 );
1273 }
1274
1275 for _ in 0..10 {
1276 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
1277 assert_eq!(
1278 snapshot.reversed_chars_at(end_ix).collect::<String>(),
1279 expected_text[..end_ix].chars().rev().collect::<String>(),
1280 );
1281 }
1282
1283 for _ in 0..10 {
1284 let end_ix = rng.gen_range(0..=text_rope.len());
1285 let start_ix = rng.gen_range(0..=end_ix);
1286 assert_eq!(
1287 snapshot
1288 .bytes_in_range(start_ix..end_ix)
1289 .flatten()
1290 .copied()
1291 .collect::<Vec<_>>(),
1292 expected_text.as_bytes()[start_ix..end_ix].to_vec(),
1293 "bytes_in_range({:?})",
1294 start_ix..end_ix,
1295 );
1296 }
1297 }
1298
1299 let snapshot = multibuffer.read(cx).snapshot(cx);
1300 for (old_snapshot, subscription) in old_versions {
1301 let edits = subscription.consume().into_inner();
1302
1303 log::info!(
1304 "applying subscription edits to old text: {:?}: {:?}",
1305 old_snapshot.text(),
1306 edits,
1307 );
1308
1309 let mut text = old_snapshot.text();
1310 for edit in edits {
1311 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1312 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1313 }
1314 assert_eq!(text.to_string(), snapshot.text());
1315 }
1316}
1317
1318#[gpui::test]
1319fn test_history(cx: &mut AppContext) {
1320 let test_settings = SettingsStore::test(cx);
1321 cx.set_global(test_settings);
1322 let group_interval: Duration = Duration::from_millis(1);
1323 let buffer_1 = cx.new_model(|cx| {
1324 let mut buf = Buffer::local("1234", cx);
1325 buf.set_group_interval(group_interval);
1326 buf
1327 });
1328 let buffer_2 = cx.new_model(|cx| {
1329 let mut buf = Buffer::local("5678", cx);
1330 buf.set_group_interval(group_interval);
1331 buf
1332 });
1333 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1334 multibuffer.update(cx, |this, _| {
1335 this.history.group_interval = group_interval;
1336 });
1337 multibuffer.update(cx, |multibuffer, cx| {
1338 multibuffer.push_excerpts(
1339 buffer_1.clone(),
1340 [ExcerptRange {
1341 context: 0..buffer_1.read(cx).len(),
1342 primary: None,
1343 }],
1344 cx,
1345 );
1346 multibuffer.push_excerpts(
1347 buffer_2.clone(),
1348 [ExcerptRange {
1349 context: 0..buffer_2.read(cx).len(),
1350 primary: None,
1351 }],
1352 cx,
1353 );
1354 });
1355
1356 let mut now = Instant::now();
1357
1358 multibuffer.update(cx, |multibuffer, cx| {
1359 let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
1360 multibuffer.edit(
1361 [
1362 (Point::new(0, 0)..Point::new(0, 0), "A"),
1363 (Point::new(1, 0)..Point::new(1, 0), "A"),
1364 ],
1365 None,
1366 cx,
1367 );
1368 multibuffer.edit(
1369 [
1370 (Point::new(0, 1)..Point::new(0, 1), "B"),
1371 (Point::new(1, 1)..Point::new(1, 1), "B"),
1372 ],
1373 None,
1374 cx,
1375 );
1376 multibuffer.end_transaction_at(now, cx);
1377 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1378
1379 // Verify edited ranges for transaction 1
1380 assert_eq!(
1381 multibuffer.edited_ranges_for_transaction(transaction_1, cx),
1382 &[
1383 Point::new(0, 0)..Point::new(0, 2),
1384 Point::new(1, 0)..Point::new(1, 2)
1385 ]
1386 );
1387
1388 // Edit buffer 1 through the multibuffer
1389 now += 2 * group_interval;
1390 multibuffer.start_transaction_at(now, cx);
1391 multibuffer.edit([(2..2, "C")], None, cx);
1392 multibuffer.end_transaction_at(now, cx);
1393 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
1394
1395 // Edit buffer 1 independently
1396 buffer_1.update(cx, |buffer_1, cx| {
1397 buffer_1.start_transaction_at(now);
1398 buffer_1.edit([(3..3, "D")], None, cx);
1399 buffer_1.end_transaction_at(now, cx);
1400
1401 now += 2 * group_interval;
1402 buffer_1.start_transaction_at(now);
1403 buffer_1.edit([(4..4, "E")], None, cx);
1404 buffer_1.end_transaction_at(now, cx);
1405 });
1406 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
1407
1408 // An undo in the multibuffer undoes the multibuffer transaction
1409 // and also any individual buffer edits that have occurred since
1410 // that transaction.
1411 multibuffer.undo(cx);
1412 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1413
1414 multibuffer.undo(cx);
1415 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1416
1417 multibuffer.redo(cx);
1418 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1419
1420 multibuffer.redo(cx);
1421 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
1422
1423 // Undo buffer 2 independently.
1424 buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx));
1425 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678");
1426
1427 // An undo in the multibuffer undoes the components of the
1428 // the last multibuffer transaction that are not already undone.
1429 multibuffer.undo(cx);
1430 assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678");
1431
1432 multibuffer.undo(cx);
1433 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1434
1435 multibuffer.redo(cx);
1436 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
1437
1438 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
1439 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
1440
1441 // Redo stack gets cleared after an edit.
1442 now += 2 * group_interval;
1443 multibuffer.start_transaction_at(now, cx);
1444 multibuffer.edit([(0..0, "X")], None, cx);
1445 multibuffer.end_transaction_at(now, cx);
1446 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1447 multibuffer.redo(cx);
1448 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1449 multibuffer.undo(cx);
1450 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
1451 multibuffer.undo(cx);
1452 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1453
1454 // Transactions can be grouped manually.
1455 multibuffer.redo(cx);
1456 multibuffer.redo(cx);
1457 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1458 multibuffer.group_until_transaction(transaction_1, cx);
1459 multibuffer.undo(cx);
1460 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
1461 multibuffer.redo(cx);
1462 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
1463 });
1464}
1465
1466#[gpui::test]
1467fn test_excerpts_in_ranges_no_ranges(cx: &mut AppContext) {
1468 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1469 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1470 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1471 multibuffer.update(cx, |multibuffer, cx| {
1472 multibuffer.push_excerpts(
1473 buffer_1.clone(),
1474 [ExcerptRange {
1475 context: 0..buffer_1.read(cx).len(),
1476 primary: None,
1477 }],
1478 cx,
1479 );
1480 multibuffer.push_excerpts(
1481 buffer_2.clone(),
1482 [ExcerptRange {
1483 context: 0..buffer_2.read(cx).len(),
1484 primary: None,
1485 }],
1486 cx,
1487 );
1488 });
1489
1490 let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
1491
1492 let mut excerpts = snapshot.excerpts_in_ranges(iter::from_fn(|| None));
1493
1494 assert!(excerpts.next().is_none());
1495}
1496
1497fn validate_excerpts(
1498 actual: &[(ExcerptId, BufferId, Range<Anchor>)],
1499 expected: &Vec<(ExcerptId, BufferId, Range<Anchor>)>,
1500) {
1501 assert_eq!(actual.len(), expected.len());
1502
1503 actual
1504 .iter()
1505 .zip(expected)
1506 .map(|(actual, expected)| {
1507 assert_eq!(actual.0, expected.0);
1508 assert_eq!(actual.1, expected.1);
1509 assert_eq!(actual.2.start, expected.2.start);
1510 assert_eq!(actual.2.end, expected.2.end);
1511 })
1512 .collect_vec();
1513}
1514
1515fn map_range_from_excerpt(
1516 snapshot: &MultiBufferSnapshot,
1517 excerpt_id: ExcerptId,
1518 excerpt_buffer: &BufferSnapshot,
1519 range: Range<usize>,
1520) -> Range<Anchor> {
1521 snapshot
1522 .anchor_in_excerpt(excerpt_id, excerpt_buffer.anchor_before(range.start))
1523 .unwrap()
1524 ..snapshot
1525 .anchor_in_excerpt(excerpt_id, excerpt_buffer.anchor_after(range.end))
1526 .unwrap()
1527}
1528
1529fn make_expected_excerpt_info(
1530 snapshot: &MultiBufferSnapshot,
1531 cx: &mut AppContext,
1532 excerpt_id: ExcerptId,
1533 buffer: &Model<Buffer>,
1534 range: Range<usize>,
1535) -> (ExcerptId, BufferId, Range<Anchor>) {
1536 (
1537 excerpt_id,
1538 buffer.read(cx).remote_id(),
1539 map_range_from_excerpt(snapshot, excerpt_id, &buffer.read(cx).snapshot(), range),
1540 )
1541}
1542
1543#[gpui::test]
1544fn test_excerpts_in_ranges_range_inside_the_excerpt(cx: &mut AppContext) {
1545 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1546 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1547 let buffer_len = buffer_1.read(cx).len();
1548 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1549 let mut expected_excerpt_id = ExcerptId(0);
1550
1551 multibuffer.update(cx, |multibuffer, cx| {
1552 expected_excerpt_id = multibuffer.push_excerpts(
1553 buffer_1.clone(),
1554 [ExcerptRange {
1555 context: 0..buffer_1.read(cx).len(),
1556 primary: None,
1557 }],
1558 cx,
1559 )[0];
1560 multibuffer.push_excerpts(
1561 buffer_2.clone(),
1562 [ExcerptRange {
1563 context: 0..buffer_2.read(cx).len(),
1564 primary: None,
1565 }],
1566 cx,
1567 );
1568 });
1569
1570 let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
1571
1572 let range = snapshot
1573 .anchor_in_excerpt(expected_excerpt_id, buffer_1.read(cx).anchor_before(1))
1574 .unwrap()
1575 ..snapshot
1576 .anchor_in_excerpt(
1577 expected_excerpt_id,
1578 buffer_1.read(cx).anchor_after(buffer_len / 2),
1579 )
1580 .unwrap();
1581
1582 let expected_excerpts = vec![make_expected_excerpt_info(
1583 &snapshot,
1584 cx,
1585 expected_excerpt_id,
1586 &buffer_1,
1587 1..(buffer_len / 2),
1588 )];
1589
1590 let excerpts = snapshot
1591 .excerpts_in_ranges(vec![range.clone()].into_iter())
1592 .map(|(excerpt_id, buffer, actual_range)| {
1593 (
1594 excerpt_id,
1595 buffer.remote_id(),
1596 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1597 )
1598 })
1599 .collect_vec();
1600
1601 validate_excerpts(&excerpts, &expected_excerpts);
1602}
1603
1604#[gpui::test]
1605fn test_excerpts_in_ranges_range_crosses_excerpts_boundary(cx: &mut AppContext) {
1606 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1607 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1608 let buffer_len = buffer_1.read(cx).len();
1609 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1610 let mut excerpt_1_id = ExcerptId(0);
1611 let mut excerpt_2_id = ExcerptId(0);
1612
1613 multibuffer.update(cx, |multibuffer, cx| {
1614 excerpt_1_id = multibuffer.push_excerpts(
1615 buffer_1.clone(),
1616 [ExcerptRange {
1617 context: 0..buffer_1.read(cx).len(),
1618 primary: None,
1619 }],
1620 cx,
1621 )[0];
1622 excerpt_2_id = multibuffer.push_excerpts(
1623 buffer_2.clone(),
1624 [ExcerptRange {
1625 context: 0..buffer_2.read(cx).len(),
1626 primary: None,
1627 }],
1628 cx,
1629 )[0];
1630 });
1631
1632 let snapshot = multibuffer.read(cx).snapshot(cx);
1633
1634 let expected_range = snapshot
1635 .anchor_in_excerpt(
1636 excerpt_1_id,
1637 buffer_1.read(cx).anchor_before(buffer_len / 2),
1638 )
1639 .unwrap()
1640 ..snapshot
1641 .anchor_in_excerpt(excerpt_2_id, buffer_2.read(cx).anchor_after(buffer_len / 2))
1642 .unwrap();
1643
1644 let expected_excerpts = vec![
1645 make_expected_excerpt_info(
1646 &snapshot,
1647 cx,
1648 excerpt_1_id,
1649 &buffer_1,
1650 (buffer_len / 2)..buffer_len,
1651 ),
1652 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, 0..buffer_len / 2),
1653 ];
1654
1655 let excerpts = snapshot
1656 .excerpts_in_ranges(vec![expected_range.clone()].into_iter())
1657 .map(|(excerpt_id, buffer, actual_range)| {
1658 (
1659 excerpt_id,
1660 buffer.remote_id(),
1661 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1662 )
1663 })
1664 .collect_vec();
1665
1666 validate_excerpts(&excerpts, &expected_excerpts);
1667}
1668
1669#[gpui::test]
1670fn test_excerpts_in_ranges_range_encloses_excerpt(cx: &mut AppContext) {
1671 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1672 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1673 let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'r'), cx));
1674 let buffer_len = buffer_1.read(cx).len();
1675 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1676 let mut excerpt_1_id = ExcerptId(0);
1677 let mut excerpt_2_id = ExcerptId(0);
1678 let mut excerpt_3_id = ExcerptId(0);
1679
1680 multibuffer.update(cx, |multibuffer, cx| {
1681 excerpt_1_id = multibuffer.push_excerpts(
1682 buffer_1.clone(),
1683 [ExcerptRange {
1684 context: 0..buffer_1.read(cx).len(),
1685 primary: None,
1686 }],
1687 cx,
1688 )[0];
1689 excerpt_2_id = multibuffer.push_excerpts(
1690 buffer_2.clone(),
1691 [ExcerptRange {
1692 context: 0..buffer_2.read(cx).len(),
1693 primary: None,
1694 }],
1695 cx,
1696 )[0];
1697 excerpt_3_id = multibuffer.push_excerpts(
1698 buffer_3.clone(),
1699 [ExcerptRange {
1700 context: 0..buffer_3.read(cx).len(),
1701 primary: None,
1702 }],
1703 cx,
1704 )[0];
1705 });
1706
1707 let snapshot = multibuffer.read(cx).snapshot(cx);
1708
1709 let expected_range = snapshot
1710 .anchor_in_excerpt(
1711 excerpt_1_id,
1712 buffer_1.read(cx).anchor_before(buffer_len / 2),
1713 )
1714 .unwrap()
1715 ..snapshot
1716 .anchor_in_excerpt(excerpt_3_id, buffer_3.read(cx).anchor_after(buffer_len / 2))
1717 .unwrap();
1718
1719 let expected_excerpts = vec![
1720 make_expected_excerpt_info(
1721 &snapshot,
1722 cx,
1723 excerpt_1_id,
1724 &buffer_1,
1725 (buffer_len / 2)..buffer_len,
1726 ),
1727 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, 0..buffer_len),
1728 make_expected_excerpt_info(&snapshot, cx, excerpt_3_id, &buffer_3, 0..buffer_len / 2),
1729 ];
1730
1731 let excerpts = snapshot
1732 .excerpts_in_ranges(vec![expected_range.clone()].into_iter())
1733 .map(|(excerpt_id, buffer, actual_range)| {
1734 (
1735 excerpt_id,
1736 buffer.remote_id(),
1737 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1738 )
1739 })
1740 .collect_vec();
1741
1742 validate_excerpts(&excerpts, &expected_excerpts);
1743}
1744
1745#[gpui::test]
1746fn test_excerpts_in_ranges_multiple_ranges(cx: &mut AppContext) {
1747 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1748 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1749 let buffer_len = buffer_1.read(cx).len();
1750 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1751 let mut excerpt_1_id = ExcerptId(0);
1752 let mut excerpt_2_id = ExcerptId(0);
1753
1754 multibuffer.update(cx, |multibuffer, cx| {
1755 excerpt_1_id = multibuffer.push_excerpts(
1756 buffer_1.clone(),
1757 [ExcerptRange {
1758 context: 0..buffer_1.read(cx).len(),
1759 primary: None,
1760 }],
1761 cx,
1762 )[0];
1763 excerpt_2_id = multibuffer.push_excerpts(
1764 buffer_2.clone(),
1765 [ExcerptRange {
1766 context: 0..buffer_2.read(cx).len(),
1767 primary: None,
1768 }],
1769 cx,
1770 )[0];
1771 });
1772
1773 let snapshot = multibuffer.read(cx).snapshot(cx);
1774
1775 let ranges = vec![
1776 1..(buffer_len / 4),
1777 (buffer_len / 3)..(buffer_len / 2),
1778 (buffer_len / 4 * 3)..(buffer_len),
1779 ];
1780
1781 let expected_excerpts = ranges
1782 .iter()
1783 .map(|range| {
1784 make_expected_excerpt_info(&snapshot, cx, excerpt_1_id, &buffer_1, range.clone())
1785 })
1786 .collect_vec();
1787
1788 let ranges = ranges.into_iter().map(|range| {
1789 map_range_from_excerpt(
1790 &snapshot,
1791 excerpt_1_id,
1792 &buffer_1.read(cx).snapshot(),
1793 range,
1794 )
1795 });
1796
1797 let excerpts = snapshot
1798 .excerpts_in_ranges(ranges)
1799 .map(|(excerpt_id, buffer, actual_range)| {
1800 (
1801 excerpt_id,
1802 buffer.remote_id(),
1803 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1804 )
1805 })
1806 .collect_vec();
1807
1808 validate_excerpts(&excerpts, &expected_excerpts);
1809}
1810
1811#[gpui::test]
1812fn test_excerpts_in_ranges_range_ends_at_excerpt_end(cx: &mut AppContext) {
1813 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1814 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1815 let buffer_len = buffer_1.read(cx).len();
1816 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1817 let mut excerpt_1_id = ExcerptId(0);
1818 let mut excerpt_2_id = ExcerptId(0);
1819
1820 multibuffer.update(cx, |multibuffer, cx| {
1821 excerpt_1_id = multibuffer.push_excerpts(
1822 buffer_1.clone(),
1823 [ExcerptRange {
1824 context: 0..buffer_1.read(cx).len(),
1825 primary: None,
1826 }],
1827 cx,
1828 )[0];
1829 excerpt_2_id = multibuffer.push_excerpts(
1830 buffer_2.clone(),
1831 [ExcerptRange {
1832 context: 0..buffer_2.read(cx).len(),
1833 primary: None,
1834 }],
1835 cx,
1836 )[0];
1837 });
1838
1839 let snapshot = multibuffer.read(cx).snapshot(cx);
1840
1841 let ranges = [0..buffer_len, (buffer_len / 3)..(buffer_len / 2)];
1842
1843 let expected_excerpts = vec![
1844 make_expected_excerpt_info(&snapshot, cx, excerpt_1_id, &buffer_1, ranges[0].clone()),
1845 make_expected_excerpt_info(&snapshot, cx, excerpt_2_id, &buffer_2, ranges[1].clone()),
1846 ];
1847
1848 let ranges = [
1849 map_range_from_excerpt(
1850 &snapshot,
1851 excerpt_1_id,
1852 &buffer_1.read(cx).snapshot(),
1853 ranges[0].clone(),
1854 ),
1855 map_range_from_excerpt(
1856 &snapshot,
1857 excerpt_2_id,
1858 &buffer_2.read(cx).snapshot(),
1859 ranges[1].clone(),
1860 ),
1861 ];
1862
1863 let excerpts = snapshot
1864 .excerpts_in_ranges(ranges.into_iter())
1865 .map(|(excerpt_id, buffer, actual_range)| {
1866 (
1867 excerpt_id,
1868 buffer.remote_id(),
1869 map_range_from_excerpt(&snapshot, excerpt_id, buffer, actual_range),
1870 )
1871 })
1872 .collect_vec();
1873
1874 validate_excerpts(&excerpts, &expected_excerpts);
1875}
1876
1877#[gpui::test]
1878fn test_split_ranges(cx: &mut AppContext) {
1879 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1880 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1881 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1882 multibuffer.update(cx, |multibuffer, cx| {
1883 multibuffer.push_excerpts(
1884 buffer_1.clone(),
1885 [ExcerptRange {
1886 context: 0..buffer_1.read(cx).len(),
1887 primary: None,
1888 }],
1889 cx,
1890 );
1891 multibuffer.push_excerpts(
1892 buffer_2.clone(),
1893 [ExcerptRange {
1894 context: 0..buffer_2.read(cx).len(),
1895 primary: None,
1896 }],
1897 cx,
1898 );
1899 });
1900
1901 let snapshot = multibuffer.read(cx).snapshot(cx);
1902
1903 let buffer_1_len = buffer_1.read(cx).len();
1904 let buffer_2_len = buffer_2.read(cx).len();
1905 let buffer_1_midpoint = buffer_1_len / 2;
1906 let buffer_2_start = buffer_1_len + '\n'.len_utf8();
1907 let buffer_2_midpoint = buffer_2_start + buffer_2_len / 2;
1908 let total_len = buffer_2_start + buffer_2_len;
1909
1910 let input_ranges = [
1911 0..buffer_1_midpoint,
1912 buffer_1_midpoint..buffer_2_midpoint,
1913 buffer_2_midpoint..total_len,
1914 ]
1915 .map(|range| snapshot.anchor_before(range.start)..snapshot.anchor_after(range.end));
1916
1917 let actual_ranges = snapshot
1918 .split_ranges(input_ranges.into_iter())
1919 .map(|range| range.to_offset(&snapshot))
1920 .collect::<Vec<_>>();
1921
1922 let expected_ranges = vec![
1923 0..buffer_1_midpoint,
1924 buffer_1_midpoint..buffer_1_len,
1925 buffer_2_start..buffer_2_midpoint,
1926 buffer_2_midpoint..total_len,
1927 ];
1928
1929 assert_eq!(actual_ranges, expected_ranges);
1930}
1931
1932#[gpui::test]
1933fn test_split_ranges_single_range_spanning_three_excerpts(cx: &mut AppContext) {
1934 let buffer_1 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
1935 let buffer_2 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
1936 let buffer_3 = cx.new_model(|cx| Buffer::local(sample_text(6, 6, 'm'), cx));
1937 let multibuffer = cx.new_model(|_| MultiBuffer::new(Capability::ReadWrite));
1938 multibuffer.update(cx, |multibuffer, cx| {
1939 multibuffer.push_excerpts(
1940 buffer_1.clone(),
1941 [ExcerptRange {
1942 context: 0..buffer_1.read(cx).len(),
1943 primary: None,
1944 }],
1945 cx,
1946 );
1947 multibuffer.push_excerpts(
1948 buffer_2.clone(),
1949 [ExcerptRange {
1950 context: 0..buffer_2.read(cx).len(),
1951 primary: None,
1952 }],
1953 cx,
1954 );
1955 multibuffer.push_excerpts(
1956 buffer_3.clone(),
1957 [ExcerptRange {
1958 context: 0..buffer_3.read(cx).len(),
1959 primary: None,
1960 }],
1961 cx,
1962 );
1963 });
1964
1965 let snapshot = multibuffer.read(cx).snapshot(cx);
1966
1967 let buffer_1_len = buffer_1.read(cx).len();
1968 let buffer_2_len = buffer_2.read(cx).len();
1969 let buffer_3_len = buffer_3.read(cx).len();
1970 let buffer_2_start = buffer_1_len + '\n'.len_utf8();
1971 let buffer_3_start = buffer_2_start + buffer_2_len + '\n'.len_utf8();
1972 let buffer_1_midpoint = buffer_1_len / 2;
1973 let buffer_3_midpoint = buffer_3_start + buffer_3_len / 2;
1974
1975 let input_range =
1976 snapshot.anchor_before(buffer_1_midpoint)..snapshot.anchor_after(buffer_3_midpoint);
1977
1978 let actual_ranges = snapshot
1979 .split_ranges(std::iter::once(input_range))
1980 .map(|range| range.to_offset(&snapshot))
1981 .collect::<Vec<_>>();
1982
1983 let expected_ranges = vec![
1984 buffer_1_midpoint..buffer_1_len,
1985 buffer_2_start..buffer_2_start + buffer_2_len,
1986 buffer_3_start..buffer_3_midpoint,
1987 ];
1988
1989 assert_eq!(actual_ranges, expected_ranges);
1990}