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