multi_buffer.rs

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