1use super::*;
   2use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
   3use gpui::{App, BackgroundExecutor, TestAppContext};
   4use indoc::indoc;
   5use language::{Buffer, Rope};
   6use parking_lot::RwLock;
   7use rand::prelude::*;
   8use settings::SettingsStore;
   9use std::env;
  10use std::time::{Duration, Instant};
  11use util::RandomCharIter;
  12use util::rel_path::rel_path;
  13use util::test::sample_text;
  14
  15#[ctor::ctor]
  16fn init_logger() {
  17    zlog::init_test();
  18}
  19
  20#[gpui::test]
  21fn test_empty_singleton(cx: &mut App) {
  22    let buffer = cx.new(|cx| Buffer::local("", cx));
  23    let buffer_id = buffer.read(cx).remote_id();
  24    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
  25    let snapshot = multibuffer.read(cx).snapshot(cx);
  26    assert_eq!(snapshot.text(), "");
  27    assert_eq!(
  28        snapshot.row_infos(MultiBufferRow(0)).collect::<Vec<_>>(),
  29        [RowInfo {
  30            buffer_id: Some(buffer_id),
  31            buffer_row: Some(0),
  32            multibuffer_row: Some(MultiBufferRow(0)),
  33            diff_status: None,
  34            expand_info: None,
  35        }]
  36    );
  37}
  38
  39#[gpui::test]
  40fn test_singleton(cx: &mut App) {
  41    let buffer = cx.new(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
  42    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
  43
  44    let snapshot = multibuffer.read(cx).snapshot(cx);
  45    assert_eq!(snapshot.text(), buffer.read(cx).text());
  46
  47    assert_eq!(
  48        snapshot
  49            .row_infos(MultiBufferRow(0))
  50            .map(|info| info.buffer_row)
  51            .collect::<Vec<_>>(),
  52        (0..buffer.read(cx).row_count())
  53            .map(Some)
  54            .collect::<Vec<_>>()
  55    );
  56    assert_consistent_line_numbers(&snapshot);
  57
  58    buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx));
  59    let snapshot = multibuffer.read(cx).snapshot(cx);
  60
  61    assert_eq!(snapshot.text(), buffer.read(cx).text());
  62    assert_eq!(
  63        snapshot
  64            .row_infos(MultiBufferRow(0))
  65            .map(|info| info.buffer_row)
  66            .collect::<Vec<_>>(),
  67        (0..buffer.read(cx).row_count())
  68            .map(Some)
  69            .collect::<Vec<_>>()
  70    );
  71    assert_consistent_line_numbers(&snapshot);
  72}
  73
  74#[gpui::test]
  75fn test_remote(cx: &mut App) {
  76    let host_buffer = cx.new(|cx| Buffer::local("a", cx));
  77    let guest_buffer = cx.new(|cx| {
  78        let state = host_buffer.read(cx).to_proto(cx);
  79        let ops = cx
  80            .background_executor()
  81            .block(host_buffer.read(cx).serialize_ops(None, cx));
  82        let mut buffer = Buffer::from_proto(
  83            ReplicaId::REMOTE_SERVER,
  84            Capability::ReadWrite,
  85            state,
  86            None,
  87            cx.background_executor(),
  88        )
  89        .unwrap();
  90        buffer.apply_ops(
  91            ops.into_iter()
  92                .map(|op| language::proto::deserialize_operation(op).unwrap()),
  93            cx,
  94        );
  95        buffer
  96    });
  97    let multibuffer = cx.new(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
  98    let snapshot = multibuffer.read(cx).snapshot(cx);
  99    assert_eq!(snapshot.text(), "a");
 100
 101    guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx));
 102    let snapshot = multibuffer.read(cx).snapshot(cx);
 103    assert_eq!(snapshot.text(), "ab");
 104
 105    guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx));
 106    let snapshot = multibuffer.read(cx).snapshot(cx);
 107    assert_eq!(snapshot.text(), "abc");
 108}
 109
 110#[gpui::test]
 111fn test_excerpt_boundaries_and_clipping(cx: &mut App) {
 112    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'a'), cx));
 113    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(6, 6, 'g'), cx));
 114    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 115
 116    let events = Arc::new(RwLock::new(Vec::<Event>::new()));
 117    multibuffer.update(cx, |_, cx| {
 118        let events = events.clone();
 119        cx.subscribe(&multibuffer, move |_, _, event, _| {
 120            if let Event::Edited { .. } = event {
 121                events.write().push(event.clone())
 122            }
 123        })
 124        .detach();
 125    });
 126
 127    let subscription = multibuffer.update(cx, |multibuffer, cx| {
 128        let subscription = multibuffer.subscribe();
 129        multibuffer.push_excerpts(
 130            buffer_1.clone(),
 131            [ExcerptRange::new(Point::new(1, 2)..Point::new(2, 5))],
 132            cx,
 133        );
 134        assert_eq!(
 135            subscription.consume().into_inner(),
 136            [Edit {
 137                old: 0..0,
 138                new: 0..10
 139            }]
 140        );
 141
 142        multibuffer.push_excerpts(
 143            buffer_1.clone(),
 144            [ExcerptRange::new(Point::new(3, 3)..Point::new(4, 4))],
 145            cx,
 146        );
 147        multibuffer.push_excerpts(
 148            buffer_2.clone(),
 149            [ExcerptRange::new(Point::new(3, 1)..Point::new(3, 3))],
 150            cx,
 151        );
 152        assert_eq!(
 153            subscription.consume().into_inner(),
 154            [Edit {
 155                old: 10..10,
 156                new: 10..22
 157            }]
 158        );
 159
 160        subscription
 161    });
 162
 163    // Adding excerpts emits an edited event.
 164    assert_eq!(
 165        events.read().as_slice(),
 166        &[
 167            Event::Edited {
 168                edited_buffer: None,
 169            },
 170            Event::Edited {
 171                edited_buffer: None,
 172            },
 173            Event::Edited {
 174                edited_buffer: None,
 175            }
 176        ]
 177    );
 178
 179    let snapshot = multibuffer.read(cx).snapshot(cx);
 180    assert_eq!(
 181        snapshot.text(),
 182        indoc!(
 183            "
 184            bbbb
 185            ccccc
 186            ddd
 187            eeee
 188            jj"
 189        ),
 190    );
 191    assert_eq!(
 192        snapshot
 193            .row_infos(MultiBufferRow(0))
 194            .map(|info| info.buffer_row)
 195            .collect::<Vec<_>>(),
 196        [Some(1), Some(2), Some(3), Some(4), Some(3)]
 197    );
 198    assert_eq!(
 199        snapshot
 200            .row_infos(MultiBufferRow(2))
 201            .map(|info| info.buffer_row)
 202            .collect::<Vec<_>>(),
 203        [Some(3), Some(4), Some(3)]
 204    );
 205    assert_eq!(
 206        snapshot
 207            .row_infos(MultiBufferRow(4))
 208            .map(|info| info.buffer_row)
 209            .collect::<Vec<_>>(),
 210        [Some(3)]
 211    );
 212    assert!(
 213        snapshot
 214            .row_infos(MultiBufferRow(5))
 215            .map(|info| info.buffer_row)
 216            .collect::<Vec<_>>()
 217            .is_empty()
 218    );
 219
 220    assert_eq!(
 221        boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
 222        &[
 223            (MultiBufferRow(0), "bbbb\nccccc".to_string(), true),
 224            (MultiBufferRow(2), "ddd\neeee".to_string(), false),
 225            (MultiBufferRow(4), "jj".to_string(), true),
 226        ]
 227    );
 228    assert_eq!(
 229        boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
 230        &[(MultiBufferRow(0), "bbbb\nccccc".to_string(), true)]
 231    );
 232    assert_eq!(
 233        boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
 234        &[]
 235    );
 236    assert_eq!(
 237        boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot),
 238        &[]
 239    );
 240    assert_eq!(
 241        boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
 242        &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
 243    );
 244    assert_eq!(
 245        boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
 246        &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
 247    );
 248    assert_eq!(
 249        boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
 250        &[(MultiBufferRow(2), "ddd\neeee".to_string(), false)]
 251    );
 252    assert_eq!(
 253        boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
 254        &[(MultiBufferRow(4), "jj".to_string(), true)]
 255    );
 256    assert_eq!(
 257        boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
 258        &[]
 259    );
 260
 261    buffer_1.update(cx, |buffer, cx| {
 262        let text = "\n";
 263        buffer.edit(
 264            [
 265                (Point::new(0, 0)..Point::new(0, 0), text),
 266                (Point::new(2, 1)..Point::new(2, 3), text),
 267            ],
 268            None,
 269            cx,
 270        );
 271    });
 272
 273    let snapshot = multibuffer.read(cx).snapshot(cx);
 274    assert_eq!(
 275        snapshot.text(),
 276        concat!(
 277            "bbbb\n", // Preserve newlines
 278            "c\n",    //
 279            "cc\n",   //
 280            "ddd\n",  //
 281            "eeee\n", //
 282            "jj"      //
 283        )
 284    );
 285
 286    assert_eq!(
 287        subscription.consume().into_inner(),
 288        [Edit {
 289            old: 6..8,
 290            new: 6..7
 291        }]
 292    );
 293
 294    let snapshot = multibuffer.read(cx).snapshot(cx);
 295    assert_eq!(
 296        snapshot.clip_point(Point::new(0, 5), Bias::Left),
 297        Point::new(0, 4)
 298    );
 299    assert_eq!(
 300        snapshot.clip_point(Point::new(0, 5), Bias::Right),
 301        Point::new(0, 4)
 302    );
 303    assert_eq!(
 304        snapshot.clip_point(Point::new(5, 1), Bias::Right),
 305        Point::new(5, 1)
 306    );
 307    assert_eq!(
 308        snapshot.clip_point(Point::new(5, 2), Bias::Right),
 309        Point::new(5, 2)
 310    );
 311    assert_eq!(
 312        snapshot.clip_point(Point::new(5, 3), Bias::Right),
 313        Point::new(5, 2)
 314    );
 315
 316    let snapshot = multibuffer.update(cx, |multibuffer, cx| {
 317        let (buffer_2_excerpt_id, _) =
 318            multibuffer.excerpts_for_buffer(buffer_2.read(cx).remote_id(), cx)[0].clone();
 319        multibuffer.remove_excerpts([buffer_2_excerpt_id], cx);
 320        multibuffer.snapshot(cx)
 321    });
 322
 323    assert_eq!(
 324        snapshot.text(),
 325        concat!(
 326            "bbbb\n", // Preserve newlines
 327            "c\n",    //
 328            "cc\n",   //
 329            "ddd\n",  //
 330            "eeee",   //
 331        )
 332    );
 333
 334    fn boundaries_in_range(
 335        range: Range<Point>,
 336        snapshot: &MultiBufferSnapshot,
 337    ) -> Vec<(MultiBufferRow, String, bool)> {
 338        snapshot
 339            .excerpt_boundaries_in_range(range)
 340            .map(|boundary| {
 341                let starts_new_buffer = boundary.starts_new_buffer();
 342                (
 343                    boundary.row,
 344                    boundary
 345                        .next
 346                        .buffer
 347                        .text_for_range(boundary.next.range.context)
 348                        .collect::<String>(),
 349                    starts_new_buffer,
 350                )
 351            })
 352            .collect::<Vec<_>>()
 353    }
 354}
 355
 356#[gpui::test]
 357fn test_diff_boundary_anchors(cx: &mut TestAppContext) {
 358    let base_text = "one\ntwo\nthree\n";
 359    let text = "one\nthree\n";
 360    let buffer = cx.new(|cx| Buffer::local(text, cx));
 361    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
 362    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 363    multibuffer.update(cx, |multibuffer, cx| multibuffer.add_diff(diff, cx));
 364
 365    let (before, after) = multibuffer.update(cx, |multibuffer, cx| {
 366        let before = multibuffer.snapshot(cx).anchor_before(Point::new(1, 0));
 367        let after = multibuffer.snapshot(cx).anchor_after(Point::new(1, 0));
 368        multibuffer.set_all_diff_hunks_expanded(cx);
 369        (before, after)
 370    });
 371    cx.run_until_parked();
 372
 373    let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 374    let actual_text = snapshot.text();
 375    let actual_row_infos = snapshot.row_infos(MultiBufferRow(0)).collect::<Vec<_>>();
 376    let actual_diff = format_diff(&actual_text, &actual_row_infos, &Default::default(), None);
 377    pretty_assertions::assert_eq!(
 378        actual_diff,
 379        indoc! {
 380            "  one
 381             - two
 382               three
 383             "
 384        },
 385    );
 386
 387    multibuffer.update(cx, |multibuffer, cx| {
 388        let snapshot = multibuffer.snapshot(cx);
 389        assert_eq!(before.to_point(&snapshot), Point::new(1, 0));
 390        assert_eq!(after.to_point(&snapshot), Point::new(2, 0));
 391        assert_eq!(
 392            vec![Point::new(1, 0), Point::new(2, 0),],
 393            snapshot.summaries_for_anchors::<Point, _>(&[before, after]),
 394        )
 395    })
 396}
 397
 398#[gpui::test]
 399fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
 400    let base_text = "one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\n";
 401    let text = "one\nfour\nseven\n";
 402    let buffer = cx.new(|cx| Buffer::local(text, cx));
 403    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
 404    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
 405    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
 406        (multibuffer.snapshot(cx), multibuffer.subscribe())
 407    });
 408
 409    multibuffer.update(cx, |multibuffer, cx| {
 410        multibuffer.add_diff(diff, cx);
 411        multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
 412    });
 413
 414    assert_new_snapshot(
 415        &multibuffer,
 416        &mut snapshot,
 417        &mut subscription,
 418        cx,
 419        indoc! {
 420            "  one
 421             - two
 422             - three
 423               four
 424             - five
 425             - six
 426               seven
 427             - eight
 428            "
 429        },
 430    );
 431
 432    assert_eq!(
 433        snapshot
 434            .diff_hunks_in_range(Point::new(1, 0)..Point::MAX)
 435            .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0)
 436            .collect::<Vec<_>>(),
 437        vec![1..3, 4..6, 7..8]
 438    );
 439
 440    assert_eq!(snapshot.diff_hunk_before(Point::new(1, 1)), None,);
 441    assert_eq!(
 442        snapshot.diff_hunk_before(Point::new(7, 0)),
 443        Some(MultiBufferRow(4))
 444    );
 445    assert_eq!(
 446        snapshot.diff_hunk_before(Point::new(4, 0)),
 447        Some(MultiBufferRow(1))
 448    );
 449
 450    multibuffer.update(cx, |multibuffer, cx| {
 451        multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
 452    });
 453
 454    assert_new_snapshot(
 455        &multibuffer,
 456        &mut snapshot,
 457        &mut subscription,
 458        cx,
 459        indoc! {
 460            "
 461            one
 462            four
 463            seven
 464            "
 465        },
 466    );
 467
 468    assert_eq!(
 469        snapshot.diff_hunk_before(Point::new(2, 0)),
 470        Some(MultiBufferRow(1)),
 471    );
 472    assert_eq!(
 473        snapshot.diff_hunk_before(Point::new(4, 0)),
 474        Some(MultiBufferRow(2))
 475    );
 476}
 477
 478#[gpui::test]
 479fn test_editing_text_in_diff_hunks(cx: &mut TestAppContext) {
 480    let base_text = "one\ntwo\nfour\nfive\nsix\nseven\n";
 481    let text = "one\ntwo\nTHREE\nfour\nfive\nseven\n";
 482    let buffer = cx.new(|cx| Buffer::local(text, cx));
 483    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
 484    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 485
 486    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
 487        multibuffer.add_diff(diff.clone(), cx);
 488        (multibuffer.snapshot(cx), multibuffer.subscribe())
 489    });
 490
 491    cx.executor().run_until_parked();
 492    multibuffer.update(cx, |multibuffer, cx| {
 493        multibuffer.set_all_diff_hunks_expanded(cx);
 494    });
 495
 496    assert_new_snapshot(
 497        &multibuffer,
 498        &mut snapshot,
 499        &mut subscription,
 500        cx,
 501        indoc! {
 502            "
 503              one
 504              two
 505            + THREE
 506              four
 507              five
 508            - six
 509              seven
 510            "
 511        },
 512    );
 513
 514    // Insert a newline within an insertion hunk
 515    multibuffer.update(cx, |multibuffer, cx| {
 516        multibuffer.edit([(Point::new(2, 0)..Point::new(2, 0), "__\n__")], None, cx);
 517    });
 518    assert_new_snapshot(
 519        &multibuffer,
 520        &mut snapshot,
 521        &mut subscription,
 522        cx,
 523        indoc! {
 524            "
 525              one
 526              two
 527            + __
 528            + __THREE
 529              four
 530              five
 531            - six
 532              seven
 533            "
 534        },
 535    );
 536
 537    // Delete the newline before a deleted hunk.
 538    multibuffer.update(cx, |multibuffer, cx| {
 539        multibuffer.edit([(Point::new(5, 4)..Point::new(6, 0), "")], None, cx);
 540    });
 541    assert_new_snapshot(
 542        &multibuffer,
 543        &mut snapshot,
 544        &mut subscription,
 545        cx,
 546        indoc! {
 547            "
 548              one
 549              two
 550            + __
 551            + __THREE
 552              four
 553              fiveseven
 554            "
 555        },
 556    );
 557
 558    multibuffer.update(cx, |multibuffer, cx| multibuffer.undo(cx));
 559    assert_new_snapshot(
 560        &multibuffer,
 561        &mut snapshot,
 562        &mut subscription,
 563        cx,
 564        indoc! {
 565            "
 566              one
 567              two
 568            + __
 569            + __THREE
 570              four
 571              five
 572            - six
 573              seven
 574            "
 575        },
 576    );
 577
 578    // Cannot (yet) insert at the beginning of a deleted hunk.
 579    // (because it would put the newline in the wrong place)
 580    multibuffer.update(cx, |multibuffer, cx| {
 581        multibuffer.edit([(Point::new(6, 0)..Point::new(6, 0), "\n")], None, cx);
 582    });
 583    assert_new_snapshot(
 584        &multibuffer,
 585        &mut snapshot,
 586        &mut subscription,
 587        cx,
 588        indoc! {
 589            "
 590              one
 591              two
 592            + __
 593            + __THREE
 594              four
 595              five
 596            - six
 597              seven
 598            "
 599        },
 600    );
 601
 602    // Replace a range that ends in a deleted hunk.
 603    multibuffer.update(cx, |multibuffer, cx| {
 604        multibuffer.edit([(Point::new(5, 2)..Point::new(6, 2), "fty-")], None, cx);
 605    });
 606    assert_new_snapshot(
 607        &multibuffer,
 608        &mut snapshot,
 609        &mut subscription,
 610        cx,
 611        indoc! {
 612            "
 613              one
 614              two
 615            + __
 616            + __THREE
 617              four
 618              fifty-seven
 619            "
 620        },
 621    );
 622}
 623
 624#[gpui::test]
 625fn test_excerpt_events(cx: &mut App) {
 626    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(10, 3, 'a'), cx));
 627    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(10, 3, 'm'), cx));
 628
 629    let leader_multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 630    let follower_multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 631    let follower_edit_event_count = Arc::new(RwLock::new(0));
 632
 633    follower_multibuffer.update(cx, |_, cx| {
 634        let follower_edit_event_count = follower_edit_event_count.clone();
 635        cx.subscribe(
 636            &leader_multibuffer,
 637            move |follower, _, event, cx| match event.clone() {
 638                Event::ExcerptsAdded {
 639                    buffer,
 640                    predecessor,
 641                    excerpts,
 642                } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
 643                Event::ExcerptsRemoved { ids, .. } => follower.remove_excerpts(ids, cx),
 644                Event::Edited { .. } => {
 645                    *follower_edit_event_count.write() += 1;
 646                }
 647                _ => {}
 648            },
 649        )
 650        .detach();
 651    });
 652
 653    leader_multibuffer.update(cx, |leader, cx| {
 654        leader.push_excerpts(
 655            buffer_1.clone(),
 656            [ExcerptRange::new(0..8), ExcerptRange::new(12..16)],
 657            cx,
 658        );
 659        leader.insert_excerpts_after(
 660            leader.excerpt_ids()[0],
 661            buffer_2.clone(),
 662            [ExcerptRange::new(0..5), ExcerptRange::new(10..15)],
 663            cx,
 664        )
 665    });
 666    assert_eq!(
 667        leader_multibuffer.read(cx).snapshot(cx).text(),
 668        follower_multibuffer.read(cx).snapshot(cx).text(),
 669    );
 670    assert_eq!(*follower_edit_event_count.read(), 2);
 671
 672    leader_multibuffer.update(cx, |leader, cx| {
 673        let excerpt_ids = leader.excerpt_ids();
 674        leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
 675    });
 676    assert_eq!(
 677        leader_multibuffer.read(cx).snapshot(cx).text(),
 678        follower_multibuffer.read(cx).snapshot(cx).text(),
 679    );
 680    assert_eq!(*follower_edit_event_count.read(), 3);
 681
 682    // Removing an empty set of excerpts is a noop.
 683    leader_multibuffer.update(cx, |leader, cx| {
 684        leader.remove_excerpts([], cx);
 685    });
 686    assert_eq!(
 687        leader_multibuffer.read(cx).snapshot(cx).text(),
 688        follower_multibuffer.read(cx).snapshot(cx).text(),
 689    );
 690    assert_eq!(*follower_edit_event_count.read(), 3);
 691
 692    // Adding an empty set of excerpts is a noop.
 693    leader_multibuffer.update(cx, |leader, cx| {
 694        leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
 695    });
 696    assert_eq!(
 697        leader_multibuffer.read(cx).snapshot(cx).text(),
 698        follower_multibuffer.read(cx).snapshot(cx).text(),
 699    );
 700    assert_eq!(*follower_edit_event_count.read(), 3);
 701
 702    leader_multibuffer.update(cx, |leader, cx| {
 703        leader.clear(cx);
 704    });
 705    assert_eq!(
 706        leader_multibuffer.read(cx).snapshot(cx).text(),
 707        follower_multibuffer.read(cx).snapshot(cx).text(),
 708    );
 709    assert_eq!(*follower_edit_event_count.read(), 4);
 710}
 711
 712#[gpui::test]
 713fn test_expand_excerpts(cx: &mut App) {
 714    let buffer = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
 715    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 716
 717    multibuffer.update(cx, |multibuffer, cx| {
 718        multibuffer.set_excerpts_for_path(
 719            PathKey::for_buffer(&buffer, cx),
 720            buffer,
 721            vec![
 722                // Note that in this test, this first excerpt
 723                // does not contain a new line
 724                Point::new(3, 2)..Point::new(3, 3),
 725                Point::new(7, 1)..Point::new(7, 3),
 726                Point::new(15, 0)..Point::new(15, 0),
 727            ],
 728            1,
 729            cx,
 730        )
 731    });
 732
 733    let snapshot = multibuffer.read(cx).snapshot(cx);
 734
 735    assert_eq!(
 736        snapshot.text(),
 737        concat!(
 738            "ccc\n", //
 739            "ddd\n", //
 740            "eee",   //
 741            "\n",    // End of excerpt
 742            "ggg\n", //
 743            "hhh\n", //
 744            "iii",   //
 745            "\n",    // End of excerpt
 746            "ooo\n", //
 747            "ppp\n", //
 748            "qqq",   // End of excerpt
 749        )
 750    );
 751    drop(snapshot);
 752
 753    multibuffer.update(cx, |multibuffer, cx| {
 754        let line_zero = multibuffer.snapshot(cx).anchor_before(Point::new(0, 0));
 755        multibuffer.expand_excerpts(
 756            multibuffer.excerpt_ids(),
 757            1,
 758            ExpandExcerptDirection::UpAndDown,
 759            cx,
 760        );
 761        let snapshot = multibuffer.snapshot(cx);
 762        let line_two = snapshot.anchor_before(Point::new(2, 0));
 763        assert_eq!(line_two.cmp(&line_zero, &snapshot), cmp::Ordering::Greater);
 764    });
 765
 766    let snapshot = multibuffer.read(cx).snapshot(cx);
 767
 768    assert_eq!(
 769        snapshot.text(),
 770        concat!(
 771            "bbb\n", //
 772            "ccc\n", //
 773            "ddd\n", //
 774            "eee\n", //
 775            "fff\n", //
 776            "ggg\n", //
 777            "hhh\n", //
 778            "iii\n", //
 779            "jjj\n", // End of excerpt
 780            "nnn\n", //
 781            "ooo\n", //
 782            "ppp\n", //
 783            "qqq\n", //
 784            "rrr",   // End of excerpt
 785        )
 786    );
 787}
 788
 789#[gpui::test(iterations = 100)]
 790async fn test_set_anchored_excerpts_for_path(cx: &mut TestAppContext) {
 791    let buffer_1 = cx.new(|cx| Buffer::local(sample_text(20, 3, 'a'), cx));
 792    let buffer_2 = cx.new(|cx| Buffer::local(sample_text(15, 4, 'a'), cx));
 793    let snapshot_1 = buffer_1.update(cx, |buffer, _| buffer.snapshot());
 794    let snapshot_2 = buffer_2.update(cx, |buffer, _| buffer.snapshot());
 795    let ranges_1 = vec![
 796        snapshot_1.anchor_before(Point::new(3, 2))..snapshot_1.anchor_before(Point::new(4, 2)),
 797        snapshot_1.anchor_before(Point::new(7, 1))..snapshot_1.anchor_before(Point::new(7, 3)),
 798        snapshot_1.anchor_before(Point::new(15, 0))..snapshot_1.anchor_before(Point::new(15, 0)),
 799    ];
 800    let ranges_2 = vec![
 801        snapshot_2.anchor_before(Point::new(2, 1))..snapshot_2.anchor_before(Point::new(3, 1)),
 802        snapshot_2.anchor_before(Point::new(10, 0))..snapshot_2.anchor_before(Point::new(10, 2)),
 803    ];
 804
 805    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 806    let anchor_ranges_1 = multibuffer
 807        .update(cx, |multibuffer, cx| {
 808            multibuffer.set_anchored_excerpts_for_path(
 809                PathKey::for_buffer(&buffer_1, cx),
 810                buffer_1.clone(),
 811                ranges_1,
 812                2,
 813                cx,
 814            )
 815        })
 816        .await;
 817    let snapshot_1 = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 818    assert_eq!(
 819        anchor_ranges_1
 820            .iter()
 821            .map(|range| range.to_point(&snapshot_1))
 822            .collect::<Vec<_>>(),
 823        vec![
 824            Point::new(2, 2)..Point::new(3, 2),
 825            Point::new(6, 1)..Point::new(6, 3),
 826            Point::new(11, 0)..Point::new(11, 0),
 827        ]
 828    );
 829    let anchor_ranges_2 = multibuffer
 830        .update(cx, |multibuffer, cx| {
 831            multibuffer.set_anchored_excerpts_for_path(
 832                PathKey::for_buffer(&buffer_2, cx),
 833                buffer_2.clone(),
 834                ranges_2,
 835                2,
 836                cx,
 837            )
 838        })
 839        .await;
 840    let snapshot_2 = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 841    assert_eq!(
 842        anchor_ranges_2
 843            .iter()
 844            .map(|range| range.to_point(&snapshot_2))
 845            .collect::<Vec<_>>(),
 846        vec![
 847            Point::new(16, 1)..Point::new(17, 1),
 848            Point::new(22, 0)..Point::new(22, 2)
 849        ]
 850    );
 851
 852    let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 853    assert_eq!(
 854        snapshot.text(),
 855        concat!(
 856            "bbb\n", // buffer_1
 857            "ccc\n", //
 858            "ddd\n", // <-- excerpt 1
 859            "eee\n", // <-- excerpt 1
 860            "fff\n", //
 861            "ggg\n", //
 862            "hhh\n", // <-- excerpt 2
 863            "iii\n", //
 864            "jjj\n", //
 865            //
 866            "nnn\n", //
 867            "ooo\n", //
 868            "ppp\n", // <-- excerpt 3
 869            "qqq\n", //
 870            "rrr\n", //
 871            //
 872            "aaaa\n", // buffer 2
 873            "bbbb\n", //
 874            "cccc\n", // <-- excerpt 4
 875            "dddd\n", // <-- excerpt 4
 876            "eeee\n", //
 877            "ffff\n", //
 878            //
 879            "iiii\n", //
 880            "jjjj\n", //
 881            "kkkk\n", // <-- excerpt 5
 882            "llll\n", //
 883            "mmmm",   //
 884        )
 885    );
 886}
 887
 888#[gpui::test]
 889fn test_empty_multibuffer(cx: &mut App) {
 890    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 891
 892    let snapshot = multibuffer.read(cx).snapshot(cx);
 893    assert_eq!(snapshot.text(), "");
 894    assert_eq!(
 895        snapshot
 896            .row_infos(MultiBufferRow(0))
 897            .map(|info| info.buffer_row)
 898            .collect::<Vec<_>>(),
 899        &[Some(0)]
 900    );
 901    assert!(
 902        snapshot
 903            .row_infos(MultiBufferRow(1))
 904            .map(|info| info.buffer_row)
 905            .collect::<Vec<_>>()
 906            .is_empty(),
 907    );
 908}
 909
 910#[gpui::test]
 911fn test_empty_diff_excerpt(cx: &mut TestAppContext) {
 912    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
 913    let buffer = cx.new(|cx| Buffer::local("", cx));
 914    let base_text = "a\nb\nc";
 915
 916    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
 917    multibuffer.update(cx, |multibuffer, cx| {
 918        multibuffer.push_excerpts(buffer.clone(), [ExcerptRange::new(0..0)], cx);
 919        multibuffer.set_all_diff_hunks_expanded(cx);
 920        multibuffer.add_diff(diff.clone(), cx);
 921    });
 922    cx.run_until_parked();
 923
 924    let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 925    assert_eq!(snapshot.text(), "a\nb\nc\n");
 926
 927    let hunk = snapshot
 928        .diff_hunks_in_range(Point::new(1, 1)..Point::new(1, 1))
 929        .next()
 930        .unwrap();
 931
 932    assert_eq!(hunk.diff_base_byte_range.start, 0);
 933
 934    let buf2 = cx.new(|cx| Buffer::local("X", cx));
 935    multibuffer.update(cx, |multibuffer, cx| {
 936        multibuffer.push_excerpts(buf2, [ExcerptRange::new(0..1)], cx);
 937    });
 938
 939    buffer.update(cx, |buffer, cx| {
 940        buffer.edit([(0..0, "a\nb\nc")], None, cx);
 941        diff.update(cx, |diff, cx| {
 942            diff.recalculate_diff_sync(buffer.snapshot().text, cx);
 943        });
 944        assert_eq!(buffer.text(), "a\nb\nc")
 945    });
 946    cx.run_until_parked();
 947
 948    let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 949    assert_eq!(snapshot.text(), "a\nb\nc\nX");
 950
 951    buffer.update(cx, |buffer, cx| {
 952        buffer.undo(cx);
 953        diff.update(cx, |diff, cx| {
 954            diff.recalculate_diff_sync(buffer.snapshot().text, cx);
 955        });
 956        assert_eq!(buffer.text(), "")
 957    });
 958    cx.run_until_parked();
 959
 960    let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
 961    assert_eq!(snapshot.text(), "a\nb\nc\n\nX");
 962}
 963
 964#[gpui::test]
 965fn test_singleton_multibuffer_anchors(cx: &mut App) {
 966    let buffer = cx.new(|cx| Buffer::local("abcd", cx));
 967    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
 968    let old_snapshot = multibuffer.read(cx).snapshot(cx);
 969    buffer.update(cx, |buffer, cx| {
 970        buffer.edit([(0..0, "X")], None, cx);
 971        buffer.edit([(5..5, "Y")], None, cx);
 972    });
 973    let new_snapshot = multibuffer.read(cx).snapshot(cx);
 974
 975    assert_eq!(old_snapshot.text(), "abcd");
 976    assert_eq!(new_snapshot.text(), "XabcdY");
 977
 978    assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
 979    assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
 980    assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
 981    assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
 982}
 983
 984#[gpui::test]
 985fn test_multibuffer_anchors(cx: &mut App) {
 986    let buffer_1 = cx.new(|cx| Buffer::local("abcd", cx));
 987    let buffer_2 = cx.new(|cx| Buffer::local("efghi", cx));
 988    let multibuffer = cx.new(|cx| {
 989        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
 990        multibuffer.push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..4)], cx);
 991        multibuffer.push_excerpts(buffer_2.clone(), [ExcerptRange::new(0..5)], cx);
 992        multibuffer
 993    });
 994    let old_snapshot = multibuffer.read(cx).snapshot(cx);
 995
 996    assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0);
 997    assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0);
 998    assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
 999    assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
1000    assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
1001    assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
1002
1003    buffer_1.update(cx, |buffer, cx| {
1004        buffer.edit([(0..0, "W")], None, cx);
1005        buffer.edit([(5..5, "X")], None, cx);
1006    });
1007    buffer_2.update(cx, |buffer, cx| {
1008        buffer.edit([(0..0, "Y")], None, cx);
1009        buffer.edit([(6..6, "Z")], None, cx);
1010    });
1011    let new_snapshot = multibuffer.read(cx).snapshot(cx);
1012
1013    assert_eq!(old_snapshot.text(), "abcd\nefghi");
1014    assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ");
1015
1016    assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
1017    assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
1018    assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2);
1019    assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
1020    assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
1021    assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
1022    assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7);
1023    assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8);
1024    assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13);
1025    assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
1026}
1027
1028#[gpui::test]
1029fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut App) {
1030    let buffer_1 = cx.new(|cx| Buffer::local("abcd", cx));
1031    let buffer_2 = cx.new(|cx| Buffer::local("ABCDEFGHIJKLMNOP", cx));
1032    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
1033
1034    // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
1035    // Add an excerpt from buffer 1 that spans this new insertion.
1036    buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
1037    let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
1038        multibuffer
1039            .push_excerpts(buffer_1.clone(), [ExcerptRange::new(0..7)], cx)
1040            .pop()
1041            .unwrap()
1042    });
1043
1044    let snapshot_1 = multibuffer.read(cx).snapshot(cx);
1045    assert_eq!(snapshot_1.text(), "abcd123");
1046
1047    // Replace the buffer 1 excerpt with new excerpts from buffer 2.
1048    let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
1049        multibuffer.remove_excerpts([excerpt_id_1], cx);
1050        let mut ids = multibuffer
1051            .push_excerpts(
1052                buffer_2.clone(),
1053                [
1054                    ExcerptRange::new(0..4),
1055                    ExcerptRange::new(6..10),
1056                    ExcerptRange::new(12..16),
1057                ],
1058                cx,
1059            )
1060            .into_iter();
1061        (ids.next().unwrap(), ids.next().unwrap())
1062    });
1063    let snapshot_2 = multibuffer.read(cx).snapshot(cx);
1064    assert_eq!(snapshot_2.text(), "ABCD\nGHIJ\nMNOP");
1065
1066    // The old excerpt id doesn't get reused.
1067    assert_ne!(excerpt_id_2, excerpt_id_1);
1068
1069    // Resolve some anchors from the previous snapshot in the new snapshot.
1070    // The current excerpts are from a different buffer, so we don't attempt to
1071    // resolve the old text anchor in the new buffer.
1072    assert_eq!(
1073        snapshot_2.summary_for_anchor::<usize>(&snapshot_1.anchor_before(2)),
1074        0
1075    );
1076    assert_eq!(
1077        snapshot_2.summaries_for_anchors::<usize, _>(&[
1078            snapshot_1.anchor_before(2),
1079            snapshot_1.anchor_after(3)
1080        ]),
1081        vec![0, 0]
1082    );
1083
1084    // Refresh anchors from the old snapshot. The return value indicates that both
1085    // anchors lost their original excerpt.
1086    let refresh =
1087        snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]);
1088    assert_eq!(
1089        refresh,
1090        &[
1091            (0, snapshot_2.anchor_before(0), false),
1092            (1, snapshot_2.anchor_after(0), false),
1093        ]
1094    );
1095
1096    // Replace the middle excerpt with a smaller excerpt in buffer 2,
1097    // that intersects the old excerpt.
1098    let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| {
1099        multibuffer.remove_excerpts([excerpt_id_3], cx);
1100        multibuffer
1101            .insert_excerpts_after(
1102                excerpt_id_2,
1103                buffer_2.clone(),
1104                [ExcerptRange::new(5..8)],
1105                cx,
1106            )
1107            .pop()
1108            .unwrap()
1109    });
1110
1111    let snapshot_3 = multibuffer.read(cx).snapshot(cx);
1112    assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP");
1113    assert_ne!(excerpt_id_5, excerpt_id_3);
1114
1115    // Resolve some anchors from the previous snapshot in the new snapshot.
1116    // The third anchor can't be resolved, since its excerpt has been removed,
1117    // so it resolves to the same position as its predecessor.
1118    let anchors = [
1119        snapshot_2.anchor_before(0),
1120        snapshot_2.anchor_after(2),
1121        snapshot_2.anchor_after(6),
1122        snapshot_2.anchor_after(14),
1123    ];
1124    assert_eq!(
1125        snapshot_3.summaries_for_anchors::<usize, _>(&anchors),
1126        &[0, 2, 9, 13]
1127    );
1128
1129    let new_anchors = snapshot_3.refresh_anchors(&anchors);
1130    assert_eq!(
1131        new_anchors.iter().map(|a| (a.0, a.2)).collect::<Vec<_>>(),
1132        &[(0, true), (1, true), (2, true), (3, true)]
1133    );
1134    assert_eq!(
1135        snapshot_3.summaries_for_anchors::<usize, _>(new_anchors.iter().map(|a| &a.1)),
1136        &[0, 2, 7, 13]
1137    );
1138}
1139
1140#[gpui::test]
1141fn test_basic_diff_hunks(cx: &mut TestAppContext) {
1142    let text = indoc!(
1143        "
1144        ZERO
1145        one
1146        TWO
1147        three
1148        six
1149        "
1150    );
1151    let base_text = indoc!(
1152        "
1153        one
1154        two
1155        three
1156        four
1157        five
1158        six
1159        "
1160    );
1161
1162    let buffer = cx.new(|cx| Buffer::local(text, cx));
1163    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
1164    cx.run_until_parked();
1165
1166    let multibuffer = cx.new(|cx| {
1167        let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
1168        multibuffer.add_diff(diff.clone(), cx);
1169        multibuffer
1170    });
1171
1172    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
1173        (multibuffer.snapshot(cx), multibuffer.subscribe())
1174    });
1175    assert_eq!(
1176        snapshot.text(),
1177        indoc!(
1178            "
1179            ZERO
1180            one
1181            TWO
1182            three
1183            six
1184            "
1185        ),
1186    );
1187
1188    multibuffer.update(cx, |multibuffer, cx| {
1189        multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
1190    });
1191
1192    assert_new_snapshot(
1193        &multibuffer,
1194        &mut snapshot,
1195        &mut subscription,
1196        cx,
1197        indoc!(
1198            "
1199            + ZERO
1200              one
1201            - two
1202            + TWO
1203              three
1204            - four
1205            - five
1206              six
1207            "
1208        ),
1209    );
1210
1211    assert_eq!(
1212        snapshot
1213            .row_infos(MultiBufferRow(0))
1214            .map(|info| (info.buffer_row, info.diff_status))
1215            .collect::<Vec<_>>(),
1216        vec![
1217            (Some(0), Some(DiffHunkStatus::added_none())),
1218            (Some(1), None),
1219            (Some(1), Some(DiffHunkStatus::deleted_none())),
1220            (Some(2), Some(DiffHunkStatus::added_none())),
1221            (Some(3), None),
1222            (Some(3), Some(DiffHunkStatus::deleted_none())),
1223            (Some(4), Some(DiffHunkStatus::deleted_none())),
1224            (Some(4), None),
1225            (Some(5), None)
1226        ]
1227    );
1228
1229    assert_chunks_in_ranges(&snapshot);
1230    assert_consistent_line_numbers(&snapshot);
1231    assert_position_translation(&snapshot);
1232    assert_line_indents(&snapshot, cx.background_executor());
1233
1234    multibuffer.update(cx, |multibuffer, cx| {
1235        multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx)
1236    });
1237    assert_new_snapshot(
1238        &multibuffer,
1239        &mut snapshot,
1240        &mut subscription,
1241        cx,
1242        indoc!(
1243            "
1244            ZERO
1245            one
1246            TWO
1247            three
1248            six
1249            "
1250        ),
1251    );
1252
1253    assert_chunks_in_ranges(&snapshot);
1254    assert_consistent_line_numbers(&snapshot);
1255    assert_position_translation(&snapshot);
1256    assert_line_indents(&snapshot, cx.background_executor());
1257
1258    // Expand the first diff hunk
1259    multibuffer.update(cx, |multibuffer, cx| {
1260        let position = multibuffer.read(cx).anchor_before(Point::new(2, 2));
1261        multibuffer.expand_diff_hunks(vec![position..position], cx)
1262    });
1263    assert_new_snapshot(
1264        &multibuffer,
1265        &mut snapshot,
1266        &mut subscription,
1267        cx,
1268        indoc!(
1269            "
1270              ZERO
1271              one
1272            - two
1273            + TWO
1274              three
1275              six
1276            "
1277        ),
1278    );
1279
1280    // Expand the second diff hunk
1281    multibuffer.update(cx, |multibuffer, cx| {
1282        let start = multibuffer.read(cx).anchor_before(Point::new(4, 0));
1283        let end = multibuffer.read(cx).anchor_before(Point::new(5, 0));
1284        multibuffer.expand_diff_hunks(vec![start..end], cx)
1285    });
1286    assert_new_snapshot(
1287        &multibuffer,
1288        &mut snapshot,
1289        &mut subscription,
1290        cx,
1291        indoc!(
1292            "
1293              ZERO
1294              one
1295            - two
1296            + TWO
1297              three
1298            - four
1299            - five
1300              six
1301            "
1302        ),
1303    );
1304
1305    assert_chunks_in_ranges(&snapshot);
1306    assert_consistent_line_numbers(&snapshot);
1307    assert_position_translation(&snapshot);
1308    assert_line_indents(&snapshot, cx.background_executor());
1309
1310    // Edit the buffer before the first hunk
1311    buffer.update(cx, |buffer, cx| {
1312        buffer.edit_via_marked_text(
1313            indoc!(
1314                "
1315                ZERO
1316                one« hundred
1317                  thousand»
1318                TWO
1319                three
1320                six
1321                "
1322            ),
1323            None,
1324            cx,
1325        );
1326    });
1327    assert_new_snapshot(
1328        &multibuffer,
1329        &mut snapshot,
1330        &mut subscription,
1331        cx,
1332        indoc!(
1333            "
1334              ZERO
1335              one hundred
1336                thousand
1337            - two
1338            + TWO
1339              three
1340            - four
1341            - five
1342              six
1343            "
1344        ),
1345    );
1346
1347    assert_chunks_in_ranges(&snapshot);
1348    assert_consistent_line_numbers(&snapshot);
1349    assert_position_translation(&snapshot);
1350    assert_line_indents(&snapshot, cx.background_executor());
1351
1352    // Recalculate the diff, changing the first diff hunk.
1353    diff.update(cx, |diff, cx| {
1354        diff.recalculate_diff_sync(buffer.read(cx).text_snapshot(), cx);
1355    });
1356    cx.run_until_parked();
1357    assert_new_snapshot(
1358        &multibuffer,
1359        &mut snapshot,
1360        &mut subscription,
1361        cx,
1362        indoc!(
1363            "
1364              ZERO
1365              one hundred
1366                thousand
1367              TWO
1368              three
1369            - four
1370            - five
1371              six
1372            "
1373        ),
1374    );
1375
1376    assert_eq!(
1377        snapshot
1378            .diff_hunks_in_range(0..snapshot.len())
1379            .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0)
1380            .collect::<Vec<_>>(),
1381        &[0..4, 5..7]
1382    );
1383}
1384
1385#[gpui::test]
1386fn test_repeatedly_expand_a_diff_hunk(cx: &mut TestAppContext) {
1387    let text = indoc!(
1388        "
1389        one
1390        TWO
1391        THREE
1392        four
1393        FIVE
1394        six
1395        "
1396    );
1397    let base_text = indoc!(
1398        "
1399        one
1400        four
1401        five
1402        six
1403        "
1404    );
1405
1406    let buffer = cx.new(|cx| Buffer::local(text, cx));
1407    let diff = cx.new(|cx| BufferDiff::new_with_base_text(base_text, &buffer, cx));
1408    cx.run_until_parked();
1409
1410    let multibuffer = cx.new(|cx| {
1411        let mut multibuffer = MultiBuffer::singleton(buffer.clone(), cx);
1412        multibuffer.add_diff(diff.clone(), cx);
1413        multibuffer
1414    });
1415
1416    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
1417        (multibuffer.snapshot(cx), multibuffer.subscribe())
1418    });
1419
1420    multibuffer.update(cx, |multibuffer, cx| {
1421        multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
1422    });
1423
1424    assert_new_snapshot(
1425        &multibuffer,
1426        &mut snapshot,
1427        &mut subscription,
1428        cx,
1429        indoc!(
1430            "
1431              one
1432            + TWO
1433            + THREE
1434              four
1435            - five
1436            + FIVE
1437              six
1438            "
1439        ),
1440    );
1441
1442    // Regression test: expanding diff hunks that are already expanded should not change anything.
1443    multibuffer.update(cx, |multibuffer, cx| {
1444        multibuffer.expand_diff_hunks(
1445            vec![
1446                snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_before(Point::new(2, 0)),
1447            ],
1448            cx,
1449        );
1450    });
1451
1452    assert_new_snapshot(
1453        &multibuffer,
1454        &mut snapshot,
1455        &mut subscription,
1456        cx,
1457        indoc!(
1458            "
1459              one
1460            + TWO
1461            + THREE
1462              four
1463            - five
1464            + FIVE
1465              six
1466            "
1467        ),
1468    );
1469
1470    // Now collapse all diff hunks
1471    multibuffer.update(cx, |multibuffer, cx| {
1472        multibuffer.collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
1473    });
1474
1475    assert_new_snapshot(
1476        &multibuffer,
1477        &mut snapshot,
1478        &mut subscription,
1479        cx,
1480        indoc!(
1481            "
1482            one
1483            TWO
1484            THREE
1485            four
1486            FIVE
1487            six
1488            "
1489        ),
1490    );
1491
1492    // Expand the hunks again, but this time provide two ranges that are both within the same hunk
1493    // Target the first hunk which is between "one" and "four"
1494    multibuffer.update(cx, |multibuffer, cx| {
1495        multibuffer.expand_diff_hunks(
1496            vec![
1497                snapshot.anchor_before(Point::new(4, 0))..snapshot.anchor_before(Point::new(4, 0)),
1498                snapshot.anchor_before(Point::new(4, 2))..snapshot.anchor_before(Point::new(4, 2)),
1499            ],
1500            cx,
1501        );
1502    });
1503    assert_new_snapshot(
1504        &multibuffer,
1505        &mut snapshot,
1506        &mut subscription,
1507        cx,
1508        indoc!(
1509            "
1510              one
1511              TWO
1512              THREE
1513              four
1514            - five
1515            + FIVE
1516              six
1517            "
1518        ),
1519    );
1520}
1521
1522#[gpui::test]
1523fn test_set_excerpts_for_buffer_ordering(cx: &mut TestAppContext) {
1524    let buf1 = cx.new(|cx| {
1525        Buffer::local(
1526            indoc! {
1527            "zero
1528            one
1529            two
1530            two.five
1531            three
1532            four
1533            five
1534            six
1535            seven
1536            eight
1537            nine
1538            ten
1539            eleven
1540            ",
1541            },
1542            cx,
1543        )
1544    });
1545    let path1: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc());
1546
1547    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
1548    multibuffer.update(cx, |multibuffer, cx| {
1549        multibuffer.set_excerpts_for_path(
1550            path1.clone(),
1551            buf1.clone(),
1552            vec![
1553                Point::row_range(1..2),
1554                Point::row_range(6..7),
1555                Point::row_range(11..12),
1556            ],
1557            1,
1558            cx,
1559        );
1560    });
1561
1562    assert_excerpts_match(
1563        &multibuffer,
1564        cx,
1565        indoc! {
1566            "-----
1567            zero
1568            one
1569            two
1570            two.five
1571            -----
1572            four
1573            five
1574            six
1575            seven
1576            -----
1577            nine
1578            ten
1579            eleven
1580            "
1581        },
1582    );
1583
1584    buf1.update(cx, |buffer, cx| buffer.edit([(0..5, "")], None, cx));
1585
1586    multibuffer.update(cx, |multibuffer, cx| {
1587        multibuffer.set_excerpts_for_path(
1588            path1.clone(),
1589            buf1.clone(),
1590            vec![
1591                Point::row_range(0..3),
1592                Point::row_range(5..7),
1593                Point::row_range(10..11),
1594            ],
1595            1,
1596            cx,
1597        );
1598    });
1599
1600    assert_excerpts_match(
1601        &multibuffer,
1602        cx,
1603        indoc! {
1604            "-----
1605             one
1606             two
1607             two.five
1608             three
1609             four
1610             five
1611             six
1612             seven
1613             eight
1614             nine
1615             ten
1616             eleven
1617            "
1618        },
1619    );
1620}
1621
1622#[gpui::test]
1623fn test_set_excerpts_for_buffer(cx: &mut TestAppContext) {
1624    let buf1 = cx.new(|cx| {
1625        Buffer::local(
1626            indoc! {
1627            "zero
1628            one
1629            two
1630            three
1631            four
1632            five
1633            six
1634            seven
1635            ",
1636            },
1637            cx,
1638        )
1639    });
1640    let path1: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc());
1641    let buf2 = cx.new(|cx| {
1642        Buffer::local(
1643            indoc! {
1644            "000
1645            111
1646            222
1647            333
1648            444
1649            555
1650            666
1651            777
1652            888
1653            999
1654            "
1655            },
1656            cx,
1657        )
1658    });
1659    let path2 = PathKey::with_sort_prefix(1, rel_path("root").into_arc());
1660
1661    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
1662    multibuffer.update(cx, |multibuffer, cx| {
1663        multibuffer.set_excerpts_for_path(
1664            path1.clone(),
1665            buf1.clone(),
1666            vec![Point::row_range(0..1)],
1667            2,
1668            cx,
1669        );
1670    });
1671
1672    assert_excerpts_match(
1673        &multibuffer,
1674        cx,
1675        indoc! {
1676        "-----
1677        zero
1678        one
1679        two
1680        three
1681        "
1682        },
1683    );
1684
1685    multibuffer.update(cx, |multibuffer, cx| {
1686        multibuffer.set_excerpts_for_path(path1.clone(), buf1.clone(), vec![], 2, cx);
1687    });
1688
1689    assert_excerpts_match(&multibuffer, cx, "");
1690
1691    multibuffer.update(cx, |multibuffer, cx| {
1692        multibuffer.set_excerpts_for_path(
1693            path1.clone(),
1694            buf1.clone(),
1695            vec![Point::row_range(0..1), Point::row_range(7..8)],
1696            2,
1697            cx,
1698        );
1699    });
1700
1701    assert_excerpts_match(
1702        &multibuffer,
1703        cx,
1704        indoc! {"-----
1705                zero
1706                one
1707                two
1708                three
1709                -----
1710                five
1711                six
1712                seven
1713                "},
1714    );
1715
1716    multibuffer.update(cx, |multibuffer, cx| {
1717        multibuffer.set_excerpts_for_path(
1718            path1.clone(),
1719            buf1.clone(),
1720            vec![Point::row_range(0..1), Point::row_range(5..6)],
1721            2,
1722            cx,
1723        );
1724    });
1725
1726    assert_excerpts_match(
1727        &multibuffer,
1728        cx,
1729        indoc! {"-----
1730                    zero
1731                    one
1732                    two
1733                    three
1734                    four
1735                    five
1736                    six
1737                    seven
1738                    "},
1739    );
1740
1741    multibuffer.update(cx, |multibuffer, cx| {
1742        multibuffer.set_excerpts_for_path(
1743            path2.clone(),
1744            buf2.clone(),
1745            vec![Point::row_range(2..3)],
1746            2,
1747            cx,
1748        );
1749    });
1750
1751    assert_excerpts_match(
1752        &multibuffer,
1753        cx,
1754        indoc! {"-----
1755                zero
1756                one
1757                two
1758                three
1759                four
1760                five
1761                six
1762                seven
1763                -----
1764                000
1765                111
1766                222
1767                333
1768                444
1769                555
1770                "},
1771    );
1772
1773    multibuffer.update(cx, |multibuffer, cx| {
1774        multibuffer.set_excerpts_for_path(path1.clone(), buf1.clone(), vec![], 2, cx);
1775    });
1776
1777    multibuffer.update(cx, |multibuffer, cx| {
1778        multibuffer.set_excerpts_for_path(
1779            path1.clone(),
1780            buf1.clone(),
1781            vec![Point::row_range(3..4)],
1782            2,
1783            cx,
1784        );
1785    });
1786
1787    assert_excerpts_match(
1788        &multibuffer,
1789        cx,
1790        indoc! {"-----
1791                one
1792                two
1793                three
1794                four
1795                five
1796                six
1797                -----
1798                000
1799                111
1800                222
1801                333
1802                444
1803                555
1804                "},
1805    );
1806
1807    multibuffer.update(cx, |multibuffer, cx| {
1808        multibuffer.set_excerpts_for_path(
1809            path1.clone(),
1810            buf1.clone(),
1811            vec![Point::row_range(3..4)],
1812            2,
1813            cx,
1814        );
1815    });
1816}
1817
1818#[gpui::test]
1819fn test_set_excerpts_for_buffer_rename(cx: &mut TestAppContext) {
1820    let buf1 = cx.new(|cx| {
1821        Buffer::local(
1822            indoc! {
1823            "zero
1824            one
1825            two
1826            three
1827            four
1828            five
1829            six
1830            seven
1831            ",
1832            },
1833            cx,
1834        )
1835    });
1836    let path: PathKey = PathKey::with_sort_prefix(0, rel_path("root").into_arc());
1837    let buf2 = cx.new(|cx| {
1838        Buffer::local(
1839            indoc! {
1840            "000
1841            111
1842            222
1843            333
1844            "
1845            },
1846            cx,
1847        )
1848    });
1849
1850    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
1851    multibuffer.update(cx, |multibuffer, cx| {
1852        multibuffer.set_excerpts_for_path(
1853            path.clone(),
1854            buf1.clone(),
1855            vec![Point::row_range(1..1), Point::row_range(4..5)],
1856            1,
1857            cx,
1858        );
1859    });
1860
1861    assert_excerpts_match(
1862        &multibuffer,
1863        cx,
1864        indoc! {
1865        "-----
1866        zero
1867        one
1868        two
1869        three
1870        four
1871        five
1872        six
1873        "
1874        },
1875    );
1876
1877    multibuffer.update(cx, |multibuffer, cx| {
1878        multibuffer.set_excerpts_for_path(
1879            path.clone(),
1880            buf2.clone(),
1881            vec![Point::row_range(0..1)],
1882            2,
1883            cx,
1884        );
1885    });
1886
1887    assert_excerpts_match(
1888        &multibuffer,
1889        cx,
1890        indoc! {"-----
1891                000
1892                111
1893                222
1894                333
1895                "},
1896    );
1897}
1898
1899#[gpui::test]
1900fn test_diff_hunks_with_multiple_excerpts(cx: &mut TestAppContext) {
1901    let base_text_1 = indoc!(
1902        "
1903        one
1904        two
1905            three
1906        four
1907        five
1908        six
1909        "
1910    );
1911    let text_1 = indoc!(
1912        "
1913        ZERO
1914        one
1915        TWO
1916            three
1917        six
1918        "
1919    );
1920    let base_text_2 = indoc!(
1921        "
1922        seven
1923          eight
1924        nine
1925        ten
1926        eleven
1927        twelve
1928        "
1929    );
1930    let text_2 = indoc!(
1931        "
1932          eight
1933        nine
1934        eleven
1935        THIRTEEN
1936        FOURTEEN
1937        "
1938    );
1939
1940    let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx));
1941    let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx));
1942    let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_1, &buffer_1, cx));
1943    let diff_2 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_2, &buffer_2, cx));
1944    cx.run_until_parked();
1945
1946    let multibuffer = cx.new(|cx| {
1947        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
1948        multibuffer.push_excerpts(
1949            buffer_1.clone(),
1950            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
1951            cx,
1952        );
1953        multibuffer.push_excerpts(
1954            buffer_2.clone(),
1955            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
1956            cx,
1957        );
1958        multibuffer.add_diff(diff_1.clone(), cx);
1959        multibuffer.add_diff(diff_2.clone(), cx);
1960        multibuffer
1961    });
1962
1963    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
1964        (multibuffer.snapshot(cx), multibuffer.subscribe())
1965    });
1966    assert_eq!(
1967        snapshot.text(),
1968        indoc!(
1969            "
1970            ZERO
1971            one
1972            TWO
1973                three
1974            six
1975
1976              eight
1977            nine
1978            eleven
1979            THIRTEEN
1980            FOURTEEN
1981            "
1982        ),
1983    );
1984
1985    multibuffer.update(cx, |multibuffer, cx| {
1986        multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
1987    });
1988
1989    assert_new_snapshot(
1990        &multibuffer,
1991        &mut snapshot,
1992        &mut subscription,
1993        cx,
1994        indoc!(
1995            "
1996            + ZERO
1997              one
1998            - two
1999            + TWO
2000                  three
2001            - four
2002            - five
2003              six
2004
2005            - seven
2006                eight
2007              nine
2008            - ten
2009              eleven
2010            - twelve
2011            + THIRTEEN
2012            + FOURTEEN
2013            "
2014        ),
2015    );
2016
2017    let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id());
2018    let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
2019    let base_id_1 = diff_1.read_with(cx, |diff, _| diff.base_text().remote_id());
2020    let base_id_2 = diff_2.read_with(cx, |diff, _| diff.base_text().remote_id());
2021
2022    let buffer_lines = (0..=snapshot.max_row().0)
2023        .map(|row| {
2024            let (buffer, range) = snapshot.buffer_line_for_row(MultiBufferRow(row))?;
2025            Some((
2026                buffer.remote_id(),
2027                buffer.text_for_range(range).collect::<String>(),
2028            ))
2029        })
2030        .collect::<Vec<_>>();
2031    pretty_assertions::assert_eq!(
2032        buffer_lines,
2033        [
2034            Some((id_1, "ZERO".into())),
2035            Some((id_1, "one".into())),
2036            Some((base_id_1, "two".into())),
2037            Some((id_1, "TWO".into())),
2038            Some((id_1, "    three".into())),
2039            Some((base_id_1, "four".into())),
2040            Some((base_id_1, "five".into())),
2041            Some((id_1, "six".into())),
2042            Some((id_1, "".into())),
2043            Some((base_id_2, "seven".into())),
2044            Some((id_2, "  eight".into())),
2045            Some((id_2, "nine".into())),
2046            Some((base_id_2, "ten".into())),
2047            Some((id_2, "eleven".into())),
2048            Some((base_id_2, "twelve".into())),
2049            Some((id_2, "THIRTEEN".into())),
2050            Some((id_2, "FOURTEEN".into())),
2051            Some((id_2, "".into())),
2052        ]
2053    );
2054
2055    let buffer_ids_by_range = [
2056        (Point::new(0, 0)..Point::new(0, 0), &[id_1] as &[_]),
2057        (Point::new(0, 0)..Point::new(2, 0), &[id_1]),
2058        (Point::new(2, 0)..Point::new(2, 0), &[id_1]),
2059        (Point::new(3, 0)..Point::new(3, 0), &[id_1]),
2060        (Point::new(8, 0)..Point::new(9, 0), &[id_1]),
2061        (Point::new(8, 0)..Point::new(10, 0), &[id_1, id_2]),
2062        (Point::new(9, 0)..Point::new(9, 0), &[id_2]),
2063    ];
2064    for (range, buffer_ids) in buffer_ids_by_range {
2065        assert_eq!(
2066            snapshot
2067                .buffer_ids_for_range(range.clone())
2068                .collect::<Vec<_>>(),
2069            buffer_ids,
2070            "buffer_ids_for_range({range:?}"
2071        );
2072    }
2073
2074    assert_position_translation(&snapshot);
2075    assert_line_indents(&snapshot, cx.background_executor());
2076
2077    assert_eq!(
2078        snapshot
2079            .diff_hunks_in_range(0..snapshot.len())
2080            .map(|hunk| hunk.row_range.start.0..hunk.row_range.end.0)
2081            .collect::<Vec<_>>(),
2082        &[0..1, 2..4, 5..7, 9..10, 12..13, 14..17]
2083    );
2084
2085    buffer_2.update(cx, |buffer, cx| {
2086        buffer.edit_via_marked_text(
2087            indoc!(
2088                "
2089                  eight
2090                «»eleven
2091                THIRTEEN
2092                FOURTEEN
2093                "
2094            ),
2095            None,
2096            cx,
2097        );
2098    });
2099
2100    assert_new_snapshot(
2101        &multibuffer,
2102        &mut snapshot,
2103        &mut subscription,
2104        cx,
2105        indoc!(
2106            "
2107            + ZERO
2108              one
2109            - two
2110            + TWO
2111                  three
2112            - four
2113            - five
2114              six
2115
2116            - seven
2117                eight
2118              eleven
2119            - twelve
2120            + THIRTEEN
2121            + FOURTEEN
2122            "
2123        ),
2124    );
2125
2126    assert_line_indents(&snapshot, cx.background_executor());
2127}
2128
2129/// A naive implementation of a multi-buffer that does not maintain
2130/// any derived state, used for comparison in a randomized test.
2131#[derive(Default)]
2132struct ReferenceMultibuffer {
2133    excerpts: Vec<ReferenceExcerpt>,
2134    diffs: HashMap<BufferId, Entity<BufferDiff>>,
2135}
2136
2137#[derive(Debug)]
2138struct ReferenceExcerpt {
2139    id: ExcerptId,
2140    buffer: Entity<Buffer>,
2141    range: Range<text::Anchor>,
2142    expanded_diff_hunks: Vec<text::Anchor>,
2143}
2144
2145#[derive(Debug)]
2146struct ReferenceRegion {
2147    buffer_id: Option<BufferId>,
2148    range: Range<usize>,
2149    buffer_start: Option<Point>,
2150    status: Option<DiffHunkStatus>,
2151    excerpt_id: Option<ExcerptId>,
2152}
2153
2154impl ReferenceMultibuffer {
2155    fn expand_excerpts(&mut self, excerpts: &HashSet<ExcerptId>, line_count: u32, cx: &App) {
2156        if line_count == 0 {
2157            return;
2158        }
2159
2160        for id in excerpts {
2161            let excerpt = self.excerpts.iter_mut().find(|e| e.id == *id).unwrap();
2162            let snapshot = excerpt.buffer.read(cx).snapshot();
2163            let mut point_range = excerpt.range.to_point(&snapshot);
2164            point_range.start = Point::new(point_range.start.row.saturating_sub(line_count), 0);
2165            point_range.end =
2166                snapshot.clip_point(Point::new(point_range.end.row + line_count, 0), Bias::Left);
2167            point_range.end.column = snapshot.line_len(point_range.end.row);
2168            excerpt.range =
2169                snapshot.anchor_before(point_range.start)..snapshot.anchor_after(point_range.end);
2170        }
2171    }
2172
2173    fn remove_excerpt(&mut self, id: ExcerptId, cx: &App) {
2174        let ix = self
2175            .excerpts
2176            .iter()
2177            .position(|excerpt| excerpt.id == id)
2178            .unwrap();
2179        let excerpt = self.excerpts.remove(ix);
2180        let buffer = excerpt.buffer.read(cx);
2181        let id = buffer.remote_id();
2182        log::info!(
2183            "Removing excerpt {}: {:?}",
2184            ix,
2185            buffer
2186                .text_for_range(excerpt.range.to_offset(buffer))
2187                .collect::<String>(),
2188        );
2189        if !self
2190            .excerpts
2191            .iter()
2192            .any(|excerpt| excerpt.buffer.read(cx).remote_id() == id)
2193        {
2194            self.diffs.remove(&id);
2195        }
2196    }
2197
2198    fn insert_excerpt_after(
2199        &mut self,
2200        prev_id: ExcerptId,
2201        new_excerpt_id: ExcerptId,
2202        (buffer_handle, anchor_range): (Entity<Buffer>, Range<text::Anchor>),
2203    ) {
2204        let excerpt_ix = if prev_id == ExcerptId::max() {
2205            self.excerpts.len()
2206        } else {
2207            self.excerpts
2208                .iter()
2209                .position(|excerpt| excerpt.id == prev_id)
2210                .unwrap()
2211                + 1
2212        };
2213        self.excerpts.insert(
2214            excerpt_ix,
2215            ReferenceExcerpt {
2216                id: new_excerpt_id,
2217                buffer: buffer_handle,
2218                range: anchor_range,
2219                expanded_diff_hunks: Vec::new(),
2220            },
2221        );
2222    }
2223
2224    fn expand_diff_hunks(&mut self, excerpt_id: ExcerptId, range: Range<text::Anchor>, cx: &App) {
2225        let excerpt = self
2226            .excerpts
2227            .iter_mut()
2228            .find(|e| e.id == excerpt_id)
2229            .unwrap();
2230        let buffer = excerpt.buffer.read(cx).snapshot();
2231        let buffer_id = buffer.remote_id();
2232        let Some(diff) = self.diffs.get(&buffer_id) else {
2233            return;
2234        };
2235        let excerpt_range = excerpt.range.to_offset(&buffer);
2236        for hunk in diff.read(cx).hunks_intersecting_range(range, &buffer, cx) {
2237            let hunk_range = hunk.buffer_range.to_offset(&buffer);
2238            if hunk_range.start < excerpt_range.start || hunk_range.start > excerpt_range.end {
2239                continue;
2240            }
2241            if let Err(ix) = excerpt
2242                .expanded_diff_hunks
2243                .binary_search_by(|anchor| anchor.cmp(&hunk.buffer_range.start, &buffer))
2244            {
2245                log::info!(
2246                    "expanding diff hunk {:?}. excerpt:{:?}, excerpt range:{:?}",
2247                    hunk_range,
2248                    excerpt_id,
2249                    excerpt_range
2250                );
2251                excerpt
2252                    .expanded_diff_hunks
2253                    .insert(ix, hunk.buffer_range.start);
2254            } else {
2255                log::trace!("hunk {hunk_range:?} already expanded in excerpt {excerpt_id:?}");
2256            }
2257        }
2258    }
2259
2260    fn expected_content(&self, cx: &App) -> (String, Vec<RowInfo>, HashSet<MultiBufferRow>) {
2261        let mut text = String::new();
2262        let mut regions = Vec::<ReferenceRegion>::new();
2263        let mut excerpt_boundary_rows = HashSet::default();
2264        for excerpt in &self.excerpts {
2265            excerpt_boundary_rows.insert(MultiBufferRow(text.matches('\n').count() as u32));
2266            let buffer = excerpt.buffer.read(cx);
2267            let buffer_range = excerpt.range.to_offset(buffer);
2268            let diff = self.diffs.get(&buffer.remote_id()).unwrap().read(cx);
2269            let base_buffer = diff.base_text();
2270
2271            let mut offset = buffer_range.start;
2272            let hunks = diff
2273                .hunks_intersecting_range(excerpt.range.clone(), buffer, cx)
2274                .peekable();
2275
2276            for hunk in hunks {
2277                // Ignore hunks that are outside the excerpt range.
2278                let mut hunk_range = hunk.buffer_range.to_offset(buffer);
2279
2280                hunk_range.end = hunk_range.end.min(buffer_range.end);
2281                if hunk_range.start > buffer_range.end || hunk_range.start < buffer_range.start {
2282                    log::trace!("skipping hunk outside excerpt range");
2283                    continue;
2284                }
2285
2286                if !excerpt.expanded_diff_hunks.iter().any(|expanded_anchor| {
2287                    expanded_anchor.to_offset(buffer).max(buffer_range.start)
2288                        == hunk_range.start.max(buffer_range.start)
2289                }) {
2290                    log::trace!("skipping a hunk that's not marked as expanded");
2291                    continue;
2292                }
2293
2294                if !hunk.buffer_range.start.is_valid(buffer) {
2295                    log::trace!("skipping hunk with deleted start: {:?}", hunk.range);
2296                    continue;
2297                }
2298
2299                if hunk_range.start >= offset {
2300                    // Add the buffer text before the hunk
2301                    let len = text.len();
2302                    text.extend(buffer.text_for_range(offset..hunk_range.start));
2303                    regions.push(ReferenceRegion {
2304                        buffer_id: Some(buffer.remote_id()),
2305                        range: len..text.len(),
2306                        buffer_start: Some(buffer.offset_to_point(offset)),
2307                        status: None,
2308                        excerpt_id: Some(excerpt.id),
2309                    });
2310
2311                    // Add the deleted text for the hunk.
2312                    if !hunk.diff_base_byte_range.is_empty() {
2313                        let mut base_text = base_buffer
2314                            .text_for_range(hunk.diff_base_byte_range.clone())
2315                            .collect::<String>();
2316                        if !base_text.ends_with('\n') {
2317                            base_text.push('\n');
2318                        }
2319                        let len = text.len();
2320                        text.push_str(&base_text);
2321                        regions.push(ReferenceRegion {
2322                            buffer_id: Some(base_buffer.remote_id()),
2323                            range: len..text.len(),
2324                            buffer_start: Some(
2325                                base_buffer.offset_to_point(hunk.diff_base_byte_range.start),
2326                            ),
2327                            status: Some(DiffHunkStatus::deleted(hunk.secondary_status)),
2328                            excerpt_id: Some(excerpt.id),
2329                        });
2330                    }
2331
2332                    offset = hunk_range.start;
2333                }
2334
2335                // Add the inserted text for the hunk.
2336                if hunk_range.end > offset {
2337                    let len = text.len();
2338                    text.extend(buffer.text_for_range(offset..hunk_range.end));
2339                    regions.push(ReferenceRegion {
2340                        buffer_id: Some(buffer.remote_id()),
2341                        range: len..text.len(),
2342                        buffer_start: Some(buffer.offset_to_point(offset)),
2343                        status: Some(DiffHunkStatus::added(hunk.secondary_status)),
2344                        excerpt_id: Some(excerpt.id),
2345                    });
2346                    offset = hunk_range.end;
2347                }
2348            }
2349
2350            // Add the buffer text for the rest of the excerpt.
2351            let len = text.len();
2352            text.extend(buffer.text_for_range(offset..buffer_range.end));
2353            text.push('\n');
2354            regions.push(ReferenceRegion {
2355                buffer_id: Some(buffer.remote_id()),
2356                range: len..text.len(),
2357                buffer_start: Some(buffer.offset_to_point(offset)),
2358                status: None,
2359                excerpt_id: Some(excerpt.id),
2360            });
2361        }
2362
2363        // Remove final trailing newline.
2364        if self.excerpts.is_empty() {
2365            regions.push(ReferenceRegion {
2366                buffer_id: None,
2367                range: 0..1,
2368                buffer_start: Some(Point::new(0, 0)),
2369                status: None,
2370                excerpt_id: None,
2371            });
2372        } else {
2373            text.pop();
2374        }
2375
2376        // Retrieve the row info using the region that contains
2377        // the start of each multi-buffer line.
2378        let mut ix = 0;
2379        let row_infos = text
2380            .split('\n')
2381            .map(|line| {
2382                let row_info = regions
2383                    .iter()
2384                    .position(|region| region.range.contains(&ix))
2385                    .map_or(RowInfo::default(), |region_ix| {
2386                        let region = ®ions[region_ix];
2387                        let buffer_row = region.buffer_start.map(|start_point| {
2388                            start_point.row
2389                                + text[region.range.start..ix].matches('\n').count() as u32
2390                        });
2391                        let is_excerpt_start = region_ix == 0
2392                            || ®ions[region_ix - 1].excerpt_id != ®ion.excerpt_id
2393                            || regions[region_ix - 1].range.is_empty();
2394                        let mut is_excerpt_end = region_ix == regions.len() - 1
2395                            || ®ions[region_ix + 1].excerpt_id != ®ion.excerpt_id;
2396                        let is_start = !text[region.range.start..ix].contains('\n');
2397                        let mut is_end = if region.range.end > text.len() {
2398                            !text[ix..].contains('\n')
2399                        } else {
2400                            text[ix..region.range.end.min(text.len())]
2401                                .matches('\n')
2402                                .count()
2403                                == 1
2404                        };
2405                        if region_ix < regions.len() - 1
2406                            && !text[ix..].contains("\n")
2407                            && region.status == Some(DiffHunkStatus::added_none())
2408                            && regions[region_ix + 1].excerpt_id == region.excerpt_id
2409                            && regions[region_ix + 1].range.start == text.len()
2410                        {
2411                            is_end = true;
2412                            is_excerpt_end = true;
2413                        }
2414                        let mut expand_direction = None;
2415                        if let Some(buffer) = &self
2416                            .excerpts
2417                            .iter()
2418                            .find(|e| e.id == region.excerpt_id.unwrap())
2419                            .map(|e| e.buffer.clone())
2420                        {
2421                            let needs_expand_up =
2422                                is_excerpt_start && is_start && buffer_row.unwrap() > 0;
2423                            let needs_expand_down = is_excerpt_end
2424                                && is_end
2425                                && buffer.read(cx).max_point().row > buffer_row.unwrap();
2426                            expand_direction = if needs_expand_up && needs_expand_down {
2427                                Some(ExpandExcerptDirection::UpAndDown)
2428                            } else if needs_expand_up {
2429                                Some(ExpandExcerptDirection::Up)
2430                            } else if needs_expand_down {
2431                                Some(ExpandExcerptDirection::Down)
2432                            } else {
2433                                None
2434                            };
2435                        }
2436                        RowInfo {
2437                            buffer_id: region.buffer_id,
2438                            diff_status: region.status,
2439                            buffer_row,
2440                            multibuffer_row: Some(MultiBufferRow(
2441                                text[..ix].matches('\n').count() as u32
2442                            )),
2443                            expand_info: expand_direction.zip(region.excerpt_id).map(
2444                                |(direction, excerpt_id)| ExpandInfo {
2445                                    direction,
2446                                    excerpt_id,
2447                                },
2448                            ),
2449                        }
2450                    });
2451                ix += line.len() + 1;
2452                row_info
2453            })
2454            .collect();
2455
2456        (text, row_infos, excerpt_boundary_rows)
2457    }
2458
2459    fn diffs_updated(&mut self, cx: &App) {
2460        for excerpt in &mut self.excerpts {
2461            let buffer = excerpt.buffer.read(cx).snapshot();
2462            let excerpt_range = excerpt.range.to_offset(&buffer);
2463            let buffer_id = buffer.remote_id();
2464            let diff = self.diffs.get(&buffer_id).unwrap().read(cx);
2465            let mut hunks = diff.hunks_in_row_range(0..u32::MAX, &buffer, cx).peekable();
2466            excerpt.expanded_diff_hunks.retain(|hunk_anchor| {
2467                if !hunk_anchor.is_valid(&buffer) {
2468                    return false;
2469                }
2470                while let Some(hunk) = hunks.peek() {
2471                    match hunk.buffer_range.start.cmp(hunk_anchor, &buffer) {
2472                        cmp::Ordering::Less => {
2473                            hunks.next();
2474                        }
2475                        cmp::Ordering::Equal => {
2476                            let hunk_range = hunk.buffer_range.to_offset(&buffer);
2477                            return hunk_range.end >= excerpt_range.start
2478                                && hunk_range.start <= excerpt_range.end;
2479                        }
2480                        cmp::Ordering::Greater => break,
2481                    }
2482                }
2483                false
2484            });
2485        }
2486    }
2487
2488    fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut App) {
2489        let buffer_id = diff.read(cx).buffer_id;
2490        self.diffs.insert(buffer_id, diff);
2491    }
2492}
2493
2494#[gpui::test(iterations = 100)]
2495async fn test_random_set_ranges(cx: &mut TestAppContext, mut rng: StdRng) {
2496    let base_text = "a\n".repeat(100);
2497    let buf = cx.update(|cx| cx.new(|cx| Buffer::local(base_text, cx)));
2498    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
2499
2500    let operations = env::var("OPERATIONS")
2501        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2502        .unwrap_or(10);
2503
2504    fn row_ranges(ranges: &Vec<Range<Point>>) -> Vec<Range<u32>> {
2505        ranges
2506            .iter()
2507            .map(|range| range.start.row..range.end.row)
2508            .collect()
2509    }
2510
2511    for _ in 0..operations {
2512        let snapshot = buf.update(cx, |buf, _| buf.snapshot());
2513        let num_ranges = rng.random_range(0..=10);
2514        let max_row = snapshot.max_point().row;
2515        let mut ranges = (0..num_ranges)
2516            .map(|_| {
2517                let start = rng.random_range(0..max_row);
2518                let end = rng.random_range(start + 1..max_row + 1);
2519                Point::row_range(start..end)
2520            })
2521            .collect::<Vec<_>>();
2522        ranges.sort_by_key(|range| range.start);
2523        log::info!("Setting ranges: {:?}", row_ranges(&ranges));
2524        let (created, _) = multibuffer.update(cx, |multibuffer, cx| {
2525            multibuffer.set_excerpts_for_path(
2526                PathKey::for_buffer(&buf, cx),
2527                buf.clone(),
2528                ranges.clone(),
2529                2,
2530                cx,
2531            )
2532        });
2533
2534        assert_eq!(created.len(), ranges.len());
2535
2536        let snapshot = multibuffer.update(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2537        let mut last_end = None;
2538        let mut seen_ranges = Vec::default();
2539
2540        for (_, buf, range) in snapshot.excerpts() {
2541            let start = range.context.start.to_point(buf);
2542            let end = range.context.end.to_point(buf);
2543            seen_ranges.push(start..end);
2544
2545            if let Some(last_end) = last_end.take() {
2546                assert!(
2547                    start > last_end,
2548                    "multibuffer has out-of-order ranges: {:?}; {:?} <= {:?}",
2549                    row_ranges(&seen_ranges),
2550                    start,
2551                    last_end
2552                )
2553            }
2554
2555            ranges.retain(|range| range.start < start || range.end > end);
2556
2557            last_end = Some(end)
2558        }
2559
2560        assert!(
2561            ranges.is_empty(),
2562            "multibuffer {:?} did not include all ranges: {:?}",
2563            row_ranges(&seen_ranges),
2564            row_ranges(&ranges)
2565        );
2566    }
2567}
2568
2569#[gpui::test(iterations = 100)]
2570async fn test_random_multibuffer(cx: &mut TestAppContext, mut rng: StdRng) {
2571    let operations = env::var("OPERATIONS")
2572        .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2573        .unwrap_or(10);
2574
2575    let mut buffers: Vec<Entity<Buffer>> = Vec::new();
2576    let mut base_texts: HashMap<BufferId, String> = HashMap::default();
2577    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
2578    let mut reference = ReferenceMultibuffer::default();
2579    let mut anchors = Vec::new();
2580    let mut old_versions = Vec::new();
2581    let mut needs_diff_calculation = false;
2582
2583    for _ in 0..operations {
2584        match rng.random_range(0..100) {
2585            0..=14 if !buffers.is_empty() => {
2586                let buffer = buffers.choose(&mut rng).unwrap();
2587                buffer.update(cx, |buf, cx| {
2588                    let edit_count = rng.random_range(1..5);
2589                    buf.randomly_edit(&mut rng, edit_count, cx);
2590                    log::info!("buffer text:\n{}", buf.text());
2591                    needs_diff_calculation = true;
2592                });
2593                cx.update(|cx| reference.diffs_updated(cx));
2594            }
2595            15..=19 if !reference.excerpts.is_empty() => {
2596                multibuffer.update(cx, |multibuffer, cx| {
2597                    let ids = multibuffer.excerpt_ids();
2598                    let mut excerpts = HashSet::default();
2599                    for _ in 0..rng.random_range(0..ids.len()) {
2600                        excerpts.extend(ids.choose(&mut rng).copied());
2601                    }
2602
2603                    let line_count = rng.random_range(0..5);
2604
2605                    let excerpt_ixs = excerpts
2606                        .iter()
2607                        .map(|id| reference.excerpts.iter().position(|e| e.id == *id).unwrap())
2608                        .collect::<Vec<_>>();
2609                    log::info!("Expanding excerpts {excerpt_ixs:?} by {line_count} lines");
2610                    multibuffer.expand_excerpts(
2611                        excerpts.iter().cloned(),
2612                        line_count,
2613                        ExpandExcerptDirection::UpAndDown,
2614                        cx,
2615                    );
2616
2617                    reference.expand_excerpts(&excerpts, line_count, cx);
2618                });
2619            }
2620            20..=29 if !reference.excerpts.is_empty() => {
2621                let mut ids_to_remove = vec![];
2622                for _ in 0..rng.random_range(1..=3) {
2623                    let Some(excerpt) = reference.excerpts.choose(&mut rng) else {
2624                        break;
2625                    };
2626                    let id = excerpt.id;
2627                    cx.update(|cx| reference.remove_excerpt(id, cx));
2628                    ids_to_remove.push(id);
2629                }
2630                let snapshot =
2631                    multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2632                ids_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
2633                drop(snapshot);
2634                multibuffer.update(cx, |multibuffer, cx| {
2635                    multibuffer.remove_excerpts(ids_to_remove, cx)
2636                });
2637            }
2638            30..=39 if !reference.excerpts.is_empty() => {
2639                let multibuffer =
2640                    multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2641                let offset =
2642                    multibuffer.clip_offset(rng.random_range(0..=multibuffer.len()), Bias::Left);
2643                let bias = if rng.random() {
2644                    Bias::Left
2645                } else {
2646                    Bias::Right
2647                };
2648                log::info!("Creating anchor at {} with bias {:?}", offset, bias);
2649                anchors.push(multibuffer.anchor_at(offset, bias));
2650                anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
2651            }
2652            40..=44 if !anchors.is_empty() => {
2653                let multibuffer =
2654                    multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2655                let prev_len = anchors.len();
2656                anchors = multibuffer
2657                    .refresh_anchors(&anchors)
2658                    .into_iter()
2659                    .map(|a| a.1)
2660                    .collect();
2661
2662                // Ensure the newly-refreshed anchors point to a valid excerpt and don't
2663                // overshoot its boundaries.
2664                assert_eq!(anchors.len(), prev_len);
2665                for anchor in &anchors {
2666                    if anchor.excerpt_id == ExcerptId::min()
2667                        || anchor.excerpt_id == ExcerptId::max()
2668                    {
2669                        continue;
2670                    }
2671
2672                    let excerpt = multibuffer.excerpt(anchor.excerpt_id).unwrap();
2673                    assert_eq!(excerpt.id, anchor.excerpt_id);
2674                    assert!(excerpt.contains(anchor));
2675                }
2676            }
2677            45..=55 if !reference.excerpts.is_empty() => {
2678                multibuffer.update(cx, |multibuffer, cx| {
2679                    let snapshot = multibuffer.snapshot(cx);
2680                    let excerpt_ix = rng.random_range(0..reference.excerpts.len());
2681                    let excerpt = &reference.excerpts[excerpt_ix];
2682                    let start = excerpt.range.start;
2683                    let end = excerpt.range.end;
2684                    let range = snapshot.anchor_in_excerpt(excerpt.id, start).unwrap()
2685                        ..snapshot.anchor_in_excerpt(excerpt.id, end).unwrap();
2686
2687                    log::info!(
2688                        "expanding diff hunks in range {:?} (excerpt id {:?}, index {excerpt_ix:?}, buffer id {:?})",
2689                        range.to_offset(&snapshot),
2690                        excerpt.id,
2691                        excerpt.buffer.read(cx).remote_id(),
2692                    );
2693                    reference.expand_diff_hunks(excerpt.id, start..end, cx);
2694                    multibuffer.expand_diff_hunks(vec![range], cx);
2695                });
2696            }
2697            56..=85 if needs_diff_calculation => {
2698                multibuffer.update(cx, |multibuffer, cx| {
2699                    for buffer in multibuffer.all_buffers() {
2700                        let snapshot = buffer.read(cx).snapshot();
2701                        multibuffer.diff_for(snapshot.remote_id()).unwrap().update(
2702                            cx,
2703                            |diff, cx| {
2704                                log::info!(
2705                                    "recalculating diff for buffer {:?}",
2706                                    snapshot.remote_id(),
2707                                );
2708                                diff.recalculate_diff_sync(snapshot.text, cx);
2709                            },
2710                        );
2711                    }
2712                    reference.diffs_updated(cx);
2713                    needs_diff_calculation = false;
2714                });
2715            }
2716            _ => {
2717                let buffer_handle = if buffers.is_empty() || rng.random_bool(0.4) {
2718                    let mut base_text = util::RandomCharIter::new(&mut rng)
2719                        .take(256)
2720                        .collect::<String>();
2721
2722                    let buffer = cx.new(|cx| Buffer::local(base_text.clone(), cx));
2723                    text::LineEnding::normalize(&mut base_text);
2724                    base_texts.insert(
2725                        buffer.read_with(cx, |buffer, _| buffer.remote_id()),
2726                        base_text,
2727                    );
2728                    buffers.push(buffer);
2729                    buffers.last().unwrap()
2730                } else {
2731                    buffers.choose(&mut rng).unwrap()
2732                };
2733
2734                let prev_excerpt_ix = rng.random_range(0..=reference.excerpts.len());
2735                let prev_excerpt_id = reference
2736                    .excerpts
2737                    .get(prev_excerpt_ix)
2738                    .map_or(ExcerptId::max(), |e| e.id);
2739                let excerpt_ix = (prev_excerpt_ix + 1).min(reference.excerpts.len());
2740
2741                let (range, anchor_range) = buffer_handle.read_with(cx, |buffer, _| {
2742                    let end_row = rng.random_range(0..=buffer.max_point().row);
2743                    let start_row = rng.random_range(0..=end_row);
2744                    let end_ix = buffer.point_to_offset(Point::new(end_row, 0));
2745                    let start_ix = buffer.point_to_offset(Point::new(start_row, 0));
2746                    let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
2747
2748                    log::info!(
2749                        "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}",
2750                        excerpt_ix,
2751                        reference.excerpts.len(),
2752                        buffer.remote_id(),
2753                        buffer.text(),
2754                        start_ix..end_ix,
2755                        &buffer.text()[start_ix..end_ix]
2756                    );
2757
2758                    (start_ix..end_ix, anchor_range)
2759                });
2760
2761                multibuffer.update(cx, |multibuffer, cx| {
2762                    let id = buffer_handle.read(cx).remote_id();
2763                    if multibuffer.diff_for(id).is_none() {
2764                        let base_text = base_texts.get(&id).unwrap();
2765                        let diff = cx
2766                            .new(|cx| BufferDiff::new_with_base_text(base_text, buffer_handle, cx));
2767                        reference.add_diff(diff.clone(), cx);
2768                        multibuffer.add_diff(diff, cx)
2769                    }
2770                });
2771
2772                let excerpt_id = multibuffer.update(cx, |multibuffer, cx| {
2773                    multibuffer
2774                        .insert_excerpts_after(
2775                            prev_excerpt_id,
2776                            buffer_handle.clone(),
2777                            [ExcerptRange::new(range.clone())],
2778                            cx,
2779                        )
2780                        .pop()
2781                        .unwrap()
2782                });
2783
2784                reference.insert_excerpt_after(
2785                    prev_excerpt_id,
2786                    excerpt_id,
2787                    (buffer_handle.clone(), anchor_range),
2788                );
2789            }
2790        }
2791
2792        if rng.random_bool(0.3) {
2793            multibuffer.update(cx, |multibuffer, cx| {
2794                old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
2795            })
2796        }
2797
2798        let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2799        let actual_text = snapshot.text();
2800        let actual_boundary_rows = snapshot
2801            .excerpt_boundaries_in_range(0..)
2802            .map(|b| b.row)
2803            .collect::<HashSet<_>>();
2804        let actual_row_infos = snapshot.row_infos(MultiBufferRow(0)).collect::<Vec<_>>();
2805
2806        let (expected_text, expected_row_infos, expected_boundary_rows) =
2807            cx.update(|cx| reference.expected_content(cx));
2808
2809        let has_diff = actual_row_infos
2810            .iter()
2811            .any(|info| info.diff_status.is_some())
2812            || expected_row_infos
2813                .iter()
2814                .any(|info| info.diff_status.is_some());
2815        let actual_diff = format_diff(
2816            &actual_text,
2817            &actual_row_infos,
2818            &actual_boundary_rows,
2819            Some(has_diff),
2820        );
2821        let expected_diff = format_diff(
2822            &expected_text,
2823            &expected_row_infos,
2824            &expected_boundary_rows,
2825            Some(has_diff),
2826        );
2827
2828        log::info!("Multibuffer content:\n{}", actual_diff);
2829
2830        assert_eq!(
2831            actual_row_infos.len(),
2832            actual_text.split('\n').count(),
2833            "line count: {}",
2834            actual_text.split('\n').count()
2835        );
2836        pretty_assertions::assert_eq!(actual_diff, expected_diff);
2837        pretty_assertions::assert_eq!(actual_text, expected_text);
2838        pretty_assertions::assert_eq!(actual_row_infos, expected_row_infos);
2839
2840        for _ in 0..5 {
2841            let start_row = rng.random_range(0..=expected_row_infos.len());
2842            assert_eq!(
2843                snapshot
2844                    .row_infos(MultiBufferRow(start_row as u32))
2845                    .collect::<Vec<_>>(),
2846                &expected_row_infos[start_row..],
2847                "buffer_rows({})",
2848                start_row
2849            );
2850        }
2851
2852        assert_eq!(
2853            snapshot.widest_line_number(),
2854            expected_row_infos
2855                .into_iter()
2856                .filter_map(|info| {
2857                    if info.diff_status.is_some_and(|status| status.is_deleted()) {
2858                        None
2859                    } else {
2860                        info.buffer_row
2861                    }
2862                })
2863                .max()
2864                .unwrap()
2865                + 1
2866        );
2867        let reference_ranges = cx.update(|cx| {
2868            reference
2869                .excerpts
2870                .iter()
2871                .map(|excerpt| {
2872                    (
2873                        excerpt.id,
2874                        excerpt.range.to_offset(&excerpt.buffer.read(cx).snapshot()),
2875                    )
2876                })
2877                .collect::<HashMap<_, _>>()
2878        });
2879        for i in 0..snapshot.len() {
2880            let excerpt = snapshot.excerpt_containing(i..i).unwrap();
2881            assert_eq!(excerpt.buffer_range(), reference_ranges[&excerpt.id()]);
2882        }
2883
2884        assert_consistent_line_numbers(&snapshot);
2885        assert_position_translation(&snapshot);
2886
2887        for (row, line) in expected_text.split('\n').enumerate() {
2888            assert_eq!(
2889                snapshot.line_len(MultiBufferRow(row as u32)),
2890                line.len() as u32,
2891                "line_len({}).",
2892                row
2893            );
2894        }
2895
2896        let text_rope = Rope::from_str(expected_text.as_str(), cx.background_executor());
2897        for _ in 0..10 {
2898            let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
2899            let start_ix = text_rope.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
2900
2901            let text_for_range = snapshot
2902                .text_for_range(start_ix..end_ix)
2903                .collect::<String>();
2904            assert_eq!(
2905                text_for_range,
2906                &expected_text[start_ix..end_ix],
2907                "incorrect text for range {:?}",
2908                start_ix..end_ix
2909            );
2910
2911            let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
2912            assert_eq!(
2913                snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
2914                expected_summary,
2915                "incorrect summary for range {:?}",
2916                start_ix..end_ix
2917            );
2918        }
2919
2920        // Anchor resolution
2921        let summaries = snapshot.summaries_for_anchors::<usize, _>(&anchors);
2922        assert_eq!(anchors.len(), summaries.len());
2923        for (anchor, resolved_offset) in anchors.iter().zip(summaries) {
2924            assert!(resolved_offset <= snapshot.len());
2925            assert_eq!(
2926                snapshot.summary_for_anchor::<usize>(anchor),
2927                resolved_offset,
2928                "anchor: {:?}",
2929                anchor
2930            );
2931        }
2932
2933        for _ in 0..10 {
2934            let end_ix = text_rope.clip_offset(rng.random_range(0..=text_rope.len()), Bias::Right);
2935            assert_eq!(
2936                snapshot.reversed_chars_at(end_ix).collect::<String>(),
2937                expected_text[..end_ix].chars().rev().collect::<String>(),
2938            );
2939        }
2940
2941        for _ in 0..10 {
2942            let end_ix = rng.random_range(0..=text_rope.len());
2943            let start_ix = rng.random_range(0..=end_ix);
2944            assert_eq!(
2945                snapshot
2946                    .bytes_in_range(start_ix..end_ix)
2947                    .flatten()
2948                    .copied()
2949                    .collect::<Vec<_>>(),
2950                expected_text.as_bytes()[start_ix..end_ix].to_vec(),
2951                "bytes_in_range({:?})",
2952                start_ix..end_ix,
2953            );
2954        }
2955    }
2956
2957    let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
2958    for (old_snapshot, subscription) in old_versions {
2959        let edits = subscription.consume().into_inner();
2960
2961        log::info!(
2962            "applying subscription edits to old text: {:?}: {:?}",
2963            old_snapshot.text(),
2964            edits,
2965        );
2966
2967        let mut text = old_snapshot.text();
2968        for edit in edits {
2969            let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2970            text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2971        }
2972        assert_eq!(text.to_string(), snapshot.text());
2973    }
2974}
2975
2976#[gpui::test]
2977fn test_history(cx: &mut App) {
2978    let test_settings = SettingsStore::test(cx);
2979    cx.set_global(test_settings);
2980    let group_interval: Duration = Duration::from_millis(1);
2981    let buffer_1 = cx.new(|cx| {
2982        let mut buf = Buffer::local("1234", cx);
2983        buf.set_group_interval(group_interval);
2984        buf
2985    });
2986    let buffer_2 = cx.new(|cx| {
2987        let mut buf = Buffer::local("5678", cx);
2988        buf.set_group_interval(group_interval);
2989        buf
2990    });
2991    let multibuffer = cx.new(|_| MultiBuffer::new(Capability::ReadWrite));
2992    multibuffer.update(cx, |this, _| {
2993        this.set_group_interval(group_interval);
2994    });
2995    multibuffer.update(cx, |multibuffer, cx| {
2996        multibuffer.push_excerpts(
2997            buffer_1.clone(),
2998            [ExcerptRange::new(0..buffer_1.read(cx).len())],
2999            cx,
3000        );
3001        multibuffer.push_excerpts(
3002            buffer_2.clone(),
3003            [ExcerptRange::new(0..buffer_2.read(cx).len())],
3004            cx,
3005        );
3006    });
3007
3008    let mut now = Instant::now();
3009
3010    multibuffer.update(cx, |multibuffer, cx| {
3011        let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
3012        multibuffer.edit(
3013            [
3014                (Point::new(0, 0)..Point::new(0, 0), "A"),
3015                (Point::new(1, 0)..Point::new(1, 0), "A"),
3016            ],
3017            None,
3018            cx,
3019        );
3020        multibuffer.edit(
3021            [
3022                (Point::new(0, 1)..Point::new(0, 1), "B"),
3023                (Point::new(1, 1)..Point::new(1, 1), "B"),
3024            ],
3025            None,
3026            cx,
3027        );
3028        multibuffer.end_transaction_at(now, cx);
3029        assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
3030
3031        // Verify edited ranges for transaction 1
3032        assert_eq!(
3033            multibuffer.edited_ranges_for_transaction(transaction_1, cx),
3034            &[
3035                Point::new(0, 0)..Point::new(0, 2),
3036                Point::new(1, 0)..Point::new(1, 2)
3037            ]
3038        );
3039
3040        // Edit buffer 1 through the multibuffer
3041        now += 2 * group_interval;
3042        multibuffer.start_transaction_at(now, cx);
3043        multibuffer.edit([(2..2, "C")], None, cx);
3044        multibuffer.end_transaction_at(now, cx);
3045        assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
3046
3047        // Edit buffer 1 independently
3048        buffer_1.update(cx, |buffer_1, cx| {
3049            buffer_1.start_transaction_at(now);
3050            buffer_1.edit([(3..3, "D")], None, cx);
3051            buffer_1.end_transaction_at(now, cx);
3052
3053            now += 2 * group_interval;
3054            buffer_1.start_transaction_at(now);
3055            buffer_1.edit([(4..4, "E")], None, cx);
3056            buffer_1.end_transaction_at(now, cx);
3057        });
3058        assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
3059
3060        // An undo in the multibuffer undoes the multibuffer transaction
3061        // and also any individual buffer edits that have occurred since
3062        // that transaction.
3063        multibuffer.undo(cx);
3064        assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
3065
3066        multibuffer.undo(cx);
3067        assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
3068
3069        multibuffer.redo(cx);
3070        assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
3071
3072        multibuffer.redo(cx);
3073        assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
3074
3075        // Undo buffer 2 independently.
3076        buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx));
3077        assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678");
3078
3079        // An undo in the multibuffer undoes the components of the
3080        // the last multibuffer transaction that are not already undone.
3081        multibuffer.undo(cx);
3082        assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678");
3083
3084        multibuffer.undo(cx);
3085        assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
3086
3087        multibuffer.redo(cx);
3088        assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
3089
3090        buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
3091        assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
3092
3093        // Redo stack gets cleared after an edit.
3094        now += 2 * group_interval;
3095        multibuffer.start_transaction_at(now, cx);
3096        multibuffer.edit([(0..0, "X")], None, cx);
3097        multibuffer.end_transaction_at(now, cx);
3098        assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
3099        multibuffer.redo(cx);
3100        assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
3101        multibuffer.undo(cx);
3102        assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
3103        multibuffer.undo(cx);
3104        assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
3105
3106        // Transactions can be grouped manually.
3107        multibuffer.redo(cx);
3108        multibuffer.redo(cx);
3109        assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
3110        multibuffer.group_until_transaction(transaction_1, cx);
3111        multibuffer.undo(cx);
3112        assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
3113        multibuffer.redo(cx);
3114        assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
3115    });
3116}
3117
3118#[gpui::test]
3119async fn test_enclosing_indent(cx: &mut TestAppContext) {
3120    async fn enclosing_indent(
3121        text: &str,
3122        buffer_row: u32,
3123        cx: &mut TestAppContext,
3124    ) -> Option<(Range<u32>, LineIndent)> {
3125        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3126        let snapshot = cx.read(|cx| buffer.read(cx).snapshot(cx));
3127        let (range, indent) = snapshot
3128            .enclosing_indent(MultiBufferRow(buffer_row))
3129            .await?;
3130        Some((range.start.0..range.end.0, indent))
3131    }
3132
3133    assert_eq!(
3134        enclosing_indent(
3135            indoc!(
3136                "
3137                fn b() {
3138                    if c {
3139                        let d = 2;
3140                    }
3141                }
3142                "
3143            ),
3144            1,
3145            cx,
3146        )
3147        .await,
3148        Some((
3149            1..2,
3150            LineIndent {
3151                tabs: 0,
3152                spaces: 4,
3153                line_blank: false,
3154            }
3155        ))
3156    );
3157
3158    assert_eq!(
3159        enclosing_indent(
3160            indoc!(
3161                "
3162                fn b() {
3163                    if c {
3164                        let d = 2;
3165                    }
3166                }
3167                "
3168            ),
3169            2,
3170            cx,
3171        )
3172        .await,
3173        Some((
3174            1..2,
3175            LineIndent {
3176                tabs: 0,
3177                spaces: 4,
3178                line_blank: false,
3179            }
3180        ))
3181    );
3182
3183    assert_eq!(
3184        enclosing_indent(
3185            indoc!(
3186                "
3187                fn b() {
3188                    if c {
3189                        let d = 2;
3190
3191                        let e = 5;
3192                    }
3193                }
3194                "
3195            ),
3196            3,
3197            cx,
3198        )
3199        .await,
3200        Some((
3201            1..4,
3202            LineIndent {
3203                tabs: 0,
3204                spaces: 4,
3205                line_blank: false,
3206            }
3207        ))
3208    );
3209}
3210
3211#[gpui::test]
3212fn test_summaries_for_anchors(cx: &mut TestAppContext) {
3213    let base_text_1 = indoc!(
3214        "
3215        bar
3216        "
3217    );
3218    let text_1 = indoc!(
3219        "
3220        BAR
3221        "
3222    );
3223    let base_text_2 = indoc!(
3224        "
3225        foo
3226        "
3227    );
3228    let text_2 = indoc!(
3229        "
3230        FOO
3231        "
3232    );
3233
3234    let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx));
3235    let buffer_2 = cx.new(|cx| Buffer::local(text_2, cx));
3236    let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_1, &buffer_1, cx));
3237    let diff_2 = cx.new(|cx| BufferDiff::new_with_base_text(base_text_2, &buffer_2, cx));
3238    cx.run_until_parked();
3239
3240    let mut ids = vec![];
3241    let multibuffer = cx.new(|cx| {
3242        let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
3243        multibuffer.set_all_diff_hunks_expanded(cx);
3244        ids.extend(multibuffer.push_excerpts(
3245            buffer_1.clone(),
3246            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
3247            cx,
3248        ));
3249        ids.extend(multibuffer.push_excerpts(
3250            buffer_2.clone(),
3251            [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
3252            cx,
3253        ));
3254        multibuffer.add_diff(diff_1.clone(), cx);
3255        multibuffer.add_diff(diff_2.clone(), cx);
3256        multibuffer
3257    });
3258
3259    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
3260        (multibuffer.snapshot(cx), multibuffer.subscribe())
3261    });
3262
3263    assert_new_snapshot(
3264        &multibuffer,
3265        &mut snapshot,
3266        &mut subscription,
3267        cx,
3268        indoc!(
3269            "
3270            - bar
3271            + BAR
3272
3273            - foo
3274            + FOO
3275            "
3276        ),
3277    );
3278
3279    let id_1 = buffer_1.read_with(cx, |buffer, _| buffer.remote_id());
3280    let id_2 = buffer_2.read_with(cx, |buffer, _| buffer.remote_id());
3281
3282    let anchor_1 = Anchor::in_buffer(ids[0], id_1, text::Anchor::MIN);
3283    let point_1 = snapshot.summaries_for_anchors::<Point, _>([&anchor_1])[0];
3284    assert_eq!(point_1, Point::new(0, 0));
3285
3286    let anchor_2 = Anchor::in_buffer(ids[1], id_2, text::Anchor::MIN);
3287    let point_2 = snapshot.summaries_for_anchors::<Point, _>([&anchor_2])[0];
3288    assert_eq!(point_2, Point::new(3, 0));
3289}
3290
3291#[gpui::test]
3292fn test_trailing_deletion_without_newline(cx: &mut TestAppContext) {
3293    let base_text_1 = "one\ntwo".to_owned();
3294    let text_1 = "one\n".to_owned();
3295
3296    let buffer_1 = cx.new(|cx| Buffer::local(text_1, cx));
3297    let diff_1 = cx.new(|cx| BufferDiff::new_with_base_text(&base_text_1, &buffer_1, cx));
3298    cx.run_until_parked();
3299
3300    let multibuffer = cx.new(|cx| {
3301        let mut multibuffer = MultiBuffer::singleton(buffer_1.clone(), cx);
3302        multibuffer.add_diff(diff_1.clone(), cx);
3303        multibuffer.expand_diff_hunks(vec![Anchor::min()..Anchor::max()], cx);
3304        multibuffer
3305    });
3306
3307    let (mut snapshot, mut subscription) = multibuffer.update(cx, |multibuffer, cx| {
3308        (multibuffer.snapshot(cx), multibuffer.subscribe())
3309    });
3310
3311    assert_new_snapshot(
3312        &multibuffer,
3313        &mut snapshot,
3314        &mut subscription,
3315        cx,
3316        indoc!(
3317            "
3318              one
3319            - two
3320            "
3321        ),
3322    );
3323
3324    assert_eq!(snapshot.max_point(), Point::new(2, 0));
3325    assert_eq!(snapshot.len(), 8);
3326
3327    assert_eq!(
3328        snapshot
3329            .dimensions_from_points::<Point>([Point::new(2, 0)])
3330            .collect::<Vec<_>>(),
3331        vec![Point::new(2, 0)]
3332    );
3333
3334    let (_, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
3335    assert_eq!(translated_offset, "one\n".len());
3336    let (_, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
3337    assert_eq!(translated_point, Point::new(1, 0));
3338
3339    // The same, for an excerpt that's not at the end of the multibuffer.
3340
3341    let text_2 = "foo\n".to_owned();
3342    let buffer_2 = cx.new(|cx| Buffer::local(&text_2, cx));
3343    multibuffer.update(cx, |multibuffer, cx| {
3344        multibuffer.push_excerpts(
3345            buffer_2.clone(),
3346            [ExcerptRange::new(Point::new(0, 0)..Point::new(1, 0))],
3347            cx,
3348        );
3349    });
3350
3351    assert_new_snapshot(
3352        &multibuffer,
3353        &mut snapshot,
3354        &mut subscription,
3355        cx,
3356        indoc!(
3357            "
3358              one
3359            - two
3360
3361              foo
3362            "
3363        ),
3364    );
3365
3366    assert_eq!(
3367        snapshot
3368            .dimensions_from_points::<Point>([Point::new(2, 0)])
3369            .collect::<Vec<_>>(),
3370        vec![Point::new(2, 0)]
3371    );
3372
3373    let buffer_1_id = buffer_1.read_with(cx, |buffer_1, _| buffer_1.remote_id());
3374    let (buffer, translated_offset) = snapshot.point_to_buffer_offset(Point::new(2, 0)).unwrap();
3375    assert_eq!(buffer.remote_id(), buffer_1_id);
3376    assert_eq!(translated_offset, "one\n".len());
3377    let (buffer, translated_point, _) = snapshot.point_to_buffer_point(Point::new(2, 0)).unwrap();
3378    assert_eq!(buffer.remote_id(), buffer_1_id);
3379    assert_eq!(translated_point, Point::new(1, 0));
3380}
3381
3382fn format_diff(
3383    text: &str,
3384    row_infos: &Vec<RowInfo>,
3385    boundary_rows: &HashSet<MultiBufferRow>,
3386    has_diff: Option<bool>,
3387) -> String {
3388    let has_diff =
3389        has_diff.unwrap_or_else(|| row_infos.iter().any(|info| info.diff_status.is_some()));
3390    text.split('\n')
3391        .enumerate()
3392        .zip(row_infos)
3393        .map(|((ix, line), info)| {
3394            let marker = match info.diff_status.map(|status| status.kind) {
3395                Some(DiffHunkStatusKind::Added) => "+ ",
3396                Some(DiffHunkStatusKind::Deleted) => "- ",
3397                Some(DiffHunkStatusKind::Modified) => unreachable!(),
3398                None => {
3399                    if has_diff && !line.is_empty() {
3400                        "  "
3401                    } else {
3402                        ""
3403                    }
3404                }
3405            };
3406            let boundary_row = if boundary_rows.contains(&MultiBufferRow(ix as u32)) {
3407                if has_diff {
3408                    "  ----------\n"
3409                } else {
3410                    "---------\n"
3411                }
3412            } else {
3413                ""
3414            };
3415            format!("{boundary_row}{marker}{line}")
3416        })
3417        .collect::<Vec<_>>()
3418        .join("\n")
3419}
3420
3421#[track_caller]
3422fn assert_excerpts_match(
3423    multibuffer: &Entity<MultiBuffer>,
3424    cx: &mut TestAppContext,
3425    expected: &str,
3426) {
3427    let mut output = String::new();
3428    multibuffer.read_with(cx, |multibuffer, cx| {
3429        for (_, buffer, range) in multibuffer.snapshot(cx).excerpts() {
3430            output.push_str("-----\n");
3431            output.extend(buffer.text_for_range(range.context));
3432            if !output.ends_with('\n') {
3433                output.push('\n');
3434            }
3435        }
3436    });
3437    assert_eq!(output, expected);
3438}
3439
3440#[track_caller]
3441fn assert_new_snapshot(
3442    multibuffer: &Entity<MultiBuffer>,
3443    snapshot: &mut MultiBufferSnapshot,
3444    subscription: &mut Subscription,
3445    cx: &mut TestAppContext,
3446    expected_diff: &str,
3447) {
3448    let new_snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
3449    let actual_text = new_snapshot.text();
3450    let line_infos = new_snapshot
3451        .row_infos(MultiBufferRow(0))
3452        .collect::<Vec<_>>();
3453    let actual_diff = format_diff(&actual_text, &line_infos, &Default::default(), None);
3454    pretty_assertions::assert_eq!(actual_diff, expected_diff);
3455    check_edits(
3456        snapshot,
3457        &new_snapshot,
3458        &subscription.consume().into_inner(),
3459    );
3460    *snapshot = new_snapshot;
3461}
3462
3463#[track_caller]
3464fn check_edits(
3465    old_snapshot: &MultiBufferSnapshot,
3466    new_snapshot: &MultiBufferSnapshot,
3467    edits: &[Edit<usize>],
3468) {
3469    let mut text = old_snapshot.text();
3470    let new_text = new_snapshot.text();
3471    for edit in edits.iter().rev() {
3472        if !text.is_char_boundary(edit.old.start)
3473            || !text.is_char_boundary(edit.old.end)
3474            || !new_text.is_char_boundary(edit.new.start)
3475            || !new_text.is_char_boundary(edit.new.end)
3476        {
3477            panic!(
3478                "invalid edits: {:?}\nold text: {:?}\nnew text: {:?}",
3479                edits, text, new_text
3480            );
3481        }
3482
3483        text.replace_range(
3484            edit.old.start..edit.old.end,
3485            &new_text[edit.new.start..edit.new.end],
3486        );
3487    }
3488
3489    pretty_assertions::assert_eq!(text, new_text, "invalid edits: {:?}", edits);
3490}
3491
3492#[track_caller]
3493fn assert_chunks_in_ranges(snapshot: &MultiBufferSnapshot) {
3494    let full_text = snapshot.text();
3495    for ix in 0..full_text.len() {
3496        let mut chunks = snapshot.chunks(0..snapshot.len(), false);
3497        chunks.seek(ix..snapshot.len());
3498        let tail = chunks.map(|chunk| chunk.text).collect::<String>();
3499        assert_eq!(tail, &full_text[ix..], "seek to range: {:?}", ix..);
3500    }
3501}
3502
3503#[track_caller]
3504fn assert_consistent_line_numbers(snapshot: &MultiBufferSnapshot) {
3505    let all_line_numbers = snapshot.row_infos(MultiBufferRow(0)).collect::<Vec<_>>();
3506    for start_row in 1..all_line_numbers.len() {
3507        let line_numbers = snapshot
3508            .row_infos(MultiBufferRow(start_row as u32))
3509            .collect::<Vec<_>>();
3510        assert_eq!(
3511            line_numbers,
3512            all_line_numbers[start_row..],
3513            "start_row: {start_row}"
3514        );
3515    }
3516}
3517
3518#[track_caller]
3519fn assert_position_translation(snapshot: &MultiBufferSnapshot) {
3520    let text = Rope::from_str_small(&snapshot.text());
3521
3522    let mut left_anchors = Vec::new();
3523    let mut right_anchors = Vec::new();
3524    let mut offsets = Vec::new();
3525    let mut points = Vec::new();
3526    for offset in 0..=text.len() + 1 {
3527        let clipped_left = snapshot.clip_offset(offset, Bias::Left);
3528        let clipped_right = snapshot.clip_offset(offset, Bias::Right);
3529        assert_eq!(
3530            clipped_left,
3531            text.clip_offset(offset, Bias::Left),
3532            "clip_offset({offset:?}, Left)"
3533        );
3534        assert_eq!(
3535            clipped_right,
3536            text.clip_offset(offset, Bias::Right),
3537            "clip_offset({offset:?}, Right)"
3538        );
3539        assert_eq!(
3540            snapshot.offset_to_point(clipped_left),
3541            text.offset_to_point(clipped_left),
3542            "offset_to_point({clipped_left})"
3543        );
3544        assert_eq!(
3545            snapshot.offset_to_point(clipped_right),
3546            text.offset_to_point(clipped_right),
3547            "offset_to_point({clipped_right})"
3548        );
3549        let anchor_after = snapshot.anchor_after(clipped_left);
3550        assert_eq!(
3551            anchor_after.to_offset(snapshot),
3552            clipped_left,
3553            "anchor_after({clipped_left}).to_offset {anchor_after:?}"
3554        );
3555        let anchor_before = snapshot.anchor_before(clipped_left);
3556        assert_eq!(
3557            anchor_before.to_offset(snapshot),
3558            clipped_left,
3559            "anchor_before({clipped_left}).to_offset"
3560        );
3561        left_anchors.push(anchor_before);
3562        right_anchors.push(anchor_after);
3563        offsets.push(clipped_left);
3564        points.push(text.offset_to_point(clipped_left));
3565    }
3566
3567    for row in 0..text.max_point().row {
3568        for column in 0..text.line_len(row) + 1 {
3569            let point = Point { row, column };
3570            let clipped_left = snapshot.clip_point(point, Bias::Left);
3571            let clipped_right = snapshot.clip_point(point, Bias::Right);
3572            assert_eq!(
3573                clipped_left,
3574                text.clip_point(point, Bias::Left),
3575                "clip_point({point:?}, Left)"
3576            );
3577            assert_eq!(
3578                clipped_right,
3579                text.clip_point(point, Bias::Right),
3580                "clip_point({point:?}, Right)"
3581            );
3582            assert_eq!(
3583                snapshot.point_to_offset(clipped_left),
3584                text.point_to_offset(clipped_left),
3585                "point_to_offset({clipped_left:?})"
3586            );
3587            assert_eq!(
3588                snapshot.point_to_offset(clipped_right),
3589                text.point_to_offset(clipped_right),
3590                "point_to_offset({clipped_right:?})"
3591            );
3592        }
3593    }
3594
3595    assert_eq!(
3596        snapshot.summaries_for_anchors::<usize, _>(&left_anchors),
3597        offsets,
3598        "left_anchors <-> offsets"
3599    );
3600    assert_eq!(
3601        snapshot.summaries_for_anchors::<Point, _>(&left_anchors),
3602        points,
3603        "left_anchors <-> points"
3604    );
3605    assert_eq!(
3606        snapshot.summaries_for_anchors::<usize, _>(&right_anchors),
3607        offsets,
3608        "right_anchors <-> offsets"
3609    );
3610    assert_eq!(
3611        snapshot.summaries_for_anchors::<Point, _>(&right_anchors),
3612        points,
3613        "right_anchors <-> points"
3614    );
3615
3616    for (anchors, bias) in [(&left_anchors, Bias::Left), (&right_anchors, Bias::Right)] {
3617        for (ix, (offset, anchor)) in offsets.iter().zip(anchors).enumerate() {
3618            if ix > 0 && *offset == 252 && offset > &offsets[ix - 1] {
3619                let prev_anchor = left_anchors[ix - 1];
3620                assert!(
3621                    anchor.cmp(&prev_anchor, snapshot).is_gt(),
3622                    "anchor({}, {bias:?}).cmp(&anchor({}, {bias:?}).is_gt()",
3623                    offsets[ix],
3624                    offsets[ix - 1],
3625                );
3626                assert!(
3627                    prev_anchor.cmp(anchor, snapshot).is_lt(),
3628                    "anchor({}, {bias:?}).cmp(&anchor({}, {bias:?}).is_lt()",
3629                    offsets[ix - 1],
3630                    offsets[ix],
3631                );
3632            }
3633        }
3634    }
3635
3636    if let Some((buffer, offset)) = snapshot.point_to_buffer_offset(snapshot.max_point()) {
3637        assert!(offset <= buffer.len());
3638    }
3639    if let Some((buffer, point, _)) = snapshot.point_to_buffer_point(snapshot.max_point()) {
3640        assert!(point <= buffer.max_point());
3641    }
3642}
3643
3644fn assert_line_indents(snapshot: &MultiBufferSnapshot, executor: &BackgroundExecutor) {
3645    let max_row = snapshot.max_point().row;
3646    let buffer_id = snapshot.excerpts().next().unwrap().1.remote_id();
3647    let text = text::Buffer::new(ReplicaId::LOCAL, buffer_id, snapshot.text(), executor);
3648    let mut line_indents = text
3649        .line_indents_in_row_range(0..max_row + 1)
3650        .collect::<Vec<_>>();
3651    for start_row in 0..snapshot.max_point().row {
3652        pretty_assertions::assert_eq!(
3653            snapshot
3654                .line_indents(MultiBufferRow(start_row), |_| true)
3655                .map(|(row, indent, _)| (row.0, indent))
3656                .collect::<Vec<_>>(),
3657            &line_indents[(start_row as usize)..],
3658            "line_indents({start_row})"
3659        );
3660    }
3661
3662    line_indents.reverse();
3663    pretty_assertions::assert_eq!(
3664        snapshot
3665            .reversed_line_indents(MultiBufferRow(max_row), |_| true)
3666            .map(|(row, indent, _)| (row.0, indent))
3667            .collect::<Vec<_>>(),
3668        &line_indents[..],
3669        "reversed_line_indents({max_row})"
3670    );
3671}
3672
3673#[gpui::test]
3674fn test_new_empty_buffer_uses_untitled_title(cx: &mut App) {
3675    let buffer = cx.new(|cx| Buffer::local("", cx));
3676    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3677
3678    assert_eq!(multibuffer.read(cx).title(cx), "untitled");
3679}
3680
3681#[gpui::test]
3682fn test_new_empty_buffer_uses_untitled_title_when_only_contains_whitespace(cx: &mut App) {
3683    let buffer = cx.new(|cx| Buffer::local("\n ", cx));
3684    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3685
3686    assert_eq!(multibuffer.read(cx).title(cx), "untitled");
3687}
3688
3689#[gpui::test]
3690fn test_new_empty_buffer_takes_first_line_for_title(cx: &mut App) {
3691    let buffer = cx.new(|cx| Buffer::local("Hello World\nSecond line", cx));
3692    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3693
3694    assert_eq!(multibuffer.read(cx).title(cx), "Hello World");
3695}
3696
3697#[gpui::test]
3698fn test_new_empty_buffer_takes_trimmed_first_line_for_title(cx: &mut App) {
3699    let buffer = cx.new(|cx| Buffer::local("\nHello, World ", cx));
3700    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3701
3702    assert_eq!(multibuffer.read(cx).title(cx), "Hello, World");
3703}
3704
3705#[gpui::test]
3706fn test_new_empty_buffer_uses_truncated_first_line_for_title(cx: &mut App) {
3707    let title = "aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeee";
3708    let title_after = "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd";
3709    let buffer = cx.new(|cx| Buffer::local(title, cx));
3710    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3711
3712    assert_eq!(multibuffer.read(cx).title(cx), title_after);
3713}
3714
3715#[gpui::test]
3716fn test_new_empty_buffer_uses_truncated_first_line_for_title_after_merging_adjacent_spaces(
3717    cx: &mut App,
3718) {
3719    let title = "aaaaaaaaaabbbbbbbbbb    ccccccccccddddddddddeeeeeeeeee";
3720    let title_after = "aaaaaaaaaabbbbbbbbbb ccccccccccddddddddd";
3721    let buffer = cx.new(|cx| Buffer::local(title, cx));
3722    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3723
3724    assert_eq!(multibuffer.read(cx).title(cx), title_after);
3725}
3726
3727#[gpui::test]
3728fn test_new_empty_buffers_title_can_be_set(cx: &mut App) {
3729    let buffer = cx.new(|cx| Buffer::local("Hello World", cx));
3730    let multibuffer = cx.new(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3731    assert_eq!(multibuffer.read(cx).title(cx), "Hello World");
3732
3733    multibuffer.update(cx, |multibuffer, cx| {
3734        multibuffer.set_title("Hey".into(), cx)
3735    });
3736    assert_eq!(multibuffer.read(cx).title(cx), "Hey");
3737}
3738
3739#[gpui::test(iterations = 100)]
3740fn test_random_chunk_bitmaps(cx: &mut App, mut rng: StdRng) {
3741    let multibuffer = if rng.random() {
3742        let len = rng.random_range(0..10000);
3743        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3744        let buffer = cx.new(|cx| Buffer::local(text, cx));
3745        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
3746    } else {
3747        MultiBuffer::build_random(&mut rng, cx)
3748    };
3749
3750    let snapshot = multibuffer.read(cx).snapshot(cx);
3751
3752    let chunks = snapshot.chunks(0..snapshot.len(), false);
3753
3754    for chunk in chunks {
3755        let chunk_text = chunk.text;
3756        let chars_bitmap = chunk.chars;
3757        let tabs_bitmap = chunk.tabs;
3758
3759        if chunk_text.is_empty() {
3760            assert_eq!(
3761                chars_bitmap, 0,
3762                "Empty chunk should have empty chars bitmap"
3763            );
3764            assert_eq!(tabs_bitmap, 0, "Empty chunk should have empty tabs bitmap");
3765            continue;
3766        }
3767
3768        assert!(
3769            chunk_text.len() <= 128,
3770            "Chunk text length {} exceeds 128 bytes",
3771            chunk_text.len()
3772        );
3773
3774        // Verify chars bitmap
3775        let char_indices = chunk_text
3776            .char_indices()
3777            .map(|(i, _)| i)
3778            .collect::<Vec<_>>();
3779
3780        for byte_idx in 0..chunk_text.len() {
3781            let should_have_bit = char_indices.contains(&byte_idx);
3782            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
3783
3784            if has_bit != should_have_bit {
3785                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
3786                eprintln!("Char indices: {:?}", char_indices);
3787                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
3788            }
3789
3790            assert_eq!(
3791                has_bit, should_have_bit,
3792                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
3793                byte_idx, chunk_text, should_have_bit, has_bit
3794            );
3795        }
3796
3797        for (byte_idx, byte) in chunk_text.bytes().enumerate() {
3798            let is_tab = byte == b'\t';
3799            let has_bit = tabs_bitmap & (1 << byte_idx) != 0;
3800
3801            if has_bit != is_tab {
3802                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
3803                eprintln!("Tabs bitmap: {:#b}", tabs_bitmap);
3804                assert_eq!(
3805                    has_bit, is_tab,
3806                    "Tabs bitmap mismatch at byte index {} in chunk {:?}. Byte: {:?}, Expected bit: {}, Got bit: {}",
3807                    byte_idx, chunk_text, byte as char, is_tab, has_bit
3808                );
3809            }
3810        }
3811    }
3812}
3813
3814#[gpui::test(iterations = 100)]
3815fn test_random_chunk_bitmaps_with_diffs(cx: &mut App, mut rng: StdRng) {
3816    use buffer_diff::BufferDiff;
3817    use util::RandomCharIter;
3818
3819    let multibuffer = if rng.random() {
3820        let len = rng.random_range(100..10000);
3821        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3822        let buffer = cx.new(|cx| Buffer::local(text, cx));
3823        cx.new(|cx| MultiBuffer::singleton(buffer, cx))
3824    } else {
3825        MultiBuffer::build_random(&mut rng, cx)
3826    };
3827
3828    let _diff_count = rng.random_range(1..5);
3829    let mut diffs = Vec::new();
3830
3831    multibuffer.update(cx, |multibuffer, cx| {
3832        for buffer_id in multibuffer.excerpt_buffer_ids() {
3833            if rng.random_bool(0.7) {
3834                if let Some(buffer_handle) = multibuffer.buffer(buffer_id) {
3835                    let buffer_text = buffer_handle.read(cx).text();
3836                    let mut base_text = String::new();
3837
3838                    for line in buffer_text.lines() {
3839                        if rng.random_bool(0.3) {
3840                            continue;
3841                        } else if rng.random_bool(0.3) {
3842                            let line_len = rng.random_range(0..50);
3843                            let modified_line = RandomCharIter::new(&mut rng)
3844                                .take(line_len)
3845                                .collect::<String>();
3846                            base_text.push_str(&modified_line);
3847                            base_text.push('\n');
3848                        } else {
3849                            base_text.push_str(line);
3850                            base_text.push('\n');
3851                        }
3852                    }
3853
3854                    if rng.random_bool(0.5) {
3855                        let extra_lines = rng.random_range(1..5);
3856                        for _ in 0..extra_lines {
3857                            let line_len = rng.random_range(0..50);
3858                            let extra_line = RandomCharIter::new(&mut rng)
3859                                .take(line_len)
3860                                .collect::<String>();
3861                            base_text.push_str(&extra_line);
3862                            base_text.push('\n');
3863                        }
3864                    }
3865
3866                    let diff =
3867                        cx.new(|cx| BufferDiff::new_with_base_text(&base_text, &buffer_handle, cx));
3868                    diffs.push(diff.clone());
3869                    multibuffer.add_diff(diff, cx);
3870                }
3871            }
3872        }
3873    });
3874
3875    multibuffer.update(cx, |multibuffer, cx| {
3876        if rng.random_bool(0.5) {
3877            multibuffer.set_all_diff_hunks_expanded(cx);
3878        } else {
3879            let snapshot = multibuffer.snapshot(cx);
3880            let text = snapshot.text();
3881
3882            let mut ranges = Vec::new();
3883            for _ in 0..rng.random_range(1..5) {
3884                if snapshot.len() == 0 {
3885                    break;
3886                }
3887
3888                let diff_size = rng.random_range(5..1000);
3889                let mut start = rng.random_range(0..snapshot.len());
3890
3891                while !text.is_char_boundary(start) {
3892                    start = start.saturating_sub(1);
3893                }
3894
3895                let mut end = rng.random_range(start..snapshot.len().min(start + diff_size));
3896
3897                while !text.is_char_boundary(end) {
3898                    end = end.saturating_add(1);
3899                }
3900                let start_anchor = snapshot.anchor_after(start);
3901                let end_anchor = snapshot.anchor_before(end);
3902                ranges.push(start_anchor..end_anchor);
3903            }
3904            multibuffer.expand_diff_hunks(ranges, cx);
3905        }
3906    });
3907
3908    let snapshot = multibuffer.read(cx).snapshot(cx);
3909
3910    let chunks = snapshot.chunks(0..snapshot.len(), false);
3911
3912    for chunk in chunks {
3913        let chunk_text = chunk.text;
3914        let chars_bitmap = chunk.chars;
3915        let tabs_bitmap = chunk.tabs;
3916
3917        if chunk_text.is_empty() {
3918            assert_eq!(
3919                chars_bitmap, 0,
3920                "Empty chunk should have empty chars bitmap"
3921            );
3922            assert_eq!(tabs_bitmap, 0, "Empty chunk should have empty tabs bitmap");
3923            continue;
3924        }
3925
3926        assert!(
3927            chunk_text.len() <= 128,
3928            "Chunk text length {} exceeds 128 bytes",
3929            chunk_text.len()
3930        );
3931
3932        let char_indices = chunk_text
3933            .char_indices()
3934            .map(|(i, _)| i)
3935            .collect::<Vec<_>>();
3936
3937        for byte_idx in 0..chunk_text.len() {
3938            let should_have_bit = char_indices.contains(&byte_idx);
3939            let has_bit = chars_bitmap & (1 << byte_idx) != 0;
3940
3941            if has_bit != should_have_bit {
3942                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
3943                eprintln!("Char indices: {:?}", char_indices);
3944                eprintln!("Chars bitmap: {:#b}", chars_bitmap);
3945            }
3946
3947            assert_eq!(
3948                has_bit, should_have_bit,
3949                "Chars bitmap mismatch at byte index {} in chunk {:?}. Expected bit: {}, Got bit: {}",
3950                byte_idx, chunk_text, should_have_bit, has_bit
3951            );
3952        }
3953
3954        for (byte_idx, byte) in chunk_text.bytes().enumerate() {
3955            let is_tab = byte == b'\t';
3956            let has_bit = tabs_bitmap & (1 << byte_idx) != 0;
3957
3958            if has_bit != is_tab {
3959                eprintln!("Chunk text bytes: {:?}", chunk_text.as_bytes());
3960                eprintln!("Tabs bitmap: {:#b}", tabs_bitmap);
3961                assert_eq!(
3962                    has_bit, is_tab,
3963                    "Tabs bitmap mismatch at byte index {} in chunk {:?}. Byte: {:?}, Expected bit: {}, Got bit: {}",
3964                    byte_idx, chunk_text, byte as char, is_tab, has_bit
3965                );
3966            }
3967        }
3968    }
3969}