multi_buffer_tests.rs

   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}