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