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