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