multi_buffer.rs

   1mod anchor;
   2#[cfg(test)]
   3mod multi_buffer_tests;
   4mod path_key;
   5mod transaction;
   6
   7use self::transaction::History;
   8
   9pub use anchor::{Anchor, AnchorRangeExt};
  10
  11use anchor::{AnchorSeekTarget, ExcerptAnchor};
  12use anyhow::{Result, anyhow};
  13use buffer_diff::{
  14    BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffChanged, DiffHunkSecondaryStatus,
  15    DiffHunkStatus, DiffHunkStatusKind,
  16};
  17use clock::ReplicaId;
  18use collections::{BTreeMap, Bound, HashMap, HashSet};
  19use gpui::{App, Context, Entity, EventEmitter};
  20use itertools::Itertools;
  21use language::{
  22    AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,
  23    CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntryRef, File, IndentGuideSettings,
  24    IndentSize, Language, LanguageAwareStyling, LanguageScope, OffsetRangeExt, OffsetUtf16,
  25    Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _,
  26    ToPoint as _, TransactionId, TreeSitterOptions, Unclipped,
  27    language_settings::{AllLanguageSettings, LanguageSettings},
  28};
  29
  30#[cfg(any(test, feature = "test-support"))]
  31use gpui::AppContext as _;
  32
  33use rope::DimensionPair;
  34use settings::Settings;
  35use smallvec::SmallVec;
  36use smol::future::yield_now;
  37use std::{
  38    any::type_name,
  39    borrow::Cow,
  40    cell::{Cell, OnceCell, Ref, RefCell},
  41    cmp::{self, Ordering},
  42    fmt,
  43    future::Future,
  44    io,
  45    iter::{self, FromIterator},
  46    mem,
  47    ops::{self, Add, AddAssign, ControlFlow, Range, RangeBounds, Sub, SubAssign},
  48    rc::Rc,
  49    str,
  50    sync::{Arc, OnceLock},
  51    time::Duration,
  52};
  53use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, TreeMap};
  54use text::{
  55    BufferId, Edit, LineIndent, TextSummary,
  56    subscription::{Subscription, Topic},
  57};
  58use theme::SyntaxTheme;
  59use unicode_segmentation::UnicodeSegmentation;
  60use ztracing::instrument;
  61
  62pub use self::path_key::PathKey;
  63
  64pub static EXCERPT_CONTEXT_LINES: OnceLock<fn(&App) -> u32> = OnceLock::new();
  65
  66pub fn excerpt_context_lines(cx: &App) -> u32 {
  67    EXCERPT_CONTEXT_LINES.get().map(|f| f(cx)).unwrap_or(2)
  68}
  69
  70/// One or more [`Buffers`](Buffer) being edited in a single view.
  71///
  72/// See <https://zed.dev/features#multi-buffers>
  73pub struct MultiBuffer {
  74    /// A snapshot of the [`Excerpt`]s in the MultiBuffer.
  75    /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot.
  76    snapshot: RefCell<MultiBufferSnapshot>,
  77    /// Contains the state of the buffers being edited
  78    buffers: BTreeMap<BufferId, BufferState>,
  79    /// Mapping from buffer IDs to their diff states
  80    diffs: HashMap<BufferId, DiffState>,
  81    subscriptions: Topic<MultiBufferOffset>,
  82    /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]
  83    singleton: bool,
  84    /// The history of the multi-buffer.
  85    history: History,
  86    /// The explicit title of the multi-buffer.
  87    /// If `None`, it will be derived from the underlying path or content.
  88    title: Option<String>,
  89    /// The writing capability of the multi-buffer.
  90    capability: Capability,
  91    buffer_changed_since_sync: Rc<Cell<bool>>,
  92}
  93
  94#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
  95struct PathKeyIndex(u64);
  96
  97#[derive(Clone, Debug, PartialEq, Eq)]
  98pub enum Event {
  99    BufferRangesUpdated {
 100        buffer: Entity<Buffer>,
 101        path_key: PathKey,
 102        ranges: Vec<ExcerptRange<text::Anchor>>,
 103    },
 104    BuffersRemoved {
 105        removed_buffer_ids: Vec<BufferId>,
 106    },
 107    BuffersEdited {
 108        buffer_ids: Vec<BufferId>,
 109    },
 110    DiffHunksToggled,
 111    Edited {
 112        edited_buffer: Option<Entity<Buffer>>,
 113        is_local: bool,
 114    },
 115    TransactionUndone {
 116        transaction_id: TransactionId,
 117    },
 118    Reloaded,
 119    LanguageChanged(BufferId, bool),
 120    Reparsed(BufferId),
 121    Saved,
 122    FileHandleChanged,
 123    DirtyChanged,
 124    DiagnosticsUpdated,
 125    BufferDiffChanged,
 126}
 127
 128/// A diff hunk, representing a range of consequent lines in a multibuffer.
 129#[derive(Debug, Clone, PartialEq, Eq)]
 130pub struct MultiBufferDiffHunk {
 131    /// The row range in the multibuffer where this diff hunk appears.
 132    pub row_range: Range<MultiBufferRow>,
 133    /// The buffer ID that this hunk belongs to.
 134    pub buffer_id: BufferId,
 135    /// The range of the underlying buffer that this hunk corresponds to.
 136    pub buffer_range: Range<text::Anchor>,
 137    /// The range within the buffer's diff base that this hunk corresponds to.
 138    pub diff_base_byte_range: Range<BufferOffset>,
 139    /// The status of this hunk (added/modified/deleted and secondary status).
 140    pub status: DiffHunkStatus,
 141    /// The word diffs for this hunk.
 142    pub word_diffs: Vec<Range<MultiBufferOffset>>,
 143    pub excerpt_range: ExcerptRange<text::Anchor>,
 144    pub multi_buffer_range: Range<Anchor>,
 145}
 146
 147impl MultiBufferDiffHunk {
 148    pub fn status(&self) -> DiffHunkStatus {
 149        self.status
 150    }
 151
 152    pub fn is_created_file(&self) -> bool {
 153        self.diff_base_byte_range == (BufferOffset(0)..BufferOffset(0))
 154            && self.buffer_range.start.is_min()
 155            && self.buffer_range.end.is_max()
 156    }
 157}
 158
 159pub type MultiBufferPoint = Point;
 160/// ExcerptOffset is offset into the non-deleted text of the multibuffer
 161type ExcerptOffset = ExcerptDimension<MultiBufferOffset>;
 162/// ExcerptOffset is based on the non-deleted text of the multibuffer
 163
 164#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
 165#[serde(transparent)]
 166pub struct MultiBufferRow(pub u32);
 167
 168impl MultiBufferRow {
 169    pub const MIN: Self = Self(0);
 170    pub const MAX: Self = Self(u32::MAX);
 171}
 172
 173impl ops::Add<usize> for MultiBufferRow {
 174    type Output = Self;
 175
 176    fn add(self, rhs: usize) -> Self::Output {
 177        MultiBufferRow(self.0 + rhs as u32)
 178    }
 179}
 180
 181pub trait MultiBufferDimension: 'static + Copy + Default + std::fmt::Debug {
 182    type TextDimension: TextDimension;
 183    fn from_summary(summary: &MBTextSummary) -> Self;
 184
 185    fn add_text_dim(&mut self, summary: &Self::TextDimension);
 186
 187    fn add_mb_text_summary(&mut self, summary: &MBTextSummary);
 188}
 189
 190// todo(lw): MultiBufferPoint
 191impl MultiBufferDimension for Point {
 192    type TextDimension = Point;
 193    fn from_summary(summary: &MBTextSummary) -> Self {
 194        summary.lines
 195    }
 196
 197    fn add_text_dim(&mut self, other: &Self::TextDimension) {
 198        *self += *other;
 199    }
 200
 201    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
 202        *self += summary.lines;
 203    }
 204}
 205
 206// todo(lw): MultiBufferPointUtf16
 207impl MultiBufferDimension for PointUtf16 {
 208    type TextDimension = PointUtf16;
 209    fn from_summary(summary: &MBTextSummary) -> Self {
 210        summary.lines_utf16()
 211    }
 212
 213    fn add_text_dim(&mut self, other: &Self::TextDimension) {
 214        *self += *other;
 215    }
 216
 217    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
 218        *self += summary.lines_utf16();
 219    }
 220}
 221
 222#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
 223pub struct MultiBufferOffset(pub usize);
 224
 225impl fmt::Display for MultiBufferOffset {
 226    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 227        write!(f, "{}", self.0)
 228    }
 229}
 230
 231impl rand::distr::uniform::SampleUniform for MultiBufferOffset {
 232    type Sampler = MultiBufferOffsetUniformSampler;
 233}
 234
 235pub struct MultiBufferOffsetUniformSampler {
 236    sampler: rand::distr::uniform::UniformUsize,
 237}
 238
 239impl rand::distr::uniform::UniformSampler for MultiBufferOffsetUniformSampler {
 240    type X = MultiBufferOffset;
 241
 242    fn new<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, rand::distr::uniform::Error>
 243    where
 244        B1: rand::distr::uniform::SampleBorrow<Self::X> + Sized,
 245        B2: rand::distr::uniform::SampleBorrow<Self::X> + Sized,
 246    {
 247        let low = *low_b.borrow();
 248        let high = *high_b.borrow();
 249        let sampler = rand::distr::uniform::UniformUsize::new(low.0, high.0);
 250        sampler.map(|sampler| MultiBufferOffsetUniformSampler { sampler })
 251    }
 252
 253    #[inline] // if the range is constant, this helps LLVM to do the
 254    // calculations at compile-time.
 255    fn new_inclusive<B1, B2>(low_b: B1, high_b: B2) -> Result<Self, rand::distr::uniform::Error>
 256    where
 257        B1: rand::distr::uniform::SampleBorrow<Self::X> + Sized,
 258        B2: rand::distr::uniform::SampleBorrow<Self::X> + Sized,
 259    {
 260        let low = *low_b.borrow();
 261        let high = *high_b.borrow();
 262        let sampler = rand::distr::uniform::UniformUsize::new_inclusive(low.0, high.0);
 263        sampler.map(|sampler| MultiBufferOffsetUniformSampler { sampler })
 264    }
 265
 266    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
 267        MultiBufferOffset(self.sampler.sample(rng))
 268    }
 269}
 270impl MultiBufferDimension for MultiBufferOffset {
 271    type TextDimension = usize;
 272    fn from_summary(summary: &MBTextSummary) -> Self {
 273        summary.len
 274    }
 275
 276    fn add_text_dim(&mut self, other: &Self::TextDimension) {
 277        self.0 += *other;
 278    }
 279
 280    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
 281        *self += summary.len;
 282    }
 283}
 284impl MultiBufferDimension for MultiBufferOffsetUtf16 {
 285    type TextDimension = OffsetUtf16;
 286    fn from_summary(summary: &MBTextSummary) -> Self {
 287        MultiBufferOffsetUtf16(summary.len_utf16)
 288    }
 289
 290    fn add_text_dim(&mut self, other: &Self::TextDimension) {
 291        self.0 += *other;
 292    }
 293
 294    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
 295        self.0 += summary.len_utf16;
 296    }
 297}
 298
 299#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
 300pub struct BufferOffset(pub usize);
 301
 302impl TextDimension for BufferOffset {
 303    fn from_text_summary(summary: &TextSummary) -> Self {
 304        BufferOffset(usize::from_text_summary(summary))
 305    }
 306    fn from_chunk(chunk: rope::ChunkSlice) -> Self {
 307        BufferOffset(usize::from_chunk(chunk))
 308    }
 309    fn add_assign(&mut self, other: &Self) {
 310        TextDimension::add_assign(&mut self.0, &other.0);
 311    }
 312}
 313impl<'a> sum_tree::Dimension<'a, rope::ChunkSummary> for BufferOffset {
 314    fn zero(cx: ()) -> Self {
 315        BufferOffset(<usize as sum_tree::Dimension<'a, rope::ChunkSummary>>::zero(cx))
 316    }
 317
 318    fn add_summary(&mut self, summary: &'a rope::ChunkSummary, cx: ()) {
 319        usize::add_summary(&mut self.0, summary, cx);
 320    }
 321}
 322
 323impl Sub for BufferOffset {
 324    type Output = usize;
 325
 326    fn sub(self, other: BufferOffset) -> Self::Output {
 327        self.0 - other.0
 328    }
 329}
 330
 331impl AddAssign<DimensionPair<usize, Point>> for BufferOffset {
 332    fn add_assign(&mut self, other: DimensionPair<usize, Point>) {
 333        self.0 += other.key;
 334    }
 335}
 336
 337impl language::ToPoint for BufferOffset {
 338    fn to_point(&self, snapshot: &text::BufferSnapshot) -> Point {
 339        self.0.to_point(snapshot)
 340    }
 341}
 342
 343impl language::ToPointUtf16 for BufferOffset {
 344    fn to_point_utf16(&self, snapshot: &text::BufferSnapshot) -> PointUtf16 {
 345        self.0.to_point_utf16(snapshot)
 346    }
 347}
 348
 349impl language::ToOffset for BufferOffset {
 350    fn to_offset(&self, snapshot: &text::BufferSnapshot) -> usize {
 351        self.0.to_offset(snapshot)
 352    }
 353}
 354
 355impl language::ToOffsetUtf16 for BufferOffset {
 356    fn to_offset_utf16(&self, snapshot: &text::BufferSnapshot) -> OffsetUtf16 {
 357        self.0.to_offset_utf16(snapshot)
 358    }
 359}
 360
 361#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 362pub struct MultiBufferOffsetUtf16(pub OffsetUtf16);
 363
 364impl ops::Add<usize> for MultiBufferOffsetUtf16 {
 365    type Output = MultiBufferOffsetUtf16;
 366
 367    fn add(self, rhs: usize) -> Self::Output {
 368        MultiBufferOffsetUtf16(OffsetUtf16(self.0.0 + rhs))
 369    }
 370}
 371
 372impl ops::Add<OffsetUtf16> for MultiBufferOffsetUtf16 {
 373    type Output = Self;
 374
 375    fn add(self, rhs: OffsetUtf16) -> Self::Output {
 376        MultiBufferOffsetUtf16(self.0 + rhs)
 377    }
 378}
 379
 380impl AddAssign<OffsetUtf16> for MultiBufferOffsetUtf16 {
 381    fn add_assign(&mut self, rhs: OffsetUtf16) {
 382        self.0 += rhs;
 383    }
 384}
 385
 386impl AddAssign<usize> for MultiBufferOffsetUtf16 {
 387    fn add_assign(&mut self, rhs: usize) {
 388        self.0.0 += rhs;
 389    }
 390}
 391
 392impl Sub for MultiBufferOffsetUtf16 {
 393    type Output = OffsetUtf16;
 394
 395    fn sub(self, other: MultiBufferOffsetUtf16) -> Self::Output {
 396        self.0 - other.0
 397    }
 398}
 399
 400impl Sub<OffsetUtf16> for MultiBufferOffsetUtf16 {
 401    type Output = MultiBufferOffsetUtf16;
 402
 403    fn sub(self, other: OffsetUtf16) -> Self::Output {
 404        MultiBufferOffsetUtf16(self.0 - other)
 405    }
 406}
 407
 408#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 409pub struct BufferOffsetUtf16(pub OffsetUtf16);
 410
 411impl MultiBufferOffset {
 412    const ZERO: Self = Self(0);
 413    pub fn saturating_sub(self, other: MultiBufferOffset) -> usize {
 414        self.0.saturating_sub(other.0)
 415    }
 416    pub fn saturating_sub_usize(self, other: usize) -> MultiBufferOffset {
 417        MultiBufferOffset(self.0.saturating_sub(other))
 418    }
 419}
 420
 421impl ops::Sub for MultiBufferOffset {
 422    type Output = usize;
 423
 424    fn sub(self, other: MultiBufferOffset) -> Self::Output {
 425        self.0 - other.0
 426    }
 427}
 428
 429impl ops::Sub<usize> for MultiBufferOffset {
 430    type Output = Self;
 431
 432    fn sub(self, other: usize) -> Self::Output {
 433        MultiBufferOffset(self.0 - other)
 434    }
 435}
 436
 437impl ops::SubAssign<usize> for MultiBufferOffset {
 438    fn sub_assign(&mut self, other: usize) {
 439        self.0 -= other;
 440    }
 441}
 442
 443impl ops::Add<usize> for BufferOffset {
 444    type Output = Self;
 445
 446    fn add(self, rhs: usize) -> Self::Output {
 447        BufferOffset(self.0 + rhs)
 448    }
 449}
 450
 451impl ops::AddAssign<usize> for BufferOffset {
 452    fn add_assign(&mut self, other: usize) {
 453        self.0 += other;
 454    }
 455}
 456
 457impl ops::Add<usize> for MultiBufferOffset {
 458    type Output = Self;
 459
 460    fn add(self, rhs: usize) -> Self::Output {
 461        MultiBufferOffset(self.0 + rhs)
 462    }
 463}
 464
 465impl ops::AddAssign<usize> for MultiBufferOffset {
 466    fn add_assign(&mut self, other: usize) {
 467        self.0 += other;
 468    }
 469}
 470
 471impl ops::Add<isize> for MultiBufferOffset {
 472    type Output = Self;
 473
 474    fn add(self, rhs: isize) -> Self::Output {
 475        MultiBufferOffset((self.0 as isize + rhs) as usize)
 476    }
 477}
 478
 479impl ops::Add for MultiBufferOffset {
 480    type Output = Self;
 481
 482    fn add(self, rhs: MultiBufferOffset) -> Self::Output {
 483        MultiBufferOffset(self.0 + rhs.0)
 484    }
 485}
 486
 487impl ops::AddAssign<MultiBufferOffset> for MultiBufferOffset {
 488    fn add_assign(&mut self, other: MultiBufferOffset) {
 489        self.0 += other.0;
 490    }
 491}
 492
 493pub trait ToOffset: 'static + fmt::Debug {
 494    fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset;
 495    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16;
 496}
 497
 498pub trait ToPoint: 'static + fmt::Debug {
 499    fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
 500    fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;
 501}
 502
 503struct BufferState {
 504    buffer: Entity<Buffer>,
 505    _subscriptions: [gpui::Subscription; 2],
 506}
 507
 508struct DiffState {
 509    diff: Entity<BufferDiff>,
 510    main_buffer: Option<Entity<language::Buffer>>,
 511    _subscription: gpui::Subscription,
 512}
 513
 514impl DiffState {
 515    fn snapshot(&self, buffer_id: BufferId, cx: &App) -> DiffStateSnapshot {
 516        DiffStateSnapshot {
 517            buffer_id,
 518            diff: self.diff.read(cx).snapshot(cx),
 519            main_buffer: self.main_buffer.as_ref().map(|b| b.read(cx).snapshot()),
 520        }
 521    }
 522}
 523
 524#[derive(Clone)]
 525struct DiffStateSnapshot {
 526    buffer_id: BufferId,
 527    diff: BufferDiffSnapshot,
 528    main_buffer: Option<language::BufferSnapshot>,
 529}
 530
 531impl std::ops::Deref for DiffStateSnapshot {
 532    type Target = BufferDiffSnapshot;
 533
 534    fn deref(&self) -> &Self::Target {
 535        &self.diff
 536    }
 537}
 538
 539#[derive(Clone, Debug, Default)]
 540struct DiffStateSummary {
 541    max_buffer_id: Option<BufferId>,
 542    added_rows: u32,
 543    removed_rows: u32,
 544}
 545
 546impl sum_tree::ContextLessSummary for DiffStateSummary {
 547    fn zero() -> Self {
 548        Self::default()
 549    }
 550
 551    fn add_summary(&mut self, other: &Self) {
 552        self.max_buffer_id = std::cmp::max(self.max_buffer_id, other.max_buffer_id);
 553        self.added_rows += other.added_rows;
 554        self.removed_rows += other.removed_rows;
 555    }
 556}
 557
 558impl sum_tree::Item for DiffStateSnapshot {
 559    type Summary = DiffStateSummary;
 560
 561    fn summary(&self, _cx: ()) -> DiffStateSummary {
 562        let (added_rows, removed_rows) = self.diff.changed_row_counts();
 563        DiffStateSummary {
 564            max_buffer_id: Some(self.buffer_id),
 565            added_rows,
 566            removed_rows,
 567        }
 568    }
 569}
 570
 571impl sum_tree::KeyedItem for DiffStateSnapshot {
 572    type Key = Option<BufferId>;
 573
 574    fn key(&self) -> Option<BufferId> {
 575        Some(self.buffer_id)
 576    }
 577}
 578
 579impl<'a> Dimension<'a, DiffStateSummary> for Option<BufferId> {
 580    fn zero(_cx: ()) -> Self {
 581        None
 582    }
 583
 584    fn add_summary(&mut self, summary: &DiffStateSummary, _cx: ()) {
 585        *self = std::cmp::max(*self, summary.max_buffer_id);
 586    }
 587}
 588
 589fn find_diff_state(
 590    diffs: &SumTree<DiffStateSnapshot>,
 591    buffer_id: BufferId,
 592) -> Option<&DiffStateSnapshot> {
 593    let key = Some(buffer_id);
 594    let (.., item) = diffs.find::<Option<BufferId>, _>((), &key, Bias::Left);
 595    item.filter(|entry| entry.buffer_id == buffer_id)
 596}
 597
 598fn remove_diff_state(diffs: &mut SumTree<DiffStateSnapshot>, buffer_id: BufferId) {
 599    let key = Some(buffer_id);
 600    let mut cursor = diffs.cursor::<Option<BufferId>>(());
 601    let mut new_tree = cursor.slice(&key, Bias::Left);
 602    if key == cursor.end() {
 603        cursor.next();
 604    }
 605    new_tree.append(cursor.suffix(), ());
 606    drop(cursor);
 607    *diffs = new_tree;
 608}
 609
 610impl DiffState {
 611    fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self {
 612        DiffState {
 613            _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {
 614                BufferDiffEvent::DiffChanged(DiffChanged {
 615                    changed_range,
 616                    base_text_changed_range: _,
 617                    extended_range,
 618                }) => {
 619                    let use_extended = this.snapshot.borrow().use_extended_diff_range;
 620                    let range = if use_extended {
 621                        extended_range.clone()
 622                    } else {
 623                        changed_range.clone()
 624                    };
 625                    if let Some(range) = range {
 626                        this.buffer_diff_changed(diff, range, cx)
 627                    }
 628                    cx.emit(Event::BufferDiffChanged);
 629                }
 630                BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
 631                _ => {}
 632            }),
 633            diff,
 634            main_buffer: None,
 635        }
 636    }
 637
 638    fn new_inverted(
 639        diff: Entity<BufferDiff>,
 640        main_buffer: Entity<language::Buffer>,
 641        cx: &mut Context<MultiBuffer>,
 642    ) -> Self {
 643        let weak_main_buffer = main_buffer.downgrade();
 644        DiffState {
 645            _subscription: cx.subscribe(&diff, {
 646                move |this, diff, event, cx| {
 647                    let Some(main_buffer) = weak_main_buffer.upgrade() else {
 648                        return;
 649                    };
 650                    match event {
 651                        BufferDiffEvent::DiffChanged(DiffChanged {
 652                            changed_range: _,
 653                            base_text_changed_range,
 654                            extended_range: _,
 655                        }) => {
 656                            this.inverted_buffer_diff_changed(
 657                                diff,
 658                                main_buffer,
 659                                base_text_changed_range.clone(),
 660                                cx,
 661                            );
 662                            cx.emit(Event::BufferDiffChanged);
 663                        }
 664                        BufferDiffEvent::LanguageChanged => {
 665                            this.inverted_buffer_diff_language_changed(diff, main_buffer, cx)
 666                        }
 667                        _ => {}
 668                    }
 669                }
 670            }),
 671            diff,
 672            main_buffer: Some(main_buffer),
 673        }
 674    }
 675}
 676
 677#[derive(Clone)]
 678struct BufferStateSnapshot {
 679    path_key: PathKey,
 680    path_key_index: PathKeyIndex,
 681    buffer_snapshot: BufferSnapshot,
 682}
 683
 684impl fmt::Debug for BufferStateSnapshot {
 685    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 686        f.debug_struct("BufferStateSnapshot")
 687            .field("path_key", &self.path_key)
 688            .field("buffer_id", &self.buffer_snapshot.remote_id())
 689            .finish()
 690    }
 691}
 692
 693/// The contents of a [`MultiBuffer`] at a single point in time.
 694#[derive(Clone, Default)]
 695pub struct MultiBufferSnapshot {
 696    excerpts: SumTree<Excerpt>,
 697    buffers: TreeMap<BufferId, BufferStateSnapshot>,
 698    path_keys_by_index: TreeMap<PathKeyIndex, PathKey>,
 699    indices_by_path_key: TreeMap<PathKey, PathKeyIndex>,
 700    diffs: SumTree<DiffStateSnapshot>,
 701    diff_transforms: SumTree<DiffTransform>,
 702    non_text_state_update_count: usize,
 703    edit_count: usize,
 704    is_dirty: bool,
 705    has_deleted_file: bool,
 706    has_conflict: bool,
 707    has_inverted_diff: bool,
 708    singleton: bool,
 709    trailing_excerpt_update_count: usize,
 710    all_diff_hunks_expanded: bool,
 711    show_deleted_hunks: bool,
 712    use_extended_diff_range: bool,
 713    show_headers: bool,
 714}
 715
 716#[derive(Debug, Clone)]
 717enum DiffTransform {
 718    BufferContent {
 719        summary: MBTextSummary,
 720        inserted_hunk_info: Option<DiffTransformHunkInfo>,
 721    },
 722    DeletedHunk {
 723        summary: TextSummary,
 724        buffer_id: BufferId,
 725        hunk_info: DiffTransformHunkInfo,
 726        base_text_byte_range: Range<usize>,
 727        has_trailing_newline: bool,
 728    },
 729}
 730
 731#[derive(Clone, Copy, Debug)]
 732struct DiffTransformHunkInfo {
 733    buffer_id: BufferId,
 734    hunk_start_anchor: text::Anchor,
 735    hunk_secondary_status: DiffHunkSecondaryStatus,
 736    is_logically_deleted: bool,
 737    excerpt_end: ExcerptAnchor,
 738}
 739
 740impl Eq for DiffTransformHunkInfo {}
 741
 742impl PartialEq for DiffTransformHunkInfo {
 743    fn eq(&self, other: &DiffTransformHunkInfo) -> bool {
 744        self.buffer_id == other.buffer_id && self.hunk_start_anchor == other.hunk_start_anchor
 745    }
 746}
 747
 748impl std::hash::Hash for DiffTransformHunkInfo {
 749    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
 750        self.buffer_id.hash(state);
 751        self.hunk_start_anchor.hash(state);
 752    }
 753}
 754
 755#[derive(Clone)]
 756pub struct ExcerptBoundaryInfo {
 757    pub start_anchor: Anchor,
 758    pub range: ExcerptRange<text::Anchor>,
 759    pub end_row: MultiBufferRow,
 760}
 761
 762impl ExcerptBoundaryInfo {
 763    pub fn start_text_anchor(&self) -> text::Anchor {
 764        self.range.context.start
 765    }
 766    pub fn buffer_id(&self) -> BufferId {
 767        self.start_text_anchor().buffer_id
 768    }
 769    pub fn buffer<'a>(&self, snapshot: &'a MultiBufferSnapshot) -> &'a BufferSnapshot {
 770        snapshot
 771            .buffer_for_id(self.buffer_id())
 772            .expect("buffer snapshot not found for excerpt boundary")
 773    }
 774}
 775
 776impl std::fmt::Debug for ExcerptBoundaryInfo {
 777    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 778        f.debug_struct(type_name::<Self>())
 779            .field("buffer_id", &self.buffer_id())
 780            .field("range", &self.range)
 781            .finish()
 782    }
 783}
 784
 785impl PartialEq for ExcerptBoundaryInfo {
 786    fn eq(&self, other: &Self) -> bool {
 787        self.start_anchor == other.start_anchor && self.range == other.range
 788    }
 789}
 790
 791impl Eq for ExcerptBoundaryInfo {}
 792
 793/// A boundary between `Excerpt`s in a [`MultiBuffer`]
 794#[derive(Debug)]
 795pub struct ExcerptBoundary {
 796    pub prev: Option<ExcerptBoundaryInfo>,
 797    pub next: ExcerptBoundaryInfo,
 798    /// The row in the `MultiBuffer` where the boundary is located
 799    pub row: MultiBufferRow,
 800}
 801
 802impl ExcerptBoundary {
 803    pub fn starts_new_buffer(&self) -> bool {
 804        match (self.prev.as_ref(), &self.next) {
 805            (None, _) => true,
 806            (Some(prev), next) => prev.buffer_id() != next.buffer_id(),
 807        }
 808    }
 809}
 810
 811#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 812pub struct ExpandInfo {
 813    pub direction: ExpandExcerptDirection,
 814    pub start_anchor: Anchor,
 815}
 816
 817#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
 818pub struct RowInfo {
 819    pub buffer_id: Option<BufferId>,
 820    pub buffer_row: Option<u32>,
 821    pub multibuffer_row: Option<MultiBufferRow>,
 822    pub diff_status: Option<buffer_diff::DiffHunkStatus>,
 823    pub expand_info: Option<ExpandInfo>,
 824    pub wrapped_buffer_row: Option<u32>,
 825}
 826
 827/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
 828#[derive(Clone, Debug)]
 829pub(crate) struct Excerpt {
 830    /// The location of the excerpt in the [`MultiBuffer`]
 831    pub(crate) path_key: PathKey,
 832    pub(crate) path_key_index: PathKeyIndex,
 833    pub(crate) buffer_id: BufferId,
 834    /// The range of the buffer to be shown in the excerpt
 835    pub(crate) range: ExcerptRange<text::Anchor>,
 836
 837    /// The last row in the excerpted slice of the buffer
 838    pub(crate) max_buffer_row: BufferRow,
 839    /// A summary of the text in the excerpt
 840    pub(crate) text_summary: TextSummary,
 841    pub(crate) has_trailing_newline: bool,
 842}
 843
 844/// A range of text from a single [`Buffer`], to be shown as an `Excerpt`.
 845/// These ranges are relative to the buffer itself
 846#[derive(Clone, Debug, Eq, PartialEq, Hash)]
 847pub struct ExcerptRange<T> {
 848    /// The full range of text to be shown in the excerpt.
 849    pub context: Range<T>,
 850    /// The primary range of text to be highlighted in the excerpt.
 851    /// In a multi-buffer search, this would be the text that matched the search
 852    pub primary: Range<T>,
 853}
 854
 855impl<T: Clone> ExcerptRange<T> {
 856    pub fn new(context: Range<T>) -> Self {
 857        Self {
 858            context: context.clone(),
 859            primary: context,
 860        }
 861    }
 862}
 863
 864impl ExcerptRange<text::Anchor> {
 865    pub fn contains(&self, t: &text::Anchor, snapshot: &BufferSnapshot) -> bool {
 866        self.context.start.cmp(t, snapshot).is_le() && self.context.end.cmp(t, snapshot).is_ge()
 867    }
 868}
 869
 870#[derive(Clone, Debug)]
 871pub struct ExcerptSummary {
 872    path_key: PathKey,
 873    path_key_index: Option<PathKeyIndex>,
 874    max_anchor: Option<text::Anchor>,
 875    widest_line_number: u32,
 876    text: MBTextSummary,
 877    count: usize,
 878}
 879
 880impl ExcerptSummary {
 881    pub fn min() -> Self {
 882        ExcerptSummary {
 883            path_key: PathKey::min(),
 884            path_key_index: None,
 885            max_anchor: None,
 886            widest_line_number: 0,
 887            text: MBTextSummary::default(),
 888            count: 0,
 889        }
 890    }
 891
 892    fn len(&self) -> ExcerptOffset {
 893        ExcerptDimension(self.text.len)
 894    }
 895}
 896
 897#[derive(Debug, Clone)]
 898pub struct DiffTransformSummary {
 899    input: MBTextSummary,
 900    output: MBTextSummary,
 901}
 902
 903/// Summary of a string of text.
 904#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
 905pub struct MBTextSummary {
 906    /// Length in bytes.
 907    pub len: MultiBufferOffset,
 908    /// Length in UTF-8.
 909    pub chars: usize,
 910    /// Length in UTF-16 code units
 911    pub len_utf16: OffsetUtf16,
 912    /// A point representing the number of lines and the length of the last line.
 913    ///
 914    /// In other words, it marks the point after the last byte in the text, (if
 915    /// EOF was a character, this would be its position).
 916    pub lines: Point,
 917    /// How many `char`s are in the first line
 918    pub first_line_chars: u32,
 919    /// How many `char`s are in the last line
 920    pub last_line_chars: u32,
 921    /// How many UTF-16 code units are in the last line
 922    pub last_line_len_utf16: u32,
 923    /// The row idx of the longest row
 924    pub longest_row: u32,
 925    /// How many `char`s are in the longest row
 926    pub longest_row_chars: u32,
 927}
 928
 929impl From<TextSummary> for MBTextSummary {
 930    fn from(summary: TextSummary) -> Self {
 931        MBTextSummary {
 932            len: MultiBufferOffset(summary.len),
 933            chars: summary.chars,
 934            len_utf16: summary.len_utf16,
 935            lines: summary.lines,
 936            first_line_chars: summary.first_line_chars,
 937            last_line_chars: summary.last_line_chars,
 938            last_line_len_utf16: summary.last_line_len_utf16,
 939            longest_row: summary.longest_row,
 940            longest_row_chars: summary.longest_row_chars,
 941        }
 942    }
 943}
 944
 945impl From<MBTextSummary> for TextSummary {
 946    fn from(summary: MBTextSummary) -> Self {
 947        TextSummary {
 948            len: summary.len.0,
 949            chars: summary.chars,
 950            len_utf16: summary.len_utf16,
 951            lines: summary.lines,
 952            first_line_chars: summary.first_line_chars,
 953            last_line_chars: summary.last_line_chars,
 954            last_line_len_utf16: summary.last_line_len_utf16,
 955            longest_row: summary.longest_row,
 956            longest_row_chars: summary.longest_row_chars,
 957        }
 958    }
 959}
 960
 961impl From<&str> for MBTextSummary {
 962    fn from(text: &str) -> Self {
 963        MBTextSummary::from(TextSummary::from(text))
 964    }
 965}
 966
 967impl MultiBufferDimension for MBTextSummary {
 968    type TextDimension = TextSummary;
 969
 970    fn from_summary(summary: &MBTextSummary) -> Self {
 971        *summary
 972    }
 973
 974    fn add_text_dim(&mut self, summary: &Self::TextDimension) {
 975        *self += *summary;
 976    }
 977
 978    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
 979        *self += *summary;
 980    }
 981}
 982
 983impl AddAssign for MBTextSummary {
 984    fn add_assign(&mut self, other: MBTextSummary) {
 985        let joined_chars = self.last_line_chars + other.first_line_chars;
 986        if joined_chars > self.longest_row_chars {
 987            self.longest_row = self.lines.row;
 988            self.longest_row_chars = joined_chars;
 989        }
 990        if other.longest_row_chars > self.longest_row_chars {
 991            self.longest_row = self.lines.row + other.longest_row;
 992            self.longest_row_chars = other.longest_row_chars;
 993        }
 994
 995        if self.lines.row == 0 {
 996            self.first_line_chars += other.first_line_chars;
 997        }
 998
 999        if other.lines.row == 0 {
1000            self.last_line_chars += other.first_line_chars;
1001            self.last_line_len_utf16 += other.last_line_len_utf16;
1002        } else {
1003            self.last_line_chars = other.last_line_chars;
1004            self.last_line_len_utf16 = other.last_line_len_utf16;
1005        }
1006
1007        self.chars += other.chars;
1008        self.len += other.len;
1009        self.len_utf16 += other.len_utf16;
1010        self.lines += other.lines;
1011    }
1012}
1013
1014impl AddAssign<TextSummary> for MBTextSummary {
1015    fn add_assign(&mut self, other: TextSummary) {
1016        *self += MBTextSummary::from(other);
1017    }
1018}
1019
1020impl MBTextSummary {
1021    pub fn lines_utf16(&self) -> PointUtf16 {
1022        PointUtf16 {
1023            row: self.lines.row,
1024            column: self.last_line_len_utf16,
1025        }
1026    }
1027}
1028
1029impl<K, V> MultiBufferDimension for DimensionPair<K, V>
1030where
1031    K: MultiBufferDimension,
1032    V: MultiBufferDimension,
1033{
1034    type TextDimension = DimensionPair<K::TextDimension, V::TextDimension>;
1035
1036    fn from_summary(summary: &MBTextSummary) -> Self {
1037        Self {
1038            key: K::from_summary(summary),
1039            value: Some(V::from_summary(summary)),
1040        }
1041    }
1042
1043    fn add_text_dim(&mut self, summary: &Self::TextDimension) {
1044        self.key.add_text_dim(&summary.key);
1045        if let Some(value) = &mut self.value {
1046            if let Some(other_value) = summary.value.as_ref() {
1047                value.add_text_dim(other_value);
1048            }
1049        }
1050    }
1051
1052    fn add_mb_text_summary(&mut self, summary: &MBTextSummary) {
1053        self.key.add_mb_text_summary(summary);
1054        if let Some(value) = &mut self.value {
1055            value.add_mb_text_summary(summary);
1056        }
1057    }
1058}
1059
1060#[derive(Clone)]
1061pub struct MultiBufferRows<'a> {
1062    point: Point,
1063    is_empty: bool,
1064    is_singleton: bool,
1065    cursor: MultiBufferCursor<'a, Point, Point>,
1066}
1067
1068pub struct MultiBufferChunks<'a> {
1069    excerpts: Cursor<'a, 'static, Excerpt, ExcerptOffset>,
1070    diff_transforms:
1071        Cursor<'a, 'static, DiffTransform, Dimensions<MultiBufferOffset, ExcerptOffset>>,
1072    diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,
1073    buffer_chunk: Option<Chunk<'a>>,
1074    range: Range<MultiBufferOffset>,
1075    excerpt_offset_range: Range<ExcerptOffset>,
1076    excerpt_chunks: Option<ExcerptChunks<'a>>,
1077    language_aware: LanguageAwareStyling,
1078    snapshot: &'a MultiBufferSnapshot,
1079}
1080
1081pub struct ReversedMultiBufferChunks<'a> {
1082    cursor: MultiBufferCursor<'a, MultiBufferOffset, BufferOffset>,
1083    current_chunks: Option<rope::Chunks<'a>>,
1084    start: MultiBufferOffset,
1085    offset: MultiBufferOffset,
1086}
1087
1088pub struct MultiBufferBytes<'a> {
1089    range: Range<MultiBufferOffset>,
1090    cursor: MultiBufferCursor<'a, MultiBufferOffset, BufferOffset>,
1091    excerpt_bytes: Option<text::Bytes<'a>>,
1092    has_trailing_newline: bool,
1093    chunk: &'a [u8],
1094}
1095
1096pub struct ReversedMultiBufferBytes<'a> {
1097    range: Range<MultiBufferOffset>,
1098    chunks: ReversedMultiBufferChunks<'a>,
1099    chunk: &'a [u8],
1100}
1101
1102#[derive(Clone)]
1103struct DiffTransforms<MBD> {
1104    output_dimension: OutputDimension<MBD>,
1105    excerpt_dimension: ExcerptDimension<MBD>,
1106}
1107
1108impl<'a, MBD: MultiBufferDimension> Dimension<'a, DiffTransformSummary> for DiffTransforms<MBD> {
1109    fn zero(cx: <DiffTransformSummary as sum_tree::Summary>::Context<'_>) -> Self {
1110        Self {
1111            output_dimension: OutputDimension::zero(cx),
1112            excerpt_dimension: <ExcerptDimension<MBD> as Dimension<'a, DiffTransformSummary>>::zero(
1113                cx,
1114            ),
1115        }
1116    }
1117
1118    fn add_summary(
1119        &mut self,
1120        summary: &'a DiffTransformSummary,
1121        cx: <DiffTransformSummary as sum_tree::Summary>::Context<'_>,
1122    ) {
1123        self.output_dimension.add_summary(summary, cx);
1124        self.excerpt_dimension.add_summary(summary, cx);
1125    }
1126}
1127
1128#[derive(Clone)]
1129struct MultiBufferCursor<'a, MBD, BD> {
1130    excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension<MBD>>,
1131    diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms<MBD>>,
1132    cached_region: OnceCell<Option<MultiBufferRegion<'a, MBD, BD>>>,
1133    snapshot: &'a MultiBufferSnapshot,
1134}
1135
1136#[derive(Clone)]
1137struct MultiBufferRegion<'a, MBD, BD> {
1138    buffer: &'a BufferSnapshot,
1139    is_main_buffer: bool,
1140    diff_hunk_status: Option<DiffHunkStatus>,
1141    excerpt: &'a Excerpt,
1142    buffer_range: Range<BD>,
1143    range: Range<MBD>,
1144    has_trailing_newline: bool,
1145}
1146
1147struct ExcerptChunks<'a> {
1148    content_chunks: BufferChunks<'a>,
1149    end: ExcerptAnchor,
1150    has_footer: bool,
1151}
1152
1153#[derive(Debug)]
1154struct BufferEdit {
1155    range: Range<BufferOffset>,
1156    new_text: Arc<str>,
1157    is_insertion: bool,
1158    original_indent_column: Option<u32>,
1159}
1160
1161#[derive(Clone, Copy, Debug, PartialEq)]
1162enum DiffChangeKind {
1163    BufferEdited,
1164    DiffUpdated { base_changed: bool },
1165    ExpandOrCollapseHunks { expand: bool },
1166}
1167
1168#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1169pub enum ExpandExcerptDirection {
1170    Up,
1171    Down,
1172    UpAndDown,
1173}
1174
1175impl ExpandExcerptDirection {
1176    pub fn should_expand_up(&self) -> bool {
1177        match self {
1178            ExpandExcerptDirection::Up => true,
1179            ExpandExcerptDirection::Down => false,
1180            ExpandExcerptDirection::UpAndDown => true,
1181        }
1182    }
1183
1184    pub fn should_expand_down(&self) -> bool {
1185        match self {
1186            ExpandExcerptDirection::Up => false,
1187            ExpandExcerptDirection::Down => true,
1188            ExpandExcerptDirection::UpAndDown => true,
1189        }
1190    }
1191}
1192
1193#[derive(Clone, Debug, PartialEq)]
1194pub struct IndentGuide {
1195    pub buffer_id: BufferId,
1196    pub start_row: MultiBufferRow,
1197    pub end_row: MultiBufferRow,
1198    pub depth: u32,
1199    pub tab_size: u32,
1200    pub settings: IndentGuideSettings,
1201}
1202
1203impl IndentGuide {
1204    pub fn indent_level(&self) -> u32 {
1205        self.depth * self.tab_size
1206    }
1207}
1208
1209impl MultiBuffer {
1210    pub fn new(capability: Capability) -> Self {
1211        Self::new_(
1212            capability,
1213            MultiBufferSnapshot {
1214                show_headers: true,
1215                show_deleted_hunks: true,
1216                ..MultiBufferSnapshot::default()
1217            },
1218        )
1219    }
1220
1221    pub fn without_headers(capability: Capability) -> Self {
1222        Self::new_(
1223            capability,
1224            MultiBufferSnapshot {
1225                show_deleted_hunks: true,
1226                ..MultiBufferSnapshot::default()
1227            },
1228        )
1229    }
1230
1231    pub fn singleton(buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Self {
1232        let mut this = Self::new_(
1233            buffer.read(cx).capability(),
1234            MultiBufferSnapshot {
1235                singleton: true,
1236                show_deleted_hunks: true,
1237                ..MultiBufferSnapshot::default()
1238            },
1239        );
1240        this.singleton = true;
1241        this.set_excerpts_for_path(
1242            PathKey::sorted(0),
1243            buffer.clone(),
1244            [Point::zero()..buffer.read(cx).max_point()],
1245            0,
1246            cx,
1247        );
1248        this
1249    }
1250
1251    #[inline]
1252    pub fn new_(capability: Capability, snapshot: MultiBufferSnapshot) -> Self {
1253        Self {
1254            snapshot: RefCell::new(snapshot),
1255            buffers: Default::default(),
1256            diffs: HashMap::default(),
1257            subscriptions: Topic::default(),
1258            singleton: false,
1259            capability,
1260            title: None,
1261            buffer_changed_since_sync: Default::default(),
1262            history: History::default(),
1263        }
1264    }
1265
1266    pub fn clone(&self, new_cx: &mut Context<Self>) -> Self {
1267        let mut buffers = BTreeMap::default();
1268        let buffer_changed_since_sync = Rc::new(Cell::new(false));
1269        for (buffer_id, buffer_state) in self.buffers.iter() {
1270            buffer_state.buffer.update(new_cx, |buffer, _| {
1271                buffer.record_changes(Rc::downgrade(&buffer_changed_since_sync));
1272            });
1273            buffers.insert(
1274                *buffer_id,
1275                BufferState {
1276                    buffer: buffer_state.buffer.clone(),
1277                    _subscriptions: [
1278                        new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
1279                        new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),
1280                    ],
1281                },
1282            );
1283        }
1284        let mut diff_bases = HashMap::default();
1285        for (buffer_id, diff) in self.diffs.iter() {
1286            diff_bases.insert(*buffer_id, DiffState::new(diff.diff.clone(), new_cx));
1287        }
1288        Self {
1289            snapshot: RefCell::new(self.snapshot.borrow().clone()),
1290            buffers,
1291            diffs: diff_bases,
1292            subscriptions: Default::default(),
1293            singleton: self.singleton,
1294            capability: self.capability,
1295            history: self.history.clone(),
1296            title: self.title.clone(),
1297            buffer_changed_since_sync,
1298        }
1299    }
1300
1301    pub fn set_group_interval(&mut self, group_interval: Duration, cx: &mut Context<Self>) {
1302        self.history.set_group_interval(group_interval);
1303        if self.singleton {
1304            for BufferState { buffer, .. } in self.buffers.values() {
1305                buffer.update(cx, |buffer, _| {
1306                    buffer.set_group_interval(group_interval);
1307                });
1308            }
1309        }
1310    }
1311
1312    pub fn with_title(mut self, title: String) -> Self {
1313        self.title = Some(title);
1314        self
1315    }
1316
1317    pub fn read_only(&self) -> bool {
1318        !self.capability.editable()
1319    }
1320
1321    pub fn capability(&self) -> Capability {
1322        self.capability
1323    }
1324
1325    /// Returns an up-to-date snapshot of the MultiBuffer.
1326    #[ztracing::instrument(skip_all)]
1327    pub fn snapshot(&self, cx: &App) -> MultiBufferSnapshot {
1328        self.sync(cx);
1329        self.snapshot.borrow().clone()
1330    }
1331
1332    pub fn read(&self, cx: &App) -> Ref<'_, MultiBufferSnapshot> {
1333        self.sync(cx);
1334        self.snapshot.borrow()
1335    }
1336
1337    pub fn as_singleton(&self) -> Option<Entity<Buffer>> {
1338        if self.singleton {
1339            Some(self.buffers.values().next().unwrap().buffer.clone())
1340        } else {
1341            None
1342        }
1343    }
1344
1345    pub fn is_singleton(&self) -> bool {
1346        self.singleton
1347    }
1348
1349    pub fn subscribe(&mut self) -> Subscription<MultiBufferOffset> {
1350        self.subscriptions.subscribe()
1351    }
1352
1353    pub fn is_dirty(&self, cx: &App) -> bool {
1354        self.read(cx).is_dirty()
1355    }
1356
1357    pub fn has_deleted_file(&self, cx: &App) -> bool {
1358        self.read(cx).has_deleted_file()
1359    }
1360
1361    pub fn has_conflict(&self, cx: &App) -> bool {
1362        self.read(cx).has_conflict()
1363    }
1364
1365    // The `is_empty` signature doesn't match what clippy expects
1366    #[allow(clippy::len_without_is_empty)]
1367    pub fn len(&self, cx: &App) -> MultiBufferOffset {
1368        self.read(cx).len()
1369    }
1370
1371    pub fn is_empty(&self) -> bool {
1372        self.buffers.is_empty()
1373    }
1374
1375    pub fn edit<I, S, T>(
1376        &mut self,
1377        edits: I,
1378        autoindent_mode: Option<AutoindentMode>,
1379        cx: &mut Context<Self>,
1380    ) where
1381        I: IntoIterator<Item = (Range<S>, T)>,
1382        S: ToOffset,
1383        T: Into<Arc<str>>,
1384    {
1385        self.edit_internal(edits, autoindent_mode, true, cx);
1386    }
1387
1388    pub fn edit_non_coalesce<I, S, T>(
1389        &mut self,
1390        edits: I,
1391        autoindent_mode: Option<AutoindentMode>,
1392        cx: &mut Context<Self>,
1393    ) where
1394        I: IntoIterator<Item = (Range<S>, T)>,
1395        S: ToOffset,
1396        T: Into<Arc<str>>,
1397    {
1398        self.edit_internal(edits, autoindent_mode, false, cx);
1399    }
1400
1401    fn edit_internal<I, S, T>(
1402        &mut self,
1403        edits: I,
1404        autoindent_mode: Option<AutoindentMode>,
1405        coalesce_adjacent: bool,
1406        cx: &mut Context<Self>,
1407    ) where
1408        I: IntoIterator<Item = (Range<S>, T)>,
1409        S: ToOffset,
1410        T: Into<Arc<str>>,
1411    {
1412        if self.read_only() || self.buffers.is_empty() {
1413            return;
1414        }
1415        self.sync_mut(cx);
1416        let edits = edits
1417            .into_iter()
1418            .map(|(range, new_text)| {
1419                let mut range = range.start.to_offset(self.snapshot.get_mut())
1420                    ..range.end.to_offset(self.snapshot.get_mut());
1421                if range.start > range.end {
1422                    mem::swap(&mut range.start, &mut range.end);
1423                }
1424                (range, new_text.into())
1425            })
1426            .collect::<Vec<_>>();
1427
1428        return edit_internal(self, edits, autoindent_mode, coalesce_adjacent, cx);
1429
1430        // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR.
1431        fn edit_internal(
1432            this: &mut MultiBuffer,
1433            edits: Vec<(Range<MultiBufferOffset>, Arc<str>)>,
1434            mut autoindent_mode: Option<AutoindentMode>,
1435            coalesce_adjacent: bool,
1436            cx: &mut Context<MultiBuffer>,
1437        ) {
1438            let original_indent_columns = match &mut autoindent_mode {
1439                Some(AutoindentMode::Block {
1440                    original_indent_columns,
1441                }) => mem::take(original_indent_columns),
1442                _ => Default::default(),
1443            };
1444
1445            let buffer_edits = MultiBuffer::convert_edits_to_buffer_edits(
1446                edits,
1447                this.snapshot.get_mut(),
1448                &original_indent_columns,
1449            );
1450
1451            let mut buffer_ids = Vec::with_capacity(buffer_edits.len());
1452            for (buffer_id, mut edits) in buffer_edits {
1453                buffer_ids.push(buffer_id);
1454                edits.sort_by_key(|edit| edit.range.start);
1455                this.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
1456                    let mut edits = edits.into_iter().peekable();
1457                    let mut insertions = Vec::new();
1458                    let mut original_indent_columns = Vec::new();
1459                    let mut deletions = Vec::new();
1460                    let empty_str: Arc<str> = Arc::default();
1461                    while let Some(BufferEdit {
1462                        mut range,
1463                        mut new_text,
1464                        mut is_insertion,
1465                        original_indent_column,
1466                    }) = edits.next()
1467                    {
1468                        while let Some(BufferEdit {
1469                            range: next_range,
1470                            is_insertion: next_is_insertion,
1471                            new_text: next_new_text,
1472                            ..
1473                        }) = edits.peek()
1474                        {
1475                            let should_coalesce = if coalesce_adjacent {
1476                                range.end >= next_range.start
1477                            } else {
1478                                range.end > next_range.start
1479                            };
1480
1481                            if should_coalesce {
1482                                range.end = cmp::max(next_range.end, range.end);
1483                                is_insertion |= *next_is_insertion;
1484                                new_text = format!("{new_text}{next_new_text}").into();
1485                                edits.next();
1486                            } else {
1487                                break;
1488                            }
1489                        }
1490
1491                        if is_insertion {
1492                            original_indent_columns.push(original_indent_column);
1493                            insertions.push((
1494                                buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
1495                                new_text.clone(),
1496                            ));
1497                        } else if !range.is_empty() {
1498                            deletions.push((
1499                                buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
1500                                empty_str.clone(),
1501                            ));
1502                        }
1503                    }
1504
1505                    let deletion_autoindent_mode =
1506                        if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
1507                            Some(AutoindentMode::Block {
1508                                original_indent_columns: Default::default(),
1509                            })
1510                        } else {
1511                            autoindent_mode.clone()
1512                        };
1513                    let insertion_autoindent_mode =
1514                        if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
1515                            Some(AutoindentMode::Block {
1516                                original_indent_columns,
1517                            })
1518                        } else {
1519                            autoindent_mode.clone()
1520                        };
1521
1522                    if coalesce_adjacent {
1523                        buffer.edit(deletions, deletion_autoindent_mode, cx);
1524                        buffer.edit(insertions, insertion_autoindent_mode, cx);
1525                    } else {
1526                        buffer.edit_non_coalesce(deletions, deletion_autoindent_mode, cx);
1527                        buffer.edit_non_coalesce(insertions, insertion_autoindent_mode, cx);
1528                    }
1529                })
1530            }
1531
1532            cx.emit(Event::BuffersEdited { buffer_ids });
1533        }
1534    }
1535
1536    fn convert_edits_to_buffer_edits(
1537        edits: Vec<(Range<MultiBufferOffset>, Arc<str>)>,
1538        snapshot: &MultiBufferSnapshot,
1539        original_indent_columns: &[Option<u32>],
1540    ) -> HashMap<BufferId, Vec<BufferEdit>> {
1541        let mut buffer_edits: HashMap<BufferId, Vec<BufferEdit>> = Default::default();
1542        let mut cursor = snapshot.cursor::<MultiBufferOffset, BufferOffset>();
1543        for (ix, (range, new_text)) in edits.into_iter().enumerate() {
1544            let original_indent_column = original_indent_columns.get(ix).copied().flatten();
1545
1546            cursor.seek(&range.start);
1547            let mut start_region = cursor.region().expect("start offset out of bounds");
1548            if !start_region.is_main_buffer {
1549                cursor.next();
1550                if let Some(region) = cursor.region() {
1551                    start_region = region;
1552                } else {
1553                    continue;
1554                }
1555            }
1556
1557            if range.end < start_region.range.start {
1558                continue;
1559            }
1560
1561            let start_region = start_region.clone();
1562            if range.end > start_region.range.end {
1563                cursor.seek_forward(&range.end);
1564            }
1565            let mut end_region = cursor.region().expect("end offset out of bounds");
1566            if !end_region.is_main_buffer {
1567                cursor.prev();
1568                if let Some(region) = cursor.region() {
1569                    end_region = region;
1570                } else {
1571                    continue;
1572                }
1573            }
1574
1575            if range.start > end_region.range.end {
1576                continue;
1577            }
1578
1579            let start_overshoot = range.start.saturating_sub(start_region.range.start);
1580            let end_overshoot = range.end.saturating_sub(end_region.range.start);
1581            let buffer_start = (start_region.buffer_range.start + start_overshoot)
1582                .min(start_region.buffer_range.end);
1583            let buffer_end =
1584                (end_region.buffer_range.start + end_overshoot).min(end_region.buffer_range.end);
1585
1586            if start_region.excerpt == end_region.excerpt {
1587                if start_region.buffer.capability == Capability::ReadWrite
1588                    && start_region.is_main_buffer
1589                {
1590                    buffer_edits
1591                        .entry(start_region.buffer.remote_id())
1592                        .or_default()
1593                        .push(BufferEdit {
1594                            range: buffer_start..buffer_end,
1595                            new_text,
1596                            is_insertion: true,
1597                            original_indent_column,
1598                        });
1599                }
1600            } else {
1601                let start_excerpt_range = buffer_start..start_region.buffer_range.end;
1602                let end_excerpt_range = end_region.buffer_range.start..buffer_end;
1603                if start_region.buffer.capability == Capability::ReadWrite
1604                    && start_region.is_main_buffer
1605                {
1606                    buffer_edits
1607                        .entry(start_region.buffer.remote_id())
1608                        .or_default()
1609                        .push(BufferEdit {
1610                            range: start_excerpt_range,
1611                            new_text: new_text.clone(),
1612                            is_insertion: true,
1613                            original_indent_column,
1614                        });
1615                }
1616                if end_region.buffer.capability == Capability::ReadWrite
1617                    && end_region.is_main_buffer
1618                {
1619                    buffer_edits
1620                        .entry(end_region.buffer.remote_id())
1621                        .or_default()
1622                        .push(BufferEdit {
1623                            range: end_excerpt_range,
1624                            new_text: new_text.clone(),
1625                            is_insertion: false,
1626                            original_indent_column,
1627                        });
1628                }
1629                let end_region_excerpt = end_region.excerpt.clone();
1630
1631                cursor.seek(&range.start);
1632                cursor.next_excerpt();
1633                while let Some(region) = cursor.region() {
1634                    if region.excerpt == &end_region_excerpt {
1635                        break;
1636                    }
1637                    if region.buffer.capability == Capability::ReadWrite && region.is_main_buffer {
1638                        buffer_edits
1639                            .entry(region.buffer.remote_id())
1640                            .or_default()
1641                            .push(BufferEdit {
1642                                range: region.buffer_range.clone(),
1643                                new_text: new_text.clone(),
1644                                is_insertion: false,
1645                                original_indent_column,
1646                            });
1647                    }
1648                    cursor.next_excerpt();
1649                }
1650            }
1651        }
1652        buffer_edits
1653    }
1654
1655    pub fn autoindent_ranges<I, S>(&mut self, ranges: I, cx: &mut Context<Self>)
1656    where
1657        I: IntoIterator<Item = Range<S>>,
1658        S: ToOffset,
1659    {
1660        if self.read_only() || self.buffers.is_empty() {
1661            return;
1662        }
1663        self.sync_mut(cx);
1664        let empty = Arc::<str>::from("");
1665        let edits = ranges
1666            .into_iter()
1667            .map(|range| {
1668                let mut range = range.start.to_offset(self.snapshot.get_mut())
1669                    ..range.end.to_offset(&self.snapshot.get_mut());
1670                if range.start > range.end {
1671                    mem::swap(&mut range.start, &mut range.end);
1672                }
1673                (range, empty.clone())
1674            })
1675            .collect::<Vec<_>>();
1676
1677        return autoindent_ranges_internal(self, edits, cx);
1678
1679        fn autoindent_ranges_internal(
1680            this: &mut MultiBuffer,
1681            edits: Vec<(Range<MultiBufferOffset>, Arc<str>)>,
1682            cx: &mut Context<MultiBuffer>,
1683        ) {
1684            let buffer_edits =
1685                MultiBuffer::convert_edits_to_buffer_edits(edits, this.snapshot.get_mut(), &[]);
1686
1687            let mut buffer_ids = Vec::new();
1688            for (buffer_id, mut edits) in buffer_edits {
1689                buffer_ids.push(buffer_id);
1690                edits.sort_unstable_by_key(|edit| edit.range.start);
1691
1692                let mut ranges: Vec<Range<BufferOffset>> = Vec::new();
1693                for edit in edits {
1694                    if let Some(last_range) = ranges.last_mut()
1695                        && edit.range.start <= last_range.end
1696                    {
1697                        last_range.end = last_range.end.max(edit.range.end);
1698                        continue;
1699                    }
1700                    ranges.push(edit.range);
1701                }
1702
1703                this.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
1704                    buffer.autoindent_ranges(ranges, cx);
1705                })
1706            }
1707
1708            cx.emit(Event::BuffersEdited { buffer_ids });
1709        }
1710    }
1711
1712    pub fn set_active_selections(
1713        &self,
1714        selections: &[Selection<Anchor>],
1715        line_mode: bool,
1716        cursor_shape: CursorShape,
1717        cx: &mut Context<Self>,
1718    ) {
1719        let snapshot = self.snapshot(cx);
1720        let mut selections_by_buffer: HashMap<BufferId, Vec<Selection<text::Anchor>>> =
1721            Default::default();
1722
1723        for selection in selections {
1724            for (buffer_snapshot, buffer_range, _) in
1725                snapshot.range_to_buffer_ranges(selection.start..selection.end)
1726            {
1727                selections_by_buffer
1728                    .entry(buffer_snapshot.remote_id())
1729                    .or_default()
1730                    .push(Selection {
1731                        id: selection.id,
1732                        start: buffer_snapshot
1733                            .anchor_at(buffer_range.start, selection.start.bias()),
1734                        end: buffer_snapshot.anchor_at(buffer_range.end, selection.end.bias()),
1735                        reversed: selection.reversed,
1736                        goal: selection.goal,
1737                    });
1738            }
1739        }
1740
1741        for (buffer_id, buffer_state) in self.buffers.iter() {
1742            if !selections_by_buffer.contains_key(buffer_id) {
1743                buffer_state
1744                    .buffer
1745                    .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
1746            }
1747        }
1748
1749        for (buffer_id, selections) in selections_by_buffer {
1750            self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
1751                buffer.set_active_selections(selections.into(), line_mode, cursor_shape, cx);
1752            });
1753        }
1754    }
1755
1756    pub fn remove_active_selections(&self, cx: &mut Context<Self>) {
1757        for buffer in self.buffers.values() {
1758            buffer
1759                .buffer
1760                .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
1761        }
1762    }
1763
1764    #[instrument(skip_all)]
1765    fn merge_excerpt_ranges<'a>(
1766        expanded_ranges: impl IntoIterator<Item = &'a ExcerptRange<Point>> + 'a,
1767    ) -> Vec<ExcerptRange<Point>> {
1768        let mut sorted: Vec<_> = expanded_ranges.into_iter().collect();
1769        sorted.sort_by_key(|range| range.context.start);
1770        let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
1771        for range in sorted {
1772            if let Some(last_range) = merged_ranges.last_mut() {
1773                if last_range.context.end >= range.context.start
1774                    || last_range.context.end.row + 1 == range.context.start.row
1775                {
1776                    last_range.context.end = range.context.end.max(last_range.context.end);
1777                    continue;
1778                }
1779            }
1780            merged_ranges.push(range.clone());
1781        }
1782        merged_ranges
1783    }
1784
1785    pub fn clear(&mut self, cx: &mut Context<Self>) {
1786        self.sync_mut(cx);
1787        let removed_buffer_ids = std::mem::take(&mut self.buffers).into_keys().collect();
1788        self.diffs.clear();
1789        let MultiBufferSnapshot {
1790            excerpts,
1791            diffs,
1792            diff_transforms: _,
1793            non_text_state_update_count: _,
1794            edit_count: _,
1795            is_dirty,
1796            has_deleted_file,
1797            has_conflict,
1798            has_inverted_diff,
1799            singleton: _,
1800            trailing_excerpt_update_count,
1801            all_diff_hunks_expanded: _,
1802            show_deleted_hunks: _,
1803            use_extended_diff_range: _,
1804            show_headers: _,
1805            path_keys_by_index: _,
1806            indices_by_path_key: _,
1807            buffers,
1808        } = self.snapshot.get_mut();
1809        let start = ExcerptDimension(MultiBufferOffset::ZERO);
1810        let prev_len = ExcerptDimension(excerpts.summary().text.len);
1811        *excerpts = Default::default();
1812        *buffers = Default::default();
1813        *diffs = Default::default();
1814        *trailing_excerpt_update_count += 1;
1815        *is_dirty = false;
1816        *has_deleted_file = false;
1817        *has_conflict = false;
1818        *has_inverted_diff = false;
1819
1820        let edits = Self::sync_diff_transforms(
1821            self.snapshot.get_mut(),
1822            vec![Edit {
1823                old: start..prev_len,
1824                new: start..start,
1825            }],
1826            DiffChangeKind::BufferEdited,
1827        );
1828        if !edits.is_empty() {
1829            self.subscriptions.publish(edits);
1830        }
1831        cx.emit(Event::Edited {
1832            edited_buffer: None,
1833            is_local: true,
1834        });
1835        cx.emit(Event::BuffersRemoved { removed_buffer_ids });
1836        cx.notify();
1837    }
1838
1839    // If point is at the end of the buffer, the last excerpt is returned
1840    pub fn point_to_buffer_offset<T: ToOffset>(
1841        &self,
1842        point: T,
1843        cx: &App,
1844    ) -> Option<(Entity<Buffer>, BufferOffset)> {
1845        let snapshot = self.read(cx);
1846        let (buffer, offset) = snapshot.point_to_buffer_offset(point)?;
1847        Some((
1848            self.buffers.get(&buffer.remote_id())?.buffer.clone(),
1849            offset,
1850        ))
1851    }
1852
1853    // If point is at the end of the buffer, the last excerpt is returned
1854    pub fn point_to_buffer_point<T: ToPoint>(
1855        &self,
1856        point: T,
1857        cx: &App,
1858    ) -> Option<(Entity<Buffer>, Point)> {
1859        let snapshot = self.read(cx);
1860        let (buffer, point) = snapshot.point_to_buffer_point(point.to_point(&snapshot))?;
1861        Some((self.buffers.get(&buffer.remote_id())?.buffer.clone(), point))
1862    }
1863
1864    pub fn buffer_point_to_anchor(
1865        &self,
1866        // todo(lw): We shouldn't need this?
1867        buffer: &Entity<Buffer>,
1868        point: Point,
1869        cx: &App,
1870    ) -> Option<Anchor> {
1871        let mut found = None;
1872        let buffer_snapshot = buffer.read(cx).snapshot();
1873        let text_anchor = buffer_snapshot.anchor_after(&point);
1874        let snapshot = self.snapshot(cx);
1875        let path_key_index = snapshot.path_key_index_for_buffer(buffer_snapshot.remote_id())?;
1876        for excerpt in snapshot.excerpts_for_buffer(buffer_snapshot.remote_id()) {
1877            if excerpt
1878                .context
1879                .start
1880                .cmp(&text_anchor, &buffer_snapshot)
1881                .is_gt()
1882            {
1883                found = Some(Anchor::in_buffer(path_key_index, excerpt.context.start));
1884                break;
1885            } else if excerpt
1886                .context
1887                .end
1888                .cmp(&text_anchor, &buffer_snapshot)
1889                .is_ge()
1890            {
1891                found = Some(Anchor::in_buffer(path_key_index, text_anchor));
1892                break;
1893            }
1894            found = Some(Anchor::in_buffer(path_key_index, excerpt.context.end));
1895        }
1896
1897        found
1898    }
1899
1900    pub fn wait_for_anchors<'a, Anchors: 'a + Iterator<Item = Anchor>>(
1901        &self,
1902        anchors: Anchors,
1903        cx: &mut Context<Self>,
1904    ) -> impl 'static + Future<Output = Result<()>> + use<Anchors> {
1905        let mut error = None;
1906        let mut futures = Vec::new();
1907        for anchor in anchors {
1908            if let Some(excerpt_anchor) = anchor.excerpt_anchor() {
1909                if let Some(buffer) = self.buffers.get(&excerpt_anchor.text_anchor.buffer_id) {
1910                    buffer.buffer.update(cx, |buffer, _| {
1911                        futures.push(buffer.wait_for_anchors([excerpt_anchor.text_anchor()]))
1912                    });
1913                } else {
1914                    error = Some(anyhow!(
1915                        "buffer {:?} is not part of this multi-buffer",
1916                        excerpt_anchor.text_anchor.buffer_id
1917                    ));
1918                    break;
1919                }
1920            }
1921        }
1922        async move {
1923            if let Some(error) = error {
1924                Err(error)?;
1925            }
1926            for future in futures {
1927                future.await?;
1928            }
1929            Ok(())
1930        }
1931    }
1932
1933    pub fn text_anchor_for_position<T: ToOffset>(
1934        &self,
1935        position: T,
1936        cx: &App,
1937    ) -> Option<(Entity<Buffer>, text::Anchor)> {
1938        let snapshot = self.read(cx);
1939        let anchor = snapshot.anchor_before(position).excerpt_anchor()?;
1940        let buffer = self
1941            .buffers
1942            .get(&anchor.text_anchor.buffer_id)?
1943            .buffer
1944            .clone();
1945        Some((buffer, anchor.text_anchor()))
1946    }
1947
1948    fn on_buffer_event(
1949        &mut self,
1950        buffer: Entity<Buffer>,
1951        event: &language::BufferEvent,
1952        cx: &mut Context<Self>,
1953    ) {
1954        use language::BufferEvent;
1955        let buffer_id = buffer.read(cx).remote_id();
1956        cx.emit(match event {
1957            &BufferEvent::Edited { is_local } => Event::Edited {
1958                edited_buffer: Some(buffer),
1959                is_local,
1960            },
1961            BufferEvent::DirtyChanged => Event::DirtyChanged,
1962            BufferEvent::Saved => Event::Saved,
1963            BufferEvent::FileHandleChanged => Event::FileHandleChanged,
1964            BufferEvent::Reloaded => Event::Reloaded,
1965            BufferEvent::LanguageChanged(has_language) => {
1966                Event::LanguageChanged(buffer_id, *has_language)
1967            }
1968            BufferEvent::Reparsed => Event::Reparsed(buffer_id),
1969            BufferEvent::DiagnosticsUpdated => Event::DiagnosticsUpdated,
1970            BufferEvent::CapabilityChanged => {
1971                self.capability = buffer.read(cx).capability();
1972                return;
1973            }
1974            BufferEvent::Operation { .. } | BufferEvent::ReloadNeeded => return,
1975        });
1976    }
1977
1978    fn buffer_diff_language_changed(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
1979        let diff = diff.read(cx);
1980        let buffer_id = diff.buffer_id;
1981        let diff = DiffStateSnapshot {
1982            buffer_id,
1983            diff: diff.snapshot(cx),
1984            main_buffer: None,
1985        };
1986        self.snapshot.get_mut().diffs.insert_or_replace(diff, ());
1987    }
1988
1989    fn inverted_buffer_diff_language_changed(
1990        &mut self,
1991        diff: Entity<BufferDiff>,
1992        main_buffer: Entity<language::Buffer>,
1993        cx: &mut Context<Self>,
1994    ) {
1995        let base_text_buffer_id = diff.read(cx).base_text_buffer().read(cx).remote_id();
1996        let main_buffer_snapshot = main_buffer.read(cx).snapshot();
1997        let diff = diff.read(cx);
1998        let diff = DiffStateSnapshot {
1999            buffer_id: base_text_buffer_id,
2000            diff: diff.snapshot(cx),
2001            main_buffer: Some(main_buffer_snapshot),
2002        };
2003        self.snapshot.get_mut().diffs.insert_or_replace(diff, ());
2004    }
2005
2006    fn buffer_diff_changed(
2007        &mut self,
2008        diff: Entity<BufferDiff>,
2009        range: Range<text::Anchor>,
2010        cx: &mut Context<Self>,
2011    ) {
2012        let Some(buffer) = self.buffer(diff.read(cx).buffer_id) else {
2013            return;
2014        };
2015        let snapshot = self.sync_mut(cx);
2016
2017        let diff = diff.read(cx);
2018        let buffer_id = diff.buffer_id;
2019
2020        let Some(path) = snapshot.path_for_buffer(buffer_id).cloned() else {
2021            return;
2022        };
2023        let new_diff = DiffStateSnapshot {
2024            buffer_id,
2025            diff: diff.snapshot(cx),
2026            main_buffer: None,
2027        };
2028        let snapshot = self.snapshot.get_mut();
2029        let base_text_changed = find_diff_state(&snapshot.diffs, buffer_id)
2030            .is_none_or(|old_diff| !new_diff.base_texts_definitely_eq(old_diff));
2031        snapshot.diffs.insert_or_replace(new_diff, ());
2032
2033        let buffer = buffer.read(cx);
2034        let diff_change_range = range.to_offset(buffer);
2035
2036        let excerpt_edits = snapshot.excerpt_edits_for_diff_change(&path, diff_change_range);
2037        let edits = Self::sync_diff_transforms(
2038            snapshot,
2039            excerpt_edits,
2040            DiffChangeKind::DiffUpdated {
2041                base_changed: base_text_changed,
2042            },
2043        );
2044        if !edits.is_empty() {
2045            self.subscriptions.publish(edits);
2046        }
2047        cx.emit(Event::Edited {
2048            edited_buffer: None,
2049            is_local: true,
2050        });
2051    }
2052
2053    fn inverted_buffer_diff_changed(
2054        &mut self,
2055        diff: Entity<BufferDiff>,
2056        main_buffer: Entity<language::Buffer>,
2057        diff_change_range: Option<Range<usize>>,
2058        cx: &mut Context<Self>,
2059    ) {
2060        let snapshot = self.sync_mut(cx);
2061
2062        let base_text_buffer_id = diff.read(cx).base_text_buffer().read(cx).remote_id();
2063        let Some(path) = snapshot.path_for_buffer(base_text_buffer_id).cloned() else {
2064            return;
2065        };
2066
2067        let main_buffer_snapshot = main_buffer.read(cx).snapshot();
2068        let diff = diff.read(cx);
2069        let new_diff = DiffStateSnapshot {
2070            buffer_id: base_text_buffer_id,
2071            diff: diff.snapshot(cx),
2072            main_buffer: Some(main_buffer_snapshot),
2073        };
2074        let snapshot = self.snapshot.get_mut();
2075        snapshot.diffs.insert_or_replace(new_diff, ());
2076
2077        let Some(diff_change_range) = diff_change_range else {
2078            return;
2079        };
2080
2081        let excerpt_edits = snapshot.excerpt_edits_for_diff_change(&path, diff_change_range);
2082        let edits = Self::sync_diff_transforms(
2083            snapshot,
2084            excerpt_edits,
2085            DiffChangeKind::DiffUpdated {
2086                // We don't read this field for inverted diffs.
2087                base_changed: false,
2088            },
2089        );
2090        if !edits.is_empty() {
2091            self.subscriptions.publish(edits);
2092        }
2093        cx.emit(Event::Edited {
2094            edited_buffer: None,
2095            is_local: true,
2096        });
2097    }
2098
2099    pub fn all_buffers_iter(&self) -> impl Iterator<Item = Entity<Buffer>> {
2100        self.buffers.values().map(|state| state.buffer.clone())
2101    }
2102
2103    pub fn all_buffers(&self) -> HashSet<Entity<Buffer>> {
2104        self.all_buffers_iter().collect()
2105    }
2106
2107    pub fn buffer(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
2108        self.buffers
2109            .get(&buffer_id)
2110            .map(|state| state.buffer.clone())
2111    }
2112
2113    pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2114        self.point_to_buffer_offset(point, cx)
2115            .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
2116    }
2117
2118    pub fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
2119        let snapshot = self.snapshot(cx);
2120        snapshot
2121            .excerpts
2122            .first()
2123            .and_then(|excerpt| self.buffer(excerpt.range.context.start.buffer_id))
2124            .map(|buffer| LanguageSettings::for_buffer(&buffer.read(cx), cx))
2125            .unwrap_or_else(move || self.language_settings_at(MultiBufferOffset::default(), cx))
2126    }
2127
2128    pub fn language_settings_at<'a, T: ToOffset>(
2129        &'a self,
2130        point: T,
2131        cx: &'a App,
2132    ) -> Cow<'a, LanguageSettings> {
2133        if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) {
2134            LanguageSettings::for_buffer_at(buffer.read(cx), offset, cx)
2135        } else {
2136            Cow::Borrowed(&AllLanguageSettings::get_global(cx).defaults)
2137        }
2138    }
2139
2140    pub fn for_each_buffer(&self, f: &mut dyn FnMut(&Entity<Buffer>)) {
2141        self.buffers.values().for_each(|state| f(&state.buffer))
2142    }
2143
2144    pub fn explicit_title(&self) -> Option<&str> {
2145        self.title.as_deref()
2146    }
2147
2148    pub fn title<'a>(&'a self, cx: &'a App) -> Cow<'a, str> {
2149        if let Some(title) = self.title.as_ref() {
2150            return title.into();
2151        }
2152
2153        if let Some(buffer) = self.as_singleton() {
2154            let buffer = buffer.read(cx);
2155
2156            if let Some(file) = buffer.file() {
2157                return file.file_name(cx).into();
2158            }
2159
2160            if let Some(title) = self.buffer_content_title(buffer) {
2161                return title;
2162            }
2163        };
2164
2165        "untitled".into()
2166    }
2167
2168    fn buffer_content_title(&self, buffer: &Buffer) -> Option<Cow<'_, str>> {
2169        let mut is_leading_whitespace = true;
2170        let mut count = 0;
2171        let mut prev_was_space = false;
2172        let mut title = String::new();
2173
2174        for ch in buffer.snapshot().chars() {
2175            if is_leading_whitespace && ch.is_whitespace() {
2176                continue;
2177            }
2178
2179            is_leading_whitespace = false;
2180
2181            if ch == '\n' || count >= 40 {
2182                break;
2183            }
2184
2185            if ch.is_whitespace() {
2186                if !prev_was_space {
2187                    title.push(' ');
2188                    count += 1;
2189                    prev_was_space = true;
2190                }
2191            } else {
2192                title.push(ch);
2193                count += 1;
2194                prev_was_space = false;
2195            }
2196        }
2197
2198        let title = title.trim_end().to_string();
2199
2200        if title.is_empty() {
2201            return None;
2202        }
2203
2204        Some(title.into())
2205    }
2206
2207    pub fn set_title(&mut self, title: String, cx: &mut Context<Self>) {
2208        self.title = Some(title);
2209        cx.notify();
2210    }
2211
2212    /// Preserve preview tabs containing this multibuffer until additional edits occur.
2213    pub fn refresh_preview(&self, cx: &mut Context<Self>) {
2214        for buffer_state in self.buffers.values() {
2215            buffer_state
2216                .buffer
2217                .update(cx, |buffer, _cx| buffer.refresh_preview());
2218        }
2219    }
2220
2221    /// Whether we should preserve the preview status of a tab containing this multi-buffer.
2222    pub fn preserve_preview(&self, cx: &App) -> bool {
2223        self.buffers
2224            .values()
2225            .all(|state| state.buffer.read(cx).preserve_preview())
2226    }
2227
2228    #[cfg(any(test, feature = "test-support"))]
2229    pub fn is_parsing(&self, cx: &App) -> bool {
2230        self.as_singleton().unwrap().read(cx).is_parsing()
2231    }
2232
2233    pub fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
2234        let buffer_id = diff.read(cx).buffer_id;
2235
2236        if let Some(existing_diff) = self.diff_for(buffer_id)
2237            && diff.entity_id() == existing_diff.entity_id()
2238        {
2239            return;
2240        }
2241
2242        self.buffer_diff_changed(
2243            diff.clone(),
2244            text::Anchor::min_max_range_for_buffer(buffer_id),
2245            cx,
2246        );
2247        self.diffs.insert(buffer_id, DiffState::new(diff, cx));
2248    }
2249
2250    pub fn add_inverted_diff(
2251        &mut self,
2252        diff: Entity<BufferDiff>,
2253        main_buffer: Entity<language::Buffer>,
2254        cx: &mut Context<Self>,
2255    ) {
2256        let snapshot = diff.read(cx).base_text(cx);
2257        let base_text_buffer_id = snapshot.remote_id();
2258        let diff_change_range = 0..snapshot.len();
2259        self.snapshot.get_mut().has_inverted_diff = true;
2260        self.inverted_buffer_diff_changed(
2261            diff.clone(),
2262            main_buffer.clone(),
2263            Some(diff_change_range),
2264            cx,
2265        );
2266        self.diffs.insert(
2267            base_text_buffer_id,
2268            DiffState::new_inverted(diff, main_buffer, cx),
2269        );
2270    }
2271
2272    pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
2273        self.diffs.get(&buffer_id).map(|state| state.diff.clone())
2274    }
2275
2276    pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
2277        self.expand_or_collapse_diff_hunks(ranges, true, cx);
2278    }
2279
2280    pub fn collapse_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
2281        self.expand_or_collapse_diff_hunks(ranges, false, cx);
2282    }
2283
2284    pub fn set_all_diff_hunks_expanded(&mut self, cx: &mut Context<Self>) {
2285        self.snapshot.get_mut().all_diff_hunks_expanded = true;
2286        self.expand_or_collapse_diff_hunks(vec![Anchor::Min..Anchor::Max], true, cx);
2287    }
2288
2289    pub fn all_diff_hunks_expanded(&self) -> bool {
2290        self.snapshot.borrow().all_diff_hunks_expanded
2291    }
2292
2293    pub fn set_all_diff_hunks_collapsed(&mut self, cx: &mut Context<Self>) {
2294        self.snapshot.get_mut().all_diff_hunks_expanded = false;
2295        self.expand_or_collapse_diff_hunks(vec![Anchor::Min..Anchor::Max], false, cx);
2296    }
2297
2298    pub fn set_show_deleted_hunks(&mut self, show: bool, cx: &mut Context<Self>) {
2299        self.snapshot.get_mut().show_deleted_hunks = show;
2300
2301        self.sync_mut(cx);
2302
2303        let old_len = self.snapshot.borrow().len();
2304
2305        let ranges = std::iter::once((Point::zero()..Point::MAX, None));
2306        let _ = self.expand_or_collapse_diff_hunks_inner(ranges, true, cx);
2307
2308        let new_len = self.snapshot.borrow().len();
2309
2310        self.subscriptions.publish(vec![Edit {
2311            old: MultiBufferOffset(0)..old_len,
2312            new: MultiBufferOffset(0)..new_len,
2313        }]);
2314
2315        cx.emit(Event::DiffHunksToggled);
2316        cx.emit(Event::Edited {
2317            edited_buffer: None,
2318            is_local: true,
2319        });
2320    }
2321
2322    pub fn set_use_extended_diff_range(&mut self, use_extended: bool, _cx: &mut Context<Self>) {
2323        self.snapshot.get_mut().use_extended_diff_range = use_extended;
2324    }
2325
2326    pub fn has_multiple_hunks(&self, cx: &App) -> bool {
2327        self.read(cx)
2328            .diff_hunks_in_range(Anchor::Min..Anchor::Max)
2329            .nth(1)
2330            .is_some()
2331    }
2332
2333    pub fn single_hunk_is_expanded(&self, range: Range<Anchor>, cx: &App) -> bool {
2334        let snapshot = self.read(cx);
2335        let mut cursor = snapshot.diff_transforms.cursor::<MultiBufferOffset>(());
2336        let offset_range = range.to_offset(&snapshot);
2337        cursor.seek(&offset_range.start, Bias::Left);
2338        while let Some(item) = cursor.item() {
2339            if *cursor.start() >= offset_range.end && *cursor.start() > offset_range.start {
2340                break;
2341            }
2342            if item.hunk_info().is_some() {
2343                return true;
2344            }
2345            cursor.next();
2346        }
2347        false
2348    }
2349
2350    pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range<Anchor>], cx: &App) -> bool {
2351        let snapshot = self.read(cx);
2352        let mut cursor = snapshot.diff_transforms.cursor::<MultiBufferOffset>(());
2353        for range in ranges {
2354            let range = range.to_point(&snapshot);
2355            let start = snapshot.point_to_offset(Point::new(range.start.row, 0));
2356            let end = (snapshot.point_to_offset(Point::new(range.end.row + 1, 0)) + 1usize)
2357                .min(snapshot.len());
2358            cursor.seek(&start, Bias::Right);
2359            while let Some(item) = cursor.item() {
2360                if *cursor.start() >= end {
2361                    break;
2362                }
2363                if item.hunk_info().is_some() {
2364                    return true;
2365                }
2366                cursor.next();
2367            }
2368        }
2369        false
2370    }
2371
2372    pub fn expand_or_collapse_diff_hunks_inner(
2373        &mut self,
2374        ranges: impl IntoIterator<Item = (Range<Point>, Option<Anchor>)>,
2375        expand: bool,
2376        cx: &mut Context<Self>,
2377    ) -> Vec<Edit<MultiBufferOffset>> {
2378        if self.snapshot.borrow().all_diff_hunks_expanded && !expand {
2379            return Vec::new();
2380        }
2381        self.sync_mut(cx);
2382        let mut snapshot = self.snapshot.get_mut();
2383        let mut excerpt_edits = Vec::new();
2384        let mut last_hunk_row = None;
2385        for (range, end_anchor) in ranges {
2386            for diff_hunk in snapshot.diff_hunks_in_range(range) {
2387                if let Some(end_anchor) = &end_anchor
2388                    && let Some(hunk_end_anchor) =
2389                        snapshot.anchor_in_excerpt(diff_hunk.excerpt_range.context.end)
2390                    && hunk_end_anchor.cmp(end_anchor, snapshot).is_gt()
2391                {
2392                    continue;
2393                }
2394                let hunk_range = diff_hunk.multi_buffer_range;
2395                if let Some(excerpt_start_anchor) =
2396                    snapshot.anchor_in_excerpt(diff_hunk.excerpt_range.context.start)
2397                    && hunk_range.start.to_point(snapshot) < excerpt_start_anchor.to_point(snapshot)
2398                {
2399                    continue;
2400                }
2401                if last_hunk_row.is_some_and(|row| row >= diff_hunk.row_range.start) {
2402                    continue;
2403                }
2404                let mut start = snapshot.excerpt_offset_for_anchor(&hunk_range.start);
2405                let mut end = snapshot.excerpt_offset_for_anchor(&hunk_range.end);
2406                if let Some(excerpt_end_anchor) =
2407                    snapshot.anchor_in_excerpt(diff_hunk.excerpt_range.context.end)
2408                {
2409                    let excerpt_end = snapshot.excerpt_offset_for_anchor(&excerpt_end_anchor);
2410                    start = start.min(excerpt_end);
2411                    end = end.min(excerpt_end);
2412                };
2413                last_hunk_row = Some(diff_hunk.row_range.start);
2414                excerpt_edits.push(text::Edit {
2415                    old: start..end,
2416                    new: start..end,
2417                });
2418            }
2419        }
2420
2421        Self::sync_diff_transforms(
2422            &mut snapshot,
2423            excerpt_edits,
2424            DiffChangeKind::ExpandOrCollapseHunks { expand },
2425        )
2426    }
2427
2428    pub fn expand_or_collapse_diff_hunks(
2429        &mut self,
2430        ranges: Vec<Range<Anchor>>,
2431        expand: bool,
2432        cx: &mut Context<Self>,
2433    ) {
2434        let snapshot = self.snapshot.borrow().clone();
2435        let ranges =
2436            ranges.iter().map(move |range| {
2437                let excerpt_end = snapshot.excerpt_containing(range.end..range.end).and_then(
2438                    |(_, excerpt_range)| snapshot.anchor_in_excerpt(excerpt_range.context.end),
2439                );
2440                let range = range.to_point(&snapshot);
2441                let mut peek_end = range.end;
2442                if range.end.row < snapshot.max_row().0 {
2443                    peek_end = Point::new(range.end.row + 1, 0);
2444                };
2445                (range.start..peek_end, excerpt_end)
2446            });
2447        let edits = self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
2448        if !edits.is_empty() {
2449            self.subscriptions.publish(edits);
2450        }
2451        cx.emit(Event::DiffHunksToggled);
2452        cx.emit(Event::Edited {
2453            edited_buffer: None,
2454            is_local: true,
2455        });
2456    }
2457
2458    #[ztracing::instrument(skip_all)]
2459    fn sync(&self, cx: &App) {
2460        let changed = self.buffer_changed_since_sync.replace(false);
2461        if !changed {
2462            return;
2463        }
2464        let edits = Self::sync_from_buffer_changes(
2465            &mut self.snapshot.borrow_mut(),
2466            &self.buffers,
2467            &self.diffs,
2468            cx,
2469        );
2470        if !edits.is_empty() {
2471            self.subscriptions.publish(edits);
2472        }
2473    }
2474
2475    fn sync_mut(&mut self, cx: &App) -> &mut MultiBufferSnapshot {
2476        let snapshot = self.snapshot.get_mut();
2477        let changed = self.buffer_changed_since_sync.replace(false);
2478        if !changed {
2479            return snapshot;
2480        }
2481        let edits = Self::sync_from_buffer_changes(snapshot, &self.buffers, &self.diffs, cx);
2482
2483        if !edits.is_empty() {
2484            self.subscriptions.publish(edits);
2485        }
2486
2487        snapshot
2488    }
2489
2490    fn sync_from_buffer_changes(
2491        snapshot: &mut MultiBufferSnapshot,
2492        buffers: &BTreeMap<BufferId, BufferState>,
2493        diffs: &HashMap<BufferId, DiffState>,
2494        cx: &App,
2495    ) -> Vec<Edit<MultiBufferOffset>> {
2496        let MultiBufferSnapshot {
2497            excerpts,
2498            diffs: buffer_diff,
2499            buffers: buffer_snapshots,
2500            path_keys_by_index: _,
2501            indices_by_path_key: _,
2502            diff_transforms: _,
2503            non_text_state_update_count,
2504            edit_count,
2505            is_dirty,
2506            has_deleted_file,
2507            has_conflict,
2508            has_inverted_diff: _,
2509            singleton: _,
2510            trailing_excerpt_update_count: _,
2511            all_diff_hunks_expanded: _,
2512            show_deleted_hunks: _,
2513            use_extended_diff_range: _,
2514            show_headers: _,
2515        } = snapshot;
2516        *is_dirty = false;
2517        *has_deleted_file = false;
2518        *has_conflict = false;
2519
2520        if !diffs.is_empty() {
2521            let mut diffs_to_add = Vec::new();
2522            for (id, diff) in diffs {
2523                if find_diff_state(buffer_diff, *id).is_none_or(|existing_diff| {
2524                    if existing_diff.main_buffer.is_none() {
2525                        return false;
2526                    }
2527                    let base_text = diff.diff.read(cx).base_text_buffer().read(cx);
2528                    base_text.remote_id() != existing_diff.base_text().remote_id()
2529                        || base_text
2530                            .version()
2531                            .changed_since(existing_diff.base_text().version())
2532                }) {
2533                    if diffs_to_add.capacity() == 0 {
2534                        diffs_to_add.reserve(diffs.len());
2535                    }
2536                    diffs_to_add.push(sum_tree::Edit::Insert(diff.snapshot(*id, cx)));
2537                }
2538            }
2539            buffer_diff.edit(diffs_to_add, ());
2540        }
2541
2542        let mut paths_to_edit = Vec::new();
2543        let mut non_text_state_updated = false;
2544        let mut edited = false;
2545        for buffer_state in buffers.values() {
2546            let buffer = buffer_state.buffer.read(cx);
2547            let last_snapshot = buffer_snapshots
2548                .get(&buffer.remote_id())
2549                .expect("each buffer should have a snapshot");
2550            let current_version = buffer.version();
2551            let non_text_state_update_count = buffer.non_text_state_update_count();
2552
2553            let buffer_edited =
2554                current_version.changed_since(last_snapshot.buffer_snapshot.version());
2555            let buffer_non_text_state_updated = non_text_state_update_count
2556                > last_snapshot.buffer_snapshot.non_text_state_update_count();
2557            if buffer_edited || buffer_non_text_state_updated {
2558                paths_to_edit.push((
2559                    last_snapshot.path_key.clone(),
2560                    last_snapshot.path_key_index,
2561                    buffer_state.buffer.clone(),
2562                    if buffer_edited {
2563                        Some(last_snapshot.buffer_snapshot.version().clone())
2564                    } else {
2565                        None
2566                    },
2567                ));
2568            }
2569
2570            edited |= buffer_edited;
2571            non_text_state_updated |= buffer_non_text_state_updated;
2572            *is_dirty |= buffer.is_dirty();
2573            *has_deleted_file |= buffer
2574                .file()
2575                .is_some_and(|file| file.disk_state().is_deleted());
2576            *has_conflict |= buffer.has_conflict();
2577        }
2578        if edited {
2579            *edit_count += 1;
2580        }
2581        if non_text_state_updated {
2582            *non_text_state_update_count += 1;
2583        }
2584
2585        paths_to_edit.sort_unstable_by_key(|(path, _, _, _)| path.clone());
2586
2587        let mut edits = Vec::new();
2588        let mut new_excerpts = SumTree::default();
2589        let mut cursor = excerpts.cursor::<ExcerptSummary>(());
2590
2591        for (path, path_key_index, buffer, prev_version) in paths_to_edit {
2592            new_excerpts.append(cursor.slice(&path, Bias::Left), ());
2593            let buffer = buffer.read(cx);
2594            let buffer_id = buffer.remote_id();
2595
2596            buffer_snapshots.insert(
2597                buffer_id,
2598                BufferStateSnapshot {
2599                    path_key: path.clone(),
2600                    path_key_index,
2601                    buffer_snapshot: buffer.snapshot(),
2602                },
2603            );
2604
2605            if let Some(prev_version) = &prev_version {
2606                while let Some(old_excerpt) = cursor.item()
2607                    && &old_excerpt.path_key == &path
2608                {
2609                    edits.extend(
2610                        buffer
2611                            .edits_since_in_range::<usize>(
2612                                prev_version,
2613                                old_excerpt.range.context.clone(),
2614                            )
2615                            .map(|edit| {
2616                                let excerpt_old_start = cursor.start().len();
2617                                let excerpt_new_start =
2618                                    ExcerptDimension(new_excerpts.summary().text.len);
2619                                let old_start = excerpt_old_start + edit.old.start;
2620                                let old_end = excerpt_old_start + edit.old.end;
2621                                let new_start = excerpt_new_start + edit.new.start;
2622                                let new_end = excerpt_new_start + edit.new.end;
2623                                Edit {
2624                                    old: old_start..old_end,
2625                                    new: new_start..new_end,
2626                                }
2627                            }),
2628                    );
2629
2630                    let excerpt = Excerpt::new(
2631                        old_excerpt.path_key.clone(),
2632                        old_excerpt.path_key_index,
2633                        &buffer.snapshot(),
2634                        old_excerpt.range.clone(),
2635                        old_excerpt.has_trailing_newline,
2636                    );
2637                    new_excerpts.push(excerpt, ());
2638                    cursor.next();
2639                }
2640            } else {
2641                new_excerpts.append(cursor.slice(&path, Bias::Right), ());
2642            };
2643        }
2644        new_excerpts.append(cursor.suffix(), ());
2645
2646        drop(cursor);
2647        *excerpts = new_excerpts;
2648
2649        Self::sync_diff_transforms(snapshot, edits, DiffChangeKind::BufferEdited)
2650    }
2651
2652    fn sync_diff_transforms(
2653        snapshot: &mut MultiBufferSnapshot,
2654        excerpt_edits: Vec<text::Edit<ExcerptOffset>>,
2655        change_kind: DiffChangeKind,
2656    ) -> Vec<Edit<MultiBufferOffset>> {
2657        if excerpt_edits.is_empty() {
2658            return vec![];
2659        }
2660
2661        let mut excerpts = snapshot.excerpts.cursor::<ExcerptOffset>(());
2662        let mut old_diff_transforms = snapshot
2663            .diff_transforms
2664            .cursor::<Dimensions<ExcerptOffset, MultiBufferOffset>>(());
2665        let mut new_diff_transforms = SumTree::default();
2666        let mut old_expanded_hunks = HashSet::default();
2667        let mut output_edits = Vec::new();
2668        let mut output_delta = 0_isize;
2669        let mut at_transform_boundary = true;
2670        let mut end_of_current_insert = None;
2671
2672        let mut excerpt_edits = excerpt_edits.into_iter().peekable();
2673        while let Some(edit) = excerpt_edits.next() {
2674            excerpts.seek_forward(&edit.new.start, Bias::Right);
2675            if excerpts.item().is_none() && *excerpts.start() == edit.new.start {
2676                excerpts.prev();
2677            }
2678
2679            // Keep any transforms that are before the edit.
2680            if at_transform_boundary {
2681                at_transform_boundary = false;
2682                let transforms_before_edit = old_diff_transforms.slice(&edit.old.start, Bias::Left);
2683                Self::append_diff_transforms(&mut new_diff_transforms, transforms_before_edit);
2684                if let Some(transform) = old_diff_transforms.item()
2685                    && old_diff_transforms.end().0 == edit.old.start
2686                    && old_diff_transforms.start().0 < edit.old.start
2687                {
2688                    Self::push_diff_transform(&mut new_diff_transforms, transform.clone());
2689                    old_diff_transforms.next();
2690                }
2691            }
2692
2693            // Compute the start of the edit in output coordinates.
2694            let edit_start_overshoot = edit.old.start - old_diff_transforms.start().0;
2695            let edit_old_start = old_diff_transforms.start().1 + edit_start_overshoot;
2696            let edit_new_start =
2697                MultiBufferOffset((edit_old_start.0 as isize + output_delta) as usize);
2698
2699            let changed_diff_hunks = Self::recompute_diff_transforms_for_edit(
2700                &edit,
2701                &mut excerpts,
2702                &mut old_diff_transforms,
2703                &mut new_diff_transforms,
2704                &mut end_of_current_insert,
2705                &mut old_expanded_hunks,
2706                snapshot,
2707                change_kind,
2708            );
2709
2710            // Compute the end of the edit in output coordinates.
2711            let edit_old_end_overshoot = edit.old.end - old_diff_transforms.start().0;
2712            let edit_new_end_overshoot = edit.new.end - new_diff_transforms.summary().excerpt_len();
2713            let edit_old_end = old_diff_transforms.start().1 + edit_old_end_overshoot;
2714            let edit_new_end = new_diff_transforms.summary().output.len + edit_new_end_overshoot;
2715            let output_edit = Edit {
2716                old: edit_old_start..edit_old_end,
2717                new: edit_new_start..edit_new_end,
2718            };
2719
2720            output_delta += (output_edit.new.end - output_edit.new.start) as isize;
2721            output_delta -= (output_edit.old.end - output_edit.old.start) as isize;
2722            if changed_diff_hunks || matches!(change_kind, DiffChangeKind::BufferEdited) {
2723                output_edits.push(output_edit);
2724            }
2725
2726            // If this is the last edit that intersects the current diff transform,
2727            // then recreate the content up to the end of this transform, to prepare
2728            // for reusing additional slices of the old transforms.
2729            if excerpt_edits
2730                .peek()
2731                .is_none_or(|next_edit| next_edit.old.start >= old_diff_transforms.end().0)
2732            {
2733                let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
2734                    && match old_diff_transforms.item() {
2735                        Some(DiffTransform::BufferContent {
2736                            inserted_hunk_info: Some(hunk),
2737                            ..
2738                        }) => excerpts.item().is_some_and(|excerpt| {
2739                            if let Some(diff) = find_diff_state(&snapshot.diffs, excerpt.buffer_id)
2740                                && diff.main_buffer.is_some()
2741                            {
2742                                return true;
2743                            }
2744                            hunk.hunk_start_anchor
2745                                .is_valid(&excerpt.buffer_snapshot(&snapshot))
2746                        }),
2747                        _ => true,
2748                    };
2749
2750                let mut excerpt_offset = edit.new.end;
2751                if !keep_next_old_transform {
2752                    excerpt_offset += old_diff_transforms.end().0 - edit.old.end;
2753                    old_diff_transforms.next();
2754                }
2755
2756                old_expanded_hunks.clear();
2757                Self::push_buffer_content_transform(
2758                    snapshot,
2759                    &mut new_diff_transforms,
2760                    excerpt_offset,
2761                    end_of_current_insert,
2762                );
2763                at_transform_boundary = true;
2764            }
2765        }
2766
2767        // Keep any transforms that are after the last edit.
2768        Self::append_diff_transforms(&mut new_diff_transforms, old_diff_transforms.suffix());
2769
2770        // Ensure there's always at least one buffer content transform.
2771        if new_diff_transforms.is_empty() {
2772            new_diff_transforms.push(
2773                DiffTransform::BufferContent {
2774                    summary: Default::default(),
2775                    inserted_hunk_info: None,
2776                },
2777                (),
2778            );
2779        }
2780
2781        drop(old_diff_transforms);
2782        drop(excerpts);
2783        snapshot.diff_transforms = new_diff_transforms;
2784        snapshot.edit_count += 1;
2785
2786        #[cfg(any(test, feature = "test-support"))]
2787        snapshot.check_invariants();
2788        output_edits
2789    }
2790
2791    fn recompute_diff_transforms_for_edit(
2792        edit: &Edit<ExcerptOffset>,
2793        excerpts: &mut Cursor<Excerpt, ExcerptOffset>,
2794        old_diff_transforms: &mut Cursor<
2795            DiffTransform,
2796            Dimensions<ExcerptOffset, MultiBufferOffset>,
2797        >,
2798        new_diff_transforms: &mut SumTree<DiffTransform>,
2799        end_of_current_insert: &mut Option<(ExcerptOffset, DiffTransformHunkInfo)>,
2800        old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
2801        snapshot: &MultiBufferSnapshot,
2802        change_kind: DiffChangeKind,
2803    ) -> bool {
2804        log::trace!(
2805            "recomputing diff transform for edit {:?} => {:?}",
2806            edit.old.start..edit.old.end,
2807            edit.new.start..edit.new.end
2808        );
2809
2810        // Record which hunks were previously expanded.
2811        while let Some(item) = old_diff_transforms.item() {
2812            if let Some(hunk_info) = item.hunk_info() {
2813                log::trace!(
2814                    "previously expanded hunk at {:?}",
2815                    old_diff_transforms.start()
2816                );
2817                old_expanded_hunks.insert(hunk_info);
2818            }
2819            if old_diff_transforms.end().0 > edit.old.end {
2820                break;
2821            }
2822            old_diff_transforms.next();
2823        }
2824
2825        // Avoid querying diff hunks if there's no possibility of hunks being expanded.
2826        // For inverted diffs, hunks are always shown, so we can't skip this.
2827        let all_diff_hunks_expanded = snapshot.all_diff_hunks_expanded;
2828        if old_expanded_hunks.is_empty()
2829            && change_kind == DiffChangeKind::BufferEdited
2830            && !all_diff_hunks_expanded
2831            && !snapshot.has_inverted_diff
2832        {
2833            return false;
2834        }
2835
2836        // Visit each excerpt that intersects the edit.
2837        let mut did_expand_hunks = false;
2838        while let Some(excerpt) = excerpts.item() {
2839            // Recompute the expanded hunks in the portion of the excerpt that
2840            // intersects the edit.
2841            if let Some(diff) = find_diff_state(&snapshot.diffs, excerpt.buffer_id) {
2842                let buffer_snapshot = &excerpt.buffer_snapshot(&snapshot);
2843                let excerpt_start = *excerpts.start();
2844                let excerpt_end = excerpt_start + excerpt.text_summary.len;
2845                let excerpt_buffer_start = excerpt.range.context.start.to_offset(buffer_snapshot);
2846                let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
2847                let edit_buffer_start =
2848                    excerpt_buffer_start + edit.new.start.saturating_sub(excerpt_start);
2849                let edit_buffer_end =
2850                    excerpt_buffer_start + edit.new.end.saturating_sub(excerpt_start);
2851                let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
2852
2853                if let Some(main_buffer) = &diff.main_buffer {
2854                    for hunk in diff.hunks_intersecting_base_text_range(
2855                        edit_buffer_start..edit_buffer_end,
2856                        main_buffer,
2857                    ) {
2858                        did_expand_hunks = true;
2859                        let hunk_buffer_range = hunk.diff_base_byte_range.clone();
2860                        if hunk_buffer_range.start < excerpt_buffer_start {
2861                            log::trace!("skipping hunk that starts before excerpt");
2862                            continue;
2863                        }
2864                        let hunk_excerpt_start = excerpt_start
2865                            + hunk_buffer_range.start.saturating_sub(excerpt_buffer_start);
2866                        let hunk_excerpt_end = excerpt_end
2867                            .min(excerpt_start + (hunk_buffer_range.end - excerpt_buffer_start));
2868                        Self::push_buffer_content_transform(
2869                            snapshot,
2870                            new_diff_transforms,
2871                            hunk_excerpt_start,
2872                            *end_of_current_insert,
2873                        );
2874                        if !hunk_buffer_range.is_empty() {
2875                            let hunk_info = DiffTransformHunkInfo {
2876                                buffer_id: buffer_snapshot.remote_id(),
2877                                hunk_start_anchor: hunk.buffer_range.start,
2878                                hunk_secondary_status: hunk.secondary_status,
2879                                excerpt_end: excerpt.end_anchor(),
2880                                is_logically_deleted: true,
2881                            };
2882                            *end_of_current_insert =
2883                                Some((hunk_excerpt_end.min(excerpt_end), hunk_info));
2884                        }
2885                    }
2886                } else {
2887                    let edit_anchor_range = buffer_snapshot.anchor_before(edit_buffer_start)
2888                        ..buffer_snapshot.anchor_after(edit_buffer_end);
2889                    for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer_snapshot) {
2890                        if hunk.is_created_file() && !all_diff_hunks_expanded {
2891                            continue;
2892                        }
2893
2894                        let hunk_buffer_range = hunk.buffer_range.to_offset(buffer_snapshot);
2895                        if hunk_buffer_range.start < excerpt_buffer_start {
2896                            log::trace!("skipping hunk that starts before excerpt");
2897                            continue;
2898                        }
2899
2900                        let hunk_info = DiffTransformHunkInfo {
2901                            buffer_id: buffer_snapshot.remote_id(),
2902                            hunk_start_anchor: hunk.buffer_range.start,
2903                            hunk_secondary_status: hunk.secondary_status,
2904                            excerpt_end: excerpt.end_anchor(),
2905                            is_logically_deleted: false,
2906                        };
2907
2908                        let hunk_excerpt_start = excerpt_start
2909                            + hunk_buffer_range.start.saturating_sub(excerpt_buffer_start);
2910                        let hunk_excerpt_end = excerpt_end
2911                            .min(excerpt_start + (hunk_buffer_range.end - excerpt_buffer_start));
2912
2913                        Self::push_buffer_content_transform(
2914                            snapshot,
2915                            new_diff_transforms,
2916                            hunk_excerpt_start,
2917                            *end_of_current_insert,
2918                        );
2919
2920                        // For every existing hunk, determine if it was previously expanded
2921                        // and if it should currently be expanded.
2922                        let was_previously_expanded = old_expanded_hunks.contains(&hunk_info);
2923                        let should_expand_hunk = match &change_kind {
2924                            DiffChangeKind::DiffUpdated { base_changed: true } => {
2925                                was_previously_expanded || all_diff_hunks_expanded
2926                            }
2927                            DiffChangeKind::ExpandOrCollapseHunks { expand } => {
2928                                let intersects = hunk_buffer_range.is_empty()
2929                                    || (hunk_buffer_range.end > edit_buffer_start);
2930                                if *expand {
2931                                    intersects || was_previously_expanded || all_diff_hunks_expanded
2932                                } else {
2933                                    !intersects
2934                                        && (was_previously_expanded || all_diff_hunks_expanded)
2935                                }
2936                            }
2937                            _ => was_previously_expanded || all_diff_hunks_expanded,
2938                        };
2939
2940                        if should_expand_hunk {
2941                            did_expand_hunks = true;
2942                            log::trace!(
2943                                "expanding hunk {:?}",
2944                                hunk_excerpt_start..hunk_excerpt_end,
2945                            );
2946
2947                            if !hunk.diff_base_byte_range.is_empty()
2948                                && hunk_buffer_range.start >= edit_buffer_start
2949                                && hunk_buffer_range.start <= excerpt_buffer_end
2950                                && snapshot.show_deleted_hunks
2951                            {
2952                                let base_text = diff.base_text();
2953                                let mut text_cursor =
2954                                    base_text.as_rope().cursor(hunk.diff_base_byte_range.start);
2955                                let mut base_text_summary = text_cursor
2956                                    .summary::<TextSummary>(hunk.diff_base_byte_range.end);
2957
2958                                let mut has_trailing_newline = false;
2959                                if base_text_summary.last_line_chars > 0 {
2960                                    base_text_summary += TextSummary::newline();
2961                                    has_trailing_newline = true;
2962                                }
2963
2964                                new_diff_transforms.push(
2965                                    DiffTransform::DeletedHunk {
2966                                        base_text_byte_range: hunk.diff_base_byte_range.clone(),
2967                                        summary: base_text_summary,
2968                                        buffer_id: buffer_snapshot.remote_id(),
2969                                        hunk_info,
2970                                        has_trailing_newline,
2971                                    },
2972                                    (),
2973                                );
2974                            }
2975
2976                            if !hunk_buffer_range.is_empty() {
2977                                *end_of_current_insert =
2978                                    Some((hunk_excerpt_end.min(excerpt_end), hunk_info));
2979                            }
2980                        }
2981                    }
2982                }
2983            }
2984
2985            if excerpts.end() <= edit.new.end {
2986                excerpts.next();
2987            } else {
2988                break;
2989            }
2990        }
2991
2992        did_expand_hunks || !old_expanded_hunks.is_empty()
2993    }
2994
2995    fn append_diff_transforms(
2996        new_transforms: &mut SumTree<DiffTransform>,
2997        subtree: SumTree<DiffTransform>,
2998    ) {
2999        if let Some(DiffTransform::BufferContent {
3000            inserted_hunk_info,
3001            summary,
3002        }) = subtree.first()
3003            && Self::extend_last_buffer_content_transform(
3004                new_transforms,
3005                *inserted_hunk_info,
3006                *summary,
3007            )
3008        {
3009            let mut cursor = subtree.cursor::<()>(());
3010            cursor.next();
3011            cursor.next();
3012            new_transforms.append(cursor.suffix(), ());
3013            return;
3014        }
3015        new_transforms.append(subtree, ());
3016    }
3017
3018    fn push_diff_transform(new_transforms: &mut SumTree<DiffTransform>, transform: DiffTransform) {
3019        if let DiffTransform::BufferContent {
3020            inserted_hunk_info: inserted_hunk_anchor,
3021            summary,
3022        } = transform
3023            && Self::extend_last_buffer_content_transform(
3024                new_transforms,
3025                inserted_hunk_anchor,
3026                summary,
3027            )
3028        {
3029            return;
3030        }
3031        new_transforms.push(transform, ());
3032    }
3033
3034    fn push_buffer_content_transform(
3035        old_snapshot: &MultiBufferSnapshot,
3036        new_transforms: &mut SumTree<DiffTransform>,
3037        end_offset: ExcerptOffset,
3038        current_inserted_hunk: Option<(ExcerptOffset, DiffTransformHunkInfo)>,
3039    ) {
3040        let inserted_region = current_inserted_hunk.map(|(insertion_end_offset, hunk_info)| {
3041            (end_offset.min(insertion_end_offset), Some(hunk_info))
3042        });
3043        let unchanged_region = [(end_offset, None)];
3044
3045        for (end_offset, inserted_hunk_info) in inserted_region.into_iter().chain(unchanged_region)
3046        {
3047            let start_offset = new_transforms.summary().excerpt_len();
3048            if end_offset <= start_offset {
3049                continue;
3050            }
3051            let summary_to_add = old_snapshot
3052                .text_summary_for_excerpt_offset_range::<MBTextSummary>(start_offset..end_offset);
3053
3054            if !Self::extend_last_buffer_content_transform(
3055                new_transforms,
3056                inserted_hunk_info,
3057                summary_to_add,
3058            ) {
3059                new_transforms.push(
3060                    DiffTransform::BufferContent {
3061                        summary: summary_to_add,
3062                        inserted_hunk_info,
3063                    },
3064                    (),
3065                )
3066            }
3067        }
3068    }
3069
3070    fn extend_last_buffer_content_transform(
3071        new_transforms: &mut SumTree<DiffTransform>,
3072        new_inserted_hunk_info: Option<DiffTransformHunkInfo>,
3073        summary_to_add: MBTextSummary,
3074    ) -> bool {
3075        let mut did_extend = false;
3076        new_transforms.update_last(
3077            |last_transform| {
3078                if let DiffTransform::BufferContent {
3079                    summary,
3080                    inserted_hunk_info: inserted_hunk_anchor,
3081                } = last_transform
3082                    && *inserted_hunk_anchor == new_inserted_hunk_info
3083                {
3084                    *summary += summary_to_add;
3085                    did_extend = true;
3086                }
3087            },
3088            (),
3089        );
3090        did_extend
3091    }
3092
3093    pub fn toggle_single_diff_hunk(&mut self, range: Range<Anchor>, cx: &mut Context<Self>) {
3094        let snapshot = self.snapshot(cx);
3095        let excerpt_end = snapshot
3096            .excerpt_containing(range.end..range.end)
3097            .and_then(|(_, excerpt_range)| snapshot.anchor_in_excerpt(excerpt_range.context.end));
3098        let point_range = range.to_point(&snapshot);
3099        let expand = !self.single_hunk_is_expanded(range, cx);
3100        let edits =
3101            self.expand_or_collapse_diff_hunks_inner([(point_range, excerpt_end)], expand, cx);
3102        if !edits.is_empty() {
3103            self.subscriptions.publish(edits);
3104        }
3105        cx.emit(Event::DiffHunksToggled);
3106        cx.emit(Event::Edited {
3107            edited_buffer: None,
3108            is_local: true,
3109        });
3110    }
3111}
3112
3113fn build_excerpt_ranges(
3114    ranges: impl IntoIterator<Item = Range<Point>>,
3115    context_line_count: u32,
3116    buffer_snapshot: &BufferSnapshot,
3117) -> Vec<ExcerptRange<Point>> {
3118    ranges
3119        .into_iter()
3120        .map(|range| {
3121            let start_row = range.start.row.saturating_sub(context_line_count);
3122            let start = Point::new(start_row, 0);
3123            let end_row = (range.end.row + context_line_count).min(buffer_snapshot.max_point().row);
3124            let end = Point::new(end_row, buffer_snapshot.line_len(end_row));
3125            ExcerptRange {
3126                context: start..end,
3127                primary: range,
3128            }
3129        })
3130        .collect()
3131}
3132
3133#[cfg(any(test, feature = "test-support"))]
3134impl MultiBuffer {
3135    pub fn build_simple(text: &str, cx: &mut gpui::App) -> Entity<Self> {
3136        let buffer = cx.new(|cx| Buffer::local(text, cx));
3137        cx.new(|cx| Self::singleton(buffer, cx))
3138    }
3139
3140    pub fn build_multi<const COUNT: usize>(
3141        excerpts: [(&str, Vec<Range<Point>>); COUNT],
3142        cx: &mut gpui::App,
3143    ) -> Entity<Self> {
3144        let multi = cx.new(|_| Self::new(Capability::ReadWrite));
3145        for (ix, (text, ranges)) in excerpts.into_iter().enumerate() {
3146            let buffer = cx.new(|cx| Buffer::local(text, cx));
3147            let snapshot = buffer.read(cx).snapshot();
3148            let excerpt_ranges = ranges
3149                .into_iter()
3150                .map(ExcerptRange::new)
3151                .collect::<Vec<_>>();
3152            multi.update(cx, |multi, cx| {
3153                multi.set_excerpt_ranges_for_path(
3154                    PathKey::sorted(ix as u64),
3155                    buffer,
3156                    &snapshot,
3157                    excerpt_ranges,
3158                    cx,
3159                )
3160            });
3161        }
3162
3163        multi
3164    }
3165
3166    pub fn build_from_buffer(buffer: Entity<Buffer>, cx: &mut gpui::App) -> Entity<Self> {
3167        cx.new(|cx| Self::singleton(buffer, cx))
3168    }
3169
3170    pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
3171        cx.new(|cx| {
3172            let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
3173            let mutation_count = rng.random_range(1..=5);
3174            multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
3175            multibuffer
3176        })
3177    }
3178
3179    pub fn randomly_edit(
3180        &mut self,
3181        rng: &mut impl rand::Rng,
3182        edit_count: usize,
3183        cx: &mut Context<Self>,
3184    ) {
3185        use util::RandomCharIter;
3186
3187        let snapshot = self.read(cx);
3188        let mut edits: Vec<(Range<MultiBufferOffset>, Arc<str>)> = Vec::new();
3189        let mut last_end = None;
3190        for _ in 0..edit_count {
3191            if last_end.is_some_and(|last_end| last_end >= snapshot.len()) {
3192                break;
3193            }
3194
3195            let new_start = last_end.map_or(MultiBufferOffset::ZERO, |last_end| last_end + 1usize);
3196            let end =
3197                snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right);
3198            let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right);
3199            last_end = Some(end);
3200
3201            let mut range = start..end;
3202            if rng.random_bool(0.2) {
3203                mem::swap(&mut range.start, &mut range.end);
3204            }
3205
3206            let new_text_len = rng.random_range(0..10);
3207            let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
3208
3209            edits.push((range, new_text.into()));
3210        }
3211        log::info!("mutating multi-buffer with {:?}", edits);
3212        drop(snapshot);
3213
3214        self.edit(edits, None, cx);
3215    }
3216
3217    pub fn randomly_edit_excerpts(
3218        &mut self,
3219        rng: &mut impl rand::Rng,
3220        mutation_count: usize,
3221        cx: &mut Context<Self>,
3222    ) {
3223        use rand::prelude::*;
3224        use std::env;
3225        use util::RandomCharIter;
3226
3227        let max_buffers = env::var("MAX_BUFFERS")
3228            .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
3229            .unwrap_or(5);
3230
3231        let mut buffers = Vec::new();
3232        for _ in 0..mutation_count {
3233            let snapshot = self.snapshot(cx);
3234            let buffer_ids = snapshot.all_buffer_ids().collect::<Vec<_>>();
3235            if buffer_ids.is_empty() || (rng.random() && buffer_ids.len() < max_buffers) {
3236                let buffer_handle = if rng.random() || self.buffers.is_empty() {
3237                    let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
3238                    buffers.push(cx.new(|cx| Buffer::local(text, cx)));
3239                    let buffer = buffers.last().unwrap().read(cx);
3240                    log::info!(
3241                        "Creating new buffer {} with text: {:?}",
3242                        buffer.remote_id(),
3243                        buffer.text()
3244                    );
3245                    buffers.last().unwrap().clone()
3246                } else {
3247                    self.buffers.values().choose(rng).unwrap().buffer.clone()
3248                };
3249
3250                let buffer = buffer_handle.read(cx);
3251                let buffer_text = buffer.text();
3252                let buffer_snapshot = buffer.snapshot();
3253                let mut next_min_start_ix = 0;
3254                let ranges = (0..rng.random_range(0..5))
3255                    .filter_map(|_| {
3256                        if next_min_start_ix >= buffer.len() {
3257                            return None;
3258                        }
3259                        let end_ix = buffer.clip_offset(
3260                            rng.random_range(next_min_start_ix..=buffer.len()),
3261                            Bias::Right,
3262                        );
3263                        let start_ix = buffer
3264                            .clip_offset(rng.random_range(next_min_start_ix..=end_ix), Bias::Left);
3265                        next_min_start_ix = buffer.text().ceil_char_boundary(end_ix + 1);
3266                        Some(ExcerptRange::new(start_ix..end_ix))
3267                    })
3268                    .collect::<Vec<_>>();
3269                log::info!(
3270                    "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
3271                    buffer_handle.read(cx).remote_id(),
3272                    ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
3273                    ranges
3274                        .iter()
3275                        .map(|r| &buffer_text[r.context.clone()])
3276                        .collect::<Vec<_>>()
3277                );
3278
3279                let path_key = PathKey::for_buffer(&buffer_handle, cx);
3280                self.set_merged_excerpt_ranges_for_path(
3281                    path_key.clone(),
3282                    buffer_handle,
3283                    &buffer_snapshot,
3284                    ranges,
3285                    cx,
3286                );
3287                log::info!("Inserted with path_key: {:?}", path_key);
3288            } else {
3289                let path_key = self
3290                    .snapshot
3291                    .borrow()
3292                    .buffers
3293                    .get(&buffer_ids.choose(rng).unwrap())
3294                    .unwrap()
3295                    .path_key
3296                    .clone();
3297                log::info!("Removing excerpts {:?}", path_key);
3298                self.remove_excerpts(path_key, cx);
3299            }
3300        }
3301    }
3302
3303    pub fn randomly_mutate(
3304        &mut self,
3305        rng: &mut impl rand::Rng,
3306        mutation_count: usize,
3307        cx: &mut Context<Self>,
3308    ) {
3309        use rand::prelude::*;
3310
3311        if rng.random_bool(0.7) || self.singleton {
3312            let buffer = self
3313                .buffers
3314                .values()
3315                .choose(rng)
3316                .map(|state| state.buffer.clone());
3317
3318            if let Some(buffer) = buffer {
3319                buffer.update(cx, |buffer, cx| {
3320                    if rng.random() {
3321                        buffer.randomly_edit(rng, mutation_count, cx);
3322                    } else {
3323                        buffer.randomly_undo_redo(rng, cx);
3324                    }
3325                });
3326            } else {
3327                self.randomly_edit(rng, mutation_count, cx);
3328            }
3329        } else {
3330            self.randomly_edit_excerpts(rng, mutation_count, cx);
3331        }
3332
3333        self.check_invariants(cx);
3334    }
3335
3336    fn check_invariants(&self, cx: &App) {
3337        self.read(cx).check_invariants();
3338    }
3339}
3340
3341impl EventEmitter<Event> for MultiBuffer {}
3342
3343impl MultiBufferSnapshot {
3344    pub fn text(&self) -> String {
3345        self.chunks(
3346            MultiBufferOffset::ZERO..self.len(),
3347            LanguageAwareStyling {
3348                tree_sitter: false,
3349                diagnostics: false,
3350            },
3351        )
3352        .map(|chunk| chunk.text)
3353        .collect()
3354    }
3355
3356    pub fn reversed_chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3357        self.reversed_chunks_in_range(MultiBufferOffset::ZERO..position.to_offset(self))
3358            .flat_map(|c| c.chars().rev())
3359    }
3360
3361    fn reversed_chunks_in_range(
3362        &self,
3363        range: Range<MultiBufferOffset>,
3364    ) -> ReversedMultiBufferChunks<'_> {
3365        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
3366        cursor.seek(&range.end);
3367        let current_chunks = cursor.region().as_ref().map(|region| {
3368            let start_overshoot = range.start.saturating_sub(region.range.start);
3369            let end_overshoot = range.end - region.range.start;
3370            let end = (region.buffer_range.start + end_overshoot).min(region.buffer_range.end);
3371            let start = region.buffer_range.start + start_overshoot;
3372            region.buffer.reversed_chunks_in_range(start..end)
3373        });
3374        ReversedMultiBufferChunks {
3375            cursor,
3376            current_chunks,
3377            start: range.start,
3378            offset: range.end,
3379        }
3380    }
3381
3382    pub fn chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3383        let offset = position.to_offset(self);
3384        self.text_for_range(offset..self.len())
3385            .flat_map(|chunk| chunk.chars())
3386    }
3387
3388    pub fn text_for_range<T: ToOffset>(&self, range: Range<T>) -> impl Iterator<Item = &str> + '_ {
3389        self.chunks(
3390            range,
3391            LanguageAwareStyling {
3392                tree_sitter: false,
3393                diagnostics: false,
3394            },
3395        )
3396        .map(|chunk| chunk.text)
3397    }
3398
3399    pub fn is_line_blank(&self, row: MultiBufferRow) -> bool {
3400        self.text_for_range(Point::new(row.0, 0)..Point::new(row.0, self.line_len(row)))
3401            .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
3402    }
3403
3404    pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
3405    where
3406        T: ToOffset,
3407    {
3408        let position = position.to_offset(self);
3409        position == self.clip_offset(position, Bias::Left)
3410            && self
3411                .bytes_in_range(position..self.len())
3412                .flatten()
3413                .copied()
3414                .take(needle.len())
3415                .eq(needle.bytes())
3416    }
3417
3418    pub fn diff_hunks(&self) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3419        self.diff_hunks_in_range(Anchor::Min..Anchor::Max)
3420    }
3421
3422    pub fn diff_hunks_in_range<T: ToPoint>(
3423        &self,
3424        range: Range<T>,
3425    ) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3426        let query_range = range.start.to_point(self)..range.end.to_point(self);
3427        self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
3428            let diff = self.diff_state(buffer.remote_id())?;
3429            let iter = if let Some(main_buffer) = &diff.main_buffer {
3430                let buffer_start = buffer.point_to_offset(buffer_range.start);
3431                let buffer_end = buffer.point_to_offset(buffer_range.end);
3432                itertools::Either::Left(
3433                    diff.hunks_intersecting_base_text_range(buffer_start..buffer_end, main_buffer)
3434                        .map(move |hunk| (hunk, buffer, true)),
3435                )
3436            } else {
3437                let buffer_start = buffer.anchor_before(buffer_range.start);
3438                let buffer_end = buffer.anchor_after(buffer_range.end);
3439                itertools::Either::Right(
3440                    diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
3441                        .map(move |hunk| (hunk, buffer, false)),
3442                )
3443            };
3444            Some(iter.filter_map(|(hunk, buffer, is_inverted)| {
3445                if hunk.is_created_file() && !self.all_diff_hunks_expanded {
3446                    return None;
3447                }
3448                let range = if is_inverted {
3449                    hunk.diff_base_byte_range.to_point(&buffer)
3450                } else {
3451                    hunk.range.clone()
3452                };
3453                Some((range, (hunk, is_inverted)))
3454            }))
3455        })
3456        .filter_map(move |(range, (hunk, is_inverted), excerpt)| {
3457            let buffer_snapshot = excerpt.buffer_snapshot(self);
3458            if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
3459            {
3460                return None;
3461            }
3462            let end_row = if range.end.column == 0 {
3463                range.end.row
3464            } else {
3465                range.end.row + 1
3466            };
3467
3468            let word_diffs =
3469                (!hunk.base_word_diffs.is_empty() || !hunk.buffer_word_diffs.is_empty())
3470                    .then(|| {
3471                        let mut word_diffs = Vec::new();
3472
3473                        if self.show_deleted_hunks || is_inverted {
3474                            let hunk_start_offset = if is_inverted {
3475                                Anchor::in_buffer(
3476                                    excerpt.path_key_index,
3477                                    buffer_snapshot.anchor_after(hunk.diff_base_byte_range.start),
3478                                )
3479                                .to_offset(self)
3480                            } else {
3481                                Anchor::in_buffer(excerpt.path_key_index, hunk.buffer_range.start)
3482                                    .to_offset(self)
3483                            };
3484
3485                            word_diffs.extend(hunk.base_word_diffs.iter().map(|diff| {
3486                                hunk_start_offset + diff.start..hunk_start_offset + diff.end
3487                            }));
3488                        }
3489
3490                        if !is_inverted {
3491                            word_diffs.extend(hunk.buffer_word_diffs.into_iter().map(|diff| {
3492                                Anchor::range_in_buffer(excerpt.path_key_index, diff)
3493                                    .to_offset(self)
3494                            }));
3495                        }
3496                        word_diffs
3497                    })
3498                    .unwrap_or_default();
3499
3500            let buffer_range = if is_inverted {
3501                buffer_snapshot.anchor_after(hunk.diff_base_byte_range.start)
3502                    ..buffer_snapshot.anchor_before(hunk.diff_base_byte_range.end)
3503            } else {
3504                hunk.buffer_range.clone()
3505            };
3506            let status_kind = if hunk.buffer_range.start == hunk.buffer_range.end {
3507                DiffHunkStatusKind::Deleted
3508            } else if hunk.diff_base_byte_range.is_empty() {
3509                DiffHunkStatusKind::Added
3510            } else {
3511                DiffHunkStatusKind::Modified
3512            };
3513            let multi_buffer_range =
3514                Anchor::range_in_buffer(excerpt.path_key_index, buffer_range.clone());
3515            Some(MultiBufferDiffHunk {
3516                row_range: MultiBufferRow(range.start.row)..MultiBufferRow(end_row),
3517                buffer_id: buffer_snapshot.remote_id(),
3518                buffer_range,
3519                word_diffs,
3520                diff_base_byte_range: BufferOffset(hunk.diff_base_byte_range.start)
3521                    ..BufferOffset(hunk.diff_base_byte_range.end),
3522                status: DiffHunkStatus {
3523                    kind: status_kind,
3524                    secondary: hunk.secondary_status,
3525                },
3526                excerpt_range: excerpt.range.clone(),
3527                multi_buffer_range,
3528            })
3529        })
3530    }
3531
3532    fn excerpts_for_range<T: ToOffset>(
3533        &self,
3534        range: Range<T>,
3535    ) -> impl Iterator<Item = &Excerpt> + '_ {
3536        let range = range.start.to_offset(self)..range.end.to_offset(self);
3537        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
3538        cursor.seek(&range.start);
3539        std::iter::from_fn(move || {
3540            let region = cursor.region()?;
3541            if region.range.start > range.end
3542                || region.range.start == range.end && region.range.start > range.start
3543            {
3544                return None;
3545            }
3546            let excerpt = region.excerpt;
3547            cursor.next_excerpt_forwards();
3548            Some(excerpt)
3549        })
3550    }
3551
3552    pub fn buffer_ids_for_range<T: ToOffset>(
3553        &self,
3554        range: Range<T>,
3555    ) -> impl Iterator<Item = BufferId> + '_ {
3556        self.excerpts_for_range(range)
3557            .map(|excerpt| excerpt.buffer_snapshot(self).remote_id())
3558    }
3559
3560    /// Resolves the given [`text::Anchor`]s to [`crate::Anchor`]s if the anchor is within a visible excerpt.
3561    ///
3562    /// The passed in anchors must be ordered.
3563    pub fn text_anchors_to_visible_anchors(
3564        &self,
3565        anchors: impl IntoIterator<Item = text::Anchor>,
3566    ) -> Vec<Option<Anchor>> {
3567        let anchors = anchors.into_iter();
3568        let mut result = Vec::with_capacity(anchors.size_hint().0);
3569        let mut anchors = anchors.peekable();
3570        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
3571        'anchors: while let Some(anchor) = anchors.peek() {
3572            let buffer_id = anchor.buffer_id;
3573            let mut same_buffer_anchors = anchors.peeking_take_while(|a| a.buffer_id == buffer_id);
3574
3575            if let Some(buffer) = self.buffers.get(&buffer_id) {
3576                let path = &buffer.path_key;
3577                let Some(mut next) = same_buffer_anchors.next() else {
3578                    continue 'anchors;
3579                };
3580                cursor.seek_forward(path, Bias::Left);
3581                'excerpts: loop {
3582                    let Some(excerpt) = cursor.item() else {
3583                        break;
3584                    };
3585                    if &excerpt.path_key != path {
3586                        break;
3587                    }
3588                    let buffer_snapshot = excerpt.buffer_snapshot(self);
3589
3590                    loop {
3591                        // anchor is before the first excerpt
3592                        if excerpt
3593                            .range
3594                            .context
3595                            .start
3596                            .cmp(&next, &buffer_snapshot)
3597                            .is_gt()
3598                        {
3599                            // so we skip it and try the next anchor
3600                            result.push(None);
3601                            match same_buffer_anchors.next() {
3602                                Some(anchor) => next = anchor,
3603                                None => continue 'anchors,
3604                            }
3605                        // anchor is within the excerpt
3606                        } else if excerpt
3607                            .range
3608                            .context
3609                            .end
3610                            .cmp(&next, &buffer_snapshot)
3611                            .is_ge()
3612                        {
3613                            // record it and all following anchors that are within
3614                            result.push(Some(Anchor::in_buffer(excerpt.path_key_index, next)));
3615                            result.extend(
3616                                same_buffer_anchors
3617                                    .peeking_take_while(|a| {
3618                                        excerpt.range.context.end.cmp(a, &buffer_snapshot).is_ge()
3619                                    })
3620                                    .map(|a| Some(Anchor::in_buffer(excerpt.path_key_index, a))),
3621                            );
3622                            match same_buffer_anchors.next() {
3623                                Some(anchor) => next = anchor,
3624                                None => continue 'anchors,
3625                            }
3626                        // anchor is after the excerpt, try the next one
3627                        } else {
3628                            cursor.next();
3629                            continue 'excerpts;
3630                        }
3631                    }
3632                }
3633                // account for `next`
3634                result.push(None);
3635            }
3636            result.extend(same_buffer_anchors.map(|_| None));
3637        }
3638
3639        result
3640    }
3641
3642    pub fn range_to_buffer_ranges<T: ToOffset>(
3643        &self,
3644        range: Range<T>,
3645    ) -> Vec<(
3646        BufferSnapshot,
3647        Range<BufferOffset>,
3648        ExcerptRange<text::Anchor>,
3649    )> {
3650        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
3651        let start = range.start.to_offset(self);
3652        let end = range.end.to_offset(self);
3653        cursor.seek(&start);
3654
3655        let mut result: Vec<(
3656            BufferSnapshot,
3657            Range<BufferOffset>,
3658            ExcerptRange<text::Anchor>,
3659        )> = Vec::new();
3660        while let Some(region) = cursor.region() {
3661            if region.range.start >= end {
3662                break;
3663            }
3664            if region.is_main_buffer {
3665                let start_overshoot = start.saturating_sub(region.range.start);
3666                let end_offset = end;
3667                let end_overshoot = end_offset.saturating_sub(region.range.start);
3668                let start = region
3669                    .buffer_range
3670                    .end
3671                    .min(region.buffer_range.start + start_overshoot);
3672                let end = region
3673                    .buffer_range
3674                    .end
3675                    .min(region.buffer_range.start + end_overshoot);
3676                let excerpt_range = region.excerpt.range.clone();
3677                if let Some(prev) =
3678                    result
3679                        .last_mut()
3680                        .filter(|(prev_buffer, prev_range, prev_excerpt)| {
3681                            prev_buffer.remote_id() == region.buffer.remote_id()
3682                                && prev_range.end == start
3683                                && prev_excerpt.context.start == excerpt_range.context.start
3684                        })
3685                {
3686                    prev.1.end = end;
3687                } else {
3688                    result.push((region.buffer.clone(), start..end, excerpt_range));
3689                }
3690            }
3691            cursor.next();
3692        }
3693
3694        if let Some(excerpt) = cursor.excerpt()
3695            && excerpt.text_summary.len == 0
3696            && end == self.len()
3697        {
3698            let buffer_snapshot = excerpt.buffer_snapshot(self);
3699
3700            let buffer_offset =
3701                BufferOffset(excerpt.range.context.start.to_offset(buffer_snapshot));
3702            let excerpt_range = excerpt.range.clone();
3703            if result
3704                .last_mut()
3705                .is_none_or(|(prev_buffer, prev_range, prev_excerpt)| {
3706                    prev_buffer.remote_id() != buffer_snapshot.remote_id()
3707                        || prev_range.end != buffer_offset
3708                        || prev_excerpt.context.start != excerpt_range.context.start
3709                })
3710            {
3711                result.push((
3712                    buffer_snapshot.clone(),
3713                    buffer_offset..buffer_offset,
3714                    excerpt_range,
3715                ));
3716            }
3717        }
3718
3719        result
3720    }
3721
3722    pub fn range_to_buffer_ranges_with_deleted_hunks<T: ToOffset>(
3723        &self,
3724        range: Range<T>,
3725    ) -> impl Iterator<Item = (&BufferSnapshot, Range<BufferOffset>, Option<Anchor>)> + '_ {
3726        let start = range.start.to_offset(self);
3727        let end = range.end.to_offset(self);
3728
3729        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
3730        cursor.seek(&start);
3731
3732        std::iter::from_fn(move || {
3733            let region = cursor.region()?;
3734            if region.range.start > end {
3735                return None;
3736            }
3737            let start_overshoot = start.saturating_sub(region.range.start);
3738            let end_overshoot = end.saturating_sub(region.range.start);
3739            let start = region
3740                .buffer_range
3741                .end
3742                .min(region.buffer_range.start + start_overshoot);
3743            let end = region
3744                .buffer_range
3745                .end
3746                .min(region.buffer_range.start + end_overshoot);
3747
3748            let deleted_hunk_anchor = if region.is_main_buffer {
3749                None
3750            } else {
3751                Some(self.anchor_before(region.range.start))
3752            };
3753            let result = (region.buffer, start..end, deleted_hunk_anchor);
3754            cursor.next();
3755            Some(result)
3756        })
3757    }
3758
3759    /// Retrieves buffer metadata for the given range, and converts it into multi-buffer
3760    /// coordinates.
3761    ///
3762    /// The given callback will be called for every excerpt intersecting the given range. It will
3763    /// be passed the excerpt's buffer and the buffer range that the input range intersects.
3764    /// The callback should return an iterator of metadata items from that buffer, each paired
3765    /// with a buffer range.
3766    ///
3767    /// The returned iterator yields each of these metadata items, paired with its range in
3768    /// multi-buffer coordinates.
3769    fn lift_buffer_metadata<'a, MBD, M, I>(
3770        &'a self,
3771        query_range: Range<MBD>,
3772        get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range<MBD::TextDimension>) -> Option<I>,
3773    ) -> impl Iterator<Item = (Range<MBD>, M, &'a Excerpt)> + 'a
3774    where
3775        I: Iterator<Item = (Range<MBD::TextDimension>, M)> + 'a,
3776        MBD: MultiBufferDimension
3777            + Ord
3778            + Sub<Output = MBD::TextDimension>
3779            + ops::Add<MBD::TextDimension, Output = MBD>
3780            + ops::AddAssign<MBD::TextDimension>,
3781        MBD::TextDimension: Sub<Output = MBD::TextDimension>
3782            + ops::Add<Output = MBD::TextDimension>
3783            + AddAssign<MBD::TextDimension>
3784            + Ord,
3785    {
3786        let mut current_excerpt_metadata: Option<(ExcerptRange<text::Anchor>, I)> = None;
3787        let mut cursor = self.cursor::<MBD, MBD::TextDimension>();
3788
3789        // Find the excerpt and buffer offset where the given range ends.
3790        cursor.seek(&query_range.end);
3791        let mut range_end = None;
3792        while let Some(region) = cursor.region() {
3793            if region.is_main_buffer {
3794                let mut buffer_end = region.buffer_range.start;
3795                let overshoot = if query_range.end > region.range.start {
3796                    query_range.end - region.range.start
3797                } else {
3798                    <MBD::TextDimension>::default()
3799                };
3800                buffer_end = buffer_end + overshoot;
3801                range_end = Some((region.excerpt.range.clone(), buffer_end));
3802                break;
3803            }
3804            cursor.next();
3805        }
3806
3807        cursor.seek(&query_range.start);
3808
3809        if let Some(region) = cursor.region().filter(|region| !region.is_main_buffer)
3810            && region.range.start > MBD::default()
3811        {
3812            cursor.prev()
3813        } else if let Some(region) = cursor.region()
3814            && region.is_main_buffer
3815            && region.diff_hunk_status.is_some()
3816        {
3817            cursor.prev();
3818            if cursor.region().is_none_or(|region| region.is_main_buffer) {
3819                cursor.next();
3820            }
3821        }
3822
3823        iter::from_fn(move || {
3824            loop {
3825                let excerpt = cursor.excerpt()?;
3826                let buffer_snapshot = excerpt.buffer_snapshot(self);
3827
3828                // If we have already retrieved metadata for this excerpt, continue to use it.
3829                let metadata_iter = if let Some((_, metadata)) = current_excerpt_metadata
3830                    .as_mut()
3831                    .filter(|(excerpt_info, _)| excerpt_info == &excerpt.range)
3832                {
3833                    Some(metadata)
3834                }
3835                // Otherwise, compute the intersection of the input range with the excerpt's range,
3836                // and retrieve the metadata for the resulting range.
3837                else {
3838                    let region = cursor.region()?;
3839                    let mut buffer_start;
3840                    if region.is_main_buffer {
3841                        buffer_start = region.buffer_range.start;
3842                        if query_range.start > region.range.start {
3843                            let overshoot = query_range.start - region.range.start;
3844                            buffer_start = buffer_start + overshoot;
3845                        }
3846                        buffer_start = buffer_start.min(region.buffer_range.end);
3847                    } else {
3848                        buffer_start = cursor.main_buffer_position()?;
3849                    };
3850                    let mut buffer_end = excerpt
3851                        .range
3852                        .context
3853                        .end
3854                        .summary::<MBD::TextDimension>(&buffer_snapshot);
3855                    if let Some((end_excerpt, end_buffer_offset)) = &range_end
3856                        && &excerpt.range == end_excerpt
3857                    {
3858                        buffer_end = buffer_end.min(*end_buffer_offset);
3859                    }
3860
3861                    get_buffer_metadata(&buffer_snapshot, buffer_start..buffer_end).map(
3862                        |iterator| {
3863                            &mut current_excerpt_metadata
3864                                .insert((excerpt.range.clone(), iterator))
3865                                .1
3866                        },
3867                    )
3868                };
3869
3870                // Visit each metadata item.
3871                if let Some((metadata_buffer_range, metadata)) =
3872                    metadata_iter.and_then(Iterator::next)
3873                {
3874                    // Find the multibuffer regions that contain the start and end of
3875                    // the metadata item's range.
3876                    if metadata_buffer_range.start > <MBD::TextDimension>::default() {
3877                        while let Some(region) = cursor.region() {
3878                            if (region.is_main_buffer
3879                                && (region.buffer_range.end >= metadata_buffer_range.start
3880                                    || cursor.is_at_end_of_excerpt()))
3881                                || (!region.is_main_buffer
3882                                    && region.buffer_range.start == metadata_buffer_range.start)
3883                            {
3884                                break;
3885                            }
3886                            cursor.next();
3887                        }
3888                    }
3889                    let start_region = cursor.region()?.clone();
3890                    while let Some(region) = cursor.region() {
3891                        if region.is_main_buffer
3892                            && (region.buffer_range.end > metadata_buffer_range.end
3893                                || cursor.is_at_end_of_excerpt())
3894                        {
3895                            break;
3896                        }
3897                        cursor.next();
3898                    }
3899                    let end_region = cursor.region();
3900
3901                    // Convert the metadata item's range into multibuffer coordinates.
3902                    let mut start_position = start_region.range.start;
3903                    let region_buffer_start = start_region.buffer_range.start;
3904                    if start_region.is_main_buffer
3905                        && metadata_buffer_range.start > region_buffer_start
3906                    {
3907                        start_position =
3908                            start_position + (metadata_buffer_range.start - region_buffer_start);
3909                        start_position = start_position.min(start_region.range.end);
3910                    }
3911
3912                    let mut end_position = self.max_position();
3913                    if let Some(end_region) = &end_region {
3914                        end_position = end_region.range.start;
3915                        debug_assert!(end_region.is_main_buffer);
3916                        let region_buffer_start = end_region.buffer_range.start;
3917                        if metadata_buffer_range.end > region_buffer_start {
3918                            end_position =
3919                                end_position + (metadata_buffer_range.end - region_buffer_start);
3920                        }
3921                        end_position = end_position.min(end_region.range.end);
3922                    }
3923
3924                    if start_position <= query_range.end && end_position >= query_range.start {
3925                        return Some((start_position..end_position, metadata, excerpt));
3926                    }
3927                }
3928                // When there are no more metadata items for this excerpt, move to the next excerpt.
3929                else {
3930                    current_excerpt_metadata.take();
3931                    if let Some((end_excerpt, _)) = &range_end
3932                        && &excerpt.range == end_excerpt
3933                    {
3934                        return None;
3935                    }
3936                    cursor.next_excerpt_forwards();
3937                }
3938            }
3939        })
3940    }
3941
3942    pub fn diff_hunk_before<T: ToOffset>(&self, position: T) -> Option<MultiBufferRow> {
3943        let offset = position.to_offset(self);
3944
3945        let mut cursor = self
3946            .cursor::<DimensionPair<MultiBufferOffset, Point>, DimensionPair<BufferOffset, Point>>(
3947            );
3948        cursor.seek(&DimensionPair {
3949            key: offset,
3950            value: None,
3951        });
3952        cursor.seek_to_start_of_current_excerpt();
3953        let excerpt = cursor.excerpt()?;
3954
3955        let buffer = excerpt.buffer_snapshot(self);
3956        let excerpt_start = excerpt.range.context.start.to_offset(buffer);
3957        let excerpt_end = excerpt.range.context.end.to_offset(buffer);
3958        let current_position = match self.anchor_before(offset) {
3959            Anchor::Min => 0,
3960            Anchor::Excerpt(excerpt_anchor) => excerpt_anchor.text_anchor().to_offset(buffer),
3961            Anchor::Max => unreachable!(),
3962        };
3963
3964        if let Some(diff) = self.diff_state(excerpt.buffer_id) {
3965            if let Some(main_buffer) = &diff.main_buffer {
3966                for hunk in diff
3967                    .hunks_intersecting_base_text_range_rev(excerpt_start..excerpt_end, main_buffer)
3968                {
3969                    if hunk.diff_base_byte_range.end >= current_position {
3970                        continue;
3971                    }
3972                    let hunk_start = buffer.anchor_after(hunk.diff_base_byte_range.start);
3973                    let start =
3974                        Anchor::in_buffer(excerpt.path_key_index, hunk_start).to_point(self);
3975                    return Some(MultiBufferRow(start.row));
3976                }
3977            } else {
3978                let excerpt_end = buffer.anchor_before(excerpt_end.min(current_position));
3979                for hunk in diff
3980                    .hunks_intersecting_range_rev(excerpt.range.context.start..excerpt_end, buffer)
3981                {
3982                    let hunk_end = hunk.buffer_range.end.to_offset(buffer);
3983                    if hunk_end >= current_position {
3984                        continue;
3985                    }
3986                    let start = Anchor::in_buffer(excerpt.path_key_index, hunk.buffer_range.start)
3987                        .to_point(self);
3988                    return Some(MultiBufferRow(start.row));
3989                }
3990            }
3991        }
3992
3993        loop {
3994            cursor.prev_excerpt();
3995            let excerpt = cursor.excerpt()?;
3996            let buffer = excerpt.buffer_snapshot(self);
3997
3998            let Some(diff) = self.diff_state(excerpt.buffer_id) else {
3999                continue;
4000            };
4001            if let Some(main_buffer) = &diff.main_buffer {
4002                let Some(hunk) = diff
4003                    .hunks_intersecting_base_text_range_rev(
4004                        excerpt.range.context.to_offset(buffer),
4005                        main_buffer,
4006                    )
4007                    .next()
4008                else {
4009                    continue;
4010                };
4011                let hunk_start = buffer.anchor_after(hunk.diff_base_byte_range.start);
4012                let start = Anchor::in_buffer(excerpt.path_key_index, hunk_start).to_point(self);
4013                return Some(MultiBufferRow(start.row));
4014            } else {
4015                let Some(hunk) = diff
4016                    .hunks_intersecting_range_rev(excerpt.range.context.clone(), buffer)
4017                    .next()
4018                else {
4019                    continue;
4020                };
4021                let start = Anchor::in_buffer(excerpt.path_key_index, hunk.buffer_range.start)
4022                    .to_point(self);
4023                return Some(MultiBufferRow(start.row));
4024            }
4025        }
4026    }
4027
4028    pub fn has_diff_hunks(&self) -> bool {
4029        self.diffs.iter().any(|diff| !diff.is_empty())
4030    }
4031
4032    pub fn is_inside_word<T: ToOffset>(
4033        &self,
4034        position: T,
4035        scope_context: Option<CharScopeContext>,
4036    ) -> bool {
4037        let position = position.to_offset(self);
4038        let classifier = self
4039            .char_classifier_at(position)
4040            .scope_context(scope_context);
4041        let next_char_kind = self.chars_at(position).next().map(|c| classifier.kind(c));
4042        let prev_char_kind = self
4043            .reversed_chars_at(position)
4044            .next()
4045            .map(|c| classifier.kind(c));
4046        prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
4047    }
4048
4049    pub fn surrounding_word<T: ToOffset>(
4050        &self,
4051        start: T,
4052        scope_context: Option<CharScopeContext>,
4053    ) -> (Range<MultiBufferOffset>, Option<CharKind>) {
4054        let mut start = start.to_offset(self);
4055        let mut end = start;
4056        let mut next_chars = self.chars_at(start).peekable();
4057        let mut prev_chars = self.reversed_chars_at(start).peekable();
4058
4059        let classifier = self.char_classifier_at(start).scope_context(scope_context);
4060
4061        let word_kind = cmp::max(
4062            prev_chars.peek().copied().map(|c| classifier.kind(c)),
4063            next_chars.peek().copied().map(|c| classifier.kind(c)),
4064        );
4065
4066        for ch in prev_chars {
4067            if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4068                start -= ch.len_utf8();
4069            } else {
4070                break;
4071            }
4072        }
4073
4074        for ch in next_chars {
4075            if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4076                end += ch.len_utf8();
4077            } else {
4078                break;
4079            }
4080        }
4081
4082        (start..end, word_kind)
4083    }
4084
4085    pub fn char_kind_before<T: ToOffset>(
4086        &self,
4087        start: T,
4088        scope_context: Option<CharScopeContext>,
4089    ) -> Option<CharKind> {
4090        let start = start.to_offset(self);
4091        let classifier = self.char_classifier_at(start).scope_context(scope_context);
4092        self.reversed_chars_at(start)
4093            .next()
4094            .map(|ch| classifier.kind(ch))
4095    }
4096
4097    pub fn all_buffer_ids(&self) -> impl Iterator<Item = BufferId> + '_ {
4098        self.buffers.iter().map(|(id, _)| *id)
4099    }
4100
4101    pub fn is_singleton(&self) -> bool {
4102        self.singleton
4103    }
4104
4105    pub fn as_singleton(&self) -> Option<&BufferSnapshot> {
4106        if self.is_singleton() {
4107            Some(self.excerpts.first()?.buffer_snapshot(&self))
4108        } else {
4109            None
4110        }
4111    }
4112
4113    pub fn len(&self) -> MultiBufferOffset {
4114        self.diff_transforms.summary().output.len
4115    }
4116
4117    pub fn max_position<MBD: MultiBufferDimension>(&self) -> MBD {
4118        MBD::from_summary(&self.text_summary())
4119    }
4120
4121    pub fn is_empty(&self) -> bool {
4122        self.diff_transforms.summary().output.len == MultiBufferOffset(0)
4123    }
4124
4125    pub fn widest_line_number(&self) -> u32 {
4126        // widest_line_number is 0-based, so 1 is added to get the displayed line number.
4127        self.excerpts.summary().widest_line_number + 1
4128    }
4129
4130    pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes<'_> {
4131        let range = range.start.to_offset(self)..range.end.to_offset(self);
4132        let mut excerpts = self.cursor::<MultiBufferOffset, BufferOffset>();
4133        excerpts.seek(&range.start);
4134
4135        let mut chunk;
4136        let mut has_trailing_newline;
4137        let excerpt_bytes;
4138        if let Some(region) = excerpts.region() {
4139            let mut bytes = region.buffer.bytes_in_range(
4140                region.buffer_range.start + (range.start - region.range.start)
4141                    ..(region.buffer_range.start + (range.end - region.range.start))
4142                        .min(region.buffer_range.end),
4143            );
4144            chunk = bytes.next().unwrap_or(&[][..]);
4145            excerpt_bytes = Some(bytes);
4146            has_trailing_newline = region.has_trailing_newline && range.end >= region.range.end;
4147            if chunk.is_empty() && has_trailing_newline {
4148                chunk = b"\n";
4149                has_trailing_newline = false;
4150            }
4151        } else {
4152            chunk = &[][..];
4153            excerpt_bytes = None;
4154            has_trailing_newline = false;
4155        };
4156
4157        MultiBufferBytes {
4158            range,
4159            cursor: excerpts,
4160            excerpt_bytes,
4161            has_trailing_newline,
4162            chunk,
4163        }
4164    }
4165
4166    pub fn reversed_bytes_in_range<T: ToOffset>(
4167        &self,
4168        range: Range<T>,
4169    ) -> ReversedMultiBufferBytes<'_> {
4170        let range = range.start.to_offset(self)..range.end.to_offset(self);
4171        let mut chunks = self.reversed_chunks_in_range(range.clone());
4172        let chunk = chunks.next().map_or(&[][..], |c| c.as_bytes());
4173        ReversedMultiBufferBytes {
4174            range,
4175            chunks,
4176            chunk,
4177        }
4178    }
4179
4180    pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> {
4181        let mut cursor = self.cursor::<Point, Point>();
4182        cursor.seek(&Point::new(start_row.0, 0));
4183        let mut result = MultiBufferRows {
4184            point: Point::new(0, 0),
4185            is_empty: self.excerpts.is_empty(),
4186            is_singleton: self.is_singleton(),
4187            cursor,
4188        };
4189        result.seek(start_row);
4190        result
4191    }
4192
4193    pub fn chunks<T: ToOffset>(
4194        &self,
4195        range: Range<T>,
4196        language_aware: LanguageAwareStyling,
4197    ) -> MultiBufferChunks<'_> {
4198        let mut chunks = MultiBufferChunks {
4199            excerpt_offset_range: ExcerptDimension(MultiBufferOffset::ZERO)
4200                ..ExcerptDimension(MultiBufferOffset::ZERO),
4201            range: MultiBufferOffset::ZERO..MultiBufferOffset::ZERO,
4202            excerpts: self.excerpts.cursor(()),
4203            diff_transforms: self.diff_transforms.cursor(()),
4204            diff_base_chunks: None,
4205            excerpt_chunks: None,
4206            buffer_chunk: None,
4207            language_aware,
4208            snapshot: self,
4209        };
4210        let range = range.start.to_offset(self)..range.end.to_offset(self);
4211        chunks.seek(range);
4212        chunks
4213    }
4214
4215    pub fn clip_offset(&self, offset: MultiBufferOffset, bias: Bias) -> MultiBufferOffset {
4216        self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset)
4217    }
4218
4219    pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
4220        self.clip_dimension(point, bias, text::BufferSnapshot::clip_point)
4221    }
4222
4223    pub fn clip_offset_utf16(
4224        &self,
4225        offset: MultiBufferOffsetUtf16,
4226        bias: Bias,
4227    ) -> MultiBufferOffsetUtf16 {
4228        self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset_utf16)
4229    }
4230
4231    pub fn clip_point_utf16(&self, point: Unclipped<PointUtf16>, bias: Bias) -> PointUtf16 {
4232        self.clip_dimension(point.0, bias, |buffer, point, bias| {
4233            buffer.clip_point_utf16(Unclipped(point), bias)
4234        })
4235    }
4236
4237    pub fn offset_to_point(&self, offset: MultiBufferOffset) -> Point {
4238        self.convert_dimension(offset, text::BufferSnapshot::offset_to_point)
4239    }
4240
4241    pub fn offset_to_point_utf16(&self, offset: MultiBufferOffset) -> PointUtf16 {
4242        self.convert_dimension(offset, text::BufferSnapshot::offset_to_point_utf16)
4243    }
4244
4245    pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
4246        self.convert_dimension(point, text::BufferSnapshot::point_to_point_utf16)
4247    }
4248
4249    pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
4250        self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_point)
4251    }
4252
4253    #[instrument(skip_all)]
4254    pub fn point_to_offset(&self, point: Point) -> MultiBufferOffset {
4255        self.convert_dimension(point, text::BufferSnapshot::point_to_offset)
4256    }
4257
4258    pub fn point_to_offset_utf16(&self, point: Point) -> MultiBufferOffsetUtf16 {
4259        self.convert_dimension(point, text::BufferSnapshot::point_to_offset_utf16)
4260    }
4261
4262    pub fn offset_utf16_to_offset(&self, offset: MultiBufferOffsetUtf16) -> MultiBufferOffset {
4263        self.convert_dimension(offset, text::BufferSnapshot::offset_utf16_to_offset)
4264    }
4265
4266    pub fn offset_to_offset_utf16(&self, offset: MultiBufferOffset) -> MultiBufferOffsetUtf16 {
4267        self.convert_dimension(offset, text::BufferSnapshot::offset_to_offset_utf16)
4268    }
4269
4270    pub fn point_utf16_to_offset(&self, point: PointUtf16) -> MultiBufferOffset {
4271        self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset)
4272    }
4273
4274    pub fn point_utf16_to_offset_utf16(&self, point: PointUtf16) -> MultiBufferOffsetUtf16 {
4275        self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset_utf16)
4276    }
4277
4278    fn clip_dimension<MBD, BD>(
4279        &self,
4280        position: MBD,
4281        bias: Bias,
4282        clip_buffer_position: fn(&text::BufferSnapshot, BD, Bias) -> BD,
4283    ) -> MBD
4284    where
4285        MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBD as Sub>::Output>,
4286        BD: TextDimension + Sub<Output = <MBD as Sub>::Output> + AddAssign<<MBD as Sub>::Output>,
4287    {
4288        let mut cursor = self.cursor::<MBD, BD>();
4289        cursor.seek(&position);
4290        if let Some(region) = cursor.region() {
4291            if position >= region.range.end {
4292                return region.range.end;
4293            }
4294            let overshoot = position - region.range.start;
4295            let mut buffer_position = region.buffer_range.start;
4296            buffer_position += overshoot;
4297            let clipped_buffer_position =
4298                clip_buffer_position(region.buffer, buffer_position, bias);
4299            let mut position = region.range.start;
4300            position += clipped_buffer_position - region.buffer_range.start;
4301            position
4302        } else {
4303            self.max_position()
4304        }
4305    }
4306
4307    #[instrument(skip_all)]
4308    fn convert_dimension<MBR1, MBR2, BR1, BR2>(
4309        &self,
4310        key: MBR1,
4311        convert_buffer_dimension: fn(&text::BufferSnapshot, BR1) -> BR2,
4312    ) -> MBR2
4313    where
4314        MBR1: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBR1 as Sub>::Output>,
4315        BR1: TextDimension + Sub<Output = <MBR1 as Sub>::Output> + AddAssign<<MBR1 as Sub>::Output>,
4316        MBR2: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBR2 as Sub>::Output>,
4317        BR2: TextDimension + Sub<Output = <MBR2 as Sub>::Output> + AddAssign<<MBR2 as Sub>::Output>,
4318    {
4319        let mut cursor = self.cursor::<DimensionPair<MBR1, MBR2>, DimensionPair<BR1, BR2>>();
4320        cursor.seek(&DimensionPair { key, value: None });
4321        if let Some(region) = cursor.region() {
4322            if key >= region.range.end.key {
4323                return region.range.end.value.unwrap();
4324            }
4325            let start_key = region.range.start.key;
4326            let start_value = region.range.start.value.unwrap();
4327            let buffer_start_key = region.buffer_range.start.key;
4328            let buffer_start_value = region.buffer_range.start.value.unwrap();
4329            let mut buffer_key = buffer_start_key;
4330            buffer_key += key - start_key;
4331            let buffer_value = convert_buffer_dimension(region.buffer, buffer_key);
4332            let mut result = start_value;
4333            result += buffer_value - buffer_start_value;
4334            result
4335        } else {
4336            self.max_position()
4337        }
4338    }
4339
4340    pub fn point_to_buffer_offset<T: ToOffset>(
4341        &self,
4342        point: T,
4343    ) -> Option<(&BufferSnapshot, BufferOffset)> {
4344        let offset = point.to_offset(self);
4345        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
4346        cursor.seek(&offset);
4347        let region = cursor.region()?;
4348        let overshoot = offset - region.range.start;
4349        let buffer_offset = region.buffer_range.start + overshoot;
4350        if buffer_offset == BufferOffset(region.buffer.len() + 1)
4351            && region.has_trailing_newline
4352            && !region.is_main_buffer
4353        {
4354            let main_buffer_position = cursor.main_buffer_position()?;
4355            let buffer_snapshot = cursor.excerpt()?.buffer_snapshot(self);
4356            return Some((buffer_snapshot, main_buffer_position));
4357        } else if buffer_offset > BufferOffset(region.buffer.len()) {
4358            return None;
4359        }
4360        Some((region.buffer, buffer_offset))
4361    }
4362
4363    pub fn point_to_buffer_point(&self, point: Point) -> Option<(&BufferSnapshot, Point)> {
4364        let mut cursor = self.cursor::<Point, Point>();
4365        cursor.seek(&point);
4366        let region = cursor.region()?;
4367        let overshoot = point - region.range.start;
4368        let buffer_point = region.buffer_range.start + overshoot;
4369        let excerpt = cursor.excerpt()?;
4370        if buffer_point == region.buffer.max_point() + Point::new(1, 0)
4371            && region.has_trailing_newline
4372            && !region.is_main_buffer
4373        {
4374            return Some((
4375                &excerpt.buffer_snapshot(self),
4376                cursor.main_buffer_position()?,
4377            ));
4378        } else if buffer_point > region.buffer.max_point() {
4379            return None;
4380        }
4381        Some((region.buffer, buffer_point))
4382    }
4383
4384    pub fn suggested_indents(
4385        &self,
4386        rows: impl IntoIterator<Item = u32>,
4387        cx: &App,
4388    ) -> BTreeMap<MultiBufferRow, IndentSize> {
4389        let mut result = BTreeMap::new();
4390        self.suggested_indents_callback(
4391            rows,
4392            &mut |row, indent| {
4393                result.insert(row, indent);
4394                ControlFlow::Continue(())
4395            },
4396            cx,
4397        );
4398        result
4399    }
4400
4401    // move this to be a generator once those are a thing
4402    pub fn suggested_indents_callback(
4403        &self,
4404        rows: impl IntoIterator<Item = u32>,
4405        cb: &mut dyn FnMut(MultiBufferRow, IndentSize) -> ControlFlow<()>,
4406        cx: &App,
4407    ) {
4408        let mut rows_for_excerpt = Vec::new();
4409        let mut cursor = self.cursor::<Point, Point>();
4410        let mut rows = rows.into_iter().peekable();
4411        let mut prev_row = u32::MAX;
4412        let mut prev_language_indent_size = IndentSize::default();
4413
4414        while let Some(row) = rows.next() {
4415            cursor.seek(&Point::new(row, 0));
4416            let Some(region) = cursor.region() else {
4417                continue;
4418            };
4419
4420            // Retrieve the language and indent size once for each disjoint region being indented.
4421            let single_indent_size = if row.saturating_sub(1) == prev_row {
4422                prev_language_indent_size
4423            } else {
4424                region
4425                    .buffer
4426                    .language_indent_size_at(Point::new(row, 0), cx)
4427            };
4428            prev_language_indent_size = single_indent_size;
4429            prev_row = row;
4430
4431            let start_buffer_row = region.buffer_range.start.row;
4432            let start_multibuffer_row = region.range.start.row;
4433            let end_multibuffer_row = region.range.end.row;
4434
4435            rows_for_excerpt.push(row);
4436            while let Some(next_row) = rows.peek().copied() {
4437                if end_multibuffer_row > next_row {
4438                    rows_for_excerpt.push(next_row);
4439                    rows.next();
4440                } else {
4441                    break;
4442                }
4443            }
4444
4445            let buffer_rows = rows_for_excerpt
4446                .drain(..)
4447                .map(|row| start_buffer_row + row - start_multibuffer_row);
4448            let buffer_indents = region
4449                .buffer
4450                .suggested_indents(buffer_rows, single_indent_size);
4451            for (row, indent) in buffer_indents {
4452                if cb(
4453                    MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
4454                    indent,
4455                )
4456                .is_break()
4457                {
4458                    return;
4459                }
4460            }
4461        }
4462    }
4463
4464    pub fn indent_size_for_line(&self, row: MultiBufferRow) -> IndentSize {
4465        if let Some((buffer, range)) = self.buffer_line_for_row(row) {
4466            let mut size = buffer.indent_size_for_line(range.start.row);
4467            size.len = size
4468                .len
4469                .min(range.end.column)
4470                .saturating_sub(range.start.column);
4471            size
4472        } else {
4473            IndentSize::spaces(0)
4474        }
4475    }
4476
4477    pub fn line_indent_for_row(&self, row: MultiBufferRow) -> LineIndent {
4478        if let Some((buffer, range)) = self.buffer_line_for_row(row) {
4479            LineIndent::from_iter(buffer.text_for_range(range).flat_map(|s| s.chars()))
4480        } else {
4481            LineIndent::spaces(0)
4482        }
4483    }
4484
4485    pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &App) -> String {
4486        let mut indent = self.indent_size_for_line(row).chars().collect::<String>();
4487
4488        if self.language_settings(cx).extend_comment_on_newline
4489            && let Some(language_scope) = self.language_scope_at(Point::new(row.0, 0))
4490        {
4491            let delimiters = language_scope.line_comment_prefixes();
4492            for delimiter in delimiters {
4493                if *self
4494                    .chars_at(Point::new(row.0, indent.len() as u32))
4495                    .take(delimiter.chars().count())
4496                    .collect::<String>()
4497                    .as_str()
4498                    == **delimiter
4499                {
4500                    indent.push_str(delimiter);
4501                    break;
4502                }
4503            }
4504        }
4505
4506        indent
4507    }
4508
4509    pub fn is_line_whitespace_upto<T>(&self, position: T) -> bool
4510    where
4511        T: ToOffset,
4512    {
4513        for char in self.reversed_chars_at(position) {
4514            if !char.is_whitespace() {
4515                return false;
4516            }
4517            if char == '\n' {
4518                return true;
4519            }
4520        }
4521        true
4522    }
4523
4524    pub fn prev_non_blank_row(&self, mut row: MultiBufferRow) -> Option<MultiBufferRow> {
4525        while row.0 > 0 {
4526            row.0 -= 1;
4527            if !self.is_line_blank(row) {
4528                return Some(row);
4529            }
4530        }
4531        None
4532    }
4533
4534    pub fn line_len(&self, row: MultiBufferRow) -> u32 {
4535        if let Some((_, range)) = self.buffer_line_for_row(row) {
4536            range.end.column - range.start.column
4537        } else {
4538            0
4539        }
4540    }
4541
4542    pub fn line_len_utf16(&self, row: MultiBufferRow) -> u32 {
4543        self.clip_point_utf16(Unclipped(PointUtf16::new(row.0, u32::MAX)), Bias::Left)
4544            .column
4545    }
4546
4547    pub fn buffer_line_for_row(
4548        &self,
4549        row: MultiBufferRow,
4550    ) -> Option<(&BufferSnapshot, Range<Point>)> {
4551        let mut cursor = self.cursor::<Point, Point>();
4552        let point = Point::new(row.0, 0);
4553        cursor.seek(&point);
4554        let region = cursor.region()?;
4555        let overshoot = point.min(region.range.end) - region.range.start;
4556        let buffer_point = region.buffer_range.start + overshoot;
4557        if buffer_point.row > region.buffer_range.end.row {
4558            return None;
4559        }
4560        let line_start = Point::new(buffer_point.row, 0).max(region.buffer_range.start);
4561        let line_end = Point::new(buffer_point.row, region.buffer.line_len(buffer_point.row))
4562            .min(region.buffer_range.end);
4563        Some((region.buffer, line_start..line_end))
4564    }
4565
4566    pub fn max_point(&self) -> Point {
4567        self.text_summary().lines
4568    }
4569
4570    pub fn max_row(&self) -> MultiBufferRow {
4571        MultiBufferRow(self.text_summary().lines.row)
4572    }
4573
4574    pub fn text_summary(&self) -> MBTextSummary {
4575        self.diff_transforms.summary().output
4576    }
4577
4578    pub fn text_summary_for_range<MBD, O>(&self, range: Range<O>) -> MBD
4579    where
4580        MBD: MultiBufferDimension + AddAssign,
4581        O: ToOffset,
4582    {
4583        let range = range.start.to_offset(self)..range.end.to_offset(self);
4584        let mut cursor = self
4585            .diff_transforms
4586            .cursor::<Dimensions<MultiBufferOffset, ExcerptOffset>>(());
4587        cursor.seek(&range.start, Bias::Right);
4588
4589        let Some(first_transform) = cursor.item() else {
4590            return MBD::from_summary(&MBTextSummary::default());
4591        };
4592
4593        let diff_transform_start = cursor.start().0;
4594        let diff_transform_end = cursor.end().0;
4595        let diff_start = range.start;
4596        let start_overshoot = diff_start - diff_transform_start;
4597        let end_overshoot = std::cmp::min(range.end, diff_transform_end) - diff_transform_start;
4598
4599        let mut result = match first_transform {
4600            DiffTransform::BufferContent { .. } => {
4601                let excerpt_start = cursor.start().1 + start_overshoot;
4602                let excerpt_end = cursor.start().1 + end_overshoot;
4603                self.text_summary_for_excerpt_offset_range(excerpt_start..excerpt_end)
4604            }
4605            DiffTransform::DeletedHunk {
4606                buffer_id,
4607                base_text_byte_range,
4608                has_trailing_newline,
4609                ..
4610            } => {
4611                let buffer_start = base_text_byte_range.start + start_overshoot;
4612                let mut buffer_end = base_text_byte_range.start + end_overshoot;
4613                let Some(base_text) = self.diff_state(*buffer_id).map(|diff| diff.base_text())
4614                else {
4615                    panic!("{:?} is in non-existent deleted hunk", range.start)
4616                };
4617
4618                let include_trailing_newline =
4619                    *has_trailing_newline && range.end >= diff_transform_end;
4620                if include_trailing_newline {
4621                    buffer_end -= 1;
4622                }
4623
4624                let mut summary = base_text
4625                    .text_summary_for_range::<MBD::TextDimension, _>(buffer_start..buffer_end);
4626
4627                if include_trailing_newline {
4628                    summary.add_assign(&<MBD::TextDimension>::from_text_summary(
4629                        &TextSummary::newline(),
4630                    ))
4631                }
4632
4633                let mut result = MBD::default();
4634                result.add_text_dim(&summary);
4635                result
4636            }
4637        };
4638        if range.end < diff_transform_end {
4639            return result;
4640        }
4641
4642        cursor.next();
4643        result.add_mb_text_summary(
4644            &cursor
4645                .summary::<_, OutputDimension<_>>(&range.end, Bias::Right)
4646                .0,
4647        );
4648
4649        let Some(last_transform) = cursor.item() else {
4650            return result;
4651        };
4652
4653        let overshoot = range.end - cursor.start().0;
4654        let suffix = match last_transform {
4655            DiffTransform::BufferContent { .. } => {
4656                let end = cursor.start().1 + overshoot;
4657                self.text_summary_for_excerpt_offset_range::<MBD>(cursor.start().1..end)
4658            }
4659            DiffTransform::DeletedHunk {
4660                base_text_byte_range,
4661                buffer_id,
4662                has_trailing_newline,
4663                ..
4664            } => {
4665                let buffer_end = base_text_byte_range.start + overshoot;
4666                let Some(base_text) = self.diff_state(*buffer_id).map(|diff| diff.base_text())
4667                else {
4668                    panic!("{:?} is in non-existent deleted hunk", range.end)
4669                };
4670
4671                let mut suffix = base_text.text_summary_for_range::<MBD::TextDimension, _>(
4672                    base_text_byte_range.start..buffer_end,
4673                );
4674                if *has_trailing_newline && buffer_end == base_text_byte_range.end + 1 {
4675                    suffix.add_assign(&<MBD::TextDimension>::from_text_summary(
4676                        &TextSummary::from("\n"),
4677                    ))
4678                }
4679
4680                let mut result = MBD::default();
4681                result.add_text_dim(&suffix);
4682                result
4683            }
4684        };
4685
4686        result += suffix;
4687        result
4688    }
4689
4690    fn text_summary_for_excerpt_offset_range<MBD>(&self, mut range: Range<ExcerptOffset>) -> MBD
4691    where
4692        MBD: MultiBufferDimension + AddAssign,
4693    {
4694        let mut summary = MBD::default();
4695        let mut cursor = self.excerpts.cursor::<ExcerptOffset>(());
4696        cursor.seek(&range.start, Bias::Right);
4697        if let Some(excerpt) = cursor.item() {
4698            let buffer_snapshot = excerpt.buffer_snapshot(self);
4699            let mut end_before_newline = cursor.end();
4700            if excerpt.has_trailing_newline {
4701                end_before_newline -= 1;
4702            }
4703
4704            let excerpt_start = excerpt.range.context.start.to_offset(&buffer_snapshot);
4705            let start_in_excerpt = excerpt_start + (range.start - *cursor.start());
4706            let end_in_excerpt =
4707                excerpt_start + (cmp::min(end_before_newline, range.end) - *cursor.start());
4708            summary.add_text_dim(
4709                &buffer_snapshot.text_summary_for_range::<MBD::TextDimension, _>(
4710                    start_in_excerpt..end_in_excerpt,
4711                ),
4712            );
4713
4714            if range.end > end_before_newline {
4715                summary.add_mb_text_summary(&MBTextSummary::from(TextSummary::newline()));
4716            }
4717
4718            cursor.next();
4719        }
4720
4721        if range.end > *cursor.start() {
4722            summary += cursor
4723                .summary::<_, ExcerptDimension<MBD>>(&range.end, Bias::Right)
4724                .0;
4725            if let Some(excerpt) = cursor.item() {
4726                let buffer_snapshot = excerpt.buffer_snapshot(self);
4727                range.end = cmp::max(*cursor.start(), range.end);
4728
4729                let excerpt_start = excerpt.range.context.start.to_offset(&buffer_snapshot);
4730                let end_in_excerpt = excerpt_start + (range.end - *cursor.start());
4731                summary.add_text_dim(
4732                    &buffer_snapshot.text_summary_for_range::<MBD::TextDimension, _>(
4733                        excerpt_start..end_in_excerpt,
4734                    ),
4735                );
4736            }
4737        }
4738
4739        summary
4740    }
4741
4742    pub fn summary_for_anchor<MBD>(&self, anchor: &Anchor) -> MBD
4743    where
4744        MBD: MultiBufferDimension
4745            + Ord
4746            + Sub<Output = MBD::TextDimension>
4747            + Sub<MBD::TextDimension, Output = MBD>
4748            + AddAssign<MBD::TextDimension>
4749            + Add<MBD::TextDimension, Output = MBD>,
4750        MBD::TextDimension: Sub<Output = MBD::TextDimension> + Ord,
4751    {
4752        let target = anchor.seek_target(self);
4753        let anchor = match anchor {
4754            Anchor::Min => {
4755                return MBD::default();
4756            }
4757            Anchor::Excerpt(excerpt_anchor) => excerpt_anchor,
4758            Anchor::Max => {
4759                return MBD::from_summary(&self.text_summary());
4760            }
4761        };
4762
4763        let (start, _, item) = self
4764            .excerpts
4765            .find::<ExcerptSummary, _>((), &target, Bias::Left);
4766        let start = MBD::from_summary(&start.text);
4767
4768        let excerpt_start_position = ExcerptDimension(start);
4769        if self.diff_transforms.is_empty() {
4770            if let Some(excerpt) = item {
4771                if !excerpt.contains(anchor, self) {
4772                    return excerpt_start_position.0;
4773                }
4774                let buffer_snapshot = excerpt.buffer_snapshot(self);
4775                let excerpt_buffer_start = excerpt
4776                    .range
4777                    .context
4778                    .start
4779                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4780                let excerpt_buffer_end = excerpt
4781                    .range
4782                    .context
4783                    .end
4784                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4785                let buffer_summary = anchor
4786                    .text_anchor()
4787                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4788                let summary = cmp::min(excerpt_buffer_end, buffer_summary);
4789                let mut position = excerpt_start_position;
4790                if summary > excerpt_buffer_start {
4791                    position += summary - excerpt_buffer_start;
4792                }
4793
4794                position.0
4795            } else {
4796                excerpt_start_position.0
4797            }
4798        } else {
4799            let mut diff_transforms_cursor = self
4800                .diff_transforms
4801                .cursor::<Dimensions<ExcerptDimension<MBD>, OutputDimension<MBD>>>(());
4802
4803            if let Some(excerpt) = item {
4804                if !excerpt.contains(anchor, self) {
4805                    diff_transforms_cursor.seek(&excerpt_start_position, Bias::Left);
4806                    return self.summary_for_excerpt_position_without_hunks(
4807                        Bias::Left,
4808                        excerpt_start_position,
4809                        &mut diff_transforms_cursor,
4810                    );
4811                }
4812                let buffer_snapshot = excerpt.buffer_snapshot(self);
4813                let excerpt_buffer_start = excerpt
4814                    .range
4815                    .context
4816                    .start
4817                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4818                let excerpt_buffer_end = excerpt
4819                    .range
4820                    .context
4821                    .end
4822                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4823                let buffer_summary = anchor
4824                    .text_anchor()
4825                    .summary::<MBD::TextDimension>(&buffer_snapshot);
4826                let summary = cmp::min(excerpt_buffer_end, buffer_summary);
4827                let mut position = excerpt_start_position;
4828                if summary > excerpt_buffer_start {
4829                    position += summary - excerpt_buffer_start;
4830                }
4831
4832                diff_transforms_cursor.seek(&position, Bias::Left);
4833                self.summary_for_anchor_with_excerpt_position(
4834                    *anchor,
4835                    position,
4836                    &mut diff_transforms_cursor,
4837                    &buffer_snapshot,
4838                )
4839            } else {
4840                diff_transforms_cursor.seek(&excerpt_start_position, Bias::Left);
4841                self.summary_for_excerpt_position_without_hunks(
4842                    Bias::Right,
4843                    excerpt_start_position,
4844                    &mut diff_transforms_cursor,
4845                )
4846            }
4847        }
4848    }
4849
4850    /// Maps an anchor's excerpt-space position to its output-space position by
4851    /// walking the diff transforms. The cursor is shared across consecutive
4852    /// calls, so it may already be partway through the transform list.
4853    fn summary_for_anchor_with_excerpt_position<MBD>(
4854        &self,
4855        anchor: ExcerptAnchor,
4856        excerpt_position: ExcerptDimension<MBD>,
4857        diff_transforms: &mut Cursor<
4858            DiffTransform,
4859            Dimensions<ExcerptDimension<MBD>, OutputDimension<MBD>>,
4860        >,
4861        excerpt_buffer: &text::BufferSnapshot,
4862    ) -> MBD
4863    where
4864        MBD: MultiBufferDimension + Ord + Sub + AddAssign<<MBD as Sub>::Output>,
4865    {
4866        loop {
4867            let transform_end_position = diff_transforms.end().0;
4868            let item = diff_transforms.item();
4869            let at_transform_end = transform_end_position == excerpt_position && item.is_some();
4870
4871            // A right-biased anchor at a transform boundary belongs to the
4872            // *next* transform, so advance past the current one.
4873            if anchor.text_anchor.bias == Bias::Right && at_transform_end {
4874                diff_transforms.next();
4875                continue;
4876            }
4877
4878            let mut position = diff_transforms.start().1;
4879            match item {
4880                Some(DiffTransform::DeletedHunk {
4881                    buffer_id,
4882                    base_text_byte_range,
4883                    hunk_info,
4884                    ..
4885                }) => {
4886                    if let Some(diff_base_anchor) = anchor.diff_base_anchor
4887                        && let Some(base_text) =
4888                            self.diff_state(*buffer_id).map(|diff| diff.base_text())
4889                        && diff_base_anchor.is_valid(&base_text)
4890                    {
4891                        // The anchor carries a diff-base position — resolve it
4892                        // to a location inside the deleted hunk.
4893                        let base_text_offset = diff_base_anchor.to_offset(base_text);
4894                        if base_text_offset >= base_text_byte_range.start
4895                            && base_text_offset <= base_text_byte_range.end
4896                        {
4897                            let position_in_hunk = base_text
4898                                .text_summary_for_range::<MBD::TextDimension, _>(
4899                                    base_text_byte_range.start..base_text_offset,
4900                                );
4901                            position.0.add_text_dim(&position_in_hunk);
4902                        } else if at_transform_end {
4903                            // diff_base offset falls outside this hunk's range;
4904                            // advance to see if the next transform is a better fit.
4905                            diff_transforms.next();
4906                            continue;
4907                        }
4908                    } else if at_transform_end
4909                        && anchor
4910                            .text_anchor()
4911                            .cmp(&hunk_info.hunk_start_anchor, excerpt_buffer)
4912                            .is_gt()
4913                    {
4914                        // The anchor has no (valid) diff-base position, so it
4915                        // belongs in the buffer content, not in the deleted
4916                        // hunk. However, after an edit deletes the text between
4917                        // the hunk boundary and this anchor, both resolve to
4918                        // the same excerpt_position—landing us here on the
4919                        // DeletedHunk left behind by the shared cursor. Use the
4920                        // CRDT ordering to detect that the anchor is strictly
4921                        // *past* the hunk boundary and skip to the following
4922                        // BufferContent.
4923                        diff_transforms.next();
4924                        continue;
4925                    }
4926                }
4927                _ => {
4928                    // On a BufferContent (or no transform). If the anchor
4929                    // carries a diff_base_anchor it needs a DeletedHunk, so
4930                    // advance to find one.
4931                    if at_transform_end && anchor.diff_base_anchor.is_some() {
4932                        diff_transforms.next();
4933                        continue;
4934                    }
4935                    let overshoot = excerpt_position - diff_transforms.start().0;
4936                    position += overshoot;
4937                }
4938            }
4939
4940            return position.0;
4941        }
4942    }
4943
4944    /// Like `resolve_summary_for_anchor` but optimized for min/max anchors.
4945    fn summary_for_excerpt_position_without_hunks<MBD>(
4946        &self,
4947        bias: Bias,
4948        excerpt_position: ExcerptDimension<MBD>,
4949        diff_transforms: &mut Cursor<
4950            DiffTransform,
4951            Dimensions<ExcerptDimension<MBD>, OutputDimension<MBD>>,
4952        >,
4953    ) -> MBD
4954    where
4955        MBD: MultiBufferDimension + Ord + Sub + AddAssign<<MBD as Sub>::Output>,
4956    {
4957        loop {
4958            let transform_end_position = diff_transforms.end().0;
4959            let item = diff_transforms.item();
4960            let at_transform_end = transform_end_position == excerpt_position && item.is_some();
4961
4962            // A right-biased anchor at a transform boundary belongs to the
4963            // *next* transform, so advance past the current one.
4964            if bias == Bias::Right && at_transform_end {
4965                diff_transforms.next();
4966                continue;
4967            }
4968
4969            let mut position = diff_transforms.start().1;
4970            if let Some(DiffTransform::BufferContent { .. }) | None = item {
4971                let overshoot = excerpt_position - diff_transforms.start().0;
4972                position += overshoot;
4973            }
4974
4975            return position.0;
4976        }
4977    }
4978
4979    fn excerpt_offset_for_anchor(&self, anchor: &Anchor) -> ExcerptOffset {
4980        let anchor = match anchor {
4981            Anchor::Min => return ExcerptOffset::default(),
4982            Anchor::Excerpt(excerpt_anchor) => excerpt_anchor,
4983            Anchor::Max => return self.excerpts.summary().len(),
4984        };
4985        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
4986        let target = anchor.seek_target(self);
4987
4988        cursor.seek(&target, Bias::Left);
4989
4990        let mut position = cursor.start().len();
4991        if let Some(excerpt) = cursor.item()
4992            && excerpt.contains(anchor, self)
4993        {
4994            let buffer_snapshot = excerpt.buffer_snapshot(self);
4995            let excerpt_buffer_start =
4996                buffer_snapshot.offset_for_anchor(&excerpt.range.context.start);
4997            let excerpt_buffer_end = buffer_snapshot.offset_for_anchor(&excerpt.range.context.end);
4998            let buffer_position = cmp::min(
4999                excerpt_buffer_end,
5000                buffer_snapshot.offset_for_anchor(&anchor.text_anchor()),
5001            );
5002            if buffer_position > excerpt_buffer_start {
5003                position += buffer_position - excerpt_buffer_start;
5004            }
5005        }
5006        position
5007    }
5008
5009    pub fn summaries_for_anchors<'a, MBD, I>(&'a self, anchors: I) -> Vec<MBD>
5010    where
5011        MBD: MultiBufferDimension
5012            + Ord
5013            + Sub<Output = MBD::TextDimension>
5014            + AddAssign<MBD::TextDimension>,
5015        MBD::TextDimension: Sub<Output = MBD::TextDimension> + Ord,
5016        I: 'a + IntoIterator<Item = &'a Anchor>,
5017    {
5018        let mut anchors = anchors.into_iter().peekable();
5019        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
5020        let mut diff_transforms_cursor = self
5021            .diff_transforms
5022            .cursor::<Dimensions<ExcerptDimension<MBD>, OutputDimension<MBD>>>(());
5023        diff_transforms_cursor.next();
5024
5025        let mut summaries = Vec::new();
5026        while let Some(anchor) = anchors.peek() {
5027            let target = anchor.seek_target(self);
5028            let excerpt_anchor = match anchor {
5029                Anchor::Min => {
5030                    summaries.push(MBD::default());
5031                    anchors.next();
5032                    continue;
5033                }
5034                Anchor::Excerpt(excerpt_anchor) => excerpt_anchor,
5035                Anchor::Max => {
5036                    summaries.push(MBD::from_summary(&self.text_summary()));
5037                    anchors.next();
5038                    continue;
5039                }
5040            };
5041
5042            cursor.seek_forward(&target, Bias::Left);
5043
5044            let excerpt_start_position = ExcerptDimension(MBD::from_summary(&cursor.start().text));
5045            if let Some(excerpt) = cursor.item() {
5046                let buffer_snapshot = excerpt.buffer_snapshot(self);
5047                if !excerpt.contains(&excerpt_anchor, self) {
5048                    diff_transforms_cursor.seek_forward(&excerpt_start_position, Bias::Left);
5049                    let position = self.summary_for_excerpt_position_without_hunks(
5050                        Bias::Left,
5051                        excerpt_start_position,
5052                        &mut diff_transforms_cursor,
5053                    );
5054                    summaries.push(position);
5055                    anchors.next();
5056                    continue;
5057                }
5058                let excerpt_buffer_start = excerpt
5059                    .range
5060                    .context
5061                    .start
5062                    .summary::<MBD::TextDimension>(buffer_snapshot);
5063                let excerpt_buffer_end = excerpt
5064                    .range
5065                    .context
5066                    .end
5067                    .summary::<MBD::TextDimension>(buffer_snapshot);
5068                for (buffer_summary, excerpt_anchor) in buffer_snapshot
5069                    .summaries_for_anchors_with_payload::<MBD::TextDimension, _, _>(
5070                        std::iter::from_fn(|| {
5071                            let excerpt_anchor = anchors.peek()?.excerpt_anchor()?;
5072                            if !excerpt.contains(&excerpt_anchor, self) {
5073                                return None;
5074                            }
5075                            anchors.next();
5076                            Some((excerpt_anchor.text_anchor(), excerpt_anchor))
5077                        }),
5078                    )
5079                {
5080                    let summary = cmp::min(excerpt_buffer_end, buffer_summary);
5081                    let mut position = excerpt_start_position;
5082                    if summary > excerpt_buffer_start {
5083                        position += summary - excerpt_buffer_start;
5084                    }
5085
5086                    if diff_transforms_cursor.start().0 < position {
5087                        diff_transforms_cursor.seek_forward(&position, Bias::Left);
5088                    }
5089
5090                    summaries.push(self.summary_for_anchor_with_excerpt_position(
5091                        excerpt_anchor,
5092                        position,
5093                        &mut diff_transforms_cursor,
5094                        &buffer_snapshot,
5095                    ));
5096                }
5097            } else {
5098                diff_transforms_cursor.seek_forward(&excerpt_start_position, Bias::Left);
5099                let position = self.summary_for_excerpt_position_without_hunks(
5100                    Bias::Right,
5101                    excerpt_start_position,
5102                    &mut diff_transforms_cursor,
5103                );
5104                summaries.push(position);
5105                anchors.next();
5106            }
5107        }
5108
5109        summaries
5110    }
5111
5112    pub fn dimensions_from_points<'a, MBD>(
5113        &'a self,
5114        points: impl 'a + IntoIterator<Item = Point>,
5115    ) -> impl 'a + Iterator<Item = MBD>
5116    where
5117        MBD: MultiBufferDimension + Sub + AddAssign<<MBD as Sub>::Output>,
5118    {
5119        let mut cursor = self.cursor::<DimensionPair<Point, MBD>, Point>();
5120        cursor.seek(&DimensionPair {
5121            key: Point::default(),
5122            value: None,
5123        });
5124        let mut points = points.into_iter();
5125        iter::from_fn(move || {
5126            let point = points.next()?;
5127
5128            cursor.seek_forward(&DimensionPair {
5129                key: point,
5130                value: None,
5131            });
5132
5133            if let Some(region) = cursor.region() {
5134                let overshoot = point - region.range.start.key;
5135                let buffer_point = region.buffer_range.start + overshoot;
5136                let mut position = region.range.start.value.unwrap();
5137                position.add_text_dim(
5138                    &region
5139                        .buffer
5140                        .text_summary_for_range(region.buffer_range.start..buffer_point),
5141                );
5142                if point == region.range.end.key && region.has_trailing_newline {
5143                    position.add_mb_text_summary(&MBTextSummary::from(TextSummary::newline()));
5144                }
5145                Some(position)
5146            } else {
5147                Some(MBD::from_summary(&self.text_summary()))
5148            }
5149        })
5150    }
5151
5152    pub fn excerpts_for_buffer(
5153        &self,
5154        buffer_id: BufferId,
5155    ) -> impl Iterator<Item = ExcerptRange<text::Anchor>> {
5156        if let Some(buffer_state) = self.buffers.get(&buffer_id) {
5157            let path_key = buffer_state.path_key.clone();
5158            let mut cursor = self.excerpts.cursor::<PathKey>(());
5159            cursor.seek_forward(&path_key, Bias::Left);
5160            Some(iter::from_fn(move || {
5161                let excerpt = cursor.item()?;
5162                if excerpt.path_key != path_key {
5163                    return None;
5164                }
5165                cursor.next();
5166                Some(excerpt.range.clone())
5167            }))
5168        } else {
5169            None
5170        }
5171        .into_iter()
5172        .flatten()
5173    }
5174
5175    pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
5176        self.anchor_at(position, Bias::Left)
5177    }
5178
5179    pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
5180        self.anchor_at(position, Bias::Right)
5181    }
5182
5183    pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
5184        let offset = position.to_offset(self);
5185
5186        // Find the given position in the diff transforms. Determine the corresponding
5187        // offset in the excerpts, and whether the position is within a deleted hunk.
5188        let mut diff_transforms = self
5189            .diff_transforms
5190            .cursor::<Dimensions<MultiBufferOffset, ExcerptOffset>>(());
5191        diff_transforms.seek(&offset, Bias::Right);
5192
5193        if offset == diff_transforms.start().0
5194            && bias == Bias::Left
5195            && let Some(prev_item) = diff_transforms.prev_item()
5196            && let DiffTransform::DeletedHunk { .. } = prev_item
5197        {
5198            diff_transforms.prev();
5199        }
5200        let offset_in_transform = offset - diff_transforms.start().0;
5201        let mut excerpt_offset = diff_transforms.start().1;
5202        let mut diff_base_anchor = None;
5203        if let Some(DiffTransform::DeletedHunk {
5204            buffer_id,
5205            base_text_byte_range,
5206            has_trailing_newline,
5207            ..
5208        }) = diff_transforms.item()
5209        {
5210            let diff = self.diff_state(*buffer_id).expect("missing diff");
5211            if offset_in_transform > base_text_byte_range.len() {
5212                debug_assert!(*has_trailing_newline);
5213                bias = Bias::Right;
5214            } else {
5215                diff_base_anchor = Some(
5216                    diff.base_text()
5217                        .anchor_at(base_text_byte_range.start + offset_in_transform, bias),
5218                );
5219                bias = Bias::Left;
5220            }
5221        } else {
5222            excerpt_offset += MultiBufferOffset(offset_in_transform);
5223        };
5224
5225        let mut excerpts = self
5226            .excerpts
5227            .cursor::<Dimensions<ExcerptOffset, ExcerptSummary>>(());
5228        excerpts.seek(&excerpt_offset, Bias::Right);
5229        if excerpts.item().is_none() && excerpt_offset == excerpts.start().0 && bias == Bias::Left {
5230            excerpts.prev();
5231        }
5232        if let Some(excerpt) = excerpts.item() {
5233            let buffer_snapshot = excerpt.buffer_snapshot(self);
5234            let mut overshoot = excerpt_offset.saturating_sub(excerpts.start().0);
5235            if excerpt.has_trailing_newline && excerpt_offset == excerpts.end().0 {
5236                overshoot -= 1;
5237                bias = Bias::Right;
5238            }
5239
5240            let buffer_start = excerpt.range.context.start.to_offset(&buffer_snapshot);
5241            let text_anchor = excerpt.clip_anchor(
5242                buffer_snapshot.anchor_at(buffer_start + overshoot, bias),
5243                self,
5244            );
5245            let anchor = ExcerptAnchor::in_buffer(excerpt.path_key_index, text_anchor);
5246            let anchor = match diff_base_anchor {
5247                Some(diff_base_anchor) => anchor.with_diff_base_anchor(diff_base_anchor),
5248                None => anchor,
5249            };
5250            anchor.into()
5251        } else if excerpt_offset == ExcerptDimension(MultiBufferOffset::ZERO) && bias == Bias::Left
5252        {
5253            Anchor::Min
5254        } else {
5255            Anchor::Max
5256        }
5257    }
5258
5259    /// Lifts a buffer anchor to a multibuffer anchor without checking against excerpt boundaries. Returns `None` if there are no excerpts for the buffer
5260    pub fn anchor_in_buffer(&self, anchor: text::Anchor) -> Option<Anchor> {
5261        let path_key_index = self.path_key_index_for_buffer(anchor.buffer_id)?;
5262        Some(Anchor::in_buffer(path_key_index, anchor))
5263    }
5264
5265    /// Creates a multibuffer anchor for the given buffer anchor, if it is contained in any excerpt.
5266    pub fn anchor_in_excerpt(&self, text_anchor: text::Anchor) -> Option<Anchor> {
5267        for excerpt in {
5268            let this = &self;
5269            let buffer_id = text_anchor.buffer_id;
5270            if let Some(buffer_state) = this.buffers.get(&buffer_id) {
5271                let path_key = buffer_state.path_key.clone();
5272                let mut cursor = this.excerpts.cursor::<PathKey>(());
5273                cursor.seek_forward(&path_key, Bias::Left);
5274                Some(iter::from_fn(move || {
5275                    let excerpt = cursor.item()?;
5276                    if excerpt.path_key != path_key {
5277                        return None;
5278                    }
5279                    cursor.next();
5280                    Some(excerpt)
5281                }))
5282            } else {
5283                None
5284            }
5285            .into_iter()
5286            .flatten()
5287        } {
5288            let buffer_snapshot = excerpt.buffer_snapshot(self);
5289            if excerpt.range.contains(&text_anchor, &buffer_snapshot) {
5290                return Some(Anchor::in_buffer(excerpt.path_key_index, text_anchor));
5291            }
5292        }
5293
5294        None
5295    }
5296
5297    /// Creates a multibuffer anchor for the given buffer anchor, if it is contained in any excerpt.
5298    pub fn buffer_anchor_range_to_anchor_range(
5299        &self,
5300        text_anchor: Range<text::Anchor>,
5301    ) -> Option<Range<Anchor>> {
5302        if self.is_singleton() {
5303            let excerpt = self.excerpts.first()?;
5304            let buffer_snapshot = excerpt.buffer_snapshot(self);
5305            if excerpt.range.contains(&text_anchor.start, &buffer_snapshot)
5306                && excerpt.range.contains(&text_anchor.end, &buffer_snapshot)
5307            {
5308                return Some(Anchor::range_in_buffer(excerpt.path_key_index, text_anchor));
5309            }
5310        }
5311
5312        // for each search match
5313
5314        let mut buffer_snapshot = None;
5315        for excerpt in {
5316            let this = &self;
5317            let buffer_id = text_anchor.start.buffer_id;
5318            if let Some(buffer_state) = this.buffers.get(&buffer_id) {
5319                let path_key = buffer_state.path_key.clone();
5320                let mut cursor = this.excerpts.cursor::<PathKey>(());
5321                cursor.seek_forward(&path_key, Bias::Left);
5322                Some(iter::from_fn(move || {
5323                    let excerpt = cursor.item()?;
5324                    if excerpt.path_key != path_key {
5325                        return None;
5326                    }
5327                    cursor.next();
5328                    Some(excerpt)
5329                }))
5330            } else {
5331                None
5332            }
5333            .into_iter()
5334            .flatten()
5335        } {
5336            let buffer_snapshot =
5337                buffer_snapshot.get_or_insert_with(|| excerpt.buffer_snapshot(self));
5338            if excerpt.range.contains(&text_anchor.start, &buffer_snapshot)
5339                && excerpt.range.contains(&text_anchor.end, &buffer_snapshot)
5340            {
5341                return Some(Anchor::range_in_buffer(excerpt.path_key_index, text_anchor));
5342            }
5343        }
5344
5345        None
5346    }
5347
5348    /// Returns a buffer anchor and its buffer snapshot for the given anchor, if it is in the multibuffer.
5349    pub fn anchor_to_buffer_anchor(
5350        &self,
5351        anchor: Anchor,
5352    ) -> Option<(text::Anchor, &BufferSnapshot)> {
5353        match anchor {
5354            Anchor::Min => {
5355                let excerpt = self.excerpts.first()?;
5356                let buffer = excerpt.buffer_snapshot(self);
5357                Some((excerpt.range.context.start, buffer))
5358            }
5359            Anchor::Excerpt(excerpt_anchor) => {
5360                let buffer = self.buffer_for_id(excerpt_anchor.buffer_id())?;
5361                Some((excerpt_anchor.text_anchor, buffer))
5362            }
5363            Anchor::Max => {
5364                let excerpt = self.excerpts.last()?;
5365                let buffer = excerpt.buffer_snapshot(self);
5366                Some((excerpt.range.context.end, buffer))
5367            }
5368        }
5369    }
5370
5371    pub fn can_resolve(&self, anchor: &Anchor) -> bool {
5372        match anchor {
5373            // todo(lw): should be `!self.is_empty()`
5374            Anchor::Min | Anchor::Max => true,
5375            Anchor::Excerpt(excerpt_anchor) => {
5376                let Some(target) = excerpt_anchor.try_seek_target(self) else {
5377                    return false;
5378                };
5379                let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
5380                cursor.seek(&target, Bias::Left);
5381                let Some(excerpt) = cursor.item() else {
5382                    return false;
5383                };
5384                excerpt
5385                    .buffer_snapshot(self)
5386                    .can_resolve(&excerpt_anchor.text_anchor())
5387            }
5388        }
5389    }
5390
5391    pub fn excerpts(&self) -> impl Iterator<Item = ExcerptRange<text::Anchor>> {
5392        self.excerpts.iter().map(|excerpt| excerpt.range.clone())
5393    }
5394
5395    fn cursor<'a, MBD, BD>(&'a self) -> MultiBufferCursor<'a, MBD, BD>
5396    where
5397        MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBD as Sub>::Output>,
5398        BD: TextDimension + AddAssign<<MBD as Sub>::Output>,
5399    {
5400        let excerpts = self.excerpts.cursor(());
5401        let diff_transforms = self.diff_transforms.cursor(());
5402        MultiBufferCursor {
5403            excerpts,
5404            diff_transforms,
5405            cached_region: OnceCell::new(),
5406            snapshot: self,
5407        }
5408    }
5409
5410    pub fn excerpt_before(&self, anchor: Anchor) -> Option<ExcerptRange<text::Anchor>> {
5411        let target = anchor.try_seek_target(&self)?;
5412        let mut excerpts = self.excerpts.cursor::<ExcerptSummary>(());
5413        excerpts.seek(&target, Bias::Left);
5414        excerpts.prev();
5415        Some(excerpts.item()?.range.clone())
5416    }
5417
5418    pub fn excerpt_boundaries_in_range<R, T>(
5419        &self,
5420        range: R,
5421    ) -> impl Iterator<Item = ExcerptBoundary> + '_
5422    where
5423        R: RangeBounds<T>,
5424        T: ToOffset,
5425    {
5426        let start_offset;
5427        let start = match range.start_bound() {
5428            Bound::Included(start) => {
5429                start_offset = start.to_offset(self);
5430                Bound::Included(start_offset)
5431            }
5432            Bound::Excluded(_) => {
5433                panic!("not supported")
5434            }
5435            Bound::Unbounded => {
5436                start_offset = MultiBufferOffset::ZERO;
5437                Bound::Unbounded
5438            }
5439        };
5440        let end = match range.end_bound() {
5441            Bound::Included(end) => Bound::Included(end.to_offset(self)),
5442            Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
5443            Bound::Unbounded => Bound::Unbounded,
5444        };
5445        let bounds = (start, end);
5446        let mut cursor = self.cursor::<DimensionPair<MultiBufferOffset, Point>, BufferOffset>();
5447        cursor.seek(&DimensionPair {
5448            key: start_offset,
5449            value: None,
5450        });
5451
5452        if cursor
5453            .fetch_excerpt_with_range()
5454            .is_some_and(|(_, range)| bounds.contains(&range.start.key))
5455        {
5456            cursor.prev_excerpt();
5457        } else {
5458            cursor.seek_to_start_of_current_excerpt();
5459        }
5460        let mut prev_excerpt = cursor
5461            .fetch_excerpt_with_range()
5462            .map(|(excerpt, _)| excerpt);
5463
5464        cursor.next_excerpt_forwards();
5465
5466        iter::from_fn(move || {
5467            loop {
5468                if self.singleton {
5469                    return None;
5470                }
5471
5472                let (next_excerpt, next_range) = cursor.fetch_excerpt_with_range()?;
5473                cursor.next_excerpt_forwards();
5474                if !bounds.contains(&next_range.start.key) {
5475                    prev_excerpt = Some(next_excerpt);
5476                    continue;
5477                }
5478
5479                let next_region_start = next_range.start.value.unwrap();
5480                let next_region_end = if let Some((_, range)) = cursor.fetch_excerpt_with_range() {
5481                    range.start.value.unwrap()
5482                } else {
5483                    self.max_point()
5484                };
5485
5486                let prev = prev_excerpt.as_ref().map(|excerpt| ExcerptBoundaryInfo {
5487                    start_anchor: Anchor::in_buffer(
5488                        excerpt.path_key_index,
5489                        excerpt.range.context.start,
5490                    ),
5491                    range: excerpt.range.clone(),
5492                    end_row: MultiBufferRow(next_region_start.row),
5493                });
5494
5495                let next = ExcerptBoundaryInfo {
5496                    start_anchor: Anchor::in_buffer(
5497                        next_excerpt.path_key_index,
5498                        next_excerpt.range.context.start,
5499                    ),
5500                    range: next_excerpt.range.clone(),
5501                    end_row: if next_excerpt.has_trailing_newline {
5502                        MultiBufferRow(next_region_end.row - 1)
5503                    } else {
5504                        MultiBufferRow(next_region_end.row)
5505                    },
5506                };
5507
5508                let row = MultiBufferRow(next_region_start.row);
5509
5510                prev_excerpt = Some(next_excerpt);
5511
5512                return Some(ExcerptBoundary { row, prev, next });
5513            }
5514        })
5515    }
5516
5517    pub fn edit_count(&self) -> usize {
5518        self.edit_count
5519    }
5520
5521    pub fn non_text_state_update_count(&self) -> usize {
5522        self.non_text_state_update_count
5523    }
5524
5525    /// Allows converting several ranges within the same excerpt between buffer offsets and multibuffer offsets.
5526    ///
5527    /// If the input range is contained in a single excerpt, invokes the callback with the full range of that excerpt
5528    /// and the input range both converted to buffer coordinates. The buffer ranges returned by the callback are lifted back
5529    /// to multibuffer offsets and returned.
5530    ///
5531    /// Returns `None` if the input range spans multiple excerpts.
5532    pub fn map_excerpt_ranges<'a, T>(
5533        &'a self,
5534        position: Range<MultiBufferOffset>,
5535        f: impl FnOnce(
5536            &'a BufferSnapshot,
5537            ExcerptRange<BufferOffset>,
5538            Range<BufferOffset>,
5539        ) -> Vec<(Range<BufferOffset>, T)>,
5540    ) -> Option<Vec<(Range<MultiBufferOffset>, T)>> {
5541        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
5542        cursor.seek(&position.start);
5543
5544        let region = cursor.region()?;
5545        if !region.is_main_buffer {
5546            return None;
5547        }
5548        let excerpt = cursor.excerpt()?;
5549        let excerpt_start = *cursor.excerpts.start();
5550        let input_buffer_start = cursor.buffer_position_at(&position.start)?;
5551
5552        cursor.seek_forward(&position.end);
5553        if cursor.excerpt()? != excerpt {
5554            return None;
5555        }
5556        let region = cursor.region()?;
5557        if !region.is_main_buffer {
5558            return None;
5559        }
5560        let input_buffer_end = cursor.buffer_position_at(&position.end)?;
5561        let input_buffer_range = input_buffer_start..input_buffer_end;
5562        let buffer = excerpt.buffer_snapshot(self);
5563        let excerpt_context_range = excerpt.range.context.to_offset(buffer);
5564        let excerpt_context_range =
5565            BufferOffset(excerpt_context_range.start)..BufferOffset(excerpt_context_range.end);
5566        let excerpt_primary_range = excerpt.range.primary.to_offset(buffer);
5567        let excerpt_primary_range =
5568            BufferOffset(excerpt_primary_range.start)..BufferOffset(excerpt_primary_range.end);
5569        let results = f(
5570            buffer,
5571            ExcerptRange {
5572                context: excerpt_context_range.clone(),
5573                primary: excerpt_primary_range,
5574            },
5575            input_buffer_range,
5576        );
5577        let mut diff_transforms = cursor.diff_transforms;
5578        Some(
5579            results
5580                .into_iter()
5581                .map(|(buffer_range, metadata)| {
5582                    let clamped_start = buffer_range
5583                        .start
5584                        .max(excerpt_context_range.start)
5585                        .min(excerpt_context_range.end);
5586                    let clamped_end = buffer_range
5587                        .end
5588                        .max(clamped_start)
5589                        .min(excerpt_context_range.end);
5590                    let excerpt_offset_start =
5591                        excerpt_start + (clamped_start.0 - excerpt_context_range.start.0);
5592                    let excerpt_offset_end =
5593                        excerpt_start + (clamped_end.0 - excerpt_context_range.start.0);
5594
5595                    diff_transforms.seek(&excerpt_offset_start, Bias::Right);
5596                    let mut output_start = diff_transforms.start().output_dimension;
5597                    output_start +=
5598                        excerpt_offset_start - diff_transforms.start().excerpt_dimension;
5599
5600                    diff_transforms.seek_forward(&excerpt_offset_end, Bias::Right);
5601                    let mut output_end = diff_transforms.start().output_dimension;
5602                    output_end += excerpt_offset_end - diff_transforms.start().excerpt_dimension;
5603
5604                    (output_start.0..output_end.0, metadata)
5605                })
5606                .collect(),
5607        )
5608    }
5609
5610    /// Returns the smallest enclosing bracket ranges containing the given range or
5611    /// None if no brackets contain range or the range is not contained in a single
5612    /// excerpt
5613    ///
5614    /// Can optionally pass a range_filter to filter the ranges of brackets to consider
5615    #[ztracing::instrument(skip_all)]
5616    pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
5617        &self,
5618        range: Range<T>,
5619        range_filter: Option<
5620            &dyn Fn(&BufferSnapshot, Range<BufferOffset>, Range<BufferOffset>) -> bool,
5621        >,
5622    ) -> Option<(Range<MultiBufferOffset>, Range<MultiBufferOffset>)> {
5623        let range = range.start.to_offset(self)..range.end.to_offset(self);
5624        let results =
5625            self.map_excerpt_ranges(range, |buffer, excerpt_range, input_buffer_range| {
5626                let filter = |open: Range<usize>, close: Range<usize>| -> bool {
5627                    excerpt_range.context.start.0 <= open.start
5628                        && close.end <= excerpt_range.context.end.0
5629                        && range_filter.is_none_or(|filter| {
5630                            filter(
5631                                buffer,
5632                                BufferOffset(open.start)..BufferOffset(close.end),
5633                                BufferOffset(close.start)..BufferOffset(close.end),
5634                            )
5635                        })
5636                };
5637                let Some((open, close)) =
5638                    buffer.innermost_enclosing_bracket_ranges(input_buffer_range, Some(&filter))
5639                else {
5640                    return Vec::new();
5641                };
5642                vec![
5643                    (BufferOffset(open.start)..BufferOffset(open.end), ()),
5644                    (BufferOffset(close.start)..BufferOffset(close.end), ()),
5645                ]
5646            })?;
5647        let [(open, _), (close, _)] = results.try_into().ok()?;
5648        Some((open, close))
5649    }
5650
5651    /// Returns enclosing bracket ranges containing the given range or returns None if the range is
5652    /// not contained in a single excerpt
5653    pub fn enclosing_bracket_ranges<T: ToOffset>(
5654        &self,
5655        range: Range<T>,
5656    ) -> Option<impl Iterator<Item = (Range<MultiBufferOffset>, Range<MultiBufferOffset>)>> {
5657        let range = range.start.to_offset(self)..range.end.to_offset(self);
5658        let results =
5659            self.map_excerpt_ranges(range, |buffer, excerpt_range, input_buffer_range| {
5660                buffer
5661                    .enclosing_bracket_ranges(input_buffer_range)
5662                    .filter(|pair| {
5663                        excerpt_range.context.start.0 <= pair.open_range.start
5664                            && pair.close_range.end <= excerpt_range.context.end.0
5665                    })
5666                    .flat_map(|pair| {
5667                        [
5668                            (
5669                                BufferOffset(pair.open_range.start)
5670                                    ..BufferOffset(pair.open_range.end),
5671                                (),
5672                            ),
5673                            (
5674                                BufferOffset(pair.close_range.start)
5675                                    ..BufferOffset(pair.close_range.end),
5676                                (),
5677                            ),
5678                        ]
5679                    })
5680                    .collect()
5681            })?;
5682        Some(results.into_iter().map(|(range, _)| range).tuples())
5683    }
5684
5685    /// Returns enclosing bracket ranges containing the given range or returns None if the range is
5686    /// not contained in a single excerpt
5687    pub fn text_object_ranges<T: ToOffset>(
5688        &self,
5689        range: Range<T>,
5690        options: TreeSitterOptions,
5691    ) -> impl Iterator<Item = (Range<MultiBufferOffset>, TextObject)> + '_ {
5692        let range = range.start.to_offset(self)..range.end.to_offset(self);
5693        self.map_excerpt_ranges(range, |buffer, excerpt_range, input_buffer_range| {
5694            buffer
5695                .text_object_ranges(input_buffer_range, options)
5696                .filter(|(range, _)| {
5697                    excerpt_range.context.start.0 <= range.start
5698                        && range.end <= excerpt_range.context.end.0
5699                })
5700                .map(|(range, text_object)| {
5701                    (
5702                        BufferOffset(range.start)..BufferOffset(range.end),
5703                        text_object,
5704                    )
5705                })
5706                .collect()
5707        })
5708        .into_iter()
5709        .flatten()
5710    }
5711
5712    pub fn bracket_ranges<T: ToOffset>(
5713        &self,
5714        range: Range<T>,
5715    ) -> Option<impl Iterator<Item = (Range<MultiBufferOffset>, Range<MultiBufferOffset>)>> {
5716        let range = range.start.to_offset(self)..range.end.to_offset(self);
5717        let results =
5718            self.map_excerpt_ranges(range, |buffer, excerpt_range, input_buffer_range| {
5719                buffer
5720                    .bracket_ranges(input_buffer_range)
5721                    .filter(|pair| {
5722                        excerpt_range.context.start.0 <= pair.open_range.start
5723                            && pair.close_range.end <= excerpt_range.context.end.0
5724                    })
5725                    .flat_map(|pair| {
5726                        [
5727                            (
5728                                BufferOffset(pair.open_range.start)
5729                                    ..BufferOffset(pair.open_range.end),
5730                                (),
5731                            ),
5732                            (
5733                                BufferOffset(pair.close_range.start)
5734                                    ..BufferOffset(pair.close_range.end),
5735                                (),
5736                            ),
5737                        ]
5738                    })
5739                    .collect()
5740            })?;
5741        Some(results.into_iter().map(|(range, _)| range).tuples())
5742    }
5743
5744    pub fn redacted_ranges<'a, T: ToOffset>(
5745        &'a self,
5746        range: Range<T>,
5747        redaction_enabled: impl Fn(Option<&Arc<dyn File>>) -> bool + 'a,
5748    ) -> impl Iterator<Item = Range<MultiBufferOffset>> + 'a {
5749        let range = range.start.to_offset(self)..range.end.to_offset(self);
5750        self.lift_buffer_metadata(range, move |buffer, range| {
5751            if redaction_enabled(buffer.file()) {
5752                Some(buffer.redacted_ranges(range).map(|range| (range, ())))
5753            } else {
5754                None
5755            }
5756        })
5757        .map(|(range, _, _)| range)
5758    }
5759
5760    pub fn runnable_ranges(
5761        &self,
5762        range: Range<Anchor>,
5763    ) -> impl Iterator<Item = (Range<Anchor>, language::RunnableRange)> + '_ {
5764        let range = range.start.to_offset(self)..range.end.to_offset(self);
5765        self.lift_buffer_metadata(range, move |buffer, range| {
5766            Some(
5767                buffer
5768                    .runnable_ranges(range.clone())
5769                    .filter(move |runnable| {
5770                        runnable.run_range.start >= range.start
5771                            && runnable.run_range.end < range.end
5772                    })
5773                    .map(|runnable| (runnable.run_range.clone(), runnable)),
5774            )
5775        })
5776        .map(|(run_range, runnable, _)| {
5777            (
5778                self.anchor_after(run_range.start)..self.anchor_before(run_range.end),
5779                runnable,
5780            )
5781        })
5782    }
5783
5784    pub fn line_indents(
5785        &self,
5786        start_row: MultiBufferRow,
5787        buffer_filter: impl Fn(&BufferSnapshot) -> bool,
5788    ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
5789        let max_point = self.max_point();
5790        let mut cursor = self.cursor::<Point, Point>();
5791        cursor.seek(&Point::new(start_row.0, 0));
5792        iter::from_fn(move || {
5793            let mut region = cursor.region()?;
5794            while !buffer_filter(&region.excerpt.buffer_snapshot(self)) {
5795                cursor.next();
5796                region = cursor.region()?;
5797            }
5798            let region = cursor.region()?;
5799            let overshoot = start_row.0.saturating_sub(region.range.start.row);
5800            let buffer_start_row =
5801                (region.buffer_range.start.row + overshoot).min(region.buffer_range.end.row);
5802
5803            let buffer_end_row = if region.is_main_buffer
5804                && (region.has_trailing_newline || region.range.end == max_point)
5805            {
5806                region.buffer_range.end.row
5807            } else {
5808                region.buffer_range.end.row.saturating_sub(1)
5809            };
5810
5811            let line_indents = region
5812                .buffer
5813                .line_indents_in_row_range(buffer_start_row..buffer_end_row);
5814            let region_buffer_row = region.buffer_range.start.row;
5815            let region_row = region.range.start.row;
5816            let region_buffer = region.excerpt.buffer_snapshot(self);
5817            cursor.next();
5818            Some(line_indents.map(move |(buffer_row, indent)| {
5819                let row = region_row + (buffer_row - region_buffer_row);
5820                (MultiBufferRow(row), indent, region_buffer)
5821            }))
5822        })
5823        .flatten()
5824    }
5825
5826    pub fn reversed_line_indents(
5827        &self,
5828        end_row: MultiBufferRow,
5829        buffer_filter: impl Fn(&BufferSnapshot) -> bool,
5830    ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
5831        let max_point = self.max_point();
5832        let mut cursor = self.cursor::<Point, Point>();
5833        cursor.seek(&Point::new(end_row.0, 0));
5834        iter::from_fn(move || {
5835            let mut region = cursor.region()?;
5836            while !buffer_filter(&region.excerpt.buffer_snapshot(self)) {
5837                cursor.prev();
5838                region = cursor.region()?;
5839            }
5840            let region = cursor.region()?;
5841
5842            let buffer_start_row = region.buffer_range.start.row;
5843            let buffer_end_row = if region.is_main_buffer
5844                && (region.has_trailing_newline || region.range.end == max_point)
5845            {
5846                region.buffer_range.end.row + 1
5847            } else {
5848                region.buffer_range.end.row
5849            };
5850
5851            let overshoot = end_row.0 - region.range.start.row;
5852            let buffer_end_row =
5853                (region.buffer_range.start.row + overshoot + 1).min(buffer_end_row);
5854
5855            let line_indents = region
5856                .buffer
5857                .reversed_line_indents_in_row_range(buffer_start_row..buffer_end_row);
5858            let region_buffer_row = region.buffer_range.start.row;
5859            let region_row = region.range.start.row;
5860            let region_buffer = region.excerpt.buffer_snapshot(self);
5861            cursor.prev();
5862            Some(line_indents.map(move |(buffer_row, indent)| {
5863                let row = region_row + (buffer_row - region_buffer_row);
5864                (MultiBufferRow(row), indent, region_buffer)
5865            }))
5866        })
5867        .flatten()
5868    }
5869
5870    pub async fn enclosing_indent(
5871        &self,
5872        mut target_row: MultiBufferRow,
5873    ) -> Option<(Range<MultiBufferRow>, LineIndent)> {
5874        let max_row = MultiBufferRow(self.max_point().row);
5875        if target_row >= max_row {
5876            return None;
5877        }
5878
5879        let mut target_indent = self.line_indent_for_row(target_row);
5880
5881        // If the current row is at the start of an indented block, we want to return this
5882        // block as the enclosing indent.
5883        if !target_indent.is_line_empty() && target_row < max_row {
5884            let next_line_indent = self.line_indent_for_row(MultiBufferRow(target_row.0 + 1));
5885            if !next_line_indent.is_line_empty()
5886                && target_indent.raw_len() < next_line_indent.raw_len()
5887            {
5888                target_indent = next_line_indent;
5889                target_row.0 += 1;
5890            }
5891        }
5892
5893        const SEARCH_ROW_LIMIT: u32 = 25000;
5894        const SEARCH_WHITESPACE_ROW_LIMIT: u32 = 2500;
5895        const YIELD_INTERVAL: u32 = 100;
5896
5897        let mut accessed_row_counter = 0;
5898
5899        // If there is a blank line at the current row, search for the next non indented lines
5900        if target_indent.is_line_empty() {
5901            let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT));
5902            let end =
5903                MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_WHITESPACE_ROW_LIMIT));
5904
5905            let mut non_empty_line_above = None;
5906            for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5907                if row < start {
5908                    break;
5909                }
5910                accessed_row_counter += 1;
5911                if accessed_row_counter == YIELD_INTERVAL {
5912                    accessed_row_counter = 0;
5913                    yield_now().await;
5914                }
5915                if !indent.is_line_empty() {
5916                    non_empty_line_above = Some((row, indent));
5917                    break;
5918                }
5919            }
5920
5921            let mut non_empty_line_below = None;
5922            for (row, indent, _) in self.line_indents(target_row, |_| true) {
5923                if row > end {
5924                    break;
5925                }
5926                accessed_row_counter += 1;
5927                if accessed_row_counter == YIELD_INTERVAL {
5928                    accessed_row_counter = 0;
5929                    yield_now().await;
5930                }
5931                if !indent.is_line_empty() {
5932                    non_empty_line_below = Some((row, indent));
5933                    break;
5934                }
5935            }
5936
5937            let (row, indent) = match (non_empty_line_above, non_empty_line_below) {
5938                (Some((above_row, above_indent)), Some((below_row, below_indent))) => {
5939                    if above_indent.raw_len() >= below_indent.raw_len() {
5940                        (above_row, above_indent)
5941                    } else {
5942                        (below_row, below_indent)
5943                    }
5944                }
5945                (Some(above), None) => above,
5946                (None, Some(below)) => below,
5947                _ => return None,
5948            };
5949
5950            target_indent = indent;
5951            target_row = row;
5952        }
5953
5954        let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_ROW_LIMIT));
5955        let end = MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_ROW_LIMIT));
5956
5957        let mut start_indent = None;
5958        for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5959            if row < start {
5960                break;
5961            }
5962            accessed_row_counter += 1;
5963            if accessed_row_counter == YIELD_INTERVAL {
5964                accessed_row_counter = 0;
5965                yield_now().await;
5966            }
5967            if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5968                start_indent = Some((row, indent));
5969                break;
5970            }
5971        }
5972        let (start_row, start_indent_size) = start_indent?;
5973
5974        let mut end_indent = (end, None);
5975        for (row, indent, _) in self.line_indents(target_row, |_| true) {
5976            if row > end {
5977                break;
5978            }
5979            accessed_row_counter += 1;
5980            if accessed_row_counter == YIELD_INTERVAL {
5981                accessed_row_counter = 0;
5982                yield_now().await;
5983            }
5984            if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5985                end_indent = (MultiBufferRow(row.0.saturating_sub(1)), Some(indent));
5986                break;
5987            }
5988        }
5989        let (end_row, end_indent_size) = end_indent;
5990
5991        let indent = if let Some(end_indent_size) = end_indent_size {
5992            if start_indent_size.raw_len() > end_indent_size.raw_len() {
5993                start_indent_size
5994            } else {
5995                end_indent_size
5996            }
5997        } else {
5998            start_indent_size
5999        };
6000
6001        Some((start_row..end_row, indent))
6002    }
6003
6004    pub fn indent_guides_in_range<T: ToPoint>(
6005        &self,
6006        range: Range<T>,
6007        ignore_disabled_for_language: bool,
6008        cx: &App,
6009    ) -> impl Iterator<Item = IndentGuide> {
6010        let range = range.start.to_point(self)..range.end.to_point(self);
6011        let start_row = MultiBufferRow(range.start.row);
6012        let end_row = MultiBufferRow(range.end.row);
6013
6014        let mut row_indents = self.line_indents(start_row, |buffer| {
6015            let settings = LanguageSettings::for_buffer_snapshot(buffer, None, cx);
6016            settings.indent_guides.enabled || ignore_disabled_for_language
6017        });
6018
6019        let mut result = Vec::new();
6020        let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
6021
6022        let mut prev_settings = None;
6023        while let Some((first_row, mut line_indent, buffer)) = row_indents.next() {
6024            if first_row > end_row {
6025                break;
6026            }
6027            let current_depth = indent_stack.len() as u32;
6028
6029            // Avoid retrieving the language settings repeatedly for every buffer row.
6030            if let Some((prev_buffer_id, _)) = &prev_settings
6031                && prev_buffer_id != &buffer.remote_id()
6032            {
6033                prev_settings.take();
6034            }
6035            let settings = &prev_settings
6036                .get_or_insert_with(|| {
6037                    (
6038                        buffer.remote_id(),
6039                        LanguageSettings::for_buffer_snapshot(buffer, None, cx),
6040                    )
6041                })
6042                .1;
6043            let tab_size = settings.tab_size.get();
6044
6045            // When encountering empty, continue until found useful line indent
6046            // then add to the indent stack with the depth found
6047            let mut found_indent = false;
6048            let mut last_row = first_row;
6049            if line_indent.is_line_blank() {
6050                while !found_indent {
6051                    let Some((target_row, new_line_indent, _)) = row_indents.next() else {
6052                        break;
6053                    };
6054                    const TRAILING_ROW_SEARCH_LIMIT: u32 = 25;
6055                    if target_row > MultiBufferRow(end_row.0 + TRAILING_ROW_SEARCH_LIMIT) {
6056                        break;
6057                    }
6058
6059                    if new_line_indent.is_line_blank() {
6060                        continue;
6061                    }
6062                    last_row = target_row.min(end_row);
6063                    line_indent = new_line_indent;
6064                    found_indent = true;
6065                    break;
6066                }
6067            } else {
6068                found_indent = true
6069            }
6070
6071            let depth = if found_indent {
6072                line_indent.len(tab_size) / tab_size
6073            } else {
6074                0
6075            };
6076
6077            match depth.cmp(&current_depth) {
6078                cmp::Ordering::Less => {
6079                    for _ in 0..(current_depth - depth) {
6080                        let mut indent = indent_stack.pop().unwrap();
6081                        if last_row != first_row {
6082                            // In this case, we landed on an empty row, had to seek forward,
6083                            // and discovered that the indent we where on is ending.
6084                            // This means that the last display row must
6085                            // be on line that ends this indent range, so we
6086                            // should display the range up to the first non-empty line
6087                            indent.end_row = MultiBufferRow(first_row.0.saturating_sub(1));
6088                        }
6089
6090                        result.push(indent)
6091                    }
6092                }
6093                cmp::Ordering::Greater => {
6094                    for next_depth in current_depth..depth {
6095                        indent_stack.push(IndentGuide {
6096                            buffer_id: buffer.remote_id(),
6097                            start_row: first_row,
6098                            end_row: last_row,
6099                            depth: next_depth,
6100                            tab_size,
6101                            settings: settings.indent_guides.clone(),
6102                        });
6103                    }
6104                }
6105                _ => {}
6106            }
6107
6108            for indent in indent_stack.iter_mut() {
6109                indent.end_row = last_row;
6110            }
6111        }
6112
6113        result.extend(indent_stack);
6114        result.into_iter()
6115    }
6116
6117    pub fn trailing_excerpt_update_count(&self) -> usize {
6118        self.trailing_excerpt_update_count
6119    }
6120
6121    pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
6122        self.point_to_buffer_offset(point)
6123            .and_then(|(buffer, _)| buffer.file())
6124    }
6125
6126    pub fn language_at<T: ToOffset>(&self, offset: T) -> Option<&Arc<Language>> {
6127        self.point_to_buffer_offset(offset)
6128            .and_then(|(buffer, offset)| buffer.language_at(offset))
6129    }
6130
6131    fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
6132        self.excerpts
6133            .first()
6134            .map(|excerpt| excerpt.buffer_snapshot(self))
6135            .map(|buffer| LanguageSettings::for_buffer_snapshot(buffer, None, cx))
6136            .unwrap_or_else(move || self.language_settings_at(MultiBufferOffset::ZERO, cx))
6137    }
6138
6139    pub fn language_settings_at<'a, T: ToOffset>(
6140        &'a self,
6141        point: T,
6142        cx: &'a App,
6143    ) -> Cow<'a, LanguageSettings> {
6144        if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
6145            buffer.settings_at(offset, cx)
6146        } else {
6147            Cow::Borrowed(&AllLanguageSettings::get_global(cx).defaults)
6148        }
6149    }
6150
6151    pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
6152        self.point_to_buffer_offset(point)
6153            .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
6154    }
6155
6156    pub fn char_classifier_at<T: ToOffset>(&self, point: T) -> CharClassifier {
6157        self.point_to_buffer_offset(point)
6158            .map(|(buffer, offset)| buffer.char_classifier_at(offset))
6159            .unwrap_or_default()
6160    }
6161
6162    pub fn language_indent_size_at<T: ToOffset>(
6163        &self,
6164        position: T,
6165        cx: &App,
6166    ) -> Option<IndentSize> {
6167        let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
6168        Some(buffer_snapshot.language_indent_size_at(offset, cx))
6169    }
6170
6171    pub fn is_dirty(&self) -> bool {
6172        self.is_dirty
6173    }
6174
6175    pub fn has_deleted_file(&self) -> bool {
6176        self.has_deleted_file
6177    }
6178
6179    pub fn has_conflict(&self) -> bool {
6180        self.has_conflict
6181    }
6182
6183    pub fn has_diagnostics(&self) -> bool {
6184        self.excerpts
6185            .iter()
6186            .any(|excerpt| excerpt.buffer_snapshot(self).has_diagnostics())
6187    }
6188
6189    pub fn diagnostic_group(
6190        &self,
6191        buffer_id: BufferId,
6192        group_id: usize,
6193    ) -> impl Iterator<Item = DiagnosticEntryRef<'_, Point>> + '_ {
6194        self.lift_buffer_metadata::<Point, _, _>(
6195            Point::zero()..self.max_point(),
6196            move |buffer, range| {
6197                if buffer.remote_id() != buffer_id {
6198                    return None;
6199                };
6200                Some(
6201                    buffer
6202                        .diagnostics_in_range(range, false)
6203                        .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id)
6204                        .map(move |DiagnosticEntryRef { diagnostic, range }| (range, diagnostic)),
6205                )
6206            },
6207        )
6208        .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
6209    }
6210
6211    pub fn diagnostics_in_range<'a, MBD>(
6212        &'a self,
6213        range: Range<MBD>,
6214    ) -> impl Iterator<Item = DiagnosticEntryRef<'a, MBD>> + 'a
6215    where
6216        MBD::TextDimension: 'a
6217            + text::ToOffset
6218            + text::FromAnchor
6219            + Sub<Output = MBD::TextDimension>
6220            + fmt::Debug
6221            + ops::Add<Output = MBD::TextDimension>
6222            + ops::AddAssign
6223            + Ord,
6224        MBD: MultiBufferDimension
6225            + Ord
6226            + Sub<Output = MBD::TextDimension>
6227            + ops::Add<MBD::TextDimension, Output = MBD>
6228            + ops::AddAssign<MBD::TextDimension>
6229            + 'a,
6230    {
6231        self.lift_buffer_metadata::<MBD, _, _>(range, move |buffer, buffer_range| {
6232            Some(
6233                buffer
6234                    .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
6235                    .map(|entry| (entry.range, entry.diagnostic)),
6236            )
6237        })
6238        .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
6239    }
6240
6241    pub fn diagnostics_with_buffer_ids_in_range<'a, MBD>(
6242        &'a self,
6243        range: Range<MBD>,
6244    ) -> impl Iterator<Item = (BufferId, DiagnosticEntryRef<'a, MBD>)> + 'a
6245    where
6246        MBD: MultiBufferDimension
6247            + Ord
6248            + Sub<Output = MBD::TextDimension>
6249            + ops::Add<MBD::TextDimension, Output = MBD>
6250            + ops::AddAssign<MBD::TextDimension>,
6251        MBD::TextDimension: Sub<Output = MBD::TextDimension>
6252            + ops::Add<Output = MBD::TextDimension>
6253            + text::ToOffset
6254            + text::FromAnchor
6255            + AddAssign<MBD::TextDimension>
6256            + Ord,
6257    {
6258        self.lift_buffer_metadata::<MBD, _, _>(range, move |buffer, buffer_range| {
6259            Some(
6260                buffer
6261                    .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
6262                    .map(|entry| (entry.range, entry.diagnostic)),
6263            )
6264        })
6265        .map(|(range, diagnostic, excerpt)| {
6266            (
6267                excerpt.buffer_snapshot(self).remote_id(),
6268                DiagnosticEntryRef { diagnostic, range },
6269            )
6270        })
6271    }
6272
6273    pub fn syntax_ancestor<T: ToOffset>(
6274        &self,
6275        range: Range<T>,
6276    ) -> Option<(tree_sitter::Node<'_>, Range<MultiBufferOffset>)> {
6277        let range = range.start.to_offset(self)..range.end.to_offset(self);
6278        let results =
6279            self.map_excerpt_ranges(range, |buffer, excerpt_range, input_buffer_range| {
6280                let Some(node) = buffer.syntax_ancestor(input_buffer_range) else {
6281                    return vec![];
6282                };
6283                let node_range = node.byte_range();
6284                if excerpt_range.context.start.0 <= node_range.start
6285                    && node_range.end <= excerpt_range.context.end.0
6286                {
6287                    vec![(
6288                        BufferOffset(node_range.start)..BufferOffset(node_range.end),
6289                        node,
6290                    )]
6291                } else {
6292                    vec![]
6293                }
6294            })?;
6295        let (output_range, node) = results.into_iter().next()?;
6296        Some((node, output_range))
6297    }
6298
6299    pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
6300        let buffer_snapshot = self.as_singleton()?;
6301        let excerpt = self.excerpts.first()?;
6302        let path_key_index = excerpt.path_key_index;
6303        let outline = buffer_snapshot.outline(theme);
6304        Some(Outline::new(
6305            outline
6306                .items
6307                .into_iter()
6308                .map(|item| OutlineItem {
6309                    depth: item.depth,
6310                    range: Anchor::range_in_buffer(path_key_index, item.range),
6311                    source_range_for_text: Anchor::range_in_buffer(
6312                        path_key_index,
6313                        item.source_range_for_text,
6314                    ),
6315                    text: item.text,
6316                    highlight_ranges: item.highlight_ranges,
6317                    name_ranges: item.name_ranges,
6318                    body_range: item
6319                        .body_range
6320                        .map(|body_range| Anchor::range_in_buffer(path_key_index, body_range)),
6321                    annotation_range: item.annotation_range.map(|annotation_range| {
6322                        Anchor::range_in_buffer(path_key_index, annotation_range)
6323                    }),
6324                })
6325                .collect(),
6326        ))
6327    }
6328
6329    pub fn symbols_containing<T: ToOffset>(
6330        &self,
6331        offset: T,
6332        theme: Option<&SyntaxTheme>,
6333    ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {
6334        let anchor = self.anchor_before(offset);
6335        let target = anchor.try_seek_target(&self)?;
6336        let (_, _, excerpt) = self.excerpts.find((), &target, Bias::Left);
6337        let excerpt = excerpt?;
6338        let buffer_snapshot = excerpt.buffer_snapshot(self);
6339        Some((
6340            buffer_snapshot.remote_id(),
6341            buffer_snapshot
6342                .symbols_containing(
6343                    anchor
6344                        .excerpt_anchor()
6345                        .map(|anchor| anchor.text_anchor())
6346                        .unwrap_or(text::Anchor::min_for_buffer(buffer_snapshot.remote_id())),
6347                    theme,
6348                )
6349                .into_iter()
6350                .flat_map(|item| {
6351                    Some(OutlineItem {
6352                        depth: item.depth,
6353                        source_range_for_text: Anchor::range_in_buffer(
6354                            excerpt.path_key_index,
6355                            item.source_range_for_text,
6356                        ),
6357                        range: Anchor::range_in_buffer(excerpt.path_key_index, item.range),
6358                        text: item.text,
6359                        highlight_ranges: item.highlight_ranges,
6360                        name_ranges: item.name_ranges,
6361                        body_range: item.body_range.map(|body_range| {
6362                            Anchor::range_in_buffer(excerpt.path_key_index, body_range)
6363                        }),
6364                        annotation_range: item.annotation_range.map(|body_range| {
6365                            Anchor::range_in_buffer(excerpt.path_key_index, body_range)
6366                        }),
6367                    })
6368                })
6369                .collect(),
6370        ))
6371    }
6372
6373    pub fn buffer_for_path(&self, path: &PathKey) -> Option<&BufferSnapshot> {
6374        let (_, _, excerpt) = self
6375            .excerpts
6376            .find::<ExcerptSummary, _>((), path, Bias::Left);
6377        Some(excerpt?.buffer_snapshot(self))
6378    }
6379
6380    pub fn path_for_buffer(&self, buffer_id: BufferId) -> Option<&PathKey> {
6381        Some(&self.buffers.get(&buffer_id)?.path_key)
6382    }
6383
6384    pub(crate) fn path_key_index_for_buffer(&self, buffer_id: BufferId) -> Option<PathKeyIndex> {
6385        let snapshot = self.buffers.get(&buffer_id)?;
6386        Some(snapshot.path_key_index)
6387    }
6388
6389    fn first_excerpt_for_buffer(&self, buffer_id: BufferId) -> Option<&Excerpt> {
6390        let path_key = &self.buffers.get(&buffer_id)?.path_key;
6391        self.first_excerpt_for_path(path_key)
6392    }
6393
6394    fn first_excerpt_for_path(&self, path_key: &PathKey) -> Option<&Excerpt> {
6395        let (_, _, first_excerpt) =
6396            self.excerpts
6397                .find::<ExcerptSummary, _>((), path_key, Bias::Left);
6398        first_excerpt
6399    }
6400
6401    pub fn buffer_for_id(&self, id: BufferId) -> Option<&BufferSnapshot> {
6402        self.buffers.get(&id).map(|state| &state.buffer_snapshot)
6403    }
6404
6405    fn try_path_for_anchor(&self, anchor: ExcerptAnchor) -> Option<&PathKey> {
6406        self.path_keys_by_index.get(&anchor.path)
6407    }
6408
6409    pub fn path_for_anchor(&self, anchor: ExcerptAnchor) -> &PathKey {
6410        self.try_path_for_anchor(anchor)
6411            .expect("invalid anchor: path was never added to multibuffer")
6412    }
6413
6414    /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
6415    pub fn excerpt_containing<T: ToOffset>(
6416        &self,
6417        range: Range<T>,
6418    ) -> Option<(&BufferSnapshot, ExcerptRange<text::Anchor>)> {
6419        let range = range.start.to_offset(self)..range.end.to_offset(self);
6420        let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
6421        cursor.seek(&range.start);
6422
6423        let start_excerpt = cursor.excerpt()?;
6424        if range.end != range.start {
6425            cursor.seek_forward(&range.end);
6426            if cursor.excerpt()? != start_excerpt {
6427                return None;
6428            }
6429        }
6430
6431        Some((
6432            start_excerpt.buffer_snapshot(self),
6433            start_excerpt.range.clone(),
6434        ))
6435    }
6436
6437    pub fn selections_in_range<'a>(
6438        &'a self,
6439        range: &'a Range<Anchor>,
6440        include_local: bool,
6441    ) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
6442        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
6443        cursor.seek(&range.start.seek_target(self), Bias::Left);
6444        cursor
6445            .take_while(move |excerpt| {
6446                let excerpt_start =
6447                    Anchor::in_buffer(excerpt.path_key_index, excerpt.range.context.start);
6448                excerpt_start.cmp(&range.end, self).is_le()
6449            })
6450            .flat_map(move |excerpt| {
6451                let buffer_snapshot = excerpt.buffer_snapshot(self);
6452                let mut query_range = excerpt.range.context.start..excerpt.range.context.end;
6453                if let Some(excerpt_anchor) = range.start.excerpt_anchor()
6454                    && excerpt.contains(&excerpt_anchor, self)
6455                {
6456                    query_range.start = excerpt_anchor.text_anchor();
6457                }
6458                if let Some(excerpt_anchor) = range.end.excerpt_anchor()
6459                    && excerpt.contains(&excerpt_anchor, self)
6460                {
6461                    query_range.end = excerpt_anchor.text_anchor();
6462                }
6463
6464                buffer_snapshot
6465                    .selections_in_range(query_range, include_local)
6466                    .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
6467                        selections.map(move |selection| {
6468                            let mut start =
6469                                Anchor::in_buffer(excerpt.path_key_index, selection.start);
6470                            let mut end = Anchor::in_buffer(excerpt.path_key_index, selection.end);
6471                            if range.start.cmp(&start, self).is_gt() {
6472                                start = range.start;
6473                            }
6474                            if range.end.cmp(&end, self).is_lt() {
6475                                end = range.end;
6476                            }
6477
6478                            (
6479                                replica_id,
6480                                line_mode,
6481                                cursor_shape,
6482                                Selection {
6483                                    id: selection.id,
6484                                    start,
6485                                    end,
6486                                    reversed: selection.reversed,
6487                                    goal: selection.goal,
6488                                },
6489                            )
6490                        })
6491                    })
6492            })
6493    }
6494
6495    pub fn show_headers(&self) -> bool {
6496        self.show_headers
6497    }
6498
6499    pub fn diff_for_buffer_id(&self, buffer_id: BufferId) -> Option<&BufferDiffSnapshot> {
6500        self.diff_state(buffer_id).map(|diff| &diff.diff)
6501    }
6502
6503    fn diff_state(&self, buffer_id: BufferId) -> Option<&DiffStateSnapshot> {
6504        find_diff_state(&self.diffs, buffer_id)
6505    }
6506
6507    pub fn total_changed_lines(&self) -> (u32, u32) {
6508        let summary = self.diffs.summary();
6509        (summary.added_rows, summary.removed_rows)
6510    }
6511
6512    pub fn all_diff_hunks_expanded(&self) -> bool {
6513        self.all_diff_hunks_expanded
6514    }
6515
6516    /// Visually annotates a position or range with the `Debug` representation of a value. The
6517    /// callsite of this function is used as a key - previous annotations will be removed.
6518    #[cfg(debug_assertions)]
6519    #[track_caller]
6520    pub fn debug<V, R>(&self, ranges: &R, value: V)
6521    where
6522        R: debug::ToMultiBufferDebugRanges,
6523        V: std::fmt::Debug,
6524    {
6525        self.debug_with_key(std::panic::Location::caller(), ranges, value);
6526    }
6527
6528    /// Visually annotates a position or range with the `Debug` representation of a value. Previous
6529    /// debug annotations with the same key will be removed. The key is also used to determine the
6530    /// annotation's color.
6531    #[cfg(debug_assertions)]
6532    #[track_caller]
6533    pub fn debug_with_key<K, R, V>(&self, key: &K, ranges: &R, value: V)
6534    where
6535        K: std::hash::Hash + 'static,
6536        R: debug::ToMultiBufferDebugRanges,
6537        V: std::fmt::Debug,
6538    {
6539        let text_ranges = ranges
6540            .to_multi_buffer_debug_ranges(self)
6541            .into_iter()
6542            .flat_map(|range| {
6543                self.range_to_buffer_ranges(range)
6544                    .into_iter()
6545                    .map(|(buffer_snapshot, range, _)| {
6546                        buffer_snapshot.anchor_after(range.start)
6547                            ..buffer_snapshot.anchor_before(range.end)
6548                    })
6549            })
6550            .collect();
6551        text::debug::GlobalDebugRanges::with_locked(|debug_ranges| {
6552            debug_ranges.insert(key, text_ranges, format!("{value:?}").into())
6553        });
6554    }
6555
6556    fn excerpt_edits_for_diff_change(
6557        &self,
6558        path: &PathKey,
6559        diff_change_range: Range<usize>,
6560    ) -> Vec<Edit<ExcerptDimension<MultiBufferOffset>>> {
6561        let mut excerpt_edits = Vec::new();
6562        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
6563        cursor.seek(path, Bias::Left);
6564        while let Some(excerpt) = cursor.item()
6565            && &excerpt.path_key == path
6566        {
6567            let buffer_snapshot = excerpt.buffer_snapshot(self);
6568            let excerpt_buffer_range = excerpt.range.context.to_offset(buffer_snapshot);
6569            let excerpt_start = cursor.start().clone();
6570            let excerpt_len = excerpt.text_summary.len;
6571            cursor.next();
6572            if diff_change_range.end < excerpt_buffer_range.start
6573                || diff_change_range.start > excerpt_buffer_range.end
6574            {
6575                continue;
6576            }
6577            let diff_change_start_in_excerpt = diff_change_range
6578                .start
6579                .saturating_sub(excerpt_buffer_range.start);
6580            let diff_change_end_in_excerpt = diff_change_range
6581                .end
6582                .saturating_sub(excerpt_buffer_range.start);
6583            let edit_start = excerpt_start.len() + diff_change_start_in_excerpt.min(excerpt_len);
6584            let edit_end = excerpt_start.len() + diff_change_end_in_excerpt.min(excerpt_len);
6585            excerpt_edits.push(Edit {
6586                old: edit_start..edit_end,
6587                new: edit_start..edit_end,
6588            });
6589        }
6590        excerpt_edits
6591    }
6592
6593    fn excerpts_for_path<'a>(
6594        &'a self,
6595        path_key: &'a PathKey,
6596    ) -> impl Iterator<Item = ExcerptRange<text::Anchor>> + 'a {
6597        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
6598        cursor.seek(path_key, Bias::Left);
6599        cursor
6600            .take_while(move |item| &item.path_key == path_key)
6601            .map(|excerpt| excerpt.range.clone())
6602    }
6603
6604    /// If the given multibuffer range is contained in a single excerpt and contains no deleted hunks,
6605    /// returns the corresponding buffer range.
6606    ///
6607    /// Otherwise, returns None.
6608    pub fn range_to_buffer_range<MBD>(
6609        &self,
6610        range: Range<MBD>,
6611    ) -> Option<(&BufferSnapshot, Range<MBD::TextDimension>)>
6612    where
6613        MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBD as Sub>::Output>,
6614        MBD::TextDimension: AddAssign<<MBD as Sub>::Output>,
6615    {
6616        let mut cursor = self.cursor::<MBD, MBD::TextDimension>();
6617        cursor.seek(&range.start);
6618
6619        let start_region = cursor.region()?.clone();
6620
6621        while let Some(region) = cursor.region()
6622            && region.range.end < range.end
6623        {
6624            if !region.is_main_buffer {
6625                return None;
6626            }
6627            cursor.next();
6628        }
6629
6630        let end_region = cursor.region()?;
6631        if end_region.buffer.remote_id() != start_region.buffer.remote_id() {
6632            return None;
6633        }
6634
6635        let mut buffer_start = start_region.buffer_range.start;
6636        buffer_start += range.start - start_region.range.start;
6637        let mut buffer_end = end_region.buffer_range.start;
6638        buffer_end += range.end - end_region.range.start;
6639
6640        Some((start_region.buffer, buffer_start..buffer_end))
6641    }
6642
6643    /// If the two endpoints of the range lie in the same excerpt, return the corresponding
6644    /// buffer range. Intervening deleted hunks are allowed.
6645    pub fn anchor_range_to_buffer_anchor_range(
6646        &self,
6647        range: Range<Anchor>,
6648    ) -> Option<(&BufferSnapshot, Range<text::Anchor>)> {
6649        let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
6650        cursor.seek(&range.start.seek_target(&self), Bias::Left);
6651
6652        let start_excerpt = cursor.item()?;
6653
6654        let snapshot = start_excerpt.buffer_snapshot(&self);
6655
6656        cursor.seek(&range.end.seek_target(&self), Bias::Left);
6657
6658        let end_excerpt = cursor.item()?;
6659
6660        if start_excerpt != end_excerpt {
6661            return None;
6662        }
6663
6664        if let Anchor::Excerpt(excerpt_anchor) = range.start
6665            && (excerpt_anchor.path != start_excerpt.path_key_index
6666                || excerpt_anchor.buffer_id() != snapshot.remote_id())
6667        {
6668            return None;
6669        }
6670        if let Anchor::Excerpt(excerpt_anchor) = range.end
6671            && (excerpt_anchor.path != end_excerpt.path_key_index
6672                || excerpt_anchor.buffer_id() != snapshot.remote_id())
6673        {
6674            return None;
6675        }
6676
6677        Some((
6678            snapshot,
6679            range.start.text_anchor_in(snapshot)..range.end.text_anchor_in(snapshot),
6680        ))
6681    }
6682
6683    /// Returns all nonempty intersections of the given buffer range with excerpts in the multibuffer in order.
6684    ///
6685    /// The multibuffer ranges are split to not intersect deleted hunks.
6686    pub fn buffer_range_to_excerpt_ranges(
6687        &self,
6688        range: Range<text::Anchor>,
6689    ) -> impl Iterator<Item = Range<Anchor>> {
6690        assert!(range.start.buffer_id == range.end.buffer_id);
6691
6692        let buffer_id = range.start.buffer_id;
6693        self.buffers
6694            .get(&buffer_id)
6695            .map(|buffer_state_snapshot| {
6696                let path_key_index = buffer_state_snapshot.path_key_index;
6697                let buffer_snapshot = &buffer_state_snapshot.buffer_snapshot;
6698                let buffer_range = range.to_offset(buffer_snapshot);
6699
6700                let start = Anchor::in_buffer(path_key_index, range.start).to_offset(self);
6701                let mut cursor = self.cursor::<MultiBufferOffset, BufferOffset>();
6702                cursor.seek(&start);
6703                std::iter::from_fn(move || {
6704                    while let Some(region) = cursor.region()
6705                        && !region.is_main_buffer
6706                    {
6707                        cursor.next();
6708                    }
6709
6710                    let region = cursor.region()?;
6711                    if region.buffer.remote_id() != buffer_id
6712                        || region.buffer_range.start > BufferOffset(buffer_range.end)
6713                    {
6714                        return None;
6715                    }
6716
6717                    let start = region
6718                        .buffer_range
6719                        .start
6720                        .max(BufferOffset(buffer_range.start));
6721                    let mut end = region.buffer_range.end.min(BufferOffset(buffer_range.end));
6722
6723                    cursor.next();
6724                    while let Some(region) = cursor.region()
6725                        && region.is_main_buffer
6726                        && region.buffer.remote_id() == buffer_id
6727                        && region.buffer_range.start <= end
6728                    {
6729                        end = end
6730                            .max(region.buffer_range.end)
6731                            .min(BufferOffset(buffer_range.end));
6732                        cursor.next();
6733                    }
6734
6735                    let multibuffer_range = Anchor::range_in_buffer(
6736                        path_key_index,
6737                        buffer_snapshot.anchor_range_inside(start..end),
6738                    );
6739                    Some(multibuffer_range)
6740                })
6741            })
6742            .into_iter()
6743            .flatten()
6744    }
6745
6746    pub fn buffers_with_paths<'a>(
6747        &'a self,
6748    ) -> impl 'a + Iterator<Item = (&'a BufferSnapshot, &'a PathKey)> {
6749        self.buffers
6750            .values()
6751            .map(|buffer| (&buffer.buffer_snapshot, &buffer.path_key))
6752    }
6753
6754    /// Returns the number of graphemes in `range`.
6755    ///
6756    /// This counts user-visible characters like `e\u{301}` as one.
6757    pub fn grapheme_count_for_range(&self, range: &Range<MultiBufferOffset>) -> usize {
6758        self.text_for_range(range.clone())
6759            .collect::<String>()
6760            .graphemes(true)
6761            .count()
6762    }
6763
6764    pub fn range_for_buffer(&self, buffer_id: BufferId) -> Option<Range<Point>> {
6765        let path_key = self.path_key_index_for_buffer(buffer_id)?;
6766        let start = Anchor::in_buffer(path_key, text::Anchor::min_for_buffer(buffer_id));
6767        let end = Anchor::in_buffer(path_key, text::Anchor::max_for_buffer(buffer_id));
6768        Some((start..end).to_point(self))
6769    }
6770}
6771
6772#[cfg(any(test, feature = "test-support"))]
6773impl MultiBufferSnapshot {
6774    pub fn random_byte_range(
6775        &self,
6776        start_offset: MultiBufferOffset,
6777        rng: &mut impl rand::Rng,
6778    ) -> Range<MultiBufferOffset> {
6779        let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
6780        let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
6781        start..end
6782    }
6783
6784    #[cfg(any(test, feature = "test-support"))]
6785    fn check_invariants(&self) {
6786        let excerpts = self.excerpts.items(());
6787
6788        let mut all_buffer_path_keys = HashSet::default();
6789        for buffer in self.buffers.values() {
6790            let path_key = buffer.path_key.clone();
6791            assert!(
6792                all_buffer_path_keys.insert(path_key),
6793                "path key reused for multiple buffers: {:#?}",
6794                self.buffers
6795            );
6796        }
6797
6798        let all_excerpt_path_keys = HashSet::from_iter(excerpts.iter().map(|e| e.path_key.clone()));
6799
6800        for (ix, excerpt) in excerpts.iter().enumerate() {
6801            if ix > 0 {
6802                let prev = &excerpts[ix - 1];
6803
6804                if excerpt.path_key < prev.path_key {
6805                    panic!("excerpt path_keys are out-of-order: {:#?}", excerpts);
6806                } else if excerpt.path_key == prev.path_key {
6807                    assert_eq!(
6808                        excerpt.buffer_id, prev.buffer_id,
6809                        "excerpts with same path_key have different buffer_ids: {:#?}",
6810                        excerpts
6811                    );
6812                    if excerpt
6813                        .start_anchor()
6814                        .cmp(&prev.end_anchor(), &self)
6815                        .is_le()
6816                    {
6817                        panic!("excerpt anchors are out-of-order: {:#?}", excerpts);
6818                    }
6819                    if excerpt
6820                        .start_anchor()
6821                        .cmp(&excerpt.end_anchor(), &self)
6822                        .is_ge()
6823                    {
6824                        panic!("excerpt with backward range: {:#?}", excerpts);
6825                    }
6826                }
6827            }
6828
6829            if ix < excerpts.len() - 1 {
6830                assert!(
6831                    excerpt.has_trailing_newline,
6832                    "non-trailing excerpt has no trailing newline: {:#?}",
6833                    excerpts
6834                );
6835            } else {
6836                assert!(
6837                    !excerpt.has_trailing_newline,
6838                    "trailing excerpt has trailing newline: {:#?}",
6839                    excerpts
6840                );
6841            }
6842            assert!(
6843                all_buffer_path_keys.contains(&excerpt.path_key),
6844                "excerpt path key not found in active path keys: {:#?}",
6845                excerpt.path_key
6846            );
6847            assert_eq!(
6848                self.path_keys_by_index.get(&excerpt.path_key_index),
6849                Some(&excerpt.path_key),
6850                "excerpt path key index does not match path key: {:#?}",
6851                excerpt.path_key,
6852            );
6853        }
6854        assert_eq!(all_buffer_path_keys, all_excerpt_path_keys);
6855
6856        if self.diff_transforms.summary().input != self.excerpts.summary().text {
6857            panic!(
6858                "incorrect input summary. expected {:?}, got {:?}. transforms: {:+?}",
6859                self.excerpts.summary().text,
6860                self.diff_transforms.summary().input,
6861                self.diff_transforms.items(()),
6862            );
6863        }
6864
6865        let mut prev_transform: Option<&DiffTransform> = None;
6866        for item in self.diff_transforms.iter() {
6867            if let DiffTransform::BufferContent {
6868                summary,
6869                inserted_hunk_info,
6870            } = item
6871            {
6872                if let Some(DiffTransform::BufferContent {
6873                    inserted_hunk_info: prev_inserted_hunk_info,
6874                    ..
6875                }) = prev_transform
6876                    && *inserted_hunk_info == *prev_inserted_hunk_info
6877                {
6878                    panic!(
6879                        "multiple adjacent buffer content transforms with is_inserted_hunk = {inserted_hunk_info:?}. transforms: {:+?}",
6880                        self.diff_transforms.items(())
6881                    );
6882                }
6883                if summary.len == MultiBufferOffset(0) && !self.is_empty() {
6884                    panic!("empty buffer content transform");
6885                }
6886            }
6887            prev_transform = Some(item);
6888        }
6889    }
6890}
6891
6892impl<'a, MBD, BD> MultiBufferCursor<'a, MBD, BD>
6893where
6894    MBD: MultiBufferDimension + Ord + Sub + ops::AddAssign<<MBD as Sub>::Output>,
6895    BD: TextDimension + AddAssign<<MBD as Sub>::Output>,
6896{
6897    #[instrument(skip_all)]
6898    fn seek(&mut self, position: &MBD) {
6899        let position = OutputDimension(*position);
6900        self.cached_region.take();
6901        self.diff_transforms.seek(&position, Bias::Right);
6902        if self.diff_transforms.item().is_none()
6903            && self.diff_transforms.start().output_dimension == position
6904        {
6905            self.diff_transforms.prev();
6906        }
6907
6908        let mut excerpt_position = self.diff_transforms.start().excerpt_dimension;
6909        if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6910            let overshoot = position - self.diff_transforms.start().output_dimension;
6911            excerpt_position += overshoot;
6912        }
6913
6914        self.excerpts.seek(&excerpt_position, Bias::Right);
6915        if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() {
6916            self.excerpts.prev();
6917        }
6918    }
6919
6920    fn seek_forward(&mut self, position: &MBD) {
6921        let position = OutputDimension(*position);
6922        self.cached_region.take();
6923        self.diff_transforms.seek_forward(&position, Bias::Right);
6924        if self.diff_transforms.item().is_none()
6925            && self.diff_transforms.start().output_dimension == position
6926        {
6927            self.diff_transforms.prev();
6928        }
6929
6930        let overshoot = position - self.diff_transforms.start().output_dimension;
6931        let mut excerpt_position = self.diff_transforms.start().excerpt_dimension;
6932        if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6933            excerpt_position += overshoot;
6934        }
6935
6936        self.excerpts.seek_forward(&excerpt_position, Bias::Right);
6937        if self.excerpts.item().is_none() && excerpt_position == *self.excerpts.start() {
6938            self.excerpts.prev();
6939        }
6940    }
6941
6942    fn next_excerpt(&mut self) {
6943        self.excerpts.next();
6944        self.seek_to_start_of_current_excerpt();
6945    }
6946
6947    fn prev_excerpt(&mut self) {
6948        self.excerpts.prev();
6949        self.seek_to_start_of_current_excerpt();
6950    }
6951
6952    fn seek_to_start_of_current_excerpt(&mut self) {
6953        self.cached_region.take();
6954
6955        if self.diff_transforms.seek(self.excerpts.start(), Bias::Left)
6956            && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
6957            && self.diff_transforms.next_item().is_some()
6958        {
6959            self.diff_transforms.next();
6960        }
6961    }
6962
6963    fn next_excerpt_forwards(&mut self) {
6964        self.excerpts.next();
6965        self.seek_to_start_of_current_excerpt_forward();
6966    }
6967
6968    fn seek_to_start_of_current_excerpt_forward(&mut self) {
6969        self.cached_region.take();
6970
6971        if self
6972            .diff_transforms
6973            .seek_forward(self.excerpts.start(), Bias::Left)
6974            && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
6975            && self.diff_transforms.next_item().is_some()
6976        {
6977            self.diff_transforms.next();
6978        }
6979    }
6980
6981    fn next(&mut self) {
6982        self.cached_region.take();
6983        match self
6984            .diff_transforms
6985            .end()
6986            .excerpt_dimension
6987            .cmp(&self.excerpts.end())
6988        {
6989            cmp::Ordering::Less => self.diff_transforms.next(),
6990            cmp::Ordering::Greater => self.excerpts.next(),
6991            cmp::Ordering::Equal => {
6992                self.diff_transforms.next();
6993                if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
6994                    || self.diff_transforms.item().is_none()
6995                {
6996                    self.excerpts.next();
6997                } else if let Some(DiffTransform::DeletedHunk { hunk_info, .. }) =
6998                    self.diff_transforms.item()
6999                    && self
7000                        .excerpts
7001                        .item()
7002                        .is_some_and(|excerpt| excerpt.end_anchor() != hunk_info.excerpt_end)
7003                {
7004                    self.excerpts.next();
7005                }
7006            }
7007        }
7008    }
7009
7010    fn prev(&mut self) {
7011        self.cached_region.take();
7012        match self
7013            .diff_transforms
7014            .start()
7015            .excerpt_dimension
7016            .cmp(self.excerpts.start())
7017        {
7018            cmp::Ordering::Less => self.excerpts.prev(),
7019            cmp::Ordering::Greater => self.diff_transforms.prev(),
7020            cmp::Ordering::Equal => {
7021                self.diff_transforms.prev();
7022                if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
7023                    || self.diff_transforms.item().is_none()
7024                {
7025                    self.excerpts.prev();
7026                }
7027            }
7028        }
7029    }
7030
7031    fn region(&self) -> Option<&MultiBufferRegion<'a, MBD, BD>> {
7032        self.cached_region
7033            .get_or_init(|| self.build_region())
7034            .as_ref()
7035    }
7036
7037    fn is_at_start_of_excerpt(&mut self) -> bool {
7038        if self.diff_transforms.start().excerpt_dimension > *self.excerpts.start() {
7039            return false;
7040        } else if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
7041            return true;
7042        }
7043
7044        self.diff_transforms.prev();
7045        let prev_transform = self.diff_transforms.item();
7046        self.diff_transforms.next();
7047
7048        prev_transform.is_none_or(|next_transform| {
7049            matches!(next_transform, DiffTransform::BufferContent { .. })
7050        })
7051    }
7052
7053    fn is_at_end_of_excerpt(&self) -> bool {
7054        if self.diff_transforms.end().excerpt_dimension < self.excerpts.end() {
7055            return false;
7056        } else if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
7057            || self.diff_transforms.item().is_none()
7058        {
7059            return true;
7060        }
7061
7062        let next_transform = self.diff_transforms.next_item();
7063        next_transform.is_none_or(|next_transform| match next_transform {
7064            DiffTransform::BufferContent { .. } => true,
7065            DiffTransform::DeletedHunk { hunk_info, .. } => self
7066                .excerpts
7067                .item()
7068                .is_some_and(|excerpt| excerpt.end_anchor() != hunk_info.excerpt_end),
7069        })
7070    }
7071
7072    fn main_buffer_position(&self) -> Option<BD> {
7073        let excerpt = self.excerpts.item()?;
7074        let buffer = excerpt.buffer_snapshot(self.snapshot);
7075        let buffer_context_start = excerpt.range.context.start.summary::<BD>(buffer);
7076        let mut buffer_start = buffer_context_start;
7077        let overshoot = self.diff_transforms.end().excerpt_dimension - *self.excerpts.start();
7078        buffer_start += overshoot;
7079        Some(buffer_start)
7080    }
7081
7082    fn buffer_position_at(&self, output_position: &MBD) -> Option<BD> {
7083        let excerpt = self.excerpts.item()?;
7084        let buffer = excerpt.buffer_snapshot(self.snapshot);
7085        let buffer_context_start = excerpt.range.context.start.summary::<BD>(buffer);
7086        let mut excerpt_offset = self.diff_transforms.start().excerpt_dimension;
7087        if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7088            excerpt_offset += *output_position - self.diff_transforms.start().output_dimension.0;
7089        }
7090        let mut result = buffer_context_start;
7091        result += excerpt_offset - *self.excerpts.start();
7092        Some(result)
7093    }
7094
7095    fn build_region(&self) -> Option<MultiBufferRegion<'a, MBD, BD>> {
7096        let excerpt = self.excerpts.item()?;
7097        match self.diff_transforms.item()? {
7098            DiffTransform::DeletedHunk {
7099                buffer_id,
7100                base_text_byte_range,
7101                has_trailing_newline,
7102                hunk_info,
7103                ..
7104            } => {
7105                let diff = find_diff_state(&self.snapshot.diffs, *buffer_id)?;
7106                let buffer = diff.base_text();
7107                let mut rope_cursor = buffer.as_rope().cursor(0);
7108                let buffer_start = rope_cursor.summary::<BD>(base_text_byte_range.start);
7109                let buffer_range_len = rope_cursor.summary::<BD>(base_text_byte_range.end);
7110                let mut buffer_end = buffer_start;
7111                TextDimension::add_assign(&mut buffer_end, &buffer_range_len);
7112                let start = self.diff_transforms.start().output_dimension.0;
7113                let end = self.diff_transforms.end().output_dimension.0;
7114                Some(MultiBufferRegion {
7115                    buffer,
7116                    excerpt,
7117                    has_trailing_newline: *has_trailing_newline,
7118                    is_main_buffer: false,
7119                    diff_hunk_status: Some(DiffHunkStatus::deleted(
7120                        hunk_info.hunk_secondary_status,
7121                    )),
7122                    buffer_range: buffer_start..buffer_end,
7123                    range: start..end,
7124                })
7125            }
7126            DiffTransform::BufferContent {
7127                inserted_hunk_info, ..
7128            } => {
7129                let buffer = excerpt.buffer_snapshot(self.snapshot);
7130                let buffer_context_start = excerpt.range.context.start.summary::<BD>(buffer);
7131
7132                let mut start = self.diff_transforms.start().output_dimension.0;
7133                let mut buffer_start = buffer_context_start;
7134                if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
7135                    let overshoot =
7136                        *self.excerpts.start() - self.diff_transforms.start().excerpt_dimension;
7137                    start += overshoot;
7138                } else {
7139                    let overshoot =
7140                        self.diff_transforms.start().excerpt_dimension - *self.excerpts.start();
7141                    buffer_start += overshoot;
7142                }
7143
7144                let mut end;
7145                let mut buffer_end;
7146                let has_trailing_newline;
7147                let transform_end = self.diff_transforms.end();
7148                if transform_end.excerpt_dimension < self.excerpts.end() {
7149                    let overshoot = transform_end.excerpt_dimension - *self.excerpts.start();
7150                    end = transform_end.output_dimension.0;
7151                    buffer_end = buffer_context_start;
7152                    buffer_end += overshoot;
7153                    has_trailing_newline = false;
7154                } else {
7155                    let overshoot =
7156                        self.excerpts.end() - self.diff_transforms.start().excerpt_dimension;
7157                    end = self.diff_transforms.start().output_dimension.0;
7158                    end += overshoot;
7159                    buffer_end = excerpt.range.context.end.summary::<BD>(buffer);
7160                    has_trailing_newline = excerpt.has_trailing_newline;
7161                };
7162
7163                let diff_hunk_status = inserted_hunk_info.map(|info| {
7164                    if info.is_logically_deleted {
7165                        DiffHunkStatus::deleted(info.hunk_secondary_status)
7166                    } else {
7167                        DiffHunkStatus::added(info.hunk_secondary_status)
7168                    }
7169                });
7170
7171                Some(MultiBufferRegion {
7172                    buffer,
7173                    excerpt,
7174                    has_trailing_newline,
7175                    is_main_buffer: true,
7176                    diff_hunk_status,
7177                    buffer_range: buffer_start..buffer_end,
7178                    range: start..end,
7179                })
7180            }
7181        }
7182    }
7183
7184    fn fetch_excerpt_with_range(&self) -> Option<(&'a Excerpt, Range<MBD>)> {
7185        let excerpt = self.excerpts.item()?;
7186        match self.diff_transforms.item()? {
7187            &DiffTransform::DeletedHunk { .. } => {
7188                let start = self.diff_transforms.start().output_dimension.0;
7189                let end = self.diff_transforms.end().output_dimension.0;
7190                Some((excerpt, start..end))
7191            }
7192            DiffTransform::BufferContent { .. } => {
7193                let mut start = self.diff_transforms.start().output_dimension.0;
7194                if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
7195                    let overshoot =
7196                        *self.excerpts.start() - self.diff_transforms.start().excerpt_dimension;
7197                    start += overshoot;
7198                }
7199
7200                let mut end;
7201                let transform_end = self.diff_transforms.end();
7202                if transform_end.excerpt_dimension < self.excerpts.end() {
7203                    end = transform_end.output_dimension.0;
7204                } else {
7205                    let overshoot =
7206                        self.excerpts.end() - self.diff_transforms.start().excerpt_dimension;
7207                    end = self.diff_transforms.start().output_dimension.0;
7208                    end += overshoot;
7209                };
7210
7211                Some((excerpt, start..end))
7212            }
7213        }
7214    }
7215
7216    fn excerpt(&self) -> Option<&'a Excerpt> {
7217        self.excerpts.item()
7218    }
7219}
7220
7221impl Excerpt {
7222    fn new(
7223        path_key: PathKey,
7224        path_key_index: PathKeyIndex,
7225        buffer_snapshot: &BufferSnapshot,
7226        range: ExcerptRange<text::Anchor>,
7227        has_trailing_newline: bool,
7228    ) -> Self {
7229        Excerpt {
7230            path_key,
7231            path_key_index,
7232            buffer_id: buffer_snapshot.remote_id(),
7233            max_buffer_row: range.context.end.to_point(&buffer_snapshot).row,
7234            text_summary: buffer_snapshot.text_summary_for_range::<TextSummary, _>(
7235                range.context.to_offset(&buffer_snapshot),
7236            ),
7237            range,
7238            has_trailing_newline,
7239        }
7240    }
7241
7242    fn buffer_snapshot<'a>(&self, snapshot: &'a MultiBufferSnapshot) -> &'a BufferSnapshot {
7243        &snapshot
7244            .buffers
7245            .get(&self.buffer_id)
7246            .expect("buffer snapshot not found for excerpt")
7247            .buffer_snapshot
7248    }
7249
7250    fn buffer(&self, multibuffer: &MultiBuffer) -> Entity<Buffer> {
7251        multibuffer
7252            .buffer(self.buffer_id)
7253            .expect("buffer entity not found for excerpt")
7254    }
7255
7256    fn chunks_in_range<'a>(
7257        &'a self,
7258        range: Range<usize>,
7259        language_aware: LanguageAwareStyling,
7260        snapshot: &'a MultiBufferSnapshot,
7261    ) -> ExcerptChunks<'a> {
7262        let buffer = self.buffer_snapshot(snapshot);
7263        let content_start = self.range.context.start.to_offset(buffer);
7264        let chunks_start = content_start + range.start;
7265        let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
7266
7267        let has_footer = self.has_trailing_newline
7268            && range.start <= self.text_summary.len
7269            && range.end > self.text_summary.len;
7270
7271        let content_chunks = buffer.chunks(chunks_start..chunks_end, language_aware);
7272
7273        ExcerptChunks {
7274            content_chunks,
7275            has_footer,
7276            end: self.end_anchor(),
7277        }
7278    }
7279
7280    fn seek_chunks(
7281        &self,
7282        excerpt_chunks: &mut ExcerptChunks,
7283        range: Range<usize>,
7284        snapshot: &MultiBufferSnapshot,
7285    ) {
7286        let buffer = self.buffer_snapshot(snapshot);
7287        let content_start = self.range.context.start.to_offset(buffer);
7288        let chunks_start = content_start + range.start;
7289        let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
7290        excerpt_chunks.content_chunks.seek(chunks_start..chunks_end);
7291        excerpt_chunks.has_footer = self.has_trailing_newline
7292            && range.start <= self.text_summary.len
7293            && range.end > self.text_summary.len;
7294    }
7295
7296    fn clip_anchor(
7297        &self,
7298        text_anchor: text::Anchor,
7299        snapshot: &MultiBufferSnapshot,
7300    ) -> text::Anchor {
7301        let buffer = self.buffer_snapshot(snapshot);
7302        if text_anchor.cmp(&self.range.context.start, buffer).is_lt() {
7303            self.range.context.start
7304        } else if text_anchor.cmp(&self.range.context.end, buffer).is_gt() {
7305            self.range.context.end
7306        } else {
7307            text_anchor
7308        }
7309    }
7310
7311    pub(crate) fn contains(&self, anchor: &ExcerptAnchor, snapshot: &MultiBufferSnapshot) -> bool {
7312        self.path_key_index == anchor.path
7313            && self.buffer_id == anchor.text_anchor.buffer_id
7314            && self
7315                .range
7316                .contains(&anchor.text_anchor(), self.buffer_snapshot(snapshot))
7317    }
7318
7319    fn start_anchor(&self) -> ExcerptAnchor {
7320        ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.start)
7321    }
7322
7323    fn end_anchor(&self) -> ExcerptAnchor {
7324        ExcerptAnchor::in_buffer(self.path_key_index, self.range.context.end)
7325    }
7326}
7327
7328impl PartialEq for Excerpt {
7329    fn eq(&self, other: &Self) -> bool {
7330        self.path_key_index == other.path_key_index
7331            && self.buffer_id == other.buffer_id
7332            && self.range.context == other.range.context
7333    }
7334}
7335
7336impl sum_tree::Item for Excerpt {
7337    type Summary = ExcerptSummary;
7338
7339    fn summary(&self, _cx: ()) -> Self::Summary {
7340        let mut text = self.text_summary;
7341        if self.has_trailing_newline {
7342            text += TextSummary::from("\n");
7343        }
7344        ExcerptSummary {
7345            path_key: self.path_key.clone(),
7346            path_key_index: Some(self.path_key_index),
7347            max_anchor: Some(self.range.context.end),
7348            widest_line_number: self.max_buffer_row,
7349            text: text.into(),
7350            count: 1,
7351        }
7352    }
7353}
7354
7355impl DiffTransform {
7356    fn hunk_info(&self) -> Option<DiffTransformHunkInfo> {
7357        match self {
7358            DiffTransform::DeletedHunk { hunk_info, .. } => Some(*hunk_info),
7359            DiffTransform::BufferContent {
7360                inserted_hunk_info, ..
7361            } => *inserted_hunk_info,
7362        }
7363    }
7364}
7365
7366impl sum_tree::Item for DiffTransform {
7367    type Summary = DiffTransformSummary;
7368
7369    fn summary(&self, _: <Self::Summary as sum_tree::Summary>::Context<'_>) -> Self::Summary {
7370        match self {
7371            DiffTransform::BufferContent { summary, .. } => DiffTransformSummary {
7372                input: *summary,
7373                output: *summary,
7374            },
7375            &DiffTransform::DeletedHunk { summary, .. } => DiffTransformSummary {
7376                input: MBTextSummary::default(),
7377                output: summary.into(),
7378            },
7379        }
7380    }
7381}
7382
7383impl DiffTransformSummary {
7384    fn excerpt_len(&self) -> ExcerptOffset {
7385        ExcerptDimension(self.input.len)
7386    }
7387}
7388
7389impl sum_tree::ContextLessSummary for DiffTransformSummary {
7390    fn zero() -> Self {
7391        DiffTransformSummary {
7392            input: MBTextSummary::default(),
7393            output: MBTextSummary::default(),
7394        }
7395    }
7396
7397    fn add_summary(&mut self, other: &Self) {
7398        self.input += other.input;
7399        self.output += other.output;
7400    }
7401}
7402
7403impl sum_tree::Dimension<'_, ExcerptSummary> for PathKey {
7404    fn zero(_: <ExcerptSummary as sum_tree::Summary>::Context<'_>) -> Self {
7405        PathKey::min()
7406    }
7407
7408    fn add_summary(
7409        &mut self,
7410        summary: &'_ ExcerptSummary,
7411        _cx: <ExcerptSummary as sum_tree::Summary>::Context<'_>,
7412    ) {
7413        *self = summary.path_key.clone();
7414    }
7415}
7416
7417impl sum_tree::Dimension<'_, ExcerptSummary> for MultiBufferOffset {
7418    fn zero(_: <ExcerptSummary as sum_tree::Summary>::Context<'_>) -> Self {
7419        MultiBufferOffset::ZERO
7420    }
7421
7422    fn add_summary(
7423        &mut self,
7424        summary: &'_ ExcerptSummary,
7425        _cx: <ExcerptSummary as sum_tree::Summary>::Context<'_>,
7426    ) {
7427        *self += summary.text.len
7428    }
7429}
7430
7431impl sum_tree::ContextLessSummary for ExcerptSummary {
7432    fn zero() -> Self {
7433        Self::min()
7434    }
7435
7436    fn add_summary(&mut self, summary: &Self) {
7437        debug_assert!(
7438            summary.path_key >= self.path_key,
7439            "Path keys must be in ascending order: {:?} > {:?}",
7440            summary.path_key,
7441            self.path_key
7442        );
7443
7444        self.path_key = summary.path_key.clone();
7445        self.path_key_index = summary.path_key_index;
7446        self.max_anchor = summary.max_anchor;
7447        self.text += summary.text;
7448        self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number);
7449        self.count += summary.count;
7450    }
7451}
7452
7453impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for AnchorSeekTarget<'_> {
7454    fn cmp(
7455        &self,
7456        cursor_location: &ExcerptSummary,
7457        _cx: <ExcerptSummary as sum_tree::Summary>::Context<'_>,
7458    ) -> cmp::Ordering {
7459        match self {
7460            AnchorSeekTarget::Missing { path_key } => {
7461                // Want to end up after any excerpts for (a different buffer at) the original path
7462                match Ord::cmp(*path_key, &cursor_location.path_key) {
7463                    Ordering::Less => Ordering::Less,
7464                    Ordering::Equal | Ordering::Greater => Ordering::Greater,
7465                }
7466            }
7467            AnchorSeekTarget::Excerpt {
7468                path_key,
7469                path_key_index,
7470                anchor,
7471                snapshot,
7472            } => {
7473                if Some(*path_key_index) != cursor_location.path_key_index {
7474                    Ord::cmp(*path_key, &cursor_location.path_key)
7475                } else if let Some(max_anchor) = cursor_location.max_anchor {
7476                    debug_assert_eq!(max_anchor.buffer_id, snapshot.remote_id());
7477                    anchor.cmp(&max_anchor, snapshot)
7478                } else {
7479                    Ordering::Greater
7480                }
7481            }
7482            AnchorSeekTarget::Empty => Ordering::Greater,
7483        }
7484    }
7485}
7486
7487impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for PathKey {
7488    fn cmp(
7489        &self,
7490        cursor_location: &ExcerptSummary,
7491        _cx: <ExcerptSummary as sum_tree::Summary>::Context<'_>,
7492    ) -> cmp::Ordering {
7493        Ord::cmp(self, &cursor_location.path_key)
7494    }
7495}
7496
7497impl<'a, MBD> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptDimension<MBD>
7498where
7499    MBD: MultiBufferDimension + Default,
7500{
7501    fn zero(_: ()) -> Self {
7502        ExcerptDimension(MBD::default())
7503    }
7504
7505    fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7506        MultiBufferDimension::add_mb_text_summary(&mut self.0, &summary.text)
7507    }
7508}
7509
7510#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
7511struct OutputDimension<T>(T);
7512
7513impl<T: PartialEq> PartialEq<T> for OutputDimension<T> {
7514    fn eq(&self, other: &T) -> bool {
7515        self.0 == *other
7516    }
7517}
7518
7519impl<T: PartialOrd> PartialOrd<T> for OutputDimension<T> {
7520    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
7521        self.0.partial_cmp(other)
7522    }
7523}
7524
7525impl<R, T, U> ops::Sub<OutputDimension<U>> for OutputDimension<T>
7526where
7527    T: ops::Sub<U, Output = R>,
7528{
7529    type Output = R;
7530
7531    fn sub(self, other: OutputDimension<U>) -> Self::Output {
7532        self.0 - other.0
7533    }
7534}
7535
7536impl<R, T, U> ops::Add<U> for OutputDimension<T>
7537where
7538    T: ops::Add<U, Output = R>,
7539{
7540    type Output = OutputDimension<R>;
7541
7542    fn add(self, other: U) -> Self::Output {
7543        OutputDimension(self.0 + other)
7544    }
7545}
7546
7547impl<T, U> AddAssign<U> for OutputDimension<T>
7548where
7549    T: AddAssign<U>,
7550{
7551    fn add_assign(&mut self, other: U) {
7552        self.0 += other;
7553    }
7554}
7555
7556impl<T, U> SubAssign<U> for OutputDimension<T>
7557where
7558    T: SubAssign<U>,
7559{
7560    fn sub_assign(&mut self, other: U) {
7561        self.0 -= other;
7562    }
7563}
7564
7565#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Debug, Default)]
7566struct ExcerptDimension<T>(T);
7567
7568impl<T: PartialEq> PartialEq<T> for ExcerptDimension<T> {
7569    fn eq(&self, other: &T) -> bool {
7570        self.0 == *other
7571    }
7572}
7573
7574impl<T: PartialOrd> PartialOrd<T> for ExcerptDimension<T> {
7575    fn partial_cmp(&self, other: &T) -> Option<cmp::Ordering> {
7576        self.0.partial_cmp(other)
7577    }
7578}
7579
7580impl ExcerptOffset {
7581    fn saturating_sub(self, other: ExcerptOffset) -> usize {
7582        self.0.saturating_sub(other.0)
7583    }
7584}
7585
7586impl<R, T, U> ops::Sub<ExcerptDimension<U>> for ExcerptDimension<T>
7587where
7588    T: ops::Sub<U, Output = R>,
7589{
7590    type Output = R;
7591
7592    fn sub(self, other: ExcerptDimension<U>) -> Self::Output {
7593        self.0 - other.0
7594    }
7595}
7596
7597impl<R, T, U> ops::Add<U> for ExcerptDimension<T>
7598where
7599    T: ops::Add<U, Output = R>,
7600{
7601    type Output = ExcerptDimension<R>;
7602
7603    fn add(self, other: U) -> Self::Output {
7604        ExcerptDimension(self.0 + other)
7605    }
7606}
7607
7608impl<T, U> AddAssign<U> for ExcerptDimension<T>
7609where
7610    T: AddAssign<U>,
7611{
7612    fn add_assign(&mut self, other: U) {
7613        self.0 += other;
7614    }
7615}
7616
7617impl<T, U> SubAssign<U> for ExcerptDimension<T>
7618where
7619    T: SubAssign<U>,
7620{
7621    fn sub_assign(&mut self, other: U) {
7622        self.0 -= other;
7623    }
7624}
7625
7626impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for MultiBufferOffset {
7627    fn zero(_: ()) -> Self {
7628        MultiBufferOffset::ZERO
7629    }
7630
7631    fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7632        *self += summary.output.len;
7633    }
7634}
7635
7636impl<MBD> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransformSummary>
7637    for ExcerptDimension<MBD>
7638where
7639    MBD: MultiBufferDimension + Ord,
7640{
7641    fn cmp(&self, cursor_location: &DiffTransformSummary, _: ()) -> cmp::Ordering {
7642        Ord::cmp(&self.0, &MBD::from_summary(&cursor_location.input))
7643    }
7644}
7645
7646impl<'a, MBD> sum_tree::SeekTarget<'a, DiffTransformSummary, DiffTransforms<MBD>>
7647    for ExcerptDimension<MBD>
7648where
7649    MBD: MultiBufferDimension + Ord,
7650{
7651    fn cmp(&self, cursor_location: &DiffTransforms<MBD>, _: ()) -> cmp::Ordering {
7652        Ord::cmp(&self.0, &cursor_location.excerpt_dimension.0)
7653    }
7654}
7655
7656impl<'a, MBD: MultiBufferDimension> sum_tree::Dimension<'a, DiffTransformSummary>
7657    for ExcerptDimension<MBD>
7658{
7659    fn zero(_: ()) -> Self {
7660        ExcerptDimension(MBD::default())
7661    }
7662
7663    fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7664        self.0.add_mb_text_summary(&summary.input)
7665    }
7666}
7667
7668impl<'a, MBD> sum_tree::SeekTarget<'a, DiffTransformSummary, DiffTransforms<MBD>>
7669    for OutputDimension<MBD>
7670where
7671    MBD: MultiBufferDimension + Ord,
7672{
7673    fn cmp(&self, cursor_location: &DiffTransforms<MBD>, _: ()) -> cmp::Ordering {
7674        Ord::cmp(&self.0, &cursor_location.output_dimension.0)
7675    }
7676}
7677
7678impl<'a, MBD: MultiBufferDimension> sum_tree::Dimension<'a, DiffTransformSummary>
7679    for OutputDimension<MBD>
7680{
7681    fn zero(_: ()) -> Self {
7682        OutputDimension(MBD::default())
7683    }
7684
7685    fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7686        self.0.add_mb_text_summary(&summary.output)
7687    }
7688}
7689
7690impl MultiBufferRows<'_> {
7691    pub fn seek(&mut self, MultiBufferRow(row): MultiBufferRow) {
7692        self.point = Point::new(row, 0);
7693        self.cursor.seek(&self.point);
7694    }
7695}
7696
7697impl Iterator for MultiBufferRows<'_> {
7698    type Item = RowInfo;
7699
7700    fn next(&mut self) -> Option<Self::Item> {
7701        if self.is_empty && self.point.row == 0 {
7702            self.point += Point::new(1, 0);
7703            return Some(RowInfo {
7704                buffer_id: None,
7705                buffer_row: Some(0),
7706                multibuffer_row: Some(MultiBufferRow(0)),
7707                diff_status: None,
7708                expand_info: None,
7709                wrapped_buffer_row: None,
7710            });
7711        }
7712
7713        let mut region = self.cursor.region()?.clone();
7714        while self.point >= region.range.end {
7715            self.cursor.next();
7716            if let Some(next_region) = self.cursor.region() {
7717                region = next_region.clone();
7718            } else if self.point == self.cursor.diff_transforms.end().output_dimension.0 {
7719                let multibuffer_row = MultiBufferRow(self.point.row);
7720                let last_excerpt = self
7721                    .cursor
7722                    .excerpts
7723                    .item()
7724                    .or(self.cursor.excerpts.prev_item())?;
7725                let buffer_snapshot = last_excerpt.buffer_snapshot(self.cursor.snapshot);
7726                let last_row = last_excerpt.range.context.end.to_point(buffer_snapshot).row;
7727
7728                let first_row = last_excerpt
7729                    .range
7730                    .context
7731                    .start
7732                    .to_point(buffer_snapshot)
7733                    .row;
7734
7735                let expand_info = if self.is_singleton {
7736                    None
7737                } else {
7738                    let needs_expand_up = first_row == last_row
7739                        && last_row > 0
7740                        && !region.diff_hunk_status.is_some_and(|d| d.is_deleted());
7741                    let needs_expand_down = last_row < buffer_snapshot.max_point().row;
7742
7743                    if needs_expand_up && needs_expand_down {
7744                        Some(ExpandExcerptDirection::UpAndDown)
7745                    } else if needs_expand_up {
7746                        Some(ExpandExcerptDirection::Up)
7747                    } else if needs_expand_down {
7748                        Some(ExpandExcerptDirection::Down)
7749                    } else {
7750                        None
7751                    }
7752                    .map(|direction| ExpandInfo {
7753                        direction,
7754                        start_anchor: Anchor::Excerpt(last_excerpt.start_anchor()),
7755                    })
7756                };
7757                self.point += Point::new(1, 0);
7758                return Some(RowInfo {
7759                    buffer_id: Some(last_excerpt.buffer_id),
7760                    buffer_row: Some(last_row),
7761                    multibuffer_row: Some(multibuffer_row),
7762                    diff_status: None,
7763                    wrapped_buffer_row: None,
7764                    expand_info,
7765                });
7766            } else {
7767                return None;
7768            };
7769        }
7770
7771        let overshoot = self.point - region.range.start;
7772        let buffer_point = region.buffer_range.start + overshoot;
7773        let expand_info = if self.is_singleton {
7774            None
7775        } else {
7776            let needs_expand_up = self.point.row == region.range.start.row
7777                && self.cursor.is_at_start_of_excerpt()
7778                && buffer_point.row > 0;
7779            let needs_expand_down = (region.excerpt.has_trailing_newline
7780                && self.point.row + 1 == region.range.end.row
7781                || !region.excerpt.has_trailing_newline && self.point.row == region.range.end.row)
7782                && self.cursor.is_at_end_of_excerpt()
7783                && buffer_point.row < region.buffer.max_point().row;
7784
7785            if needs_expand_up && needs_expand_down {
7786                Some(ExpandExcerptDirection::UpAndDown)
7787            } else if needs_expand_up {
7788                Some(ExpandExcerptDirection::Up)
7789            } else if needs_expand_down {
7790                Some(ExpandExcerptDirection::Down)
7791            } else {
7792                None
7793            }
7794            .map(|direction| ExpandInfo {
7795                direction,
7796                start_anchor: Anchor::Excerpt(region.excerpt.start_anchor()),
7797            })
7798        };
7799
7800        let result = Some(RowInfo {
7801            buffer_id: Some(region.buffer.remote_id()),
7802            buffer_row: Some(buffer_point.row),
7803            multibuffer_row: Some(MultiBufferRow(self.point.row)),
7804            diff_status: region
7805                .diff_hunk_status
7806                .filter(|_| self.point < region.range.end),
7807            expand_info,
7808            wrapped_buffer_row: None,
7809        });
7810        self.point += Point::new(1, 0);
7811        result
7812    }
7813}
7814
7815impl<'a> MultiBufferChunks<'a> {
7816    pub fn offset(&self) -> MultiBufferOffset {
7817        self.range.start
7818    }
7819
7820    pub fn seek(&mut self, range: Range<MultiBufferOffset>) {
7821        self.diff_transforms.seek(&range.end, Bias::Right);
7822        let mut excerpt_end = self.diff_transforms.start().1;
7823        if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7824            let overshoot = range.end - self.diff_transforms.start().0;
7825            excerpt_end += overshoot;
7826        }
7827
7828        self.diff_transforms.seek(&range.start, Bias::Right);
7829        let mut excerpt_start = self.diff_transforms.start().1;
7830        if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7831            let overshoot = range.start - self.diff_transforms.start().0;
7832            excerpt_start += overshoot;
7833        }
7834
7835        self.seek_to_excerpt_offset_range(excerpt_start..excerpt_end);
7836        self.buffer_chunk.take();
7837        self.range = range;
7838    }
7839
7840    fn seek_to_excerpt_offset_range(&mut self, new_range: Range<ExcerptOffset>) {
7841        self.excerpt_offset_range = new_range.clone();
7842        self.excerpts.seek(&new_range.start, Bias::Right);
7843        if let Some(excerpt) = self.excerpts.item() {
7844            let excerpt_start = *self.excerpts.start();
7845            if let Some(excerpt_chunks) = self
7846                .excerpt_chunks
7847                .as_mut()
7848                .filter(|chunks| excerpt.end_anchor() == chunks.end)
7849            {
7850                excerpt.seek_chunks(
7851                    excerpt_chunks,
7852                    (self.excerpt_offset_range.start - excerpt_start)
7853                        ..(self.excerpt_offset_range.end - excerpt_start),
7854                    self.snapshot,
7855                );
7856            } else {
7857                self.excerpt_chunks = Some(excerpt.chunks_in_range(
7858                    (self.excerpt_offset_range.start - excerpt_start)
7859                        ..(self.excerpt_offset_range.end - excerpt_start),
7860                    self.language_aware,
7861                    self.snapshot,
7862                ));
7863            }
7864        } else {
7865            self.excerpt_chunks = None;
7866        }
7867    }
7868
7869    #[ztracing::instrument(skip_all)]
7870    fn next_excerpt_chunk(&mut self) -> Option<Chunk<'a>> {
7871        loop {
7872            if self.excerpt_offset_range.is_empty() {
7873                return None;
7874            } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
7875                self.excerpt_offset_range.start += chunk.text.len();
7876                return Some(chunk);
7877            } else {
7878                self.excerpts.next();
7879                let excerpt = self.excerpts.item()?;
7880                self.excerpt_chunks = Some(excerpt.chunks_in_range(
7881                    0..(self.excerpt_offset_range.end - *self.excerpts.start()),
7882                    self.language_aware,
7883                    self.snapshot,
7884                ));
7885            }
7886        }
7887    }
7888}
7889
7890impl<'a> Iterator for ReversedMultiBufferChunks<'a> {
7891    type Item = &'a str;
7892
7893    fn next(&mut self) -> Option<Self::Item> {
7894        let mut region = self.cursor.region()?;
7895        if self.offset == region.range.start {
7896            self.cursor.prev();
7897            region = self.cursor.region()?;
7898            let start_overshoot = self.start.saturating_sub(region.range.start);
7899            self.current_chunks = Some(region.buffer.reversed_chunks_in_range(
7900                region.buffer_range.start + start_overshoot..region.buffer_range.end,
7901            ));
7902        }
7903
7904        if self.offset == region.range.end && region.has_trailing_newline {
7905            self.offset -= 1;
7906            Some("\n")
7907        } else {
7908            let chunk = self.current_chunks.as_mut().unwrap().next()?;
7909            self.offset -= chunk.len();
7910            Some(chunk)
7911        }
7912    }
7913}
7914
7915impl<'a> Iterator for MultiBufferChunks<'a> {
7916    type Item = Chunk<'a>;
7917
7918    #[ztracing::instrument(skip_all)]
7919    fn next(&mut self) -> Option<Chunk<'a>> {
7920        if self.range.start >= self.range.end {
7921            return None;
7922        }
7923        if self.range.start == self.diff_transforms.end().0 {
7924            self.diff_transforms.next();
7925        }
7926
7927        let diff_transform_start = self.diff_transforms.start().0;
7928        let diff_transform_end = self.diff_transforms.end().0;
7929        debug_assert!(
7930            self.range.start < diff_transform_end,
7931            "{:?} < {:?} of ({1:?}..{2:?})",
7932            self.range.start,
7933            diff_transform_end,
7934            diff_transform_start
7935        );
7936
7937        let diff_transform = self.diff_transforms.item()?;
7938        match diff_transform {
7939            DiffTransform::BufferContent { .. } => {
7940                let chunk = if let Some(chunk) = &mut self.buffer_chunk {
7941                    chunk
7942                } else {
7943                    let chunk = self.next_excerpt_chunk().unwrap();
7944                    self.buffer_chunk.insert(chunk)
7945                };
7946
7947                let chunk_end = self.range.start + chunk.text.len();
7948                let diff_transform_end = diff_transform_end.min(self.range.end);
7949
7950                if diff_transform_end < chunk_end {
7951                    let split_idx = diff_transform_end - self.range.start;
7952                    let (before, after) = chunk.text.split_at(split_idx);
7953                    self.range.start = diff_transform_end;
7954                    let mask = 1u128.unbounded_shl(split_idx as u32).wrapping_sub(1);
7955                    let chars = chunk.chars & mask;
7956                    let tabs = chunk.tabs & mask;
7957                    let newlines = chunk.newlines & mask;
7958
7959                    chunk.text = after;
7960                    chunk.chars = chunk.chars >> split_idx;
7961                    chunk.tabs = chunk.tabs >> split_idx;
7962                    chunk.newlines = chunk.newlines >> split_idx;
7963
7964                    Some(Chunk {
7965                        text: before,
7966                        chars,
7967                        tabs,
7968                        newlines,
7969                        ..chunk.clone()
7970                    })
7971                } else {
7972                    self.range.start = chunk_end;
7973                    self.buffer_chunk.take()
7974                }
7975            }
7976            DiffTransform::DeletedHunk {
7977                buffer_id,
7978                base_text_byte_range,
7979                has_trailing_newline,
7980                ..
7981            } => {
7982                let base_text_start =
7983                    base_text_byte_range.start + (self.range.start - diff_transform_start);
7984                let base_text_end =
7985                    base_text_byte_range.start + (self.range.end - diff_transform_start);
7986                let base_text_end = base_text_end.min(base_text_byte_range.end);
7987
7988                let mut chunks = if let Some((_, mut chunks)) = self
7989                    .diff_base_chunks
7990                    .take()
7991                    .filter(|(id, _)| id == buffer_id)
7992                {
7993                    if chunks.range().start != base_text_start || chunks.range().end < base_text_end
7994                    {
7995                        chunks.seek(base_text_start..base_text_end);
7996                    }
7997                    chunks
7998                } else {
7999                    let base_buffer =
8000                        &find_diff_state(&self.snapshot.diffs, *buffer_id)?.base_text();
8001                    base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
8002                };
8003
8004                let chunk = if let Some(chunk) = chunks.next() {
8005                    self.range.start += chunk.text.len();
8006                    self.diff_base_chunks = Some((*buffer_id, chunks));
8007                    chunk
8008                } else {
8009                    debug_assert!(has_trailing_newline);
8010                    self.range.start += "\n".len();
8011                    Chunk {
8012                        text: "\n",
8013                        chars: 1u128,
8014                        newlines: 1u128,
8015                        ..Default::default()
8016                    }
8017                };
8018                Some(chunk)
8019            }
8020        }
8021    }
8022}
8023
8024impl MultiBufferBytes<'_> {
8025    fn consume(&mut self, len: usize) {
8026        self.range.start += len;
8027        self.chunk = &self.chunk[len..];
8028
8029        if !self.range.is_empty() && self.chunk.is_empty() {
8030            if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
8031                self.chunk = chunk;
8032            } else if self.has_trailing_newline {
8033                self.has_trailing_newline = false;
8034                self.chunk = b"\n";
8035            } else {
8036                self.cursor.next();
8037                if let Some(region) = self.cursor.region() {
8038                    let mut excerpt_bytes = region.buffer.bytes_in_range(
8039                        region.buffer_range.start
8040                            ..(region.buffer_range.start + (self.range.end - region.range.start))
8041                                .min(region.buffer_range.end),
8042                    );
8043                    self.chunk = excerpt_bytes.next().unwrap_or(&[]);
8044                    self.excerpt_bytes = Some(excerpt_bytes);
8045                    self.has_trailing_newline =
8046                        region.has_trailing_newline && self.range.end >= region.range.end;
8047                    if self.chunk.is_empty() && self.has_trailing_newline {
8048                        self.has_trailing_newline = false;
8049                        self.chunk = b"\n";
8050                    }
8051                }
8052            }
8053        }
8054    }
8055}
8056
8057impl<'a> Iterator for MultiBufferBytes<'a> {
8058    type Item = &'a [u8];
8059
8060    fn next(&mut self) -> Option<Self::Item> {
8061        let chunk = self.chunk;
8062        if chunk.is_empty() {
8063            None
8064        } else {
8065            self.consume(chunk.len());
8066            Some(chunk)
8067        }
8068    }
8069}
8070
8071impl io::Read for MultiBufferBytes<'_> {
8072    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
8073        let len = cmp::min(buf.len(), self.chunk.len());
8074        buf[..len].copy_from_slice(&self.chunk[..len]);
8075        if len > 0 {
8076            self.consume(len);
8077        }
8078        Ok(len)
8079    }
8080}
8081
8082impl io::Read for ReversedMultiBufferBytes<'_> {
8083    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
8084        let len = cmp::min(buf.len(), self.chunk.len());
8085        buf[..len].copy_from_slice(&self.chunk[..len]);
8086        buf[..len].reverse();
8087        if len > 0 {
8088            self.range.end -= len;
8089            self.chunk = &self.chunk[..self.chunk.len() - len];
8090            if !self.range.is_empty()
8091                && self.chunk.is_empty()
8092                && let Some(chunk) = self.chunks.next()
8093            {
8094                self.chunk = chunk.as_bytes();
8095            }
8096        }
8097        Ok(len)
8098    }
8099}
8100
8101impl<'a> Iterator for ExcerptChunks<'a> {
8102    type Item = Chunk<'a>;
8103
8104    fn next(&mut self) -> Option<Self::Item> {
8105        if let Some(chunk) = self.content_chunks.next() {
8106            return Some(chunk);
8107        }
8108
8109        if self.has_footer {
8110            let text = "\n";
8111            let chars = 0b1;
8112            let newlines = 0b1;
8113            self.has_footer = false;
8114            return Some(Chunk {
8115                text,
8116                chars,
8117                newlines,
8118                ..Default::default()
8119            });
8120        }
8121
8122        None
8123    }
8124}
8125
8126impl ToOffset for Point {
8127    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
8128        snapshot.point_to_offset(*self)
8129    }
8130    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
8131        snapshot.point_to_offset_utf16(*self)
8132    }
8133}
8134
8135impl ToOffset for MultiBufferOffset {
8136    #[track_caller]
8137    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
8138        assert!(
8139            *self <= snapshot.len(),
8140            "offset {} is greater than the snapshot.len() {}",
8141            self.0,
8142            snapshot.len().0,
8143        );
8144        *self
8145    }
8146    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
8147        snapshot.offset_to_offset_utf16(*self)
8148    }
8149}
8150
8151impl ToOffset for MultiBufferOffsetUtf16 {
8152    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
8153        snapshot.offset_utf16_to_offset(*self)
8154    }
8155
8156    fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
8157        *self
8158    }
8159}
8160
8161impl ToOffset for PointUtf16 {
8162    fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffset {
8163        snapshot.point_utf16_to_offset(*self)
8164    }
8165    fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> MultiBufferOffsetUtf16 {
8166        snapshot.point_utf16_to_offset_utf16(*self)
8167    }
8168}
8169
8170impl ToPoint for MultiBufferOffset {
8171    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
8172        snapshot.offset_to_point(*self)
8173    }
8174    fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
8175        snapshot.offset_to_point_utf16(*self)
8176    }
8177}
8178
8179impl ToPoint for Point {
8180    fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
8181        *self
8182    }
8183    fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
8184        snapshot.point_to_point_utf16(*self)
8185    }
8186}
8187
8188impl ToPoint for PointUtf16 {
8189    fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
8190        snapshot.point_utf16_to_point(*self)
8191    }
8192    fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 {
8193        *self
8194    }
8195}
8196
8197#[cfg(debug_assertions)]
8198pub mod debug {
8199    use super::*;
8200
8201    pub trait ToMultiBufferDebugRanges {
8202        fn to_multi_buffer_debug_ranges(
8203            &self,
8204            snapshot: &MultiBufferSnapshot,
8205        ) -> Vec<Range<MultiBufferOffset>>;
8206    }
8207
8208    impl<T: ToOffset> ToMultiBufferDebugRanges for T {
8209        fn to_multi_buffer_debug_ranges(
8210            &self,
8211            snapshot: &MultiBufferSnapshot,
8212        ) -> Vec<Range<MultiBufferOffset>> {
8213            [self.to_offset(snapshot)].to_multi_buffer_debug_ranges(snapshot)
8214        }
8215    }
8216
8217    impl<T: ToOffset> ToMultiBufferDebugRanges for Range<T> {
8218        fn to_multi_buffer_debug_ranges(
8219            &self,
8220            snapshot: &MultiBufferSnapshot,
8221        ) -> Vec<Range<MultiBufferOffset>> {
8222            [self.start.to_offset(snapshot)..self.end.to_offset(snapshot)]
8223                .to_multi_buffer_debug_ranges(snapshot)
8224        }
8225    }
8226
8227    impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<T> {
8228        fn to_multi_buffer_debug_ranges(
8229            &self,
8230            snapshot: &MultiBufferSnapshot,
8231        ) -> Vec<Range<MultiBufferOffset>> {
8232            self.as_slice().to_multi_buffer_debug_ranges(snapshot)
8233        }
8234    }
8235
8236    impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<Range<T>> {
8237        fn to_multi_buffer_debug_ranges(
8238            &self,
8239            snapshot: &MultiBufferSnapshot,
8240        ) -> Vec<Range<MultiBufferOffset>> {
8241            self.as_slice().to_multi_buffer_debug_ranges(snapshot)
8242        }
8243    }
8244
8245    impl<T: ToOffset> ToMultiBufferDebugRanges for [T] {
8246        fn to_multi_buffer_debug_ranges(
8247            &self,
8248            snapshot: &MultiBufferSnapshot,
8249        ) -> Vec<Range<MultiBufferOffset>> {
8250            self.iter()
8251                .map(|item| {
8252                    let offset = item.to_offset(snapshot);
8253                    offset..offset
8254                })
8255                .collect()
8256        }
8257    }
8258
8259    impl<T: ToOffset> ToMultiBufferDebugRanges for [Range<T>] {
8260        fn to_multi_buffer_debug_ranges(
8261            &self,
8262            snapshot: &MultiBufferSnapshot,
8263        ) -> Vec<Range<MultiBufferOffset>> {
8264            self.iter()
8265                .map(|range| range.start.to_offset(snapshot)..range.end.to_offset(snapshot))
8266                .collect()
8267        }
8268    }
8269}