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