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