Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T02:28:46.596Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

multi_buffer.rs

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