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 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}