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 buffer_anchor_to_anchor(
1544 &self,
1545 buffer: &Entity<Buffer>,
1546 anchor: text::Anchor,
1547 cx: &App,
1548 ) -> Option<Anchor> {
1549 let snapshot = buffer.read(cx).snapshot();
1550 for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
1551 if range.context.start.cmp(&anchor, &snapshot).is_le()
1552 && range.context.end.cmp(&anchor, &snapshot).is_ge()
1553 {
1554 return Some(Anchor::in_buffer(excerpt_id, snapshot.remote_id(), anchor));
1555 }
1556 }
1557
1558 None
1559 }
1560
1561 pub fn remove_excerpts(
1562 &mut self,
1563 excerpt_ids: impl IntoIterator<Item = ExcerptId>,
1564 cx: &mut Context<Self>,
1565 ) {
1566 self.sync_mut(cx);
1567 let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
1568 if ids.is_empty() {
1569 return;
1570 }
1571 self.buffer_changed_since_sync.replace(true);
1572
1573 let mut snapshot = self.snapshot.get_mut();
1574 let mut new_excerpts = SumTree::default();
1575 let mut cursor = snapshot
1576 .excerpts
1577 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
1578 let mut edits = Vec::new();
1579 let mut excerpt_ids = ids.iter().copied().peekable();
1580 let mut removed_buffer_ids = Vec::new();
1581
1582 while let Some(excerpt_id) = excerpt_ids.next() {
1583 self.paths_by_excerpt.remove(&excerpt_id);
1584 // Seek to the next excerpt to remove, preserving any preceding excerpts.
1585 let locator = snapshot.excerpt_locator_for_id(excerpt_id);
1586 new_excerpts.append(cursor.slice(&Some(locator), Bias::Left), ());
1587
1588 if let Some(mut excerpt) = cursor.item() {
1589 if excerpt.id != excerpt_id {
1590 continue;
1591 }
1592 let mut old_start = cursor.start().1;
1593
1594 // Skip over the removed excerpt.
1595 'remove_excerpts: loop {
1596 if let Some(buffer_state) = self.buffers.get_mut(&excerpt.buffer_id) {
1597 buffer_state.excerpts.retain(|l| l != &excerpt.locator);
1598 if buffer_state.excerpts.is_empty() {
1599 log::debug!(
1600 "removing buffer and diff for buffer {}",
1601 excerpt.buffer_id
1602 );
1603 self.buffers.remove(&excerpt.buffer_id);
1604 removed_buffer_ids.push(excerpt.buffer_id);
1605 }
1606 }
1607 cursor.next();
1608
1609 // Skip over any subsequent excerpts that are also removed.
1610 if let Some(&next_excerpt_id) = excerpt_ids.peek() {
1611 let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id);
1612 if let Some(next_excerpt) = cursor.item()
1613 && next_excerpt.locator == *next_locator
1614 {
1615 excerpt_ids.next();
1616 excerpt = next_excerpt;
1617 continue 'remove_excerpts;
1618 }
1619 }
1620
1621 break;
1622 }
1623
1624 // When removing the last excerpt, remove the trailing newline from
1625 // the previous excerpt.
1626 if cursor.item().is_none() && old_start.value > 0 {
1627 old_start.value -= 1;
1628 new_excerpts.update_last(|e| e.has_trailing_newline = false, ());
1629 }
1630
1631 // Push an edit for the removal of this run of excerpts.
1632 let old_end = cursor.start().1;
1633 let new_start = ExcerptOffset::new(new_excerpts.summary().text.len);
1634 edits.push(Edit {
1635 old: old_start..old_end,
1636 new: new_start..new_start,
1637 });
1638 }
1639 }
1640 let suffix = cursor.suffix();
1641 let changed_trailing_excerpt = suffix.is_empty();
1642 new_excerpts.append(suffix, ());
1643 drop(cursor);
1644 snapshot.excerpts = new_excerpts;
1645 for buffer_id in &removed_buffer_ids {
1646 self.diffs.remove(buffer_id);
1647 snapshot.diffs.remove(buffer_id);
1648 }
1649
1650 if changed_trailing_excerpt {
1651 snapshot.trailing_excerpt_update_count += 1;
1652 }
1653
1654 let edits = Self::sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
1655 if !edits.is_empty() {
1656 self.subscriptions.publish(edits);
1657 }
1658 cx.emit(Event::Edited {
1659 edited_buffer: None,
1660 });
1661 cx.emit(Event::ExcerptsRemoved {
1662 ids,
1663 removed_buffer_ids,
1664 });
1665 cx.notify();
1666 }
1667
1668 pub fn wait_for_anchors<'a, Anchors: 'a + Iterator<Item = Anchor>>(
1669 &self,
1670 anchors: Anchors,
1671 cx: &mut Context<Self>,
1672 ) -> impl 'static + Future<Output = Result<()>> + use<Anchors> {
1673 let mut error = None;
1674 let mut futures = Vec::new();
1675 for anchor in anchors {
1676 if let Some(buffer_id) = anchor.buffer_id {
1677 if let Some(buffer) = self.buffers.get(&buffer_id) {
1678 buffer.buffer.update(cx, |buffer, _| {
1679 futures.push(buffer.wait_for_anchors([anchor.text_anchor]))
1680 });
1681 } else {
1682 error = Some(anyhow!(
1683 "buffer {buffer_id} is not part of this multi-buffer"
1684 ));
1685 break;
1686 }
1687 }
1688 }
1689 async move {
1690 if let Some(error) = error {
1691 Err(error)?;
1692 }
1693 for future in futures {
1694 future.await?;
1695 }
1696 Ok(())
1697 }
1698 }
1699
1700 pub fn text_anchor_for_position<T: ToOffset>(
1701 &self,
1702 position: T,
1703 cx: &App,
1704 ) -> Option<(Entity<Buffer>, language::Anchor)> {
1705 let snapshot = self.read(cx);
1706 let anchor = snapshot.anchor_before(position);
1707 let buffer = self.buffers.get(&anchor.buffer_id?)?.buffer.clone();
1708 Some((buffer, anchor.text_anchor))
1709 }
1710
1711 fn on_buffer_event(
1712 &mut self,
1713 buffer: Entity<Buffer>,
1714 event: &language::BufferEvent,
1715 cx: &mut Context<Self>,
1716 ) {
1717 use language::BufferEvent;
1718 cx.emit(match event {
1719 BufferEvent::Edited => Event::Edited {
1720 edited_buffer: Some(buffer),
1721 },
1722 BufferEvent::DirtyChanged => Event::DirtyChanged,
1723 BufferEvent::Saved => Event::Saved,
1724 BufferEvent::FileHandleChanged => Event::FileHandleChanged,
1725 BufferEvent::Reloaded => Event::Reloaded,
1726 BufferEvent::LanguageChanged => Event::LanguageChanged(buffer.read(cx).remote_id()),
1727 BufferEvent::Reparsed => Event::Reparsed(buffer.read(cx).remote_id()),
1728 BufferEvent::DiagnosticsUpdated => Event::DiagnosticsUpdated,
1729 BufferEvent::CapabilityChanged => {
1730 self.capability = buffer.read(cx).capability();
1731 return;
1732 }
1733 BufferEvent::Operation { .. } | BufferEvent::ReloadNeeded => return,
1734 });
1735 }
1736
1737 fn buffer_diff_language_changed(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
1738 let diff = diff.read(cx);
1739 let buffer_id = diff.buffer_id;
1740 let diff = diff.snapshot(cx);
1741 self.snapshot.get_mut().diffs.insert(buffer_id, diff);
1742 }
1743
1744 fn buffer_diff_changed(
1745 &mut self,
1746 diff: Entity<BufferDiff>,
1747 range: Range<text::Anchor>,
1748 cx: &mut Context<Self>,
1749 ) {
1750 self.sync_mut(cx);
1751
1752 let diff = diff.read(cx);
1753 let buffer_id = diff.buffer_id;
1754 let Some(buffer_state) = self.buffers.get(&buffer_id) else {
1755 return;
1756 };
1757 self.buffer_changed_since_sync.replace(true);
1758
1759 let buffer = buffer_state.buffer.read(cx);
1760 let diff_change_range = range.to_offset(buffer);
1761
1762 let new_diff = diff.snapshot(cx);
1763 let mut snapshot = self.snapshot.get_mut();
1764 let base_text_changed = snapshot
1765 .diffs
1766 .get(&buffer_id)
1767 .is_none_or(|old_diff| !new_diff.base_texts_eq(old_diff));
1768
1769 snapshot.diffs.insert(buffer_id, new_diff);
1770
1771 let mut excerpt_edits = Vec::new();
1772 for locator in &buffer_state.excerpts {
1773 let mut cursor = snapshot
1774 .excerpts
1775 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
1776 cursor.seek_forward(&Some(locator), Bias::Left);
1777 if let Some(excerpt) = cursor.item()
1778 && excerpt.locator == *locator
1779 {
1780 let excerpt_buffer_range = excerpt.range.context.to_offset(&excerpt.buffer);
1781 if diff_change_range.end < excerpt_buffer_range.start
1782 || diff_change_range.start > excerpt_buffer_range.end
1783 {
1784 continue;
1785 }
1786 let excerpt_start = cursor.start().1;
1787 let excerpt_len = ExcerptOffset::new(excerpt.text_summary.len);
1788 let diff_change_start_in_excerpt = ExcerptOffset::new(
1789 diff_change_range
1790 .start
1791 .saturating_sub(excerpt_buffer_range.start),
1792 );
1793 let diff_change_end_in_excerpt = ExcerptOffset::new(
1794 diff_change_range
1795 .end
1796 .saturating_sub(excerpt_buffer_range.start),
1797 );
1798 let edit_start = excerpt_start + diff_change_start_in_excerpt.min(excerpt_len);
1799 let edit_end = excerpt_start + diff_change_end_in_excerpt.min(excerpt_len);
1800 excerpt_edits.push(Edit {
1801 old: edit_start..edit_end,
1802 new: edit_start..edit_end,
1803 });
1804 }
1805 }
1806
1807 let edits = Self::sync_diff_transforms(
1808 &mut snapshot,
1809 excerpt_edits,
1810 DiffChangeKind::DiffUpdated {
1811 base_changed: base_text_changed,
1812 },
1813 );
1814 if !edits.is_empty() {
1815 self.subscriptions.publish(edits);
1816 }
1817 cx.emit(Event::Edited {
1818 edited_buffer: None,
1819 });
1820 }
1821
1822 pub fn all_buffers(&self) -> HashSet<Entity<Buffer>> {
1823 self.buffers
1824 .values()
1825 .map(|state| state.buffer.clone())
1826 .collect()
1827 }
1828
1829 pub fn all_buffer_ids(&self) -> Vec<BufferId> {
1830 self.buffers.keys().copied().collect()
1831 }
1832
1833 pub fn buffer(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
1834 self.buffers
1835 .get(&buffer_id)
1836 .map(|state| state.buffer.clone())
1837 }
1838
1839 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
1840 self.point_to_buffer_offset(point, cx)
1841 .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
1842 }
1843
1844 pub fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
1845 let buffer_id = self
1846 .snapshot
1847 .borrow()
1848 .excerpts
1849 .first()
1850 .map(|excerpt| excerpt.buffer.remote_id());
1851 buffer_id
1852 .and_then(|buffer_id| self.buffer(buffer_id))
1853 .map(|buffer| {
1854 let buffer = buffer.read(cx);
1855 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
1856 })
1857 .unwrap_or_else(move || self.language_settings_at(0, cx))
1858 }
1859
1860 pub fn language_settings_at<'a, T: ToOffset>(
1861 &'a self,
1862 point: T,
1863 cx: &'a App,
1864 ) -> Cow<'a, LanguageSettings> {
1865 let mut language = None;
1866 let mut file = None;
1867 if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) {
1868 let buffer = buffer.read(cx);
1869 language = buffer.language_at(offset);
1870 file = buffer.file();
1871 }
1872 language_settings(language.map(|l| l.name()), file, cx)
1873 }
1874
1875 pub fn for_each_buffer(&self, mut f: impl FnMut(&Entity<Buffer>)) {
1876 self.buffers.values().for_each(|state| f(&state.buffer))
1877 }
1878
1879 pub fn explicit_title(&self) -> Option<&str> {
1880 self.title.as_deref()
1881 }
1882
1883 pub fn title<'a>(&'a self, cx: &'a App) -> Cow<'a, str> {
1884 if let Some(title) = self.title.as_ref() {
1885 return title.into();
1886 }
1887
1888 if let Some(buffer) = self.as_singleton() {
1889 let buffer = buffer.read(cx);
1890
1891 if let Some(file) = buffer.file() {
1892 return file.file_name(cx).into();
1893 }
1894
1895 if let Some(title) = self.buffer_content_title(buffer) {
1896 return title;
1897 }
1898 };
1899
1900 "untitled".into()
1901 }
1902
1903 fn buffer_content_title(&self, buffer: &Buffer) -> Option<Cow<'_, str>> {
1904 let mut is_leading_whitespace = true;
1905 let mut count = 0;
1906 let mut prev_was_space = false;
1907 let mut title = String::new();
1908
1909 for ch in buffer.snapshot().chars() {
1910 if is_leading_whitespace && ch.is_whitespace() {
1911 continue;
1912 }
1913
1914 is_leading_whitespace = false;
1915
1916 if ch == '\n' || count >= 40 {
1917 break;
1918 }
1919
1920 if ch.is_whitespace() {
1921 if !prev_was_space {
1922 title.push(' ');
1923 count += 1;
1924 prev_was_space = true;
1925 }
1926 } else {
1927 title.push(ch);
1928 count += 1;
1929 prev_was_space = false;
1930 }
1931 }
1932
1933 let title = title.trim_end().to_string();
1934
1935 if title.is_empty() {
1936 return None;
1937 }
1938
1939 Some(title.into())
1940 }
1941
1942 pub fn set_title(&mut self, title: String, cx: &mut Context<Self>) {
1943 self.title = Some(title);
1944 cx.notify();
1945 }
1946
1947 /// Preserve preview tabs containing this multibuffer until additional edits occur.
1948 pub fn refresh_preview(&self, cx: &mut Context<Self>) {
1949 for buffer_state in self.buffers.values() {
1950 buffer_state
1951 .buffer
1952 .update(cx, |buffer, _cx| buffer.refresh_preview());
1953 }
1954 }
1955
1956 /// Whether we should preserve the preview status of a tab containing this multi-buffer.
1957 pub fn preserve_preview(&self, cx: &App) -> bool {
1958 self.buffers
1959 .values()
1960 .all(|state| state.buffer.read(cx).preserve_preview())
1961 }
1962
1963 #[cfg(any(test, feature = "test-support"))]
1964 pub fn is_parsing(&self, cx: &App) -> bool {
1965 self.as_singleton().unwrap().read(cx).is_parsing()
1966 }
1967
1968 pub fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
1969 let buffer_id = diff.read(cx).buffer_id;
1970 self.buffer_diff_changed(diff.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
1971 self.diffs.insert(buffer_id, DiffState::new(diff, cx));
1972 }
1973
1974 pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
1975 self.diffs.get(&buffer_id).map(|state| state.diff.clone())
1976 }
1977
1978 pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
1979 self.expand_or_collapse_diff_hunks(ranges, true, cx);
1980 }
1981
1982 pub fn collapse_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
1983 self.expand_or_collapse_diff_hunks(ranges, false, cx);
1984 }
1985
1986 pub fn set_all_diff_hunks_expanded(&mut self, cx: &mut Context<Self>) {
1987 self.snapshot.get_mut().all_diff_hunks_expanded = true;
1988 self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx);
1989 }
1990
1991 pub fn all_diff_hunks_expanded(&self) -> bool {
1992 self.snapshot.borrow().all_diff_hunks_expanded
1993 }
1994
1995 pub fn set_all_diff_hunks_collapsed(&mut self, cx: &mut Context<Self>) {
1996 self.snapshot.get_mut().all_diff_hunks_expanded = false;
1997 self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], false, cx);
1998 }
1999
2000 pub fn has_multiple_hunks(&self, cx: &App) -> bool {
2001 self.read(cx)
2002 .diff_hunks_in_range(Anchor::min()..Anchor::max())
2003 .nth(1)
2004 .is_some()
2005 }
2006
2007 pub fn single_hunk_is_expanded(&self, range: Range<Anchor>, cx: &App) -> bool {
2008 let snapshot = self.read(cx);
2009 let mut cursor = snapshot.diff_transforms.cursor::<usize>(());
2010 let offset_range = range.to_offset(&snapshot);
2011 cursor.seek(&offset_range.start, Bias::Left);
2012 while let Some(item) = cursor.item() {
2013 if *cursor.start() >= offset_range.end && *cursor.start() > offset_range.start {
2014 break;
2015 }
2016 if item.hunk_info().is_some() {
2017 return true;
2018 }
2019 cursor.next();
2020 }
2021 false
2022 }
2023
2024 pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range<Anchor>], cx: &App) -> bool {
2025 let snapshot = self.read(cx);
2026 let mut cursor = snapshot.diff_transforms.cursor::<usize>(());
2027 for range in ranges {
2028 let range = range.to_point(&snapshot);
2029 let start = snapshot.point_to_offset(Point::new(range.start.row, 0));
2030 let end = snapshot.point_to_offset(Point::new(range.end.row + 1, 0));
2031 let start = start.saturating_sub(1);
2032 let end = snapshot.len().min(end + 1);
2033 cursor.seek(&start, Bias::Right);
2034 while let Some(item) = cursor.item() {
2035 if *cursor.start() >= end {
2036 break;
2037 }
2038 if item.hunk_info().is_some() {
2039 return true;
2040 }
2041 cursor.next();
2042 }
2043 }
2044 false
2045 }
2046
2047 pub fn expand_or_collapse_diff_hunks_inner(
2048 &mut self,
2049 ranges: impl IntoIterator<Item = (Range<Point>, ExcerptId)>,
2050 expand: bool,
2051 cx: &mut Context<Self>,
2052 ) {
2053 if self.snapshot.borrow().all_diff_hunks_expanded && !expand {
2054 return;
2055 }
2056 self.sync_mut(cx);
2057 let mut snapshot = self.snapshot.get_mut();
2058 let mut excerpt_edits = Vec::new();
2059 let mut last_hunk_row = None;
2060 for (range, end_excerpt_id) in ranges {
2061 for diff_hunk in snapshot.diff_hunks_in_range(range) {
2062 if diff_hunk.excerpt_id.cmp(&end_excerpt_id, &snapshot).is_gt() {
2063 continue;
2064 }
2065 if last_hunk_row.is_some_and(|row| row >= diff_hunk.row_range.start) {
2066 continue;
2067 }
2068 let start = Anchor::in_buffer(
2069 diff_hunk.excerpt_id,
2070 diff_hunk.buffer_id,
2071 diff_hunk.buffer_range.start,
2072 );
2073 let end = Anchor::in_buffer(
2074 diff_hunk.excerpt_id,
2075 diff_hunk.buffer_id,
2076 diff_hunk.buffer_range.end,
2077 );
2078 let start = snapshot.excerpt_offset_for_anchor(&start);
2079 let end = snapshot.excerpt_offset_for_anchor(&end);
2080 last_hunk_row = Some(diff_hunk.row_range.start);
2081 excerpt_edits.push(text::Edit {
2082 old: start..end,
2083 new: start..end,
2084 });
2085 }
2086 }
2087
2088 let edits = Self::sync_diff_transforms(
2089 &mut snapshot,
2090 excerpt_edits,
2091 DiffChangeKind::ExpandOrCollapseHunks { expand },
2092 );
2093 if !edits.is_empty() {
2094 self.subscriptions.publish(edits);
2095 }
2096 cx.emit(Event::DiffHunksToggled);
2097 cx.emit(Event::Edited {
2098 edited_buffer: None,
2099 });
2100 }
2101
2102 pub fn expand_or_collapse_diff_hunks(
2103 &mut self,
2104 ranges: Vec<Range<Anchor>>,
2105 expand: bool,
2106 cx: &mut Context<Self>,
2107 ) {
2108 let snapshot = self.snapshot.borrow().clone();
2109 let ranges = ranges.iter().map(move |range| {
2110 let end_excerpt_id = range.end.excerpt_id;
2111 let range = range.to_point(&snapshot);
2112 let mut peek_end = range.end;
2113 if range.end.row < snapshot.max_row().0 {
2114 peek_end = Point::new(range.end.row + 1, 0);
2115 };
2116 (range.start..peek_end, end_excerpt_id)
2117 });
2118 self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
2119 }
2120
2121 pub fn resize_excerpt(
2122 &mut self,
2123 id: ExcerptId,
2124 range: Range<text::Anchor>,
2125 cx: &mut Context<Self>,
2126 ) {
2127 self.sync_mut(cx);
2128
2129 let mut snapshot = self.snapshot.get_mut();
2130 let locator = snapshot.excerpt_locator_for_id(id);
2131 let mut new_excerpts = SumTree::default();
2132 let mut cursor = snapshot
2133 .excerpts
2134 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2135 let mut edits = Vec::<Edit<ExcerptOffset>>::new();
2136
2137 let prefix = cursor.slice(&Some(locator), Bias::Left);
2138 new_excerpts.append(prefix, ());
2139
2140 let mut excerpt = cursor.item().unwrap().clone();
2141 let old_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2142
2143 excerpt.range.context.start = range.start;
2144 excerpt.range.context.end = range.end;
2145 excerpt.max_buffer_row = range.end.to_point(&excerpt.buffer).row;
2146
2147 excerpt.text_summary = excerpt
2148 .buffer
2149 .text_summary_for_range(excerpt.range.context.clone());
2150
2151 let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len);
2152 let old_start_offset = cursor.start().1;
2153 let new_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2154 let edit = Edit {
2155 old: old_start_offset..old_start_offset + old_text_len,
2156 new: new_start_offset..new_start_offset + new_text_len,
2157 };
2158
2159 if let Some(last_edit) = edits.last_mut() {
2160 if last_edit.old.end == edit.old.start {
2161 last_edit.old.end = edit.old.end;
2162 last_edit.new.end = edit.new.end;
2163 } else {
2164 edits.push(edit);
2165 }
2166 } else {
2167 edits.push(edit);
2168 }
2169
2170 new_excerpts.push(excerpt, ());
2171
2172 cursor.next();
2173
2174 new_excerpts.append(cursor.suffix(), ());
2175
2176 drop(cursor);
2177 snapshot.excerpts = new_excerpts;
2178
2179 let edits = Self::sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
2180 if !edits.is_empty() {
2181 self.subscriptions.publish(edits);
2182 }
2183 cx.emit(Event::Edited {
2184 edited_buffer: None,
2185 });
2186 cx.emit(Event::ExcerptsExpanded { ids: vec![id] });
2187 cx.notify();
2188 }
2189
2190 pub fn expand_excerpts(
2191 &mut self,
2192 ids: impl IntoIterator<Item = ExcerptId>,
2193 line_count: u32,
2194 direction: ExpandExcerptDirection,
2195 cx: &mut Context<Self>,
2196 ) {
2197 if line_count == 0 {
2198 return;
2199 }
2200 self.sync_mut(cx);
2201 if !self.excerpts_by_path.is_empty() {
2202 self.expand_excerpts_with_paths(ids, line_count, direction, cx);
2203 return;
2204 }
2205 let mut snapshot = self.snapshot.get_mut();
2206
2207 let ids = ids.into_iter().collect::<Vec<_>>();
2208 let locators = snapshot.excerpt_locators_for_ids(ids.iter().copied());
2209 let mut new_excerpts = SumTree::default();
2210 let mut cursor = snapshot
2211 .excerpts
2212 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2213 let mut edits = Vec::<Edit<ExcerptOffset>>::new();
2214
2215 for locator in &locators {
2216 let prefix = cursor.slice(&Some(locator), Bias::Left);
2217 new_excerpts.append(prefix, ());
2218
2219 let mut excerpt = cursor.item().unwrap().clone();
2220 let old_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2221
2222 let up_line_count = if direction.should_expand_up() {
2223 line_count
2224 } else {
2225 0
2226 };
2227
2228 let start_row = excerpt
2229 .range
2230 .context
2231 .start
2232 .to_point(&excerpt.buffer)
2233 .row
2234 .saturating_sub(up_line_count);
2235 let start_point = Point::new(start_row, 0);
2236 excerpt.range.context.start = excerpt.buffer.anchor_before(start_point);
2237
2238 let down_line_count = if direction.should_expand_down() {
2239 line_count
2240 } else {
2241 0
2242 };
2243
2244 let mut end_point = excerpt.buffer.clip_point(
2245 excerpt.range.context.end.to_point(&excerpt.buffer)
2246 + Point::new(down_line_count, 0),
2247 Bias::Left,
2248 );
2249 end_point.column = excerpt.buffer.line_len(end_point.row);
2250 excerpt.range.context.end = excerpt.buffer.anchor_after(end_point);
2251 excerpt.max_buffer_row = end_point.row;
2252
2253 excerpt.text_summary = excerpt
2254 .buffer
2255 .text_summary_for_range(excerpt.range.context.clone());
2256
2257 let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len);
2258 let old_start_offset = cursor.start().1;
2259 let new_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2260 let edit = Edit {
2261 old: old_start_offset..old_start_offset + old_text_len,
2262 new: new_start_offset..new_start_offset + new_text_len,
2263 };
2264
2265 if let Some(last_edit) = edits.last_mut() {
2266 if last_edit.old.end == edit.old.start {
2267 last_edit.old.end = edit.old.end;
2268 last_edit.new.end = edit.new.end;
2269 } else {
2270 edits.push(edit);
2271 }
2272 } else {
2273 edits.push(edit);
2274 }
2275
2276 new_excerpts.push(excerpt, ());
2277
2278 cursor.next();
2279 }
2280
2281 new_excerpts.append(cursor.suffix(), ());
2282
2283 drop(cursor);
2284 snapshot.excerpts = new_excerpts;
2285
2286 let edits = Self::sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
2287 if !edits.is_empty() {
2288 self.subscriptions.publish(edits);
2289 }
2290 cx.emit(Event::Edited {
2291 edited_buffer: None,
2292 });
2293 cx.emit(Event::ExcerptsExpanded { ids });
2294 cx.notify();
2295 }
2296
2297 fn sync(&self, cx: &App) {
2298 let changed = self.buffer_changed_since_sync.replace(false);
2299 if !changed {
2300 return;
2301 }
2302 let edits = Self::sync_(
2303 &mut self.snapshot.borrow_mut(),
2304 &self.buffers,
2305 &self.diffs,
2306 cx,
2307 );
2308 if !edits.is_empty() {
2309 self.subscriptions.publish(edits);
2310 }
2311 }
2312
2313 fn sync_mut(&mut self, cx: &App) {
2314 let changed = self.buffer_changed_since_sync.replace(false);
2315 if !changed {
2316 return;
2317 }
2318 let edits = Self::sync_(self.snapshot.get_mut(), &self.buffers, &self.diffs, cx);
2319
2320 if !edits.is_empty() {
2321 self.subscriptions.publish(edits);
2322 }
2323 }
2324
2325 fn sync_(
2326 snapshot: &mut MultiBufferSnapshot,
2327 buffers: &HashMap<BufferId, BufferState>,
2328 diffs: &HashMap<BufferId, DiffState>,
2329 cx: &App,
2330 ) -> Vec<Edit<usize>> {
2331 let MultiBufferSnapshot {
2332 excerpts,
2333 diffs: buffer_diff,
2334 diff_transforms: _,
2335 non_text_state_update_count,
2336 edit_count,
2337 is_dirty,
2338 has_deleted_file,
2339 has_conflict,
2340 singleton: _,
2341 excerpt_ids: _,
2342 replaced_excerpts: _,
2343 trailing_excerpt_update_count: _,
2344 all_diff_hunks_expanded: _,
2345 show_headers: _,
2346 } = snapshot;
2347 *is_dirty = false;
2348 *has_deleted_file = false;
2349 *has_conflict = false;
2350
2351 let mut excerpts_to_edit = Vec::new();
2352 let mut non_text_state_updated = false;
2353 let mut edited = false;
2354 for buffer_state in buffers.values() {
2355 let buffer = buffer_state.buffer.read(cx);
2356 let version = buffer.version();
2357 let non_text_state_update_count = buffer.non_text_state_update_count();
2358
2359 let buffer_edited = version.changed_since(&buffer_state.last_version.borrow());
2360 let buffer_non_text_state_updated =
2361 non_text_state_update_count > buffer_state.last_non_text_state_update_count.get();
2362 if buffer_edited || buffer_non_text_state_updated {
2363 *buffer_state.last_version.borrow_mut() = version;
2364 buffer_state
2365 .last_non_text_state_update_count
2366 .set(non_text_state_update_count);
2367 excerpts_to_edit.extend(
2368 buffer_state
2369 .excerpts
2370 .iter()
2371 .map(|locator| (locator, buffer_state.buffer.clone(), buffer_edited)),
2372 );
2373 }
2374
2375 edited |= buffer_edited;
2376 non_text_state_updated |= buffer_non_text_state_updated;
2377 *is_dirty |= buffer.is_dirty();
2378 *has_deleted_file |= buffer
2379 .file()
2380 .is_some_and(|file| file.disk_state() == DiskState::Deleted);
2381 *has_conflict |= buffer.has_conflict();
2382 }
2383 if edited {
2384 *edit_count += 1;
2385 }
2386 if non_text_state_updated {
2387 *non_text_state_update_count += 1;
2388 }
2389
2390 for (id, diff) in diffs.iter() {
2391 if buffer_diff.get(id).is_none() {
2392 buffer_diff.insert(*id, diff.diff.read(cx).snapshot(cx));
2393 }
2394 }
2395
2396 excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
2397
2398 let mut edits = Vec::new();
2399 let mut new_excerpts = SumTree::default();
2400 let mut cursor = excerpts.cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2401
2402 for (locator, buffer, buffer_edited) in excerpts_to_edit {
2403 new_excerpts.append(cursor.slice(&Some(locator), Bias::Left), ());
2404 let old_excerpt = cursor.item().unwrap();
2405 let buffer = buffer.read(cx);
2406 let buffer_id = buffer.remote_id();
2407
2408 let mut new_excerpt;
2409 if buffer_edited {
2410 edits.extend(
2411 buffer
2412 .edits_since_in_range::<usize>(
2413 old_excerpt.buffer.version(),
2414 old_excerpt.range.context.clone(),
2415 )
2416 .map(|edit| {
2417 let excerpt_old_start = cursor.start().1;
2418 let excerpt_new_start =
2419 ExcerptOffset::new(new_excerpts.summary().text.len);
2420 let old_start = excerpt_old_start + ExcerptOffset::new(edit.old.start);
2421 let old_end = excerpt_old_start + ExcerptOffset::new(edit.old.end);
2422 let new_start = excerpt_new_start + ExcerptOffset::new(edit.new.start);
2423 let new_end = excerpt_new_start + ExcerptOffset::new(edit.new.end);
2424 Edit {
2425 old: old_start..old_end,
2426 new: new_start..new_end,
2427 }
2428 }),
2429 );
2430
2431 new_excerpt = Excerpt::new(
2432 old_excerpt.id,
2433 locator.clone(),
2434 buffer_id,
2435 buffer.snapshot(),
2436 old_excerpt.range.clone(),
2437 old_excerpt.has_trailing_newline,
2438 );
2439 } else {
2440 new_excerpt = old_excerpt.clone();
2441 new_excerpt.buffer = buffer.snapshot();
2442 }
2443
2444 new_excerpts.push(new_excerpt, ());
2445 cursor.next();
2446 }
2447 new_excerpts.append(cursor.suffix(), ());
2448
2449 drop(cursor);
2450 *excerpts = new_excerpts;
2451 Self::sync_diff_transforms(snapshot, edits, DiffChangeKind::BufferEdited)
2452 }
2453
2454 fn sync_diff_transforms(
2455 snapshot: &mut MultiBufferSnapshot,
2456 excerpt_edits: Vec<text::Edit<ExcerptOffset>>,
2457 change_kind: DiffChangeKind,
2458 ) -> Vec<Edit<usize>> {
2459 if excerpt_edits.is_empty() {
2460 return vec![];
2461 }
2462
2463 let mut excerpts = snapshot.excerpts.cursor::<ExcerptOffset>(());
2464 let mut old_diff_transforms = snapshot
2465 .diff_transforms
2466 .cursor::<Dimensions<ExcerptOffset, usize>>(());
2467 let mut new_diff_transforms = SumTree::default();
2468 let mut old_expanded_hunks = HashSet::default();
2469 let mut output_edits = Vec::new();
2470 let mut output_delta = 0_isize;
2471 let mut at_transform_boundary = true;
2472 let mut end_of_current_insert = None;
2473
2474 let mut excerpt_edits = excerpt_edits.into_iter().peekable();
2475 while let Some(edit) = excerpt_edits.next() {
2476 excerpts.seek_forward(&edit.new.start, Bias::Right);
2477 if excerpts.item().is_none() && *excerpts.start() == edit.new.start {
2478 excerpts.prev();
2479 }
2480
2481 // Keep any transforms that are before the edit.
2482 if at_transform_boundary {
2483 at_transform_boundary = false;
2484 let transforms_before_edit = old_diff_transforms.slice(&edit.old.start, Bias::Left);
2485 Self::append_diff_transforms(&mut new_diff_transforms, transforms_before_edit);
2486 if let Some(transform) = old_diff_transforms.item()
2487 && old_diff_transforms.end().0 == edit.old.start
2488 && old_diff_transforms.start().0 < edit.old.start
2489 {
2490 Self::push_diff_transform(&mut new_diff_transforms, transform.clone());
2491 old_diff_transforms.next();
2492 }
2493 }
2494
2495 // Compute the start of the edit in output coordinates.
2496 let edit_start_overshoot = (edit.old.start - old_diff_transforms.start().0).value;
2497 let edit_old_start = old_diff_transforms.start().1 + edit_start_overshoot;
2498 let edit_new_start = (edit_old_start as isize + output_delta) as usize;
2499
2500 let changed_diff_hunks = Self::recompute_diff_transforms_for_edit(
2501 &edit,
2502 &mut excerpts,
2503 &mut old_diff_transforms,
2504 &mut new_diff_transforms,
2505 &mut end_of_current_insert,
2506 &mut old_expanded_hunks,
2507 snapshot,
2508 change_kind,
2509 );
2510
2511 // Compute the end of the edit in output coordinates.
2512 let edit_old_end_overshoot = edit.old.end - old_diff_transforms.start().0;
2513 let edit_new_end_overshoot = edit.new.end - new_diff_transforms.summary().excerpt_len();
2514 let edit_old_end = old_diff_transforms.start().1 + edit_old_end_overshoot.value;
2515 let edit_new_end =
2516 new_diff_transforms.summary().output.len + edit_new_end_overshoot.value;
2517 let output_edit = Edit {
2518 old: edit_old_start..edit_old_end,
2519 new: edit_new_start..edit_new_end,
2520 };
2521
2522 output_delta += (output_edit.new.end - output_edit.new.start) as isize;
2523 output_delta -= (output_edit.old.end - output_edit.old.start) as isize;
2524 if changed_diff_hunks || matches!(change_kind, DiffChangeKind::BufferEdited) {
2525 output_edits.push(output_edit);
2526 }
2527
2528 // If this is the last edit that intersects the current diff transform,
2529 // then recreate the content up to the end of this transform, to prepare
2530 // for reusing additional slices of the old transforms.
2531 if excerpt_edits
2532 .peek()
2533 .is_none_or(|next_edit| next_edit.old.start >= old_diff_transforms.end().0)
2534 {
2535 let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
2536 && match old_diff_transforms.item() {
2537 Some(DiffTransform::BufferContent {
2538 inserted_hunk_info: Some(hunk),
2539 ..
2540 }) => excerpts.item().is_some_and(|excerpt| {
2541 hunk.hunk_start_anchor.is_valid(&excerpt.buffer)
2542 }),
2543 _ => true,
2544 };
2545
2546 let mut excerpt_offset = edit.new.end;
2547 if !keep_next_old_transform {
2548 excerpt_offset += old_diff_transforms.end().0 - edit.old.end;
2549 old_diff_transforms.next();
2550 }
2551
2552 old_expanded_hunks.clear();
2553 Self::push_buffer_content_transform(
2554 snapshot,
2555 &mut new_diff_transforms,
2556 excerpt_offset,
2557 end_of_current_insert,
2558 );
2559 at_transform_boundary = true;
2560 }
2561 }
2562
2563 // Keep any transforms that are after the last edit.
2564 Self::append_diff_transforms(&mut new_diff_transforms, old_diff_transforms.suffix());
2565
2566 // Ensure there's always at least one buffer content transform.
2567 if new_diff_transforms.is_empty() {
2568 new_diff_transforms.push(
2569 DiffTransform::BufferContent {
2570 summary: Default::default(),
2571 inserted_hunk_info: None,
2572 },
2573 (),
2574 );
2575 }
2576
2577 drop(old_diff_transforms);
2578 drop(excerpts);
2579 snapshot.diff_transforms = new_diff_transforms;
2580 snapshot.edit_count += 1;
2581
2582 #[cfg(any(test, feature = "test-support"))]
2583 snapshot.check_invariants();
2584 output_edits
2585 }
2586
2587 fn recompute_diff_transforms_for_edit(
2588 edit: &Edit<TypedOffset<Excerpt>>,
2589 excerpts: &mut Cursor<Excerpt, TypedOffset<Excerpt>>,
2590 old_diff_transforms: &mut Cursor<DiffTransform, Dimensions<TypedOffset<Excerpt>, usize>>,
2591 new_diff_transforms: &mut SumTree<DiffTransform>,
2592 end_of_current_insert: &mut Option<(TypedOffset<Excerpt>, DiffTransformHunkInfo)>,
2593 old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
2594 snapshot: &MultiBufferSnapshot,
2595 change_kind: DiffChangeKind,
2596 ) -> bool {
2597 log::trace!(
2598 "recomputing diff transform for edit {:?} => {:?}",
2599 edit.old.start.value..edit.old.end.value,
2600 edit.new.start.value..edit.new.end.value
2601 );
2602
2603 // Record which hunks were previously expanded.
2604 while let Some(item) = old_diff_transforms.item() {
2605 if let Some(hunk_info) = item.hunk_info() {
2606 log::trace!(
2607 "previously expanded hunk at {}",
2608 old_diff_transforms.start().0
2609 );
2610 old_expanded_hunks.insert(hunk_info);
2611 }
2612 if old_diff_transforms.end().0 > edit.old.end {
2613 break;
2614 }
2615 old_diff_transforms.next();
2616 }
2617
2618 // Avoid querying diff hunks if there's no possibility of hunks being expanded.
2619 let all_diff_hunks_expanded = snapshot.all_diff_hunks_expanded;
2620 if old_expanded_hunks.is_empty()
2621 && change_kind == DiffChangeKind::BufferEdited
2622 && !all_diff_hunks_expanded
2623 {
2624 return false;
2625 }
2626
2627 // Visit each excerpt that intersects the edit.
2628 let mut did_expand_hunks = false;
2629 while let Some(excerpt) = excerpts.item() {
2630 // Recompute the expanded hunks in the portion of the excerpt that
2631 // intersects the edit.
2632 if let Some(diff) = snapshot.diffs.get(&excerpt.buffer_id) {
2633 let buffer = &excerpt.buffer;
2634 let excerpt_start = *excerpts.start();
2635 let excerpt_end = excerpt_start + ExcerptOffset::new(excerpt.text_summary.len);
2636 let excerpt_buffer_start = excerpt.range.context.start.to_offset(buffer);
2637 let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
2638 let edit_buffer_start =
2639 excerpt_buffer_start + edit.new.start.value.saturating_sub(excerpt_start.value);
2640 let edit_buffer_end =
2641 excerpt_buffer_start + edit.new.end.value.saturating_sub(excerpt_start.value);
2642 let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
2643 let edit_anchor_range =
2644 buffer.anchor_before(edit_buffer_start)..buffer.anchor_after(edit_buffer_end);
2645
2646 for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) {
2647 if hunk.is_created_file() && !all_diff_hunks_expanded {
2648 continue;
2649 }
2650
2651 let hunk_buffer_range = hunk.buffer_range.to_offset(buffer);
2652 if hunk_buffer_range.start < excerpt_buffer_start {
2653 log::trace!("skipping hunk that starts before excerpt");
2654 continue;
2655 }
2656
2657 let hunk_info = DiffTransformHunkInfo {
2658 excerpt_id: excerpt.id,
2659 hunk_start_anchor: hunk.buffer_range.start,
2660 hunk_secondary_status: hunk.secondary_status,
2661 };
2662
2663 let hunk_excerpt_start = excerpt_start
2664 + ExcerptOffset::new(
2665 hunk_buffer_range.start.saturating_sub(excerpt_buffer_start),
2666 );
2667 let hunk_excerpt_end = excerpt_end.min(
2668 excerpt_start
2669 + ExcerptOffset::new(hunk_buffer_range.end - excerpt_buffer_start),
2670 );
2671
2672 Self::push_buffer_content_transform(
2673 snapshot,
2674 new_diff_transforms,
2675 hunk_excerpt_start,
2676 *end_of_current_insert,
2677 );
2678
2679 // For every existing hunk, determine if it was previously expanded
2680 // and if it should currently be expanded.
2681 let was_previously_expanded = old_expanded_hunks.contains(&hunk_info);
2682 let should_expand_hunk = match &change_kind {
2683 DiffChangeKind::DiffUpdated { base_changed: true } => {
2684 was_previously_expanded || all_diff_hunks_expanded
2685 }
2686 DiffChangeKind::ExpandOrCollapseHunks { expand } => {
2687 let intersects = hunk_buffer_range.is_empty()
2688 || hunk_buffer_range.end > edit_buffer_start;
2689 if *expand {
2690 intersects || was_previously_expanded || all_diff_hunks_expanded
2691 } else {
2692 !intersects && (was_previously_expanded || all_diff_hunks_expanded)
2693 }
2694 }
2695 _ => was_previously_expanded || all_diff_hunks_expanded,
2696 };
2697
2698 if should_expand_hunk {
2699 did_expand_hunks = true;
2700 log::trace!(
2701 "expanding hunk {:?}, excerpt:{:?}",
2702 hunk_excerpt_start.value..hunk_excerpt_end.value,
2703 excerpt.id
2704 );
2705
2706 if !hunk.diff_base_byte_range.is_empty()
2707 && hunk_buffer_range.start >= edit_buffer_start
2708 && hunk_buffer_range.start <= excerpt_buffer_end
2709 {
2710 let base_text = diff.base_text();
2711 let mut text_cursor =
2712 base_text.as_rope().cursor(hunk.diff_base_byte_range.start);
2713 let mut base_text_summary =
2714 text_cursor.summary::<TextSummary>(hunk.diff_base_byte_range.end);
2715
2716 let mut has_trailing_newline = false;
2717 if base_text_summary.last_line_chars > 0 {
2718 base_text_summary += TextSummary::newline();
2719 has_trailing_newline = true;
2720 }
2721
2722 new_diff_transforms.push(
2723 DiffTransform::DeletedHunk {
2724 base_text_byte_range: hunk.diff_base_byte_range.clone(),
2725 summary: base_text_summary,
2726 buffer_id: excerpt.buffer_id,
2727 hunk_info,
2728 has_trailing_newline,
2729 },
2730 (),
2731 );
2732 }
2733
2734 if !hunk_buffer_range.is_empty() {
2735 *end_of_current_insert =
2736 Some((hunk_excerpt_end.min(excerpt_end), hunk_info));
2737 }
2738 }
2739 }
2740 }
2741
2742 if excerpts.end() <= edit.new.end {
2743 excerpts.next();
2744 } else {
2745 break;
2746 }
2747 }
2748
2749 did_expand_hunks || !old_expanded_hunks.is_empty()
2750 }
2751
2752 fn append_diff_transforms(
2753 new_transforms: &mut SumTree<DiffTransform>,
2754 subtree: SumTree<DiffTransform>,
2755 ) {
2756 if let Some(DiffTransform::BufferContent {
2757 inserted_hunk_info,
2758 summary,
2759 }) = subtree.first()
2760 && Self::extend_last_buffer_content_transform(
2761 new_transforms,
2762 *inserted_hunk_info,
2763 *summary,
2764 )
2765 {
2766 let mut cursor = subtree.cursor::<()>(());
2767 cursor.next();
2768 cursor.next();
2769 new_transforms.append(cursor.suffix(), ());
2770 return;
2771 }
2772 new_transforms.append(subtree, ());
2773 }
2774
2775 fn push_diff_transform(new_transforms: &mut SumTree<DiffTransform>, transform: DiffTransform) {
2776 if let DiffTransform::BufferContent {
2777 inserted_hunk_info: inserted_hunk_anchor,
2778 summary,
2779 } = transform
2780 && Self::extend_last_buffer_content_transform(
2781 new_transforms,
2782 inserted_hunk_anchor,
2783 summary,
2784 )
2785 {
2786 return;
2787 }
2788 new_transforms.push(transform, ());
2789 }
2790
2791 fn push_buffer_content_transform(
2792 old_snapshot: &MultiBufferSnapshot,
2793 new_transforms: &mut SumTree<DiffTransform>,
2794 end_offset: ExcerptOffset,
2795 current_inserted_hunk: Option<(ExcerptOffset, DiffTransformHunkInfo)>,
2796 ) {
2797 let inserted_region = current_inserted_hunk.map(|(insertion_end_offset, hunk_info)| {
2798 (end_offset.min(insertion_end_offset), Some(hunk_info))
2799 });
2800 let unchanged_region = [(end_offset, None)];
2801
2802 for (end_offset, inserted_hunk_info) in inserted_region.into_iter().chain(unchanged_region)
2803 {
2804 let start_offset = new_transforms.summary().excerpt_len();
2805 if end_offset <= start_offset {
2806 continue;
2807 }
2808 let summary_to_add = old_snapshot
2809 .text_summary_for_excerpt_offset_range::<TextSummary>(start_offset..end_offset);
2810
2811 if !Self::extend_last_buffer_content_transform(
2812 new_transforms,
2813 inserted_hunk_info,
2814 summary_to_add,
2815 ) {
2816 new_transforms.push(
2817 DiffTransform::BufferContent {
2818 summary: summary_to_add,
2819 inserted_hunk_info,
2820 },
2821 (),
2822 )
2823 }
2824 }
2825 }
2826
2827 fn extend_last_buffer_content_transform(
2828 new_transforms: &mut SumTree<DiffTransform>,
2829 new_inserted_hunk_info: Option<DiffTransformHunkInfo>,
2830 summary_to_add: TextSummary,
2831 ) -> bool {
2832 let mut did_extend = false;
2833 new_transforms.update_last(
2834 |last_transform| {
2835 if let DiffTransform::BufferContent {
2836 summary,
2837 inserted_hunk_info: inserted_hunk_anchor,
2838 } = last_transform
2839 && *inserted_hunk_anchor == new_inserted_hunk_info
2840 {
2841 *summary += summary_to_add;
2842 did_extend = true;
2843 }
2844 },
2845 (),
2846 );
2847 did_extend
2848 }
2849}
2850
2851fn build_excerpt_ranges(
2852 ranges: impl IntoIterator<Item = Range<Point>>,
2853 context_line_count: u32,
2854 buffer_snapshot: &BufferSnapshot,
2855) -> Vec<ExcerptRange<Point>> {
2856 ranges
2857 .into_iter()
2858 .map(|range| {
2859 let start_row = range.start.row.saturating_sub(context_line_count);
2860 let start = Point::new(start_row, 0);
2861 let end_row = (range.end.row + context_line_count).min(buffer_snapshot.max_point().row);
2862 let end = Point::new(end_row, buffer_snapshot.line_len(end_row));
2863 ExcerptRange {
2864 context: start..end,
2865 primary: range,
2866 }
2867 })
2868 .collect()
2869}
2870
2871#[cfg(any(test, feature = "test-support"))]
2872impl MultiBuffer {
2873 pub fn build_simple(text: &str, cx: &mut gpui::App) -> Entity<Self> {
2874 let buffer = cx.new(|cx| Buffer::local(text, cx));
2875 cx.new(|cx| Self::singleton(buffer, cx))
2876 }
2877
2878 pub fn build_multi<const COUNT: usize>(
2879 excerpts: [(&str, Vec<Range<Point>>); COUNT],
2880 cx: &mut gpui::App,
2881 ) -> Entity<Self> {
2882 let multi = cx.new(|_| Self::new(Capability::ReadWrite));
2883 for (text, ranges) in excerpts {
2884 let buffer = cx.new(|cx| Buffer::local(text, cx));
2885 let excerpt_ranges = ranges.into_iter().map(ExcerptRange::new);
2886 multi.update(cx, |multi, cx| {
2887 multi.push_excerpts(buffer, excerpt_ranges, cx)
2888 });
2889 }
2890
2891 multi
2892 }
2893
2894 pub fn build_from_buffer(buffer: Entity<Buffer>, cx: &mut gpui::App) -> Entity<Self> {
2895 cx.new(|cx| Self::singleton(buffer, cx))
2896 }
2897
2898 pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
2899 cx.new(|cx| {
2900 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
2901 let mutation_count = rng.random_range(1..=5);
2902 multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
2903 multibuffer
2904 })
2905 }
2906
2907 pub fn randomly_edit(
2908 &mut self,
2909 rng: &mut impl rand::Rng,
2910 edit_count: usize,
2911 cx: &mut Context<Self>,
2912 ) {
2913 use util::RandomCharIter;
2914
2915 let snapshot = self.read(cx);
2916 let mut edits: Vec<(Range<usize>, Arc<str>)> = Vec::new();
2917 let mut last_end = None;
2918 for _ in 0..edit_count {
2919 if last_end.is_some_and(|last_end| last_end >= snapshot.len()) {
2920 break;
2921 }
2922
2923 let new_start = last_end.map_or(0, |last_end| last_end + 1);
2924 let end =
2925 snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right);
2926 let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right);
2927 last_end = Some(end);
2928
2929 let mut range = start..end;
2930 if rng.random_bool(0.2) {
2931 mem::swap(&mut range.start, &mut range.end);
2932 }
2933
2934 let new_text_len = rng.random_range(0..10);
2935 let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
2936
2937 edits.push((range, new_text.into()));
2938 }
2939 log::info!("mutating multi-buffer with {:?}", edits);
2940 drop(snapshot);
2941
2942 self.edit(edits, None, cx);
2943 }
2944
2945 pub fn randomly_edit_excerpts(
2946 &mut self,
2947 rng: &mut impl rand::Rng,
2948 mutation_count: usize,
2949 cx: &mut Context<Self>,
2950 ) {
2951 use rand::prelude::*;
2952 use std::env;
2953 use util::RandomCharIter;
2954
2955 let max_excerpts = env::var("MAX_EXCERPTS")
2956 .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
2957 .unwrap_or(5);
2958
2959 let mut buffers = Vec::new();
2960 for _ in 0..mutation_count {
2961 if rng.random_bool(0.05) {
2962 log::info!("Clearing multi-buffer");
2963 self.clear(cx);
2964 continue;
2965 } else if rng.random_bool(0.1) && !self.excerpt_ids().is_empty() {
2966 let ids = self.excerpt_ids();
2967 let mut excerpts = HashSet::default();
2968 for _ in 0..rng.random_range(0..ids.len()) {
2969 excerpts.extend(ids.choose(rng).copied());
2970 }
2971
2972 let line_count = rng.random_range(0..5);
2973
2974 log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
2975
2976 self.expand_excerpts(
2977 excerpts.iter().cloned(),
2978 line_count,
2979 ExpandExcerptDirection::UpAndDown,
2980 cx,
2981 );
2982 continue;
2983 }
2984
2985 let excerpt_ids = self.excerpt_ids();
2986 if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) {
2987 let buffer_handle = if rng.random() || self.buffers.is_empty() {
2988 let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
2989 buffers.push(cx.new(|cx| Buffer::local(text, cx)));
2990 let buffer = buffers.last().unwrap().read(cx);
2991 log::info!(
2992 "Creating new buffer {} with text: {:?}",
2993 buffer.remote_id(),
2994 buffer.text()
2995 );
2996 buffers.last().unwrap().clone()
2997 } else {
2998 self.buffers.values().choose(rng).unwrap().buffer.clone()
2999 };
3000
3001 let buffer = buffer_handle.read(cx);
3002 let buffer_text = buffer.text();
3003 let ranges = (0..rng.random_range(0..5))
3004 .map(|_| {
3005 let end_ix =
3006 buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right);
3007 let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
3008 ExcerptRange::new(start_ix..end_ix)
3009 })
3010 .collect::<Vec<_>>();
3011 log::info!(
3012 "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
3013 buffer_handle.read(cx).remote_id(),
3014 ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
3015 ranges
3016 .iter()
3017 .map(|r| &buffer_text[r.context.clone()])
3018 .collect::<Vec<_>>()
3019 );
3020
3021 let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
3022 log::info!("Inserted with ids: {:?}", excerpt_id);
3023 } else {
3024 let remove_count = rng.random_range(1..=excerpt_ids.len());
3025 let mut excerpts_to_remove = excerpt_ids
3026 .choose_multiple(rng, remove_count)
3027 .cloned()
3028 .collect::<Vec<_>>();
3029 let snapshot = self.snapshot.borrow();
3030 excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
3031 drop(snapshot);
3032 log::info!("Removing excerpts {:?}", excerpts_to_remove);
3033 self.remove_excerpts(excerpts_to_remove, cx);
3034 }
3035 }
3036 }
3037
3038 pub fn randomly_mutate(
3039 &mut self,
3040 rng: &mut impl rand::Rng,
3041 mutation_count: usize,
3042 cx: &mut Context<Self>,
3043 ) {
3044 use rand::prelude::*;
3045
3046 if rng.random_bool(0.7) || self.singleton {
3047 let buffer = self
3048 .buffers
3049 .values()
3050 .choose(rng)
3051 .map(|state| state.buffer.clone());
3052
3053 if let Some(buffer) = buffer {
3054 buffer.update(cx, |buffer, cx| {
3055 if rng.random() {
3056 buffer.randomly_edit(rng, mutation_count, cx);
3057 } else {
3058 buffer.randomly_undo_redo(rng, cx);
3059 }
3060 });
3061 } else {
3062 self.randomly_edit(rng, mutation_count, cx);
3063 }
3064 } else {
3065 self.randomly_edit_excerpts(rng, mutation_count, cx);
3066 }
3067
3068 self.check_invariants(cx);
3069 }
3070
3071 fn check_invariants(&self, cx: &App) {
3072 self.read(cx).check_invariants();
3073 }
3074}
3075
3076impl EventEmitter<Event> for MultiBuffer {}
3077
3078impl MultiBufferSnapshot {
3079 pub fn text(&self) -> String {
3080 self.chunks(0..self.len(), false)
3081 .map(|chunk| chunk.text)
3082 .collect()
3083 }
3084
3085 pub fn reversed_chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3086 self.reversed_chunks_in_range(0..position.to_offset(self))
3087 .flat_map(|c| c.chars().rev())
3088 }
3089
3090 fn reversed_chunks_in_range(&self, range: Range<usize>) -> ReversedMultiBufferChunks<'_> {
3091 let mut cursor = self.cursor::<usize>();
3092 cursor.seek(&range.end);
3093 let current_chunks = cursor.region().as_ref().map(|region| {
3094 let start_overshoot = range.start.saturating_sub(region.range.start);
3095 let end_overshoot = range.end - region.range.start;
3096 let end = (region.buffer_range.start + end_overshoot).min(region.buffer_range.end);
3097 let start = region.buffer_range.start + start_overshoot;
3098 region.buffer.reversed_chunks_in_range(start..end)
3099 });
3100 ReversedMultiBufferChunks {
3101 cursor,
3102 current_chunks,
3103 start: range.start,
3104 offset: range.end,
3105 }
3106 }
3107
3108 pub fn chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3109 let offset = position.to_offset(self);
3110 self.text_for_range(offset..self.len())
3111 .flat_map(|chunk| chunk.chars())
3112 }
3113
3114 pub fn text_for_range<T: ToOffset>(&self, range: Range<T>) -> impl Iterator<Item = &str> + '_ {
3115 self.chunks(range, false).map(|chunk| chunk.text)
3116 }
3117
3118 pub fn is_line_blank(&self, row: MultiBufferRow) -> bool {
3119 self.text_for_range(Point::new(row.0, 0)..Point::new(row.0, self.line_len(row)))
3120 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
3121 }
3122
3123 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
3124 where
3125 T: ToOffset,
3126 {
3127 let position = position.to_offset(self);
3128 position == self.clip_offset(position, Bias::Left)
3129 && self
3130 .bytes_in_range(position..self.len())
3131 .flatten()
3132 .copied()
3133 .take(needle.len())
3134 .eq(needle.bytes())
3135 }
3136
3137 pub fn diff_hunks(&self) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3138 self.diff_hunks_in_range(Anchor::min()..Anchor::max())
3139 }
3140
3141 pub fn diff_hunks_in_range<T: ToPoint>(
3142 &self,
3143 range: Range<T>,
3144 ) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3145 let query_range = range.start.to_point(self)..range.end.to_point(self);
3146 self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
3147 let diff = self.diffs.get(&buffer.remote_id())?;
3148 let buffer_start = buffer.anchor_before(buffer_range.start);
3149 let buffer_end = buffer.anchor_after(buffer_range.end);
3150 Some(
3151 diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
3152 .filter_map(|hunk| {
3153 if hunk.is_created_file() && !self.all_diff_hunks_expanded {
3154 return None;
3155 }
3156 Some((hunk.range.clone(), hunk))
3157 }),
3158 )
3159 })
3160 .filter_map(move |(range, hunk, excerpt)| {
3161 if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
3162 {
3163 return None;
3164 }
3165 let end_row = if range.end.column == 0 {
3166 range.end.row
3167 } else {
3168 range.end.row + 1
3169 };
3170 Some(MultiBufferDiffHunk {
3171 row_range: MultiBufferRow(range.start.row)..MultiBufferRow(end_row),
3172 buffer_id: excerpt.buffer_id,
3173 excerpt_id: excerpt.id,
3174 buffer_range: hunk.buffer_range.clone(),
3175 diff_base_byte_range: hunk.diff_base_byte_range.clone(),
3176 secondary_status: hunk.secondary_status,
3177 })
3178 })
3179 }
3180
3181 pub fn excerpt_ids_for_range<T: ToOffset>(
3182 &self,
3183 range: Range<T>,
3184 ) -> impl Iterator<Item = ExcerptId> + '_ {
3185 let range = range.start.to_offset(self)..range.end.to_offset(self);
3186 let mut cursor = self.cursor::<usize>();
3187 cursor.seek(&range.start);
3188 std::iter::from_fn(move || {
3189 let region = cursor.region()?;
3190 if region.range.start >= range.end {
3191 return None;
3192 }
3193 cursor.next_excerpt();
3194 Some(region.excerpt.id)
3195 })
3196 }
3197
3198 pub fn buffer_ids_for_range<T: ToOffset>(
3199 &self,
3200 range: Range<T>,
3201 ) -> impl Iterator<Item = BufferId> + '_ {
3202 let range = range.start.to_offset(self)..range.end.to_offset(self);
3203 let mut cursor = self.cursor::<usize>();
3204 cursor.seek(&range.start);
3205 std::iter::from_fn(move || {
3206 let region = cursor.region()?;
3207 if region.range.start > range.end
3208 || region.range.start == range.end && region.range.start > range.start
3209 {
3210 return None;
3211 }
3212 cursor.next_excerpt();
3213 Some(region.excerpt.buffer_id)
3214 })
3215 }
3216
3217 pub fn ranges_to_buffer_ranges<T: ToOffset>(
3218 &self,
3219 ranges: impl Iterator<Item = Range<T>>,
3220 ) -> impl Iterator<Item = (&BufferSnapshot, Range<usize>, ExcerptId)> {
3221 ranges.flat_map(|range| self.range_to_buffer_ranges(range).into_iter())
3222 }
3223
3224 pub fn range_to_buffer_ranges<T: ToOffset>(
3225 &self,
3226 range: Range<T>,
3227 ) -> Vec<(&BufferSnapshot, Range<usize>, ExcerptId)> {
3228 let start = range.start.to_offset(self);
3229 let end = range.end.to_offset(self);
3230
3231 let mut cursor = self.cursor::<usize>();
3232 cursor.seek(&start);
3233
3234 let mut result: Vec<(&BufferSnapshot, Range<usize>, ExcerptId)> = Vec::new();
3235 while let Some(region) = cursor.region() {
3236 if region.range.start > end {
3237 break;
3238 }
3239 if region.is_main_buffer {
3240 let start_overshoot = start.saturating_sub(region.range.start);
3241 let end_overshoot = end.saturating_sub(region.range.start);
3242 let start = region
3243 .buffer_range
3244 .end
3245 .min(region.buffer_range.start + start_overshoot);
3246 let end = region
3247 .buffer_range
3248 .end
3249 .min(region.buffer_range.start + end_overshoot);
3250 if let Some(prev) = result.last_mut().filter(|(_, prev_range, excerpt_id)| {
3251 *excerpt_id == region.excerpt.id && prev_range.end == start
3252 }) {
3253 prev.1.end = end;
3254 } else {
3255 result.push((region.buffer, start..end, region.excerpt.id));
3256 }
3257 }
3258 cursor.next();
3259 }
3260 result
3261 }
3262
3263 pub fn range_to_buffer_ranges_with_deleted_hunks<T: ToOffset>(
3264 &self,
3265 range: Range<T>,
3266 ) -> impl Iterator<Item = (&BufferSnapshot, Range<usize>, ExcerptId, Option<Anchor>)> + '_ {
3267 let start = range.start.to_offset(self);
3268 let end = range.end.to_offset(self);
3269
3270 let mut cursor = self.cursor::<usize>();
3271 cursor.seek(&start);
3272
3273 std::iter::from_fn(move || {
3274 let region = cursor.region()?;
3275 if region.range.start > end {
3276 return None;
3277 }
3278 let start_overshoot = start.saturating_sub(region.range.start);
3279 let end_overshoot = end.saturating_sub(region.range.start);
3280 let start = region
3281 .buffer_range
3282 .end
3283 .min(region.buffer_range.start + start_overshoot);
3284 let end = region
3285 .buffer_range
3286 .end
3287 .min(region.buffer_range.start + end_overshoot);
3288
3289 let region_excerpt_id = region.excerpt.id;
3290 let deleted_hunk_anchor = if region.is_main_buffer {
3291 None
3292 } else {
3293 Some(self.anchor_before(region.range.start))
3294 };
3295 let result = (
3296 region.buffer,
3297 start..end,
3298 region_excerpt_id,
3299 deleted_hunk_anchor,
3300 );
3301 cursor.next();
3302 Some(result)
3303 })
3304 }
3305
3306 /// Retrieves buffer metadata for the given range, and converts it into multi-buffer
3307 /// coordinates.
3308 ///
3309 /// The given callback will be called for every excerpt intersecting the given range. It will
3310 /// be passed the excerpt's buffer and the buffer range that the input range intersects.
3311 /// The callback should return an iterator of metadata items from that buffer, each paired
3312 /// with a buffer range.
3313 ///
3314 /// The returned iterator yields each of these metadata items, paired with its range in
3315 /// multi-buffer coordinates.
3316 fn lift_buffer_metadata<'a, D, M, I>(
3317 &'a self,
3318 query_range: Range<D>,
3319 get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range<D>) -> Option<I>,
3320 ) -> impl Iterator<Item = (Range<D>, M, &'a Excerpt)> + 'a
3321 where
3322 I: Iterator<Item = (Range<D>, M)> + 'a,
3323 D: TextDimension + Ord + Sub<D, Output = D>,
3324 {
3325 let max_position = D::from_text_summary(&self.text_summary());
3326 let mut current_excerpt_metadata: Option<(ExcerptId, I)> = None;
3327 let mut cursor = self.cursor::<D>();
3328
3329 // Find the excerpt and buffer offset where the given range ends.
3330 cursor.seek(&query_range.end);
3331 let mut range_end = None;
3332 while let Some(region) = cursor.region() {
3333 if region.is_main_buffer {
3334 let mut buffer_end = region.buffer_range.start;
3335 let overshoot = if query_range.end > region.range.start {
3336 query_range.end - region.range.start
3337 } else {
3338 D::default()
3339 };
3340 buffer_end.add_assign(&overshoot);
3341 range_end = Some((region.excerpt.id, buffer_end));
3342 break;
3343 }
3344 cursor.next();
3345 }
3346
3347 cursor.seek(&query_range.start);
3348
3349 if let Some(region) = cursor.region().filter(|region| !region.is_main_buffer)
3350 && region.range.start > D::zero(())
3351 {
3352 cursor.prev()
3353 }
3354
3355 iter::from_fn(move || {
3356 loop {
3357 let excerpt = cursor.excerpt()?;
3358
3359 // If we have already retrieved metadata for this excerpt, continue to use it.
3360 let metadata_iter = if let Some((_, metadata)) = current_excerpt_metadata
3361 .as_mut()
3362 .filter(|(excerpt_id, _)| *excerpt_id == excerpt.id)
3363 {
3364 Some(metadata)
3365 }
3366 // Otherwise, compute the intersection of the input range with the excerpt's range,
3367 // and retrieve the metadata for the resulting range.
3368 else {
3369 let region = cursor.region()?;
3370 let mut buffer_start;
3371 if region.is_main_buffer {
3372 buffer_start = region.buffer_range.start;
3373 if query_range.start > region.range.start {
3374 let overshoot = query_range.start - region.range.start;
3375 buffer_start.add_assign(&overshoot);
3376 }
3377 buffer_start = buffer_start.min(region.buffer_range.end);
3378 } else {
3379 buffer_start = cursor.main_buffer_position()?;
3380 };
3381 let mut buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
3382 if let Some((end_excerpt_id, end_buffer_offset)) = range_end
3383 && excerpt.id == end_excerpt_id
3384 {
3385 buffer_end = buffer_end.min(end_buffer_offset);
3386 }
3387
3388 get_buffer_metadata(&excerpt.buffer, buffer_start..buffer_end).map(|iterator| {
3389 &mut current_excerpt_metadata.insert((excerpt.id, iterator)).1
3390 })
3391 };
3392
3393 // Visit each metadata item.
3394 if let Some((metadata_buffer_range, metadata)) =
3395 metadata_iter.and_then(Iterator::next)
3396 {
3397 // Find the multibuffer regions that contain the start and end of
3398 // the metadata item's range.
3399 if metadata_buffer_range.start > D::default() {
3400 while let Some(region) = cursor.region() {
3401 if region.is_main_buffer
3402 && (region.buffer_range.end >= metadata_buffer_range.start
3403 || cursor.is_at_end_of_excerpt())
3404 {
3405 break;
3406 }
3407 cursor.next();
3408 }
3409 }
3410 let start_region = cursor.region()?;
3411 while let Some(region) = cursor.region() {
3412 if region.is_main_buffer
3413 && (region.buffer_range.end > metadata_buffer_range.end
3414 || cursor.is_at_end_of_excerpt())
3415 {
3416 break;
3417 }
3418 cursor.next();
3419 }
3420 let end_region = cursor.region();
3421
3422 // Convert the metadata item's range into multibuffer coordinates.
3423 let mut start_position = start_region.range.start;
3424 let region_buffer_start = start_region.buffer_range.start;
3425 if start_region.is_main_buffer
3426 && metadata_buffer_range.start > region_buffer_start
3427 {
3428 start_position
3429 .add_assign(&(metadata_buffer_range.start - region_buffer_start));
3430 start_position = start_position.min(start_region.range.end);
3431 }
3432
3433 let mut end_position = max_position;
3434 if let Some(end_region) = &end_region {
3435 end_position = end_region.range.start;
3436 debug_assert!(end_region.is_main_buffer);
3437 let region_buffer_start = end_region.buffer_range.start;
3438 if metadata_buffer_range.end > region_buffer_start {
3439 end_position
3440 .add_assign(&(metadata_buffer_range.end - region_buffer_start));
3441 }
3442 end_position = end_position.min(end_region.range.end);
3443 }
3444
3445 if start_position <= query_range.end && end_position >= query_range.start {
3446 return Some((start_position..end_position, metadata, excerpt));
3447 }
3448 }
3449 // When there are no more metadata items for this excerpt, move to the next excerpt.
3450 else {
3451 current_excerpt_metadata.take();
3452 if let Some((end_excerpt_id, _)) = range_end
3453 && excerpt.id == end_excerpt_id
3454 {
3455 return None;
3456 }
3457 cursor.next_excerpt();
3458 }
3459 }
3460 })
3461 }
3462
3463 pub fn diff_hunk_before<T: ToOffset>(&self, position: T) -> Option<MultiBufferRow> {
3464 let offset = position.to_offset(self);
3465
3466 let mut cursor = self.cursor::<DimensionPair<usize, Point>>();
3467 cursor.seek(&DimensionPair {
3468 key: offset,
3469 value: None,
3470 });
3471 cursor.seek_to_start_of_current_excerpt();
3472 let excerpt = cursor.excerpt()?;
3473
3474 let excerpt_end = excerpt.range.context.end.to_offset(&excerpt.buffer);
3475 let current_position = self
3476 .anchor_before(offset)
3477 .text_anchor
3478 .to_offset(&excerpt.buffer);
3479 let excerpt_end = excerpt
3480 .buffer
3481 .anchor_before(excerpt_end.min(current_position));
3482
3483 if let Some(diff) = self.diffs.get(&excerpt.buffer_id) {
3484 for hunk in diff.hunks_intersecting_range_rev(
3485 excerpt.range.context.start..excerpt_end,
3486 &excerpt.buffer,
3487 ) {
3488 let hunk_end = hunk.buffer_range.end.to_offset(&excerpt.buffer);
3489 if hunk_end >= current_position {
3490 continue;
3491 }
3492 let start =
3493 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 loop {
3500 cursor.prev_excerpt();
3501 let excerpt = cursor.excerpt()?;
3502
3503 let Some(diff) = self.diffs.get(&excerpt.buffer_id) else {
3504 continue;
3505 };
3506 let mut hunks =
3507 diff.hunks_intersecting_range_rev(excerpt.range.context.clone(), &excerpt.buffer);
3508 let Some(hunk) = hunks.next() else {
3509 continue;
3510 };
3511 let start = Anchor::in_buffer(excerpt.id, excerpt.buffer_id, hunk.buffer_range.start)
3512 .to_point(self);
3513 return Some(MultiBufferRow(start.row));
3514 }
3515 }
3516
3517 pub fn has_diff_hunks(&self) -> bool {
3518 self.diffs.values().any(|diff| !diff.is_empty())
3519 }
3520
3521 pub fn is_inside_word<T: ToOffset>(
3522 &self,
3523 position: T,
3524 scope_context: Option<CharScopeContext>,
3525 ) -> bool {
3526 let position = position.to_offset(self);
3527 let classifier = self
3528 .char_classifier_at(position)
3529 .scope_context(scope_context);
3530 let next_char_kind = self.chars_at(position).next().map(|c| classifier.kind(c));
3531 let prev_char_kind = self
3532 .reversed_chars_at(position)
3533 .next()
3534 .map(|c| classifier.kind(c));
3535 prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
3536 }
3537
3538 pub fn surrounding_word<T: ToOffset>(
3539 &self,
3540 start: T,
3541 scope_context: Option<CharScopeContext>,
3542 ) -> (Range<usize>, Option<CharKind>) {
3543 let mut start = start.to_offset(self);
3544 let mut end = start;
3545 let mut next_chars = self.chars_at(start).peekable();
3546 let mut prev_chars = self.reversed_chars_at(start).peekable();
3547
3548 let classifier = self.char_classifier_at(start).scope_context(scope_context);
3549
3550 let word_kind = cmp::max(
3551 prev_chars.peek().copied().map(|c| classifier.kind(c)),
3552 next_chars.peek().copied().map(|c| classifier.kind(c)),
3553 );
3554
3555 for ch in prev_chars {
3556 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
3557 start -= ch.len_utf8();
3558 } else {
3559 break;
3560 }
3561 }
3562
3563 for ch in next_chars {
3564 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
3565 end += ch.len_utf8();
3566 } else {
3567 break;
3568 }
3569 }
3570
3571 (start..end, word_kind)
3572 }
3573
3574 pub fn char_kind_before<T: ToOffset>(
3575 &self,
3576 start: T,
3577 scope_context: Option<CharScopeContext>,
3578 ) -> Option<CharKind> {
3579 let start = start.to_offset(self);
3580 let classifier = self.char_classifier_at(start).scope_context(scope_context);
3581 self.reversed_chars_at(start)
3582 .next()
3583 .map(|ch| classifier.kind(ch))
3584 }
3585
3586 pub fn is_singleton(&self) -> bool {
3587 self.singleton
3588 }
3589
3590 pub fn as_singleton(&self) -> Option<(&ExcerptId, BufferId, &BufferSnapshot)> {
3591 if self.singleton {
3592 self.excerpts
3593 .iter()
3594 .next()
3595 .map(|e| (&e.id, e.buffer_id, &e.buffer))
3596 } else {
3597 None
3598 }
3599 }
3600
3601 pub fn len(&self) -> usize {
3602 self.diff_transforms.summary().output.len
3603 }
3604
3605 pub fn is_empty(&self) -> bool {
3606 self.excerpts.summary().text.len == 0
3607 }
3608
3609 pub fn widest_line_number(&self) -> u32 {
3610 // widest_line_number is 0-based, so 1 is added to get the displayed line number.
3611 self.excerpts.summary().widest_line_number + 1
3612 }
3613
3614 pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes<'_> {
3615 let range = range.start.to_offset(self)..range.end.to_offset(self);
3616 let mut excerpts = self.cursor::<usize>();
3617 excerpts.seek(&range.start);
3618
3619 let mut chunk;
3620 let mut has_trailing_newline;
3621 let excerpt_bytes;
3622 if let Some(region) = excerpts.region() {
3623 let mut bytes = region.buffer.bytes_in_range(
3624 region.buffer_range.start + range.start - region.range.start
3625 ..(region.buffer_range.start + range.end - region.range.start)
3626 .min(region.buffer_range.end),
3627 );
3628 chunk = bytes.next().unwrap_or(&[][..]);
3629 excerpt_bytes = Some(bytes);
3630 has_trailing_newline = region.has_trailing_newline && range.end >= region.range.end;
3631 if chunk.is_empty() && has_trailing_newline {
3632 chunk = b"\n";
3633 has_trailing_newline = false;
3634 }
3635 } else {
3636 chunk = &[][..];
3637 excerpt_bytes = None;
3638 has_trailing_newline = false;
3639 };
3640
3641 MultiBufferBytes {
3642 range,
3643 cursor: excerpts,
3644 excerpt_bytes,
3645 has_trailing_newline,
3646 chunk,
3647 }
3648 }
3649
3650 pub fn reversed_bytes_in_range<T: ToOffset>(
3651 &self,
3652 range: Range<T>,
3653 ) -> ReversedMultiBufferBytes<'_> {
3654 let range = range.start.to_offset(self)..range.end.to_offset(self);
3655 let mut chunks = self.reversed_chunks_in_range(range.clone());
3656 let chunk = chunks.next().map_or(&[][..], |c| c.as_bytes());
3657 ReversedMultiBufferBytes {
3658 range,
3659 chunks,
3660 chunk,
3661 }
3662 }
3663
3664 pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> {
3665 let mut cursor = self.cursor::<Point>();
3666 cursor.seek(&Point::new(start_row.0, 0));
3667 let mut result = MultiBufferRows {
3668 point: Point::new(0, 0),
3669 is_empty: self.excerpts.is_empty(),
3670 is_singleton: self.is_singleton(),
3671 cursor,
3672 };
3673 result.seek(start_row);
3674 result
3675 }
3676
3677 pub fn chunks<T: ToOffset>(
3678 &self,
3679 range: Range<T>,
3680 language_aware: bool,
3681 ) -> MultiBufferChunks<'_> {
3682 let mut chunks = MultiBufferChunks {
3683 excerpt_offset_range: ExcerptOffset::new(0)..ExcerptOffset::new(0),
3684 range: 0..0,
3685 excerpts: self.excerpts.cursor(()),
3686 diff_transforms: self.diff_transforms.cursor(()),
3687 diffs: &self.diffs,
3688 diff_base_chunks: None,
3689 excerpt_chunks: None,
3690 buffer_chunk: None,
3691 language_aware,
3692 };
3693 let range = range.start.to_offset(self)..range.end.to_offset(self);
3694 chunks.seek(range);
3695 chunks
3696 }
3697
3698 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
3699 self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset)
3700 }
3701
3702 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
3703 self.clip_dimension(point, bias, text::BufferSnapshot::clip_point)
3704 }
3705
3706 pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 {
3707 self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset_utf16)
3708 }
3709
3710 pub fn clip_point_utf16(&self, point: Unclipped<PointUtf16>, bias: Bias) -> PointUtf16 {
3711 self.clip_dimension(point.0, bias, |buffer, point, bias| {
3712 buffer.clip_point_utf16(Unclipped(point), bias)
3713 })
3714 }
3715
3716 pub fn offset_to_point(&self, offset: usize) -> Point {
3717 self.convert_dimension(offset, text::BufferSnapshot::offset_to_point)
3718 }
3719
3720 pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 {
3721 self.convert_dimension(offset, text::BufferSnapshot::offset_to_point_utf16)
3722 }
3723
3724 pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
3725 self.convert_dimension(point, text::BufferSnapshot::point_to_point_utf16)
3726 }
3727
3728 pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
3729 self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_point)
3730 }
3731
3732 pub fn point_to_offset(&self, point: Point) -> usize {
3733 self.convert_dimension(point, text::BufferSnapshot::point_to_offset)
3734 }
3735
3736 pub fn point_to_offset_utf16(&self, point: Point) -> OffsetUtf16 {
3737 self.convert_dimension(point, text::BufferSnapshot::point_to_offset_utf16)
3738 }
3739
3740 pub fn offset_utf16_to_offset(&self, offset: OffsetUtf16) -> usize {
3741 self.convert_dimension(offset, text::BufferSnapshot::offset_utf16_to_offset)
3742 }
3743
3744 pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 {
3745 self.convert_dimension(offset, text::BufferSnapshot::offset_to_offset_utf16)
3746 }
3747
3748 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
3749 self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset)
3750 }
3751
3752 pub fn point_utf16_to_offset_utf16(&self, point: PointUtf16) -> OffsetUtf16 {
3753 self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset_utf16)
3754 }
3755
3756 fn clip_dimension<D>(
3757 &self,
3758 position: D,
3759 bias: Bias,
3760 clip_buffer_position: fn(&text::BufferSnapshot, D, Bias) -> D,
3761 ) -> D
3762 where
3763 D: TextDimension + Ord + Sub<D, Output = D>,
3764 {
3765 let mut cursor = self.cursor();
3766 cursor.seek(&position);
3767 if let Some(region) = cursor.region() {
3768 if position >= region.range.end {
3769 return region.range.end;
3770 }
3771 let overshoot = position - region.range.start;
3772 let mut buffer_position = region.buffer_range.start;
3773 buffer_position.add_assign(&overshoot);
3774 let clipped_buffer_position =
3775 clip_buffer_position(region.buffer, buffer_position, bias);
3776 let mut position = region.range.start;
3777 position.add_assign(&(clipped_buffer_position - region.buffer_range.start));
3778 position
3779 } else {
3780 D::from_text_summary(&self.text_summary())
3781 }
3782 }
3783
3784 fn convert_dimension<D1, D2>(
3785 &self,
3786 key: D1,
3787 convert_buffer_dimension: fn(&text::BufferSnapshot, D1) -> D2,
3788 ) -> D2
3789 where
3790 D1: TextDimension + Ord + Sub<D1, Output = D1>,
3791 D2: TextDimension + Ord + Sub<D2, Output = D2>,
3792 {
3793 let mut cursor = self.cursor::<DimensionPair<D1, D2>>();
3794 cursor.seek(&DimensionPair { key, value: None });
3795 if let Some(region) = cursor.region() {
3796 if key >= region.range.end.key {
3797 return region.range.end.value.unwrap();
3798 }
3799 let start_key = region.range.start.key;
3800 let start_value = region.range.start.value.unwrap();
3801 let buffer_start_key = region.buffer_range.start.key;
3802 let buffer_start_value = region.buffer_range.start.value.unwrap();
3803 let mut buffer_key = buffer_start_key;
3804 buffer_key.add_assign(&(key - start_key));
3805 let buffer_value = convert_buffer_dimension(region.buffer, buffer_key);
3806 let mut result = start_value;
3807 result.add_assign(&(buffer_value - buffer_start_value));
3808 result
3809 } else {
3810 D2::from_text_summary(&self.text_summary())
3811 }
3812 }
3813
3814 pub fn point_to_buffer_offset<T: ToOffset>(
3815 &self,
3816 point: T,
3817 ) -> Option<(&BufferSnapshot, usize)> {
3818 let offset = point.to_offset(self);
3819 let mut cursor = self.cursor::<usize>();
3820 cursor.seek(&offset);
3821 let region = cursor.region()?;
3822 let overshoot = offset - region.range.start;
3823 let buffer_offset = region.buffer_range.start + overshoot;
3824 if buffer_offset == region.buffer.len() + 1
3825 && region.has_trailing_newline
3826 && !region.is_main_buffer
3827 {
3828 let main_buffer_position = cursor.main_buffer_position()?;
3829 let buffer_snapshot = &cursor.excerpt()?.buffer;
3830 // remove this assert once we figure out the cause of the panics for #40453
3831 buffer_snapshot
3832 .text
3833 .as_rope()
3834 .assert_char_boundary(main_buffer_position);
3835 return Some((buffer_snapshot, main_buffer_position));
3836 } else if buffer_offset > region.buffer.len() {
3837 return None;
3838 }
3839 // remove this assert once we figure out the cause of the panics for #40453
3840 region
3841 .buffer
3842 .text
3843 .as_rope()
3844 .assert_char_boundary(buffer_offset);
3845 Some((region.buffer, buffer_offset))
3846 }
3847
3848 pub fn point_to_buffer_point(
3849 &self,
3850 point: Point,
3851 ) -> Option<(&BufferSnapshot, Point, ExcerptId)> {
3852 let mut cursor = self.cursor::<Point>();
3853 cursor.seek(&point);
3854 let region = cursor.region()?;
3855 let overshoot = point - region.range.start;
3856 let buffer_point = region.buffer_range.start + overshoot;
3857 let excerpt = cursor.excerpt()?;
3858 if buffer_point == region.buffer.max_point() + Point::new(1, 0)
3859 && region.has_trailing_newline
3860 && !region.is_main_buffer
3861 {
3862 return Some((&excerpt.buffer, cursor.main_buffer_position()?, excerpt.id));
3863 } else if buffer_point > region.buffer.max_point() {
3864 return None;
3865 }
3866 Some((region.buffer, buffer_point, excerpt.id))
3867 }
3868
3869 pub fn suggested_indents(
3870 &self,
3871 rows: impl IntoIterator<Item = u32>,
3872 cx: &App,
3873 ) -> BTreeMap<MultiBufferRow, IndentSize> {
3874 let mut result = BTreeMap::new();
3875
3876 let mut rows_for_excerpt = Vec::new();
3877 let mut cursor = self.cursor::<Point>();
3878 let mut rows = rows.into_iter().peekable();
3879 let mut prev_row = u32::MAX;
3880 let mut prev_language_indent_size = IndentSize::default();
3881
3882 while let Some(row) = rows.next() {
3883 cursor.seek(&Point::new(row, 0));
3884 let Some(region) = cursor.region() else {
3885 continue;
3886 };
3887
3888 // Retrieve the language and indent size once for each disjoint region being indented.
3889 let single_indent_size = if row.saturating_sub(1) == prev_row {
3890 prev_language_indent_size
3891 } else {
3892 region
3893 .buffer
3894 .language_indent_size_at(Point::new(row, 0), cx)
3895 };
3896 prev_language_indent_size = single_indent_size;
3897 prev_row = row;
3898
3899 let start_buffer_row = region.buffer_range.start.row;
3900 let start_multibuffer_row = region.range.start.row;
3901 let end_multibuffer_row = region.range.end.row;
3902
3903 rows_for_excerpt.push(row);
3904 while let Some(next_row) = rows.peek().copied() {
3905 if end_multibuffer_row > next_row {
3906 rows_for_excerpt.push(next_row);
3907 rows.next();
3908 } else {
3909 break;
3910 }
3911 }
3912
3913 let buffer_rows = rows_for_excerpt
3914 .drain(..)
3915 .map(|row| start_buffer_row + row - start_multibuffer_row);
3916 let buffer_indents = region
3917 .buffer
3918 .suggested_indents(buffer_rows, single_indent_size);
3919 let multibuffer_indents = buffer_indents.into_iter().map(|(row, indent)| {
3920 (
3921 MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
3922 indent,
3923 )
3924 });
3925 result.extend(multibuffer_indents);
3926 }
3927
3928 result
3929 }
3930
3931 pub fn indent_size_for_line(&self, row: MultiBufferRow) -> IndentSize {
3932 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
3933 let mut size = buffer.indent_size_for_line(range.start.row);
3934 size.len = size
3935 .len
3936 .min(range.end.column)
3937 .saturating_sub(range.start.column);
3938 size
3939 } else {
3940 IndentSize::spaces(0)
3941 }
3942 }
3943
3944 pub fn line_indent_for_row(&self, row: MultiBufferRow) -> LineIndent {
3945 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
3946 LineIndent::from_iter(buffer.text_for_range(range).flat_map(|s| s.chars()))
3947 } else {
3948 LineIndent::spaces(0)
3949 }
3950 }
3951
3952 pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &App) -> String {
3953 let mut indent = self.indent_size_for_line(row).chars().collect::<String>();
3954
3955 if self.language_settings(cx).extend_comment_on_newline
3956 && let Some(language_scope) = self.language_scope_at(Point::new(row.0, 0))
3957 {
3958 let delimiters = language_scope.line_comment_prefixes();
3959 for delimiter in delimiters {
3960 if *self
3961 .chars_at(Point::new(row.0, indent.len() as u32))
3962 .take(delimiter.chars().count())
3963 .collect::<String>()
3964 .as_str()
3965 == **delimiter
3966 {
3967 indent.push_str(delimiter);
3968 break;
3969 }
3970 }
3971 }
3972
3973 indent
3974 }
3975
3976 pub fn is_line_whitespace_upto<T>(&self, position: T) -> bool
3977 where
3978 T: ToOffset,
3979 {
3980 for char in self.reversed_chars_at(position) {
3981 if !char.is_whitespace() {
3982 return false;
3983 }
3984 if char == '\n' {
3985 return true;
3986 }
3987 }
3988 true
3989 }
3990
3991 pub fn prev_non_blank_row(&self, mut row: MultiBufferRow) -> Option<MultiBufferRow> {
3992 while row.0 > 0 {
3993 row.0 -= 1;
3994 if !self.is_line_blank(row) {
3995 return Some(row);
3996 }
3997 }
3998 None
3999 }
4000
4001 pub fn line_len(&self, row: MultiBufferRow) -> u32 {
4002 if let Some((_, range)) = self.buffer_line_for_row(row) {
4003 range.end.column - range.start.column
4004 } else {
4005 0
4006 }
4007 }
4008
4009 pub fn buffer_line_for_row(
4010 &self,
4011 row: MultiBufferRow,
4012 ) -> Option<(&BufferSnapshot, Range<Point>)> {
4013 let mut cursor = self.cursor::<Point>();
4014 let point = Point::new(row.0, 0);
4015 cursor.seek(&point);
4016 let region = cursor.region()?;
4017 let overshoot = point.min(region.range.end) - region.range.start;
4018 let buffer_point = region.buffer_range.start + overshoot;
4019 if buffer_point.row > region.buffer_range.end.row {
4020 return None;
4021 }
4022 let line_start = Point::new(buffer_point.row, 0).max(region.buffer_range.start);
4023 let line_end = Point::new(buffer_point.row, region.buffer.line_len(buffer_point.row))
4024 .min(region.buffer_range.end);
4025 Some((region.buffer, line_start..line_end))
4026 }
4027
4028 pub fn max_point(&self) -> Point {
4029 self.text_summary().lines
4030 }
4031
4032 pub fn max_row(&self) -> MultiBufferRow {
4033 MultiBufferRow(self.text_summary().lines.row)
4034 }
4035
4036 pub fn text_summary(&self) -> TextSummary {
4037 self.diff_transforms.summary().output
4038 }
4039
4040 pub fn text_summary_for_range<D, O>(&self, range: Range<O>) -> D
4041 where
4042 D: TextDimension,
4043 O: ToOffset,
4044 {
4045 let range = range.start.to_offset(self)..range.end.to_offset(self);
4046 let mut cursor = self
4047 .diff_transforms
4048 .cursor::<Dimensions<usize, ExcerptOffset>>(());
4049 cursor.seek(&range.start, Bias::Right);
4050
4051 let Some(first_transform) = cursor.item() else {
4052 return D::from_text_summary(&TextSummary::default());
4053 };
4054
4055 let diff_transform_start = cursor.start().0;
4056 let diff_transform_end = cursor.end().0;
4057 let diff_start = range.start;
4058 let start_overshoot = diff_start - diff_transform_start;
4059 let end_overshoot = std::cmp::min(range.end, diff_transform_end) - diff_transform_start;
4060
4061 let mut result = match first_transform {
4062 DiffTransform::BufferContent { .. } => {
4063 let excerpt_start = cursor.start().1 + ExcerptOffset::new(start_overshoot);
4064 let excerpt_end = cursor.start().1 + ExcerptOffset::new(end_overshoot);
4065 self.text_summary_for_excerpt_offset_range(excerpt_start..excerpt_end)
4066 }
4067 DiffTransform::DeletedHunk {
4068 buffer_id,
4069 base_text_byte_range,
4070 has_trailing_newline,
4071 ..
4072 } => {
4073 let buffer_start = base_text_byte_range.start + start_overshoot;
4074 let mut buffer_end = base_text_byte_range.start + end_overshoot;
4075 let Some(base_text) = self.diffs.get(buffer_id).map(|diff| diff.base_text()) else {
4076 panic!("{:?} is in non-existent deleted hunk", range.start)
4077 };
4078
4079 let include_trailing_newline =
4080 *has_trailing_newline && range.end >= diff_transform_end;
4081 if include_trailing_newline {
4082 buffer_end -= 1;
4083 }
4084
4085 let mut summary =
4086 base_text.text_summary_for_range::<D, _>(buffer_start..buffer_end);
4087
4088 if include_trailing_newline {
4089 summary.add_assign(&D::from_text_summary(&TextSummary::newline()))
4090 }
4091
4092 summary
4093 }
4094 };
4095 if range.end < diff_transform_end {
4096 return result;
4097 }
4098
4099 cursor.next();
4100 result.add_assign(&D::from_text_summary(
4101 &cursor.summary(&range.end, Bias::Right),
4102 ));
4103
4104 let Some(last_transform) = cursor.item() else {
4105 return result;
4106 };
4107
4108 let overshoot = range.end - cursor.start().0;
4109 let suffix = match last_transform {
4110 DiffTransform::BufferContent { .. } => {
4111 let end = cursor.start().1 + ExcerptOffset::new(overshoot);
4112 self.text_summary_for_excerpt_offset_range::<D>(cursor.start().1..end)
4113 }
4114 DiffTransform::DeletedHunk {
4115 base_text_byte_range,
4116 buffer_id,
4117 has_trailing_newline,
4118 ..
4119 } => {
4120 let buffer_end = base_text_byte_range.start + overshoot;
4121 let Some(base_text) = self.diffs.get(buffer_id).map(|diff| diff.base_text()) else {
4122 panic!("{:?} is in non-existent deleted hunk", range.end)
4123 };
4124
4125 let mut suffix = base_text
4126 .text_summary_for_range::<D, _>(base_text_byte_range.start..buffer_end);
4127 if *has_trailing_newline && buffer_end == base_text_byte_range.end + 1 {
4128 suffix.add_assign(&D::from_text_summary(&TextSummary::newline()))
4129 }
4130 suffix
4131 }
4132 };
4133
4134 result.add_assign(&suffix);
4135 result
4136 }
4137
4138 fn text_summary_for_excerpt_offset_range<D>(&self, mut range: Range<ExcerptOffset>) -> D
4139 where
4140 D: TextDimension,
4141 {
4142 let mut summary = D::zero(());
4143 let mut cursor = self.excerpts.cursor::<ExcerptOffset>(());
4144 cursor.seek(&range.start, Bias::Right);
4145 if let Some(excerpt) = cursor.item() {
4146 let mut end_before_newline = cursor.end();
4147 if excerpt.has_trailing_newline {
4148 end_before_newline -= ExcerptOffset::new(1);
4149 }
4150
4151 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
4152 let start_in_excerpt = excerpt_start + (range.start - *cursor.start()).value;
4153 let end_in_excerpt =
4154 excerpt_start + (cmp::min(end_before_newline, range.end) - *cursor.start()).value;
4155 summary.add_assign(
4156 &excerpt
4157 .buffer
4158 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
4159 );
4160
4161 if range.end > end_before_newline {
4162 summary.add_assign(&D::from_text_summary(&TextSummary::from("\n")));
4163 }
4164
4165 cursor.next();
4166 }
4167
4168 if range.end > *cursor.start() {
4169 summary.add_assign(
4170 &cursor
4171 .summary::<_, ExcerptDimension<D>>(&range.end, Bias::Right)
4172 .0,
4173 );
4174 if let Some(excerpt) = cursor.item() {
4175 range.end = cmp::max(*cursor.start(), range.end);
4176
4177 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
4178 let end_in_excerpt = excerpt_start + (range.end - *cursor.start()).value;
4179 summary.add_assign(
4180 &excerpt
4181 .buffer
4182 .text_summary_for_range(excerpt_start..end_in_excerpt),
4183 );
4184 }
4185 }
4186
4187 summary
4188 }
4189
4190 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
4191 where
4192 D: TextDimension + Ord + Sub<D, Output = D>,
4193 {
4194 self.summaries_for_anchors([anchor])[0]
4195 }
4196
4197 fn resolve_summary_for_anchor<D>(
4198 &self,
4199 anchor: &Anchor,
4200 excerpt_position: D,
4201 diff_transforms: &mut Cursor<
4202 DiffTransform,
4203 Dimensions<ExcerptDimension<D>, OutputDimension<D>>,
4204 >,
4205 ) -> D
4206 where
4207 D: TextDimension + Ord + Sub<D, Output = D>,
4208 {
4209 loop {
4210 let transform_end_position = diff_transforms.end().0.0;
4211 let at_transform_end =
4212 excerpt_position == transform_end_position && diff_transforms.item().is_some();
4213 if at_transform_end && anchor.text_anchor.bias == Bias::Right {
4214 diff_transforms.next();
4215 continue;
4216 }
4217
4218 let mut position = diff_transforms.start().1.0;
4219 match diff_transforms.item() {
4220 Some(DiffTransform::DeletedHunk {
4221 buffer_id,
4222 base_text_byte_range,
4223 ..
4224 }) => {
4225 if let Some(diff_base_anchor) = &anchor.diff_base_anchor
4226 && let Some(base_text) =
4227 self.diffs.get(buffer_id).map(|diff| diff.base_text())
4228 && base_text.can_resolve(diff_base_anchor)
4229 {
4230 let base_text_offset = diff_base_anchor.to_offset(base_text);
4231 if base_text_offset >= base_text_byte_range.start
4232 && base_text_offset <= base_text_byte_range.end
4233 {
4234 let position_in_hunk = base_text.text_summary_for_range::<D, _>(
4235 base_text_byte_range.start..base_text_offset,
4236 );
4237 position.add_assign(&position_in_hunk);
4238 } else if at_transform_end {
4239 diff_transforms.next();
4240 continue;
4241 }
4242 }
4243 }
4244 _ => {
4245 if at_transform_end && anchor.diff_base_anchor.is_some() {
4246 diff_transforms.next();
4247 continue;
4248 }
4249 let overshoot = excerpt_position - diff_transforms.start().0.0;
4250 position.add_assign(&overshoot);
4251 }
4252 }
4253
4254 return position;
4255 }
4256 }
4257
4258 fn excerpt_offset_for_anchor(&self, anchor: &Anchor) -> ExcerptOffset {
4259 let mut cursor = self
4260 .excerpts
4261 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
4262 let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
4263
4264 cursor.seek(&Some(locator), Bias::Left);
4265 if cursor.item().is_none() && anchor.excerpt_id == ExcerptId::max() {
4266 cursor.prev();
4267 }
4268
4269 let mut position = cursor.start().1;
4270 if let Some(excerpt) = cursor.item()
4271 && (excerpt.id == anchor.excerpt_id || anchor.excerpt_id == ExcerptId::max())
4272 {
4273 let excerpt_buffer_start = excerpt
4274 .buffer
4275 .offset_for_anchor(&excerpt.range.context.start);
4276 let excerpt_buffer_end = excerpt.buffer.offset_for_anchor(&excerpt.range.context.end);
4277 let buffer_position = cmp::min(
4278 excerpt_buffer_end,
4279 excerpt.buffer.offset_for_anchor(&anchor.text_anchor),
4280 );
4281 if buffer_position > excerpt_buffer_start {
4282 position.value += buffer_position - excerpt_buffer_start;
4283 }
4284 }
4285 position
4286 }
4287
4288 pub fn latest_excerpt_id(&self, mut excerpt_id: ExcerptId) -> ExcerptId {
4289 while let Some(replacement) = self.replaced_excerpts.get(&excerpt_id) {
4290 excerpt_id = *replacement;
4291 }
4292 excerpt_id
4293 }
4294
4295 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
4296 where
4297 D: TextDimension + Ord + Sub<D, Output = D>,
4298 I: 'a + IntoIterator<Item = &'a Anchor>,
4299 {
4300 let mut anchors = anchors.into_iter().peekable();
4301 let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
4302 let mut diff_transforms_cursor = self
4303 .diff_transforms
4304 .cursor::<Dimensions<ExcerptDimension<D>, OutputDimension<D>>>(());
4305 diff_transforms_cursor.next();
4306
4307 let mut summaries = Vec::new();
4308 while let Some(anchor) = anchors.peek() {
4309 let excerpt_id = self.latest_excerpt_id(anchor.excerpt_id);
4310
4311 let excerpt_anchors = anchors.peeking_take_while(|anchor| {
4312 self.latest_excerpt_id(anchor.excerpt_id) == excerpt_id
4313 });
4314
4315 let locator = self.excerpt_locator_for_id(excerpt_id);
4316 cursor.seek_forward(locator, Bias::Left);
4317 if cursor.item().is_none() && excerpt_id == ExcerptId::max() {
4318 cursor.prev();
4319 }
4320
4321 let excerpt_start_position = D::from_text_summary(&cursor.start().text);
4322 if let Some(excerpt) = cursor.item() {
4323 if excerpt.id != excerpt_id && excerpt_id != ExcerptId::max() {
4324 let position = self.resolve_summary_for_anchor(
4325 &Anchor::min(),
4326 excerpt_start_position,
4327 &mut diff_transforms_cursor,
4328 );
4329 summaries.extend(excerpt_anchors.map(|_| position));
4330 continue;
4331 }
4332 let excerpt_buffer_start =
4333 excerpt.range.context.start.summary::<D>(&excerpt.buffer);
4334 let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
4335 for (buffer_summary, anchor) in excerpt
4336 .buffer
4337 .summaries_for_anchors_with_payload::<D, _, _>(
4338 excerpt_anchors.map(|a| (&a.text_anchor, a)),
4339 )
4340 {
4341 let summary = cmp::min(excerpt_buffer_end, buffer_summary);
4342 let mut position = excerpt_start_position;
4343 if summary > excerpt_buffer_start {
4344 position.add_assign(&(summary - excerpt_buffer_start));
4345 }
4346
4347 if position > diff_transforms_cursor.start().0.0 {
4348 diff_transforms_cursor
4349 .seek_forward(&ExcerptDimension(position), Bias::Left);
4350 }
4351
4352 summaries.push(self.resolve_summary_for_anchor(
4353 anchor,
4354 position,
4355 &mut diff_transforms_cursor,
4356 ));
4357 }
4358 } else {
4359 diff_transforms_cursor
4360 .seek_forward(&ExcerptDimension(excerpt_start_position), Bias::Left);
4361 let position = self.resolve_summary_for_anchor(
4362 &Anchor::max(),
4363 excerpt_start_position,
4364 &mut diff_transforms_cursor,
4365 );
4366 summaries.extend(excerpt_anchors.map(|_| position));
4367 }
4368 }
4369
4370 summaries
4371 }
4372
4373 pub fn dimensions_from_points<'a, D>(
4374 &'a self,
4375 points: impl 'a + IntoIterator<Item = Point>,
4376 ) -> impl 'a + Iterator<Item = D>
4377 where
4378 D: TextDimension + Sub<D, Output = D>,
4379 {
4380 let mut cursor = self.cursor::<DimensionPair<Point, D>>();
4381 cursor.seek(&DimensionPair {
4382 key: Point::default(),
4383 value: None,
4384 });
4385 let mut points = points.into_iter();
4386 iter::from_fn(move || {
4387 let point = points.next()?;
4388
4389 cursor.seek_forward(&DimensionPair {
4390 key: point,
4391 value: None,
4392 });
4393
4394 if let Some(region) = cursor.region() {
4395 let overshoot = point - region.range.start.key;
4396 let buffer_point = region.buffer_range.start.key + overshoot;
4397 let mut position = region.range.start.value.unwrap();
4398 position.add_assign(
4399 ®ion
4400 .buffer
4401 .text_summary_for_range(region.buffer_range.start.key..buffer_point),
4402 );
4403 if point == region.range.end.key && region.has_trailing_newline {
4404 position.add_assign(&D::from_text_summary(&TextSummary::newline()));
4405 }
4406 Some(position)
4407 } else {
4408 Some(D::from_text_summary(&self.text_summary()))
4409 }
4410 })
4411 }
4412
4413 pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
4414 where
4415 I: 'a + IntoIterator<Item = &'a Anchor>,
4416 {
4417 let mut anchors = anchors.into_iter().enumerate().peekable();
4418 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
4419 cursor.next();
4420
4421 let mut result = Vec::new();
4422
4423 while let Some((_, anchor)) = anchors.peek() {
4424 let old_excerpt_id = anchor.excerpt_id;
4425
4426 // Find the location where this anchor's excerpt should be.
4427 let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
4428 cursor.seek_forward(&Some(old_locator), Bias::Left);
4429
4430 let next_excerpt = cursor.item();
4431 let prev_excerpt = cursor.prev_item();
4432
4433 // Process all of the anchors for this excerpt.
4434 while let Some((anchor_ix, &anchor)) =
4435 anchors.next_if(|(_, anchor)| anchor.excerpt_id == old_excerpt_id)
4436 {
4437 let mut anchor = anchor;
4438
4439 // Leave min and max anchors unchanged if invalid or
4440 // if the old excerpt still exists at this location
4441 let mut kept_position = next_excerpt
4442 .is_some_and(|e| e.id == old_excerpt_id && e.contains(&anchor))
4443 || old_excerpt_id == ExcerptId::max()
4444 || old_excerpt_id == ExcerptId::min();
4445
4446 // If the old excerpt no longer exists at this location, then attempt to
4447 // find an equivalent position for this anchor in an adjacent excerpt.
4448 if !kept_position {
4449 for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
4450 if excerpt.contains(&anchor) {
4451 anchor.excerpt_id = excerpt.id;
4452 kept_position = true;
4453 break;
4454 }
4455 }
4456 }
4457
4458 // If there's no adjacent excerpt that contains the anchor's position,
4459 // then report that the anchor has lost its position.
4460 if !kept_position {
4461 anchor = if let Some(excerpt) = next_excerpt {
4462 let mut text_anchor = excerpt
4463 .range
4464 .context
4465 .start
4466 .bias(anchor.text_anchor.bias, &excerpt.buffer);
4467 if text_anchor
4468 .cmp(&excerpt.range.context.end, &excerpt.buffer)
4469 .is_gt()
4470 {
4471 text_anchor = excerpt.range.context.end;
4472 }
4473 Anchor::in_buffer(excerpt.id, excerpt.buffer_id, text_anchor)
4474 } else if let Some(excerpt) = prev_excerpt {
4475 let mut text_anchor = excerpt
4476 .range
4477 .context
4478 .end
4479 .bias(anchor.text_anchor.bias, &excerpt.buffer);
4480 if text_anchor
4481 .cmp(&excerpt.range.context.start, &excerpt.buffer)
4482 .is_lt()
4483 {
4484 text_anchor = excerpt.range.context.start;
4485 }
4486 Anchor::in_buffer(excerpt.id, excerpt.buffer_id, text_anchor)
4487 } else if anchor.text_anchor.bias == Bias::Left {
4488 Anchor::min()
4489 } else {
4490 Anchor::max()
4491 };
4492 }
4493
4494 result.push((anchor_ix, anchor, kept_position));
4495 }
4496 }
4497 result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
4498 result
4499 }
4500
4501 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
4502 self.anchor_at(position, Bias::Left)
4503 }
4504
4505 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
4506 self.anchor_at(position, Bias::Right)
4507 }
4508
4509 pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
4510 let offset = position.to_offset(self);
4511
4512 // Find the given position in the diff transforms. Determine the corresponding
4513 // offset in the excerpts, and whether the position is within a deleted hunk.
4514 let mut diff_transforms = self
4515 .diff_transforms
4516 .cursor::<Dimensions<usize, ExcerptOffset>>(());
4517 diff_transforms.seek(&offset, Bias::Right);
4518
4519 if offset == diff_transforms.start().0
4520 && bias == Bias::Left
4521 && let Some(prev_item) = diff_transforms.prev_item()
4522 && let DiffTransform::DeletedHunk { .. } = prev_item
4523 {
4524 diff_transforms.prev();
4525 }
4526 let offset_in_transform = offset - diff_transforms.start().0;
4527 let mut excerpt_offset = diff_transforms.start().1;
4528 let mut diff_base_anchor = None;
4529 if let Some(DiffTransform::DeletedHunk {
4530 buffer_id,
4531 base_text_byte_range,
4532 has_trailing_newline,
4533 ..
4534 }) = diff_transforms.item()
4535 {
4536 let diff = self.diffs.get(buffer_id).expect("missing diff");
4537 if offset_in_transform > base_text_byte_range.len() {
4538 debug_assert!(*has_trailing_newline);
4539 bias = Bias::Right;
4540 } else {
4541 diff_base_anchor = Some(
4542 diff.base_text()
4543 .anchor_at(base_text_byte_range.start + offset_in_transform, bias),
4544 );
4545 bias = Bias::Left;
4546 }
4547 } else {
4548 excerpt_offset += ExcerptOffset::new(offset_in_transform);
4549 };
4550
4551 let mut excerpts = self
4552 .excerpts
4553 .cursor::<Dimensions<ExcerptOffset, Option<ExcerptId>>>(());
4554 excerpts.seek(&excerpt_offset, Bias::Right);
4555 if excerpts.item().is_none() && excerpt_offset == excerpts.start().0 && bias == Bias::Left {
4556 excerpts.prev();
4557 }
4558 if let Some(excerpt) = excerpts.item() {
4559 let mut overshoot = excerpt_offset.saturating_sub(excerpts.start().0).value;
4560 if excerpt.has_trailing_newline && excerpt_offset == excerpts.end().0 {
4561 overshoot -= 1;
4562 bias = Bias::Right;
4563 }
4564
4565 let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
4566 let text_anchor =
4567 excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
4568 let anchor = Anchor::in_buffer(excerpt.id, excerpt.buffer_id, text_anchor);
4569 match diff_base_anchor {
4570 Some(diff_base_anchor) => anchor.with_diff_base_anchor(diff_base_anchor),
4571 None => anchor,
4572 }
4573 } else if excerpt_offset.is_zero() && bias == Bias::Left {
4574 Anchor::min()
4575 } else {
4576 Anchor::max()
4577 }
4578 }
4579
4580 /// Wraps the [`text::Anchor`] in a [`multi_buffer::Anchor`] if this multi-buffer is a singleton.
4581 pub fn as_singleton_anchor(&self, text_anchor: text::Anchor) -> Option<Anchor> {
4582 let (excerpt, buffer, _) = self.as_singleton()?;
4583 Some(Anchor::in_buffer(*excerpt, buffer, text_anchor))
4584 }
4585
4586 /// Returns an anchor for the given excerpt and text anchor,
4587 /// Returns [`None`] if the excerpt_id is no longer valid or the text anchor range is out of excerpt's bounds.
4588 pub fn anchor_range_in_excerpt(
4589 &self,
4590 excerpt_id: ExcerptId,
4591 text_anchor: Range<text::Anchor>,
4592 ) -> Option<Range<Anchor>> {
4593 let excerpt_id = self.latest_excerpt_id(excerpt_id);
4594 let excerpt = self.excerpt(excerpt_id)?;
4595
4596 Some(
4597 self.anchor_in_excerpt_(excerpt, text_anchor.start)?
4598 ..self.anchor_in_excerpt_(excerpt, text_anchor.end)?,
4599 )
4600 }
4601
4602 /// Returns an anchor for the given excerpt and text anchor,
4603 /// Returns [`None`] if the excerpt_id is no longer valid or the text anchor range is out of excerpt's bounds.
4604 pub fn anchor_in_excerpt(
4605 &self,
4606 excerpt_id: ExcerptId,
4607 text_anchor: text::Anchor,
4608 ) -> Option<Anchor> {
4609 let excerpt_id = self.latest_excerpt_id(excerpt_id);
4610 let excerpt = self.excerpt(excerpt_id)?;
4611 self.anchor_in_excerpt_(excerpt, text_anchor)
4612 }
4613
4614 fn anchor_in_excerpt_(&self, excerpt: &Excerpt, text_anchor: text::Anchor) -> Option<Anchor> {
4615 match text_anchor.buffer_id {
4616 Some(buffer_id) if buffer_id == excerpt.buffer_id => (),
4617 Some(_) => return None,
4618 None if text_anchor == text::Anchor::MAX || text_anchor == text::Anchor::MIN => {
4619 return Some(Anchor::in_buffer(
4620 excerpt.id,
4621 excerpt.buffer_id,
4622 text_anchor,
4623 ));
4624 }
4625 None => return None,
4626 }
4627
4628 let context = &excerpt.range.context;
4629 if context.start.cmp(&text_anchor, &excerpt.buffer).is_gt()
4630 || context.end.cmp(&text_anchor, &excerpt.buffer).is_lt()
4631 {
4632 return None;
4633 }
4634
4635 Some(Anchor::in_buffer(
4636 excerpt.id,
4637 excerpt.buffer_id,
4638 text_anchor,
4639 ))
4640 }
4641
4642 pub fn context_range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<text::Anchor>> {
4643 Some(self.excerpt(excerpt_id)?.range.context.clone())
4644 }
4645
4646 pub fn can_resolve(&self, anchor: &Anchor) -> bool {
4647 if anchor.excerpt_id == ExcerptId::min() || anchor.excerpt_id == ExcerptId::max() {
4648 true
4649 } else if let Some(excerpt) = self.excerpt(anchor.excerpt_id) {
4650 excerpt.buffer.can_resolve(&anchor.text_anchor)
4651 } else {
4652 false
4653 }
4654 }
4655
4656 pub fn excerpts(
4657 &self,
4658 ) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
4659 self.excerpts
4660 .iter()
4661 .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
4662 }
4663
4664 fn cursor<D: TextDimension + Default>(&self) -> MultiBufferCursor<'_, D> {
4665 let excerpts = self.excerpts.cursor(());
4666 let diff_transforms = self.diff_transforms.cursor(());
4667 MultiBufferCursor {
4668 excerpts,
4669 diff_transforms,
4670 diffs: &self.diffs,
4671 cached_region: None,
4672 }
4673 }
4674
4675 pub fn excerpt_before(&self, excerpt_id: ExcerptId) -> Option<MultiBufferExcerpt<'_>> {
4676 let start_locator = self.excerpt_locator_for_id(excerpt_id);
4677 let mut excerpts = self
4678 .excerpts
4679 .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<usize>>>(());
4680 excerpts.seek(&Some(start_locator), Bias::Left);
4681 excerpts.prev();
4682
4683 let mut diff_transforms = self.diff_transforms.cursor::<DiffTransforms<usize>>(());
4684 diff_transforms.seek(&excerpts.start().1, Bias::Left);
4685 if diff_transforms.end().excerpt_dimension < excerpts.start().1 {
4686 diff_transforms.next();
4687 }
4688
4689 let excerpt = excerpts.item()?;
4690 Some(MultiBufferExcerpt {
4691 excerpt,
4692 offset: diff_transforms.start().output_dimension.0,
4693 buffer_offset: excerpt.range.context.start.to_offset(&excerpt.buffer),
4694 excerpt_offset: excerpts.start().1.clone(),
4695 diff_transforms,
4696 })
4697 }
4698
4699 pub fn excerpt_boundaries_in_range<R, T>(
4700 &self,
4701 range: R,
4702 ) -> impl Iterator<Item = ExcerptBoundary> + '_
4703 where
4704 R: RangeBounds<T>,
4705 T: ToOffset,
4706 {
4707 let start_offset;
4708 let start = match range.start_bound() {
4709 Bound::Included(start) => {
4710 start_offset = start.to_offset(self);
4711 Bound::Included(start_offset)
4712 }
4713 Bound::Excluded(_) => {
4714 panic!("not supported")
4715 }
4716 Bound::Unbounded => {
4717 start_offset = 0;
4718 Bound::Unbounded
4719 }
4720 };
4721 let end = match range.end_bound() {
4722 Bound::Included(end) => Bound::Included(end.to_offset(self)),
4723 Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
4724 Bound::Unbounded => Bound::Unbounded,
4725 };
4726 let bounds = (start, end);
4727
4728 let mut cursor = self.cursor::<DimensionPair<usize, Point>>();
4729 cursor.seek(&DimensionPair {
4730 key: start_offset,
4731 value: None,
4732 });
4733
4734 if cursor
4735 .region()
4736 .is_some_and(|region| bounds.contains(®ion.range.start.key))
4737 {
4738 cursor.prev_excerpt();
4739 } else {
4740 cursor.seek_to_start_of_current_excerpt();
4741 }
4742 let mut prev_region = cursor.region();
4743
4744 cursor.next_excerpt();
4745
4746 iter::from_fn(move || {
4747 loop {
4748 if self.singleton {
4749 return None;
4750 }
4751
4752 let next_region = cursor.region()?;
4753 cursor.next_excerpt();
4754 if !bounds.contains(&next_region.range.start.key) {
4755 prev_region = Some(next_region);
4756 continue;
4757 }
4758
4759 let next_region_start = next_region.range.start.value.unwrap();
4760 let next_region_end = if let Some(region) = cursor.region() {
4761 region.range.start.value.unwrap()
4762 } else {
4763 self.max_point()
4764 };
4765
4766 let prev = prev_region.as_ref().map(|region| ExcerptInfo {
4767 id: region.excerpt.id,
4768 buffer: region.excerpt.buffer.clone(),
4769 buffer_id: region.excerpt.buffer_id,
4770 range: region.excerpt.range.clone(),
4771 end_row: MultiBufferRow(next_region_start.row),
4772 });
4773
4774 let next = ExcerptInfo {
4775 id: next_region.excerpt.id,
4776 buffer: next_region.excerpt.buffer.clone(),
4777 buffer_id: next_region.excerpt.buffer_id,
4778 range: next_region.excerpt.range.clone(),
4779 end_row: if next_region.excerpt.has_trailing_newline {
4780 MultiBufferRow(next_region_end.row - 1)
4781 } else {
4782 MultiBufferRow(next_region_end.row)
4783 },
4784 };
4785
4786 let row = MultiBufferRow(next_region_start.row);
4787
4788 prev_region = Some(next_region);
4789
4790 return Some(ExcerptBoundary { row, prev, next });
4791 }
4792 })
4793 }
4794
4795 pub fn edit_count(&self) -> usize {
4796 self.edit_count
4797 }
4798
4799 pub fn non_text_state_update_count(&self) -> usize {
4800 self.non_text_state_update_count
4801 }
4802
4803 /// Returns the smallest enclosing bracket ranges containing the given range or
4804 /// None if no brackets contain range or the range is not contained in a single
4805 /// excerpt
4806 ///
4807 /// Can optionally pass a range_filter to filter the ranges of brackets to consider
4808 pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
4809 &self,
4810 range: Range<T>,
4811 range_filter: Option<&dyn Fn(&BufferSnapshot, Range<usize>, Range<usize>) -> bool>,
4812 ) -> Option<(Range<usize>, Range<usize>)> {
4813 let range = range.start.to_offset(self)..range.end.to_offset(self);
4814 let mut excerpt = self.excerpt_containing(range.clone())?;
4815 let buffer = excerpt.buffer();
4816 let excerpt_buffer_range = excerpt.buffer_range();
4817
4818 // Filter to ranges contained in the excerpt
4819 let range_filter = |open: Range<usize>, close: Range<usize>| -> bool {
4820 excerpt_buffer_range.contains(&open.start)
4821 && excerpt_buffer_range.contains(&close.end)
4822 && range_filter.is_none_or(|filter| filter(buffer, open, close))
4823 };
4824
4825 let (open, close) = excerpt.buffer().innermost_enclosing_bracket_ranges(
4826 excerpt.map_range_to_buffer(range),
4827 Some(&range_filter),
4828 )?;
4829
4830 Some((
4831 excerpt.map_range_from_buffer(open),
4832 excerpt.map_range_from_buffer(close),
4833 ))
4834 }
4835
4836 /// Returns enclosing bracket ranges containing the given range or returns None if the range is
4837 /// not contained in a single excerpt
4838 pub fn enclosing_bracket_ranges<T: ToOffset>(
4839 &self,
4840 range: Range<T>,
4841 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + '_> {
4842 let range = range.start.to_offset(self)..range.end.to_offset(self);
4843 let mut excerpt = self.excerpt_containing(range.clone())?;
4844
4845 Some(
4846 excerpt
4847 .buffer()
4848 .enclosing_bracket_ranges(excerpt.map_range_to_buffer(range))
4849 .filter_map(move |pair| {
4850 if excerpt.contains_buffer_range(pair.open_range.start..pair.close_range.end) {
4851 Some((
4852 excerpt.map_range_from_buffer(pair.open_range),
4853 excerpt.map_range_from_buffer(pair.close_range),
4854 ))
4855 } else {
4856 None
4857 }
4858 }),
4859 )
4860 }
4861
4862 /// Returns enclosing bracket ranges containing the given range or returns None if the range is
4863 /// not contained in a single excerpt
4864 pub fn text_object_ranges<T: ToOffset>(
4865 &self,
4866 range: Range<T>,
4867 options: TreeSitterOptions,
4868 ) -> impl Iterator<Item = (Range<usize>, TextObject)> + '_ {
4869 let range = range.start.to_offset(self)..range.end.to_offset(self);
4870 self.excerpt_containing(range.clone())
4871 .map(|mut excerpt| {
4872 excerpt
4873 .buffer()
4874 .text_object_ranges(excerpt.map_range_to_buffer(range), options)
4875 .filter_map(move |(range, text_object)| {
4876 if excerpt.contains_buffer_range(range.clone()) {
4877 Some((excerpt.map_range_from_buffer(range), text_object))
4878 } else {
4879 None
4880 }
4881 })
4882 })
4883 .into_iter()
4884 .flatten()
4885 }
4886
4887 /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is
4888 /// not contained in a single excerpt
4889 pub fn bracket_ranges<T: ToOffset>(
4890 &self,
4891 range: Range<T>,
4892 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + '_> {
4893 let range = range.start.to_offset(self)..range.end.to_offset(self);
4894 let mut excerpt = self.excerpt_containing(range.clone())?;
4895
4896 Some(
4897 excerpt
4898 .buffer()
4899 .bracket_ranges(excerpt.map_range_to_buffer(range))
4900 .filter_map(move |pair| {
4901 let buffer_range = pair.open_range.start..pair.close_range.end;
4902 if excerpt.contains_buffer_range(buffer_range) {
4903 Some((
4904 excerpt.map_range_from_buffer(pair.open_range),
4905 excerpt.map_range_from_buffer(pair.close_range),
4906 ))
4907 } else {
4908 None
4909 }
4910 }),
4911 )
4912 }
4913
4914 pub fn redacted_ranges<'a, T: ToOffset>(
4915 &'a self,
4916 range: Range<T>,
4917 redaction_enabled: impl Fn(Option<&Arc<dyn File>>) -> bool + 'a,
4918 ) -> impl Iterator<Item = Range<usize>> + 'a {
4919 let range = range.start.to_offset(self)..range.end.to_offset(self);
4920 self.lift_buffer_metadata(range, move |buffer, range| {
4921 if redaction_enabled(buffer.file()) {
4922 Some(buffer.redacted_ranges(range).map(|range| (range, ())))
4923 } else {
4924 None
4925 }
4926 })
4927 .map(|(range, _, _)| range)
4928 }
4929
4930 pub fn runnable_ranges(
4931 &self,
4932 range: Range<Anchor>,
4933 ) -> impl Iterator<Item = language::RunnableRange> + '_ {
4934 let range = range.start.to_offset(self)..range.end.to_offset(self);
4935 self.lift_buffer_metadata(range, move |buffer, range| {
4936 Some(
4937 buffer
4938 .runnable_ranges(range.clone())
4939 .filter(move |runnable| {
4940 runnable.run_range.start >= range.start
4941 && runnable.run_range.end < range.end
4942 })
4943 .map(|runnable| (runnable.run_range.clone(), runnable)),
4944 )
4945 })
4946 .map(|(run_range, runnable, _)| language::RunnableRange {
4947 run_range,
4948 ..runnable
4949 })
4950 }
4951
4952 pub fn line_indents(
4953 &self,
4954 start_row: MultiBufferRow,
4955 buffer_filter: impl Fn(&BufferSnapshot) -> bool,
4956 ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
4957 let max_point = self.max_point();
4958 let mut cursor = self.cursor::<Point>();
4959 cursor.seek(&Point::new(start_row.0, 0));
4960 iter::from_fn(move || {
4961 let mut region = cursor.region()?;
4962 while !buffer_filter(®ion.excerpt.buffer) {
4963 cursor.next();
4964 region = cursor.region()?;
4965 }
4966 let overshoot = start_row.0.saturating_sub(region.range.start.row);
4967 let buffer_start_row =
4968 (region.buffer_range.start.row + overshoot).min(region.buffer_range.end.row);
4969
4970 let buffer_end_row = if region.is_main_buffer
4971 && (region.has_trailing_newline || region.range.end == max_point)
4972 {
4973 region.buffer_range.end.row
4974 } else {
4975 region.buffer_range.end.row.saturating_sub(1)
4976 };
4977
4978 let line_indents = region
4979 .buffer
4980 .line_indents_in_row_range(buffer_start_row..buffer_end_row);
4981 cursor.next();
4982 Some(line_indents.map(move |(buffer_row, indent)| {
4983 let row = region.range.start.row + (buffer_row - region.buffer_range.start.row);
4984 (MultiBufferRow(row), indent, ®ion.excerpt.buffer)
4985 }))
4986 })
4987 .flatten()
4988 }
4989
4990 pub fn reversed_line_indents(
4991 &self,
4992 end_row: MultiBufferRow,
4993 buffer_filter: impl Fn(&BufferSnapshot) -> bool,
4994 ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
4995 let max_point = self.max_point();
4996 let mut cursor = self.cursor::<Point>();
4997 cursor.seek(&Point::new(end_row.0, 0));
4998 iter::from_fn(move || {
4999 let mut region = cursor.region()?;
5000 while !buffer_filter(®ion.excerpt.buffer) {
5001 cursor.prev();
5002 region = cursor.region()?;
5003 }
5004
5005 let buffer_start_row = region.buffer_range.start.row;
5006 let buffer_end_row = if region.is_main_buffer
5007 && (region.has_trailing_newline || region.range.end == max_point)
5008 {
5009 region.buffer_range.end.row + 1
5010 } else {
5011 region.buffer_range.end.row
5012 };
5013
5014 let overshoot = end_row.0 - region.range.start.row;
5015 let buffer_end_row =
5016 (region.buffer_range.start.row + overshoot + 1).min(buffer_end_row);
5017
5018 let line_indents = region
5019 .buffer
5020 .reversed_line_indents_in_row_range(buffer_start_row..buffer_end_row);
5021 cursor.prev();
5022 Some(line_indents.map(move |(buffer_row, indent)| {
5023 let row = region.range.start.row + (buffer_row - region.buffer_range.start.row);
5024 (MultiBufferRow(row), indent, ®ion.excerpt.buffer)
5025 }))
5026 })
5027 .flatten()
5028 }
5029
5030 pub async fn enclosing_indent(
5031 &self,
5032 mut target_row: MultiBufferRow,
5033 ) -> Option<(Range<MultiBufferRow>, LineIndent)> {
5034 let max_row = MultiBufferRow(self.max_point().row);
5035 if target_row >= max_row {
5036 return None;
5037 }
5038
5039 let mut target_indent = self.line_indent_for_row(target_row);
5040
5041 // If the current row is at the start of an indented block, we want to return this
5042 // block as the enclosing indent.
5043 if !target_indent.is_line_empty() && target_row < max_row {
5044 let next_line_indent = self.line_indent_for_row(MultiBufferRow(target_row.0 + 1));
5045 if !next_line_indent.is_line_empty()
5046 && target_indent.raw_len() < next_line_indent.raw_len()
5047 {
5048 target_indent = next_line_indent;
5049 target_row.0 += 1;
5050 }
5051 }
5052
5053 const SEARCH_ROW_LIMIT: u32 = 25000;
5054 const SEARCH_WHITESPACE_ROW_LIMIT: u32 = 2500;
5055 const YIELD_INTERVAL: u32 = 100;
5056
5057 let mut accessed_row_counter = 0;
5058
5059 // If there is a blank line at the current row, search for the next non indented lines
5060 if target_indent.is_line_empty() {
5061 let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT));
5062 let end =
5063 MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_WHITESPACE_ROW_LIMIT));
5064
5065 let mut non_empty_line_above = None;
5066 for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5067 if row < start {
5068 break;
5069 }
5070 accessed_row_counter += 1;
5071 if accessed_row_counter == YIELD_INTERVAL {
5072 accessed_row_counter = 0;
5073 yield_now().await;
5074 }
5075 if !indent.is_line_empty() {
5076 non_empty_line_above = Some((row, indent));
5077 break;
5078 }
5079 }
5080
5081 let mut non_empty_line_below = None;
5082 for (row, indent, _) in self.line_indents(target_row, |_| true) {
5083 if row > end {
5084 break;
5085 }
5086 accessed_row_counter += 1;
5087 if accessed_row_counter == YIELD_INTERVAL {
5088 accessed_row_counter = 0;
5089 yield_now().await;
5090 }
5091 if !indent.is_line_empty() {
5092 non_empty_line_below = Some((row, indent));
5093 break;
5094 }
5095 }
5096
5097 let (row, indent) = match (non_empty_line_above, non_empty_line_below) {
5098 (Some((above_row, above_indent)), Some((below_row, below_indent))) => {
5099 if above_indent.raw_len() >= below_indent.raw_len() {
5100 (above_row, above_indent)
5101 } else {
5102 (below_row, below_indent)
5103 }
5104 }
5105 (Some(above), None) => above,
5106 (None, Some(below)) => below,
5107 _ => return None,
5108 };
5109
5110 target_indent = indent;
5111 target_row = row;
5112 }
5113
5114 let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_ROW_LIMIT));
5115 let end = MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_ROW_LIMIT));
5116
5117 let mut start_indent = None;
5118 for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5119 if row < start {
5120 break;
5121 }
5122 accessed_row_counter += 1;
5123 if accessed_row_counter == YIELD_INTERVAL {
5124 accessed_row_counter = 0;
5125 yield_now().await;
5126 }
5127 if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5128 start_indent = Some((row, indent));
5129 break;
5130 }
5131 }
5132 let (start_row, start_indent_size) = start_indent?;
5133
5134 let mut end_indent = (end, None);
5135 for (row, indent, _) in self.line_indents(target_row, |_| true) {
5136 if row > end {
5137 break;
5138 }
5139 accessed_row_counter += 1;
5140 if accessed_row_counter == YIELD_INTERVAL {
5141 accessed_row_counter = 0;
5142 yield_now().await;
5143 }
5144 if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5145 end_indent = (MultiBufferRow(row.0.saturating_sub(1)), Some(indent));
5146 break;
5147 }
5148 }
5149 let (end_row, end_indent_size) = end_indent;
5150
5151 let indent = if let Some(end_indent_size) = end_indent_size {
5152 if start_indent_size.raw_len() > end_indent_size.raw_len() {
5153 start_indent_size
5154 } else {
5155 end_indent_size
5156 }
5157 } else {
5158 start_indent_size
5159 };
5160
5161 Some((start_row..end_row, indent))
5162 }
5163
5164 pub fn indent_guides_in_range<T: ToPoint>(
5165 &self,
5166 range: Range<T>,
5167 ignore_disabled_for_language: bool,
5168 cx: &App,
5169 ) -> impl Iterator<Item = IndentGuide> {
5170 let range = range.start.to_point(self)..range.end.to_point(self);
5171 let start_row = MultiBufferRow(range.start.row);
5172 let end_row = MultiBufferRow(range.end.row);
5173
5174 let mut row_indents = self.line_indents(start_row, |buffer| {
5175 let settings =
5176 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx);
5177 settings.indent_guides.enabled || ignore_disabled_for_language
5178 });
5179
5180 let mut result = Vec::new();
5181 let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
5182
5183 let mut prev_settings = None;
5184 while let Some((first_row, mut line_indent, buffer)) = row_indents.next() {
5185 if first_row > end_row {
5186 break;
5187 }
5188 let current_depth = indent_stack.len() as u32;
5189
5190 // Avoid retrieving the language settings repeatedly for every buffer row.
5191 if let Some((prev_buffer_id, _)) = &prev_settings
5192 && prev_buffer_id != &buffer.remote_id()
5193 {
5194 prev_settings.take();
5195 }
5196 let settings = &prev_settings
5197 .get_or_insert_with(|| {
5198 (
5199 buffer.remote_id(),
5200 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx),
5201 )
5202 })
5203 .1;
5204 let tab_size = settings.tab_size.get();
5205
5206 // When encountering empty, continue until found useful line indent
5207 // then add to the indent stack with the depth found
5208 let mut found_indent = false;
5209 let mut last_row = first_row;
5210 if line_indent.is_line_blank() {
5211 while !found_indent {
5212 let Some((target_row, new_line_indent, _)) = row_indents.next() else {
5213 break;
5214 };
5215 const TRAILING_ROW_SEARCH_LIMIT: u32 = 25;
5216 if target_row > MultiBufferRow(end_row.0 + TRAILING_ROW_SEARCH_LIMIT) {
5217 break;
5218 }
5219
5220 if new_line_indent.is_line_blank() {
5221 continue;
5222 }
5223 last_row = target_row.min(end_row);
5224 line_indent = new_line_indent;
5225 found_indent = true;
5226 break;
5227 }
5228 } else {
5229 found_indent = true
5230 }
5231
5232 let depth = if found_indent {
5233 line_indent.len(tab_size) / tab_size
5234 } else {
5235 0
5236 };
5237
5238 match depth.cmp(¤t_depth) {
5239 cmp::Ordering::Less => {
5240 for _ in 0..(current_depth - depth) {
5241 let mut indent = indent_stack.pop().unwrap();
5242 if last_row != first_row {
5243 // In this case, we landed on an empty row, had to seek forward,
5244 // and discovered that the indent we where on is ending.
5245 // This means that the last display row must
5246 // be on line that ends this indent range, so we
5247 // should display the range up to the first non-empty line
5248 indent.end_row = MultiBufferRow(first_row.0.saturating_sub(1));
5249 }
5250
5251 result.push(indent)
5252 }
5253 }
5254 cmp::Ordering::Greater => {
5255 for next_depth in current_depth..depth {
5256 indent_stack.push(IndentGuide {
5257 buffer_id: buffer.remote_id(),
5258 start_row: first_row,
5259 end_row: last_row,
5260 depth: next_depth,
5261 tab_size,
5262 settings: settings.indent_guides.clone(),
5263 });
5264 }
5265 }
5266 _ => {}
5267 }
5268
5269 for indent in indent_stack.iter_mut() {
5270 indent.end_row = last_row;
5271 }
5272 }
5273
5274 result.extend(indent_stack);
5275 result.into_iter()
5276 }
5277
5278 pub fn trailing_excerpt_update_count(&self) -> usize {
5279 self.trailing_excerpt_update_count
5280 }
5281
5282 pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
5283 self.point_to_buffer_offset(point)
5284 .and_then(|(buffer, _)| buffer.file())
5285 }
5286
5287 pub fn language_at<T: ToOffset>(&self, point: T) -> Option<&Arc<Language>> {
5288 self.point_to_buffer_offset(point)
5289 .and_then(|(buffer, offset)| buffer.language_at(offset))
5290 }
5291
5292 fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
5293 self.excerpts
5294 .first()
5295 .map(|excerpt| &excerpt.buffer)
5296 .map(|buffer| {
5297 language_settings(
5298 buffer.language().map(|language| language.name()),
5299 buffer.file(),
5300 cx,
5301 )
5302 })
5303 .unwrap_or_else(move || self.language_settings_at(0, cx))
5304 }
5305
5306 pub fn language_settings_at<'a, T: ToOffset>(
5307 &'a self,
5308 point: T,
5309 cx: &'a App,
5310 ) -> Cow<'a, LanguageSettings> {
5311 let mut language = None;
5312 let mut file = None;
5313 if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
5314 language = buffer.language_at(offset);
5315 file = buffer.file();
5316 }
5317 language_settings(language.map(|l| l.name()), file, cx)
5318 }
5319
5320 pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
5321 self.point_to_buffer_offset(point)
5322 .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
5323 }
5324
5325 pub fn char_classifier_at<T: ToOffset>(&self, point: T) -> CharClassifier {
5326 self.point_to_buffer_offset(point)
5327 .map(|(buffer, offset)| buffer.char_classifier_at(offset))
5328 .unwrap_or_default()
5329 }
5330
5331 pub fn language_indent_size_at<T: ToOffset>(
5332 &self,
5333 position: T,
5334 cx: &App,
5335 ) -> Option<IndentSize> {
5336 let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
5337 Some(buffer_snapshot.language_indent_size_at(offset, cx))
5338 }
5339
5340 pub fn is_dirty(&self) -> bool {
5341 self.is_dirty
5342 }
5343
5344 pub fn has_deleted_file(&self) -> bool {
5345 self.has_deleted_file
5346 }
5347
5348 pub fn has_conflict(&self) -> bool {
5349 self.has_conflict
5350 }
5351
5352 pub fn has_diagnostics(&self) -> bool {
5353 self.excerpts
5354 .iter()
5355 .any(|excerpt| excerpt.buffer.has_diagnostics())
5356 }
5357
5358 pub fn diagnostic_group(
5359 &self,
5360 buffer_id: BufferId,
5361 group_id: usize,
5362 ) -> impl Iterator<Item = DiagnosticEntryRef<'_, Point>> + '_ {
5363 self.lift_buffer_metadata(Point::zero()..self.max_point(), move |buffer, range| {
5364 if buffer.remote_id() != buffer_id {
5365 return None;
5366 };
5367 Some(
5368 buffer
5369 .diagnostics_in_range(range, false)
5370 .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id)
5371 .map(move |DiagnosticEntryRef { diagnostic, range }| (range, diagnostic)),
5372 )
5373 })
5374 .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
5375 }
5376
5377 pub fn diagnostics_in_range<'a, T>(
5378 &'a self,
5379 range: Range<T>,
5380 ) -> impl Iterator<Item = DiagnosticEntryRef<'a, T>> + 'a
5381 where
5382 T: 'a
5383 + text::ToOffset
5384 + text::FromAnchor
5385 + TextDimension
5386 + Ord
5387 + Sub<T, Output = T>
5388 + fmt::Debug,
5389 {
5390 self.lift_buffer_metadata(range, move |buffer, buffer_range| {
5391 Some(
5392 buffer
5393 .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
5394 .map(|entry| (entry.range, entry.diagnostic)),
5395 )
5396 })
5397 .map(|(range, diagnostic, _)| DiagnosticEntryRef { diagnostic, range })
5398 }
5399
5400 pub fn diagnostics_with_buffer_ids_in_range<'a, T>(
5401 &'a self,
5402 range: Range<T>,
5403 ) -> impl Iterator<Item = (BufferId, DiagnosticEntryRef<'a, T>)> + 'a
5404 where
5405 T: 'a
5406 + text::ToOffset
5407 + text::FromAnchor
5408 + TextDimension
5409 + Ord
5410 + Sub<T, Output = T>
5411 + fmt::Debug,
5412 {
5413 self.lift_buffer_metadata(range, move |buffer, buffer_range| {
5414 Some(
5415 buffer
5416 .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
5417 .map(|entry| (entry.range, entry.diagnostic)),
5418 )
5419 })
5420 .map(|(range, diagnostic, b)| (b.buffer_id, DiagnosticEntryRef { diagnostic, range }))
5421 }
5422
5423 pub fn syntax_ancestor<T: ToOffset>(
5424 &self,
5425 range: Range<T>,
5426 ) -> Option<(tree_sitter::Node<'_>, Range<usize>)> {
5427 let range = range.start.to_offset(self)..range.end.to_offset(self);
5428 let mut excerpt = self.excerpt_containing(range.clone())?;
5429 let node = excerpt
5430 .buffer()
5431 .syntax_ancestor(excerpt.map_range_to_buffer(range))?;
5432 let node_range = node.byte_range();
5433 if !excerpt.contains_buffer_range(node_range.clone()) {
5434 return None;
5435 };
5436 Some((node, excerpt.map_range_from_buffer(node_range)))
5437 }
5438
5439 pub fn syntax_next_sibling<T: ToOffset>(
5440 &self,
5441 range: Range<T>,
5442 ) -> Option<tree_sitter::Node<'_>> {
5443 let range = range.start.to_offset(self)..range.end.to_offset(self);
5444 let mut excerpt = self.excerpt_containing(range.clone())?;
5445 excerpt
5446 .buffer()
5447 .syntax_next_sibling(excerpt.map_range_to_buffer(range))
5448 }
5449
5450 pub fn syntax_prev_sibling<T: ToOffset>(
5451 &self,
5452 range: Range<T>,
5453 ) -> Option<tree_sitter::Node<'_>> {
5454 let range = range.start.to_offset(self)..range.end.to_offset(self);
5455 let mut excerpt = self.excerpt_containing(range.clone())?;
5456 excerpt
5457 .buffer()
5458 .syntax_prev_sibling(excerpt.map_range_to_buffer(range))
5459 }
5460
5461 pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
5462 let (excerpt_id, _, buffer) = self.as_singleton()?;
5463 let outline = buffer.outline(theme);
5464 Some(Outline::new(
5465 outline
5466 .items
5467 .into_iter()
5468 .flat_map(|item| {
5469 Some(OutlineItem {
5470 depth: item.depth,
5471 range: self.anchor_range_in_excerpt(*excerpt_id, item.range)?,
5472 source_range_for_text: self
5473 .anchor_range_in_excerpt(*excerpt_id, item.source_range_for_text)?,
5474 text: item.text,
5475 highlight_ranges: item.highlight_ranges,
5476 name_ranges: item.name_ranges,
5477 body_range: item.body_range.and_then(|body_range| {
5478 self.anchor_range_in_excerpt(*excerpt_id, body_range)
5479 }),
5480 annotation_range: item.annotation_range.and_then(|annotation_range| {
5481 self.anchor_range_in_excerpt(*excerpt_id, annotation_range)
5482 }),
5483 })
5484 })
5485 .collect(),
5486 ))
5487 }
5488
5489 pub fn symbols_containing<T: ToOffset>(
5490 &self,
5491 offset: T,
5492 theme: Option<&SyntaxTheme>,
5493 ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {
5494 let anchor = self.anchor_before(offset);
5495 let excerpt_id = anchor.excerpt_id;
5496 let excerpt = self.excerpt(excerpt_id)?;
5497 let buffer_id = excerpt.buffer_id;
5498 Some((
5499 buffer_id,
5500 excerpt
5501 .buffer
5502 .symbols_containing(anchor.text_anchor, theme)
5503 .into_iter()
5504 .flat_map(|item| {
5505 Some(OutlineItem {
5506 depth: item.depth,
5507 source_range_for_text: Anchor::range_in_buffer(
5508 excerpt_id,
5509 buffer_id,
5510 item.source_range_for_text,
5511 ),
5512 range: Anchor::range_in_buffer(excerpt_id, buffer_id, item.range),
5513 text: item.text,
5514 highlight_ranges: item.highlight_ranges,
5515 name_ranges: item.name_ranges,
5516 body_range: item.body_range.map(|body_range| {
5517 Anchor::range_in_buffer(excerpt_id, buffer_id, body_range)
5518 }),
5519 annotation_range: item.annotation_range.map(|body_range| {
5520 Anchor::range_in_buffer(excerpt_id, buffer_id, body_range)
5521 }),
5522 })
5523 })
5524 .collect(),
5525 ))
5526 }
5527
5528 fn excerpt_locator_for_id(&self, id: ExcerptId) -> &Locator {
5529 if id == ExcerptId::min() {
5530 Locator::min_ref()
5531 } else if id == ExcerptId::max() {
5532 Locator::max_ref()
5533 } else {
5534 let (_, _, item) = self.excerpt_ids.find::<ExcerptId, _>((), &id, Bias::Left);
5535 if let Some(entry) = item
5536 && entry.id == id
5537 {
5538 return &entry.locator;
5539 }
5540 panic!("invalid excerpt id {id:?}")
5541 }
5542 }
5543
5544 /// Returns the locators referenced by the given excerpt IDs, sorted by locator.
5545 fn excerpt_locators_for_ids(
5546 &self,
5547 ids: impl IntoIterator<Item = ExcerptId>,
5548 ) -> SmallVec<[Locator; 1]> {
5549 let mut sorted_ids = ids.into_iter().collect::<SmallVec<[_; 1]>>();
5550 sorted_ids.sort_unstable();
5551 sorted_ids.dedup();
5552 let mut locators = SmallVec::new();
5553
5554 while sorted_ids.last() == Some(&ExcerptId::max()) {
5555 sorted_ids.pop();
5556 locators.push(Locator::max());
5557 }
5558
5559 let mut sorted_ids = sorted_ids.into_iter().peekable();
5560 locators.extend(
5561 sorted_ids
5562 .peeking_take_while(|excerpt| *excerpt == ExcerptId::min())
5563 .map(|_| Locator::min()),
5564 );
5565
5566 let mut cursor = self.excerpt_ids.cursor::<ExcerptId>(());
5567 for id in sorted_ids {
5568 if cursor.seek_forward(&id, Bias::Left) {
5569 locators.push(cursor.item().unwrap().locator.clone());
5570 } else {
5571 panic!("invalid excerpt id {:?}", id);
5572 }
5573 }
5574
5575 locators.sort_unstable();
5576 locators
5577 }
5578
5579 pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<BufferId> {
5580 Some(self.excerpt(excerpt_id)?.buffer_id)
5581 }
5582
5583 pub fn buffer_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&BufferSnapshot> {
5584 Some(&self.excerpt(excerpt_id)?.buffer)
5585 }
5586
5587 pub fn range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<Point>> {
5588 let mut cursor = self
5589 .excerpts
5590 .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(());
5591 let locator = self.excerpt_locator_for_id(excerpt_id);
5592 let mut sought_exact = cursor.seek(&Some(locator), Bias::Left);
5593 if cursor.item().is_none() && excerpt_id == ExcerptId::max() {
5594 sought_exact = true;
5595 cursor.prev();
5596 } else if excerpt_id == ExcerptId::min() {
5597 sought_exact = true;
5598 }
5599 if sought_exact {
5600 let start = cursor.start().1.clone();
5601 let end = cursor.end().1;
5602 let mut diff_transforms = self
5603 .diff_transforms
5604 .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(());
5605 diff_transforms.seek(&start, Bias::Left);
5606 let overshoot = start.0 - diff_transforms.start().0.0;
5607 let start = diff_transforms.start().1.0 + overshoot;
5608 diff_transforms.seek(&end, Bias::Right);
5609 let overshoot = end.0 - diff_transforms.start().0.0;
5610 let end = diff_transforms.start().1.0 + overshoot;
5611 Some(start..end)
5612 } else {
5613 None
5614 }
5615 }
5616
5617 fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> {
5618 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
5619 let locator = self.excerpt_locator_for_id(excerpt_id);
5620 cursor.seek(&Some(locator), Bias::Left);
5621 if let Some(excerpt) = cursor.item()
5622 && excerpt.id == excerpt_id
5623 {
5624 return Some(excerpt);
5625 } else if cursor.item().is_none() && excerpt_id == ExcerptId::max() {
5626 cursor.prev();
5627 return cursor.item();
5628 }
5629 None
5630 }
5631
5632 /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
5633 pub fn excerpt_containing<T: ToOffset>(
5634 &self,
5635 range: Range<T>,
5636 ) -> Option<MultiBufferExcerpt<'_>> {
5637 let range = range.start.to_offset(self)..range.end.to_offset(self);
5638 let mut cursor = self.cursor::<usize>();
5639 cursor.seek(&range.start);
5640
5641 let start_excerpt = cursor.excerpt()?;
5642 if range.end != range.start {
5643 cursor.seek_forward(&range.end);
5644 if cursor.excerpt()?.id != start_excerpt.id {
5645 return None;
5646 }
5647 }
5648
5649 cursor.seek_to_start_of_current_excerpt();
5650 let region = cursor.region()?;
5651 let offset = region.range.start;
5652 let buffer_offset = start_excerpt.buffer_start_offset();
5653 let excerpt_offset = cursor.excerpts.start().clone();
5654 Some(MultiBufferExcerpt {
5655 diff_transforms: cursor.diff_transforms,
5656 excerpt: start_excerpt,
5657 offset,
5658 buffer_offset,
5659 excerpt_offset,
5660 })
5661 }
5662
5663 pub fn buffer_id_for_anchor(&self, anchor: Anchor) -> Option<BufferId> {
5664 if let Some(id) = anchor.buffer_id {
5665 return Some(id);
5666 }
5667 let excerpt = self.excerpt_containing(anchor..anchor)?;
5668 Some(excerpt.buffer_id())
5669 }
5670
5671 pub fn selections_in_range<'a>(
5672 &'a self,
5673 range: &'a Range<Anchor>,
5674 include_local: bool,
5675 ) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
5676 let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
5677 let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id);
5678 let end_locator = self.excerpt_locator_for_id(range.end.excerpt_id);
5679 cursor.seek(start_locator, Bias::Left);
5680 cursor
5681 .take_while(move |excerpt| excerpt.locator <= *end_locator)
5682 .flat_map(move |excerpt| {
5683 let mut query_range = excerpt.range.context.start..excerpt.range.context.end;
5684 if excerpt.id == range.start.excerpt_id {
5685 query_range.start = range.start.text_anchor;
5686 }
5687 if excerpt.id == range.end.excerpt_id {
5688 query_range.end = range.end.text_anchor;
5689 }
5690
5691 excerpt
5692 .buffer
5693 .selections_in_range(query_range, include_local)
5694 .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
5695 selections.map(move |selection| {
5696 let mut start =
5697 Anchor::in_buffer(excerpt.id, excerpt.buffer_id, selection.start);
5698 let mut end =
5699 Anchor::in_buffer(excerpt.id, excerpt.buffer_id, selection.end);
5700 if range.start.cmp(&start, self).is_gt() {
5701 start = range.start;
5702 }
5703 if range.end.cmp(&end, self).is_lt() {
5704 end = range.end;
5705 }
5706
5707 (
5708 replica_id,
5709 line_mode,
5710 cursor_shape,
5711 Selection {
5712 id: selection.id,
5713 start,
5714 end,
5715 reversed: selection.reversed,
5716 goal: selection.goal,
5717 },
5718 )
5719 })
5720 })
5721 })
5722 }
5723
5724 pub fn show_headers(&self) -> bool {
5725 self.show_headers
5726 }
5727
5728 pub fn diff_for_buffer_id(&self, buffer_id: BufferId) -> Option<&BufferDiffSnapshot> {
5729 self.diffs.get(&buffer_id)
5730 }
5731
5732 /// Visually annotates a position or range with the `Debug` representation of a value. The
5733 /// callsite of this function is used as a key - previous annotations will be removed.
5734 #[cfg(debug_assertions)]
5735 #[track_caller]
5736 pub fn debug<V, R>(&self, ranges: &R, value: V)
5737 where
5738 R: debug::ToMultiBufferDebugRanges,
5739 V: std::fmt::Debug,
5740 {
5741 self.debug_with_key(std::panic::Location::caller(), ranges, value);
5742 }
5743
5744 /// Visually annotates a position or range with the `Debug` representation of a value. Previous
5745 /// debug annotations with the same key will be removed. The key is also used to determine the
5746 /// annotation's color.
5747 #[cfg(debug_assertions)]
5748 #[track_caller]
5749 pub fn debug_with_key<K, R, V>(&self, key: &K, ranges: &R, value: V)
5750 where
5751 K: std::hash::Hash + 'static,
5752 R: debug::ToMultiBufferDebugRanges,
5753 V: std::fmt::Debug,
5754 {
5755 let text_ranges = ranges
5756 .to_multi_buffer_debug_ranges(self)
5757 .into_iter()
5758 .flat_map(|range| {
5759 self.range_to_buffer_ranges(range).into_iter().map(
5760 |(buffer, range, _excerpt_id)| {
5761 buffer.anchor_after(range.start)..buffer.anchor_before(range.end)
5762 },
5763 )
5764 })
5765 .collect();
5766 text::debug::GlobalDebugRanges::with_locked(|debug_ranges| {
5767 debug_ranges.insert(key, text_ranges, format!("{value:?}").into())
5768 });
5769 }
5770}
5771
5772#[cfg(any(test, feature = "test-support"))]
5773impl MultiBufferSnapshot {
5774 pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
5775 let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
5776 let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
5777 start..end
5778 }
5779
5780 #[cfg(any(test, feature = "test-support"))]
5781 fn check_invariants(&self) {
5782 let excerpts = self.excerpts.items(());
5783 let excerpt_ids = self.excerpt_ids.items(());
5784
5785 for (ix, excerpt) in excerpts.iter().enumerate() {
5786 if ix == 0 {
5787 if excerpt.locator <= Locator::min() {
5788 panic!("invalid first excerpt locator {:?}", excerpt.locator);
5789 }
5790 } else if excerpt.locator <= excerpts[ix - 1].locator {
5791 panic!("excerpts are out-of-order: {:?}", excerpts);
5792 }
5793 }
5794
5795 for (ix, entry) in excerpt_ids.iter().enumerate() {
5796 if ix == 0 {
5797 if entry.id.cmp(&ExcerptId::min(), self).is_le() {
5798 panic!("invalid first excerpt id {:?}", entry.id);
5799 }
5800 } else if entry.id <= excerpt_ids[ix - 1].id {
5801 panic!("excerpt ids are out-of-order: {:?}", excerpt_ids);
5802 }
5803 }
5804
5805 if self.diff_transforms.summary().input != self.excerpts.summary().text {
5806 panic!(
5807 "incorrect input summary. expected {:?}, got {:?}. transforms: {:+?}",
5808 self.excerpts.summary().text.len,
5809 self.diff_transforms.summary().input,
5810 self.diff_transforms.items(()),
5811 );
5812 }
5813
5814 let mut prev_transform: Option<&DiffTransform> = None;
5815 for item in self.diff_transforms.iter() {
5816 if let DiffTransform::BufferContent {
5817 summary,
5818 inserted_hunk_info,
5819 } = item
5820 {
5821 if let Some(DiffTransform::BufferContent {
5822 inserted_hunk_info: prev_inserted_hunk_info,
5823 ..
5824 }) = prev_transform
5825 && *inserted_hunk_info == *prev_inserted_hunk_info
5826 {
5827 panic!(
5828 "multiple adjacent buffer content transforms with is_inserted_hunk = {inserted_hunk_info:?}. transforms: {:+?}",
5829 self.diff_transforms.items(())
5830 );
5831 }
5832 if summary.len == 0 && !self.is_empty() {
5833 panic!("empty buffer content transform");
5834 }
5835 }
5836 prev_transform = Some(item);
5837 }
5838 }
5839}
5840
5841impl<'a, D> MultiBufferCursor<'a, D>
5842where
5843 D: TextDimension + Ord + Sub<D, Output = D>,
5844{
5845 fn seek(&mut self, position: &D) {
5846 self.cached_region.take();
5847 self.diff_transforms
5848 .seek(&OutputDimension(*position), Bias::Right);
5849 if self.diff_transforms.item().is_none()
5850 && *position == self.diff_transforms.start().output_dimension.0
5851 {
5852 self.diff_transforms.prev();
5853 }
5854
5855 let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0;
5856 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
5857 let overshoot = *position - self.diff_transforms.start().output_dimension.0;
5858 excerpt_position.add_assign(&overshoot);
5859 }
5860
5861 self.excerpts
5862 .seek(&ExcerptDimension(excerpt_position), Bias::Right);
5863 if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 {
5864 self.excerpts.prev();
5865 }
5866 }
5867
5868 fn seek_forward(&mut self, position: &D) {
5869 self.cached_region.take();
5870 self.diff_transforms
5871 .seek_forward(&OutputDimension(*position), Bias::Right);
5872 if self.diff_transforms.item().is_none()
5873 && *position == self.diff_transforms.start().output_dimension.0
5874 {
5875 self.diff_transforms.prev();
5876 }
5877
5878 let overshoot = *position - self.diff_transforms.start().output_dimension.0;
5879 let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0;
5880 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
5881 excerpt_position.add_assign(&overshoot);
5882 }
5883
5884 self.excerpts
5885 .seek_forward(&ExcerptDimension(excerpt_position), Bias::Right);
5886 if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 {
5887 self.excerpts.prev();
5888 }
5889 }
5890
5891 fn next_excerpt(&mut self) {
5892 self.excerpts.next();
5893 self.seek_to_start_of_current_excerpt();
5894 }
5895
5896 fn prev_excerpt(&mut self) {
5897 self.excerpts.prev();
5898 self.seek_to_start_of_current_excerpt();
5899 }
5900
5901 fn seek_to_start_of_current_excerpt(&mut self) {
5902 self.cached_region.take();
5903 self.diff_transforms.seek(self.excerpts.start(), Bias::Left);
5904 if self.diff_transforms.end().excerpt_dimension == *self.excerpts.start()
5905 && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
5906 && self.diff_transforms.next_item().is_some()
5907 {
5908 self.diff_transforms.next();
5909 }
5910 }
5911
5912 fn next(&mut self) {
5913 self.cached_region.take();
5914 match self
5915 .diff_transforms
5916 .end()
5917 .excerpt_dimension
5918 .cmp(&self.excerpts.end())
5919 {
5920 cmp::Ordering::Less => self.diff_transforms.next(),
5921 cmp::Ordering::Greater => self.excerpts.next(),
5922 cmp::Ordering::Equal => {
5923 self.diff_transforms.next();
5924 if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
5925 || self.diff_transforms.item().is_none()
5926 {
5927 self.excerpts.next();
5928 } else if let Some(DiffTransform::DeletedHunk { hunk_info, .. }) =
5929 self.diff_transforms.item()
5930 && self
5931 .excerpts
5932 .item()
5933 .is_some_and(|excerpt| excerpt.id != hunk_info.excerpt_id)
5934 {
5935 self.excerpts.next();
5936 }
5937 }
5938 }
5939 }
5940
5941 fn prev(&mut self) {
5942 self.cached_region.take();
5943 match self
5944 .diff_transforms
5945 .start()
5946 .excerpt_dimension
5947 .cmp(self.excerpts.start())
5948 {
5949 cmp::Ordering::Less => self.excerpts.prev(),
5950 cmp::Ordering::Greater => self.diff_transforms.prev(),
5951 cmp::Ordering::Equal => {
5952 self.diff_transforms.prev();
5953 if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
5954 || self.diff_transforms.item().is_none()
5955 {
5956 self.excerpts.prev();
5957 }
5958 }
5959 }
5960 }
5961
5962 fn region(&mut self) -> Option<MultiBufferRegion<'a, D>> {
5963 if self.cached_region.is_none() {
5964 self.cached_region = self.build_region();
5965 }
5966 self.cached_region.clone()
5967 }
5968
5969 fn is_at_start_of_excerpt(&mut self) -> bool {
5970 if self.diff_transforms.start().excerpt_dimension > *self.excerpts.start() {
5971 return false;
5972 } else if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
5973 return true;
5974 }
5975
5976 self.diff_transforms.prev();
5977 let prev_transform = self.diff_transforms.item();
5978 self.diff_transforms.next();
5979
5980 prev_transform.is_none_or(|next_transform| {
5981 matches!(next_transform, DiffTransform::BufferContent { .. })
5982 })
5983 }
5984
5985 fn is_at_end_of_excerpt(&mut self) -> bool {
5986 if self.diff_transforms.end().excerpt_dimension < self.excerpts.end() {
5987 return false;
5988 } else if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
5989 || self.diff_transforms.item().is_none()
5990 {
5991 return true;
5992 }
5993
5994 let next_transform = self.diff_transforms.next_item();
5995 next_transform.is_none_or(|next_transform| match next_transform {
5996 DiffTransform::BufferContent { .. } => true,
5997 DiffTransform::DeletedHunk { hunk_info, .. } => self
5998 .excerpts
5999 .item()
6000 .is_some_and(|excerpt| excerpt.id != hunk_info.excerpt_id),
6001 })
6002 }
6003
6004 fn main_buffer_position(&self) -> Option<D> {
6005 let excerpt = self.excerpts.item()?;
6006 let buffer = &excerpt.buffer;
6007 let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
6008 let mut buffer_start = buffer_context_start;
6009 let overshoot = self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0;
6010 buffer_start.add_assign(&overshoot);
6011 Some(buffer_start)
6012 }
6013
6014 fn build_region(&self) -> Option<MultiBufferRegion<'a, D>> {
6015 let excerpt = self.excerpts.item()?;
6016 match self.diff_transforms.item()? {
6017 DiffTransform::DeletedHunk {
6018 buffer_id,
6019 base_text_byte_range,
6020 has_trailing_newline,
6021 hunk_info,
6022 ..
6023 } => {
6024 let diff = self.diffs.get(buffer_id)?;
6025 let buffer = diff.base_text();
6026 let mut rope_cursor = buffer.as_rope().cursor(0);
6027 let buffer_start = rope_cursor.summary::<D>(base_text_byte_range.start);
6028 let buffer_range_len = rope_cursor.summary::<D>(base_text_byte_range.end);
6029 let mut buffer_end = buffer_start;
6030 buffer_end.add_assign(&buffer_range_len);
6031 let start = self.diff_transforms.start().output_dimension.0;
6032 let end = self.diff_transforms.end().output_dimension.0;
6033 Some(MultiBufferRegion {
6034 buffer,
6035 excerpt,
6036 has_trailing_newline: *has_trailing_newline,
6037 is_main_buffer: false,
6038 diff_hunk_status: Some(DiffHunkStatus::deleted(
6039 hunk_info.hunk_secondary_status,
6040 )),
6041 buffer_range: buffer_start..buffer_end,
6042 range: start..end,
6043 })
6044 }
6045 DiffTransform::BufferContent {
6046 inserted_hunk_info, ..
6047 } => {
6048 let buffer = &excerpt.buffer;
6049 let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
6050
6051 let mut start = self.diff_transforms.start().output_dimension.0;
6052 let mut buffer_start = buffer_context_start;
6053 if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
6054 let overshoot =
6055 self.excerpts.start().0 - self.diff_transforms.start().excerpt_dimension.0;
6056 start.add_assign(&overshoot);
6057 } else {
6058 let overshoot =
6059 self.diff_transforms.start().excerpt_dimension.0 - self.excerpts.start().0;
6060 buffer_start.add_assign(&overshoot);
6061 }
6062
6063 let mut end;
6064 let mut buffer_end;
6065 let has_trailing_newline;
6066 if self.diff_transforms.end().excerpt_dimension.0 < self.excerpts.end().0 {
6067 let overshoot =
6068 self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0;
6069 end = self.diff_transforms.end().output_dimension.0;
6070 buffer_end = buffer_context_start;
6071 buffer_end.add_assign(&overshoot);
6072 has_trailing_newline = false;
6073 } else {
6074 let overshoot =
6075 self.excerpts.end().0 - self.diff_transforms.start().excerpt_dimension.0;
6076 end = self.diff_transforms.start().output_dimension.0;
6077 end.add_assign(&overshoot);
6078 buffer_end = excerpt.range.context.end.summary::<D>(buffer);
6079 has_trailing_newline = excerpt.has_trailing_newline;
6080 };
6081
6082 Some(MultiBufferRegion {
6083 buffer,
6084 excerpt,
6085 has_trailing_newline,
6086 is_main_buffer: true,
6087 diff_hunk_status: inserted_hunk_info
6088 .map(|info| DiffHunkStatus::added(info.hunk_secondary_status)),
6089 buffer_range: buffer_start..buffer_end,
6090 range: start..end,
6091 })
6092 }
6093 }
6094 }
6095
6096 fn excerpt(&self) -> Option<&'a Excerpt> {
6097 self.excerpts.item()
6098 }
6099}
6100
6101impl Excerpt {
6102 fn new(
6103 id: ExcerptId,
6104 locator: Locator,
6105 buffer_id: BufferId,
6106 buffer: BufferSnapshot,
6107 range: ExcerptRange<text::Anchor>,
6108 has_trailing_newline: bool,
6109 ) -> Self {
6110 Excerpt {
6111 id,
6112 locator,
6113 max_buffer_row: range.context.end.to_point(&buffer).row,
6114 text_summary: buffer
6115 .text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
6116 buffer_id,
6117 buffer,
6118 range,
6119 has_trailing_newline,
6120 }
6121 }
6122
6123 fn chunks_in_range(&self, range: Range<usize>, language_aware: bool) -> ExcerptChunks<'_> {
6124 let content_start = self.range.context.start.to_offset(&self.buffer);
6125 let chunks_start = content_start + range.start;
6126 let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
6127
6128 let has_footer = self.has_trailing_newline
6129 && range.start <= self.text_summary.len
6130 && range.end > self.text_summary.len;
6131
6132 let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware);
6133
6134 ExcerptChunks {
6135 excerpt_id: self.id,
6136 content_chunks,
6137 has_footer,
6138 }
6139 }
6140
6141 fn seek_chunks(&self, excerpt_chunks: &mut ExcerptChunks, range: Range<usize>) {
6142 let content_start = self.range.context.start.to_offset(&self.buffer);
6143 let chunks_start = content_start + range.start;
6144 let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
6145 excerpt_chunks.content_chunks.seek(chunks_start..chunks_end);
6146 excerpt_chunks.has_footer = self.has_trailing_newline
6147 && range.start <= self.text_summary.len
6148 && range.end > self.text_summary.len;
6149 }
6150
6151 fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
6152 if text_anchor
6153 .cmp(&self.range.context.start, &self.buffer)
6154 .is_lt()
6155 {
6156 self.range.context.start
6157 } else if text_anchor
6158 .cmp(&self.range.context.end, &self.buffer)
6159 .is_gt()
6160 {
6161 self.range.context.end
6162 } else {
6163 text_anchor
6164 }
6165 }
6166
6167 fn contains(&self, anchor: &Anchor) -> bool {
6168 (anchor.buffer_id == None || anchor.buffer_id == Some(self.buffer_id))
6169 && self
6170 .range
6171 .context
6172 .start
6173 .cmp(&anchor.text_anchor, &self.buffer)
6174 .is_le()
6175 && self
6176 .range
6177 .context
6178 .end
6179 .cmp(&anchor.text_anchor, &self.buffer)
6180 .is_ge()
6181 }
6182
6183 /// The [`Excerpt`]'s start offset in its [`Buffer`]
6184 fn buffer_start_offset(&self) -> usize {
6185 self.range.context.start.to_offset(&self.buffer)
6186 }
6187
6188 /// The [`Excerpt`]'s end offset in its [`Buffer`]
6189 fn buffer_end_offset(&self) -> usize {
6190 self.buffer_start_offset() + self.text_summary.len
6191 }
6192}
6193
6194impl<'a> MultiBufferExcerpt<'a> {
6195 pub fn id(&self) -> ExcerptId {
6196 self.excerpt.id
6197 }
6198
6199 pub fn buffer_id(&self) -> BufferId {
6200 self.excerpt.buffer_id
6201 }
6202
6203 pub fn start_anchor(&self) -> Anchor {
6204 Anchor::in_buffer(
6205 self.excerpt.id,
6206 self.excerpt.buffer_id,
6207 self.excerpt.range.context.start,
6208 )
6209 }
6210
6211 pub fn end_anchor(&self) -> Anchor {
6212 Anchor::in_buffer(
6213 self.excerpt.id,
6214 self.excerpt.buffer_id,
6215 self.excerpt.range.context.end,
6216 )
6217 }
6218
6219 pub fn buffer(&self) -> &'a BufferSnapshot {
6220 &self.excerpt.buffer
6221 }
6222
6223 pub fn buffer_range(&self) -> Range<usize> {
6224 self.buffer_offset
6225 ..self
6226 .excerpt
6227 .range
6228 .context
6229 .end
6230 .to_offset(&self.excerpt.buffer.text)
6231 }
6232
6233 pub fn start_offset(&self) -> usize {
6234 self.offset
6235 }
6236
6237 /// Maps an offset within the [`MultiBuffer`] to an offset within the [`Buffer`]
6238 pub fn map_offset_to_buffer(&mut self, offset: usize) -> usize {
6239 self.map_range_to_buffer(offset..offset).start
6240 }
6241
6242 /// Maps a range within the [`MultiBuffer`] to a range within the [`Buffer`]
6243 pub fn map_range_to_buffer(&mut self, range: Range<usize>) -> Range<usize> {
6244 self.diff_transforms
6245 .seek(&OutputDimension(range.start), Bias::Right);
6246 let start = self.map_offset_to_buffer_internal(range.start);
6247 let end = if range.end > range.start {
6248 self.diff_transforms
6249 .seek_forward(&OutputDimension(range.end), Bias::Right);
6250 self.map_offset_to_buffer_internal(range.end)
6251 } else {
6252 start
6253 };
6254 start..end
6255 }
6256
6257 fn map_offset_to_buffer_internal(&self, offset: usize) -> usize {
6258 let mut excerpt_offset = self.diff_transforms.start().excerpt_dimension.clone();
6259 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6260 excerpt_offset.0 += offset - self.diff_transforms.start().output_dimension.0;
6261 };
6262 let offset_in_excerpt = excerpt_offset.0.saturating_sub(self.excerpt_offset.0);
6263 self.buffer_offset + offset_in_excerpt
6264 }
6265
6266 /// Map an offset within the [`Buffer`] to an offset within the [`MultiBuffer`]
6267 pub fn map_offset_from_buffer(&mut self, buffer_offset: usize) -> usize {
6268 self.map_range_from_buffer(buffer_offset..buffer_offset)
6269 .start
6270 }
6271
6272 /// Map a range within the [`Buffer`] to a range within the [`MultiBuffer`]
6273 pub fn map_range_from_buffer(&mut self, buffer_range: Range<usize>) -> Range<usize> {
6274 if buffer_range.start < self.buffer_offset {
6275 log::warn!(
6276 "Attempting to map a range from a buffer offset that starts before the current buffer offset"
6277 );
6278 return buffer_range;
6279 }
6280 let overshoot = buffer_range.start - self.buffer_offset;
6281 let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot);
6282 self.diff_transforms.seek(&excerpt_offset, Bias::Right);
6283 if excerpt_offset.0 < self.diff_transforms.start().excerpt_dimension.0 {
6284 log::warn!(
6285 "Attempting to map a range from a buffer offset that starts before the current buffer offset"
6286 );
6287 return buffer_range;
6288 }
6289 let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0;
6290 let start = self.diff_transforms.start().output_dimension.0 + overshoot;
6291
6292 let end = if buffer_range.end > buffer_range.start {
6293 let overshoot = buffer_range.end - self.buffer_offset;
6294 let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot);
6295 self.diff_transforms
6296 .seek_forward(&excerpt_offset, Bias::Right);
6297 let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0;
6298 self.diff_transforms.start().output_dimension.0 + overshoot
6299 } else {
6300 start
6301 };
6302
6303 start..end
6304 }
6305
6306 /// Returns true if the entirety of the given range is in the buffer's excerpt
6307 pub fn contains_buffer_range(&self, range: Range<usize>) -> bool {
6308 range.start >= self.excerpt.buffer_start_offset()
6309 && range.end <= self.excerpt.buffer_end_offset()
6310 }
6311
6312 pub fn max_buffer_row(&self) -> u32 {
6313 self.excerpt.max_buffer_row
6314 }
6315}
6316
6317impl ExcerptId {
6318 pub fn min() -> Self {
6319 Self(0)
6320 }
6321
6322 pub fn max() -> Self {
6323 Self(u32::MAX)
6324 }
6325
6326 pub fn to_proto(self) -> u64 {
6327 self.0 as _
6328 }
6329
6330 pub fn from_proto(proto: u64) -> Self {
6331 Self(proto as _)
6332 }
6333
6334 pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
6335 let a = snapshot.excerpt_locator_for_id(*self);
6336 let b = snapshot.excerpt_locator_for_id(*other);
6337 a.cmp(b).then_with(|| self.0.cmp(&other.0))
6338 }
6339}
6340
6341impl From<ExcerptId> for usize {
6342 fn from(val: ExcerptId) -> Self {
6343 val.0 as usize
6344 }
6345}
6346
6347impl fmt::Debug for Excerpt {
6348 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
6349 f.debug_struct("Excerpt")
6350 .field("id", &self.id)
6351 .field("locator", &self.locator)
6352 .field("buffer_id", &self.buffer_id)
6353 .field("range", &self.range)
6354 .field("text_summary", &self.text_summary)
6355 .field("has_trailing_newline", &self.has_trailing_newline)
6356 .finish()
6357 }
6358}
6359
6360impl sum_tree::Item for Excerpt {
6361 type Summary = ExcerptSummary;
6362
6363 fn summary(&self, _cx: ()) -> Self::Summary {
6364 let mut text = self.text_summary;
6365 if self.has_trailing_newline {
6366 text += TextSummary::from("\n");
6367 }
6368 ExcerptSummary {
6369 excerpt_id: self.id,
6370 excerpt_locator: self.locator.clone(),
6371 widest_line_number: self.max_buffer_row,
6372 text,
6373 }
6374 }
6375}
6376
6377impl sum_tree::Item for ExcerptIdMapping {
6378 type Summary = ExcerptId;
6379
6380 fn summary(&self, _cx: ()) -> Self::Summary {
6381 self.id
6382 }
6383}
6384
6385impl sum_tree::KeyedItem for ExcerptIdMapping {
6386 type Key = ExcerptId;
6387
6388 fn key(&self) -> Self::Key {
6389 self.id
6390 }
6391}
6392
6393impl DiffTransform {
6394 fn hunk_info(&self) -> Option<DiffTransformHunkInfo> {
6395 match self {
6396 DiffTransform::DeletedHunk { hunk_info, .. } => Some(*hunk_info),
6397 DiffTransform::BufferContent {
6398 inserted_hunk_info, ..
6399 } => *inserted_hunk_info,
6400 }
6401 }
6402}
6403
6404impl sum_tree::Item for DiffTransform {
6405 type Summary = DiffTransformSummary;
6406
6407 fn summary(&self, _: <Self::Summary as sum_tree::Summary>::Context<'_>) -> Self::Summary {
6408 match self {
6409 DiffTransform::BufferContent { summary, .. } => DiffTransformSummary {
6410 input: *summary,
6411 output: *summary,
6412 },
6413 DiffTransform::DeletedHunk { summary, .. } => DiffTransformSummary {
6414 input: TextSummary::default(),
6415 output: *summary,
6416 },
6417 }
6418 }
6419}
6420
6421impl DiffTransformSummary {
6422 fn excerpt_len(&self) -> ExcerptOffset {
6423 ExcerptOffset::new(self.input.len)
6424 }
6425}
6426
6427impl sum_tree::ContextLessSummary for DiffTransformSummary {
6428 fn zero() -> Self {
6429 DiffTransformSummary {
6430 input: TextSummary::default(),
6431 output: TextSummary::default(),
6432 }
6433 }
6434
6435 fn add_summary(&mut self, other: &Self) {
6436 self.input += other.input;
6437 self.output += other.output;
6438 }
6439}
6440
6441impl sum_tree::ContextLessSummary for ExcerptId {
6442 fn zero() -> Self {
6443 Self(0)
6444 }
6445
6446 fn add_summary(&mut self, summary: &Self) {
6447 *self = cmp::max(*self, *summary);
6448 }
6449}
6450
6451impl sum_tree::ContextLessSummary for ExcerptSummary {
6452 fn zero() -> Self {
6453 Self::default()
6454 }
6455
6456 fn add_summary(&mut self, summary: &Self) {
6457 debug_assert!(summary.excerpt_locator > self.excerpt_locator);
6458 self.excerpt_locator = summary.excerpt_locator.clone();
6459 Summary::add_summary(&mut self.text, &summary.text, ());
6460 self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number);
6461 }
6462}
6463
6464impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptOffset {
6465 fn zero(_cx: ()) -> Self {
6466 Default::default()
6467 }
6468
6469 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
6470 self.value += summary.text.len;
6471 }
6472}
6473
6474impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for ExcerptOffset {
6475 fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering {
6476 Ord::cmp(&self.value, &cursor_location.text.len)
6477 }
6478}
6479
6480impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
6481 fn cmp(&self, cursor_location: &Option<&'a Locator>, _: ()) -> cmp::Ordering {
6482 Ord::cmp(&Some(self), cursor_location)
6483 }
6484}
6485
6486impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for Locator {
6487 fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering {
6488 Ord::cmp(self, &cursor_location.excerpt_locator)
6489 }
6490}
6491
6492impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptPoint {
6493 fn zero(_cx: ()) -> Self {
6494 Default::default()
6495 }
6496
6497 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
6498 self.value += summary.text.lines;
6499 }
6500}
6501
6502impl<'a, D: TextDimension + Default> sum_tree::Dimension<'a, ExcerptSummary>
6503 for ExcerptDimension<D>
6504{
6505 fn zero(_: ()) -> Self {
6506 ExcerptDimension(D::default())
6507 }
6508
6509 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
6510 self.0.add_assign(&D::from_text_summary(&summary.text))
6511 }
6512}
6513
6514impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> {
6515 fn zero(_cx: ()) -> Self {
6516 Default::default()
6517 }
6518
6519 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
6520 *self = Some(&summary.excerpt_locator);
6521 }
6522}
6523
6524impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptId> {
6525 fn zero(_cx: ()) -> Self {
6526 Default::default()
6527 }
6528
6529 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
6530 *self = Some(summary.excerpt_id);
6531 }
6532}
6533
6534#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
6535struct ExcerptDimension<T>(T);
6536
6537#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
6538struct OutputDimension<T>(T);
6539
6540impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptOffset {
6541 fn zero(_: ()) -> Self {
6542 ExcerptOffset::new(0)
6543 }
6544
6545 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6546 self.value += summary.input.len;
6547 }
6548}
6549
6550impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptPoint {
6551 fn zero(_: ()) -> Self {
6552 ExcerptPoint::new(0, 0)
6553 }
6554
6555 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6556 self.value += summary.input.lines;
6557 }
6558}
6559
6560impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransformSummary>
6561 for ExcerptDimension<D>
6562{
6563 fn cmp(&self, cursor_location: &DiffTransformSummary, _: ()) -> cmp::Ordering {
6564 Ord::cmp(&self.0, &D::from_text_summary(&cursor_location.input))
6565 }
6566}
6567
6568impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms<D>>
6569 for ExcerptDimension<D>
6570{
6571 fn cmp(&self, cursor_location: &DiffTransforms<D>, _: ()) -> cmp::Ordering {
6572 Ord::cmp(&self.0, &cursor_location.excerpt_dimension.0)
6573 }
6574}
6575
6576impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptDimension<D> {
6577 fn zero(_: ()) -> Self {
6578 ExcerptDimension(D::default())
6579 }
6580
6581 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6582 self.0.add_assign(&D::from_text_summary(&summary.input))
6583 }
6584}
6585
6586impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms<D>>
6587 for OutputDimension<D>
6588{
6589 fn cmp(&self, cursor_location: &DiffTransforms<D>, _: ()) -> cmp::Ordering {
6590 Ord::cmp(&self.0, &cursor_location.output_dimension.0)
6591 }
6592}
6593
6594impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for OutputDimension<D> {
6595 fn zero(_: ()) -> Self {
6596 OutputDimension(D::default())
6597 }
6598
6599 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6600 self.0.add_assign(&D::from_text_summary(&summary.output))
6601 }
6602}
6603
6604impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for TextSummary {
6605 fn zero(_: ()) -> Self {
6606 TextSummary::default()
6607 }
6608
6609 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6610 *self += summary.output
6611 }
6612}
6613
6614impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for usize {
6615 fn zero(_: ()) -> Self {
6616 0
6617 }
6618
6619 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6620 *self += summary.output.len
6621 }
6622}
6623
6624impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for Point {
6625 fn zero(_: ()) -> Self {
6626 Point::new(0, 0)
6627 }
6628
6629 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
6630 *self += summary.output.lines
6631 }
6632}
6633
6634impl MultiBufferRows<'_> {
6635 pub fn seek(&mut self, MultiBufferRow(row): MultiBufferRow) {
6636 self.point = Point::new(row, 0);
6637 self.cursor.seek(&self.point);
6638 }
6639}
6640
6641impl Iterator for MultiBufferRows<'_> {
6642 type Item = RowInfo;
6643
6644 fn next(&mut self) -> Option<Self::Item> {
6645 if self.is_empty && self.point.row == 0 {
6646 self.point += Point::new(1, 0);
6647 return Some(RowInfo {
6648 buffer_id: None,
6649 buffer_row: Some(0),
6650 multibuffer_row: Some(MultiBufferRow(0)),
6651 diff_status: None,
6652 expand_info: None,
6653 });
6654 }
6655
6656 let mut region = self.cursor.region()?;
6657 while self.point >= region.range.end {
6658 self.cursor.next();
6659 if let Some(next_region) = self.cursor.region() {
6660 region = next_region;
6661 } else if self.point == self.cursor.diff_transforms.end().output_dimension.0 {
6662 let multibuffer_row = MultiBufferRow(self.point.row);
6663 let last_excerpt = self
6664 .cursor
6665 .excerpts
6666 .item()
6667 .or(self.cursor.excerpts.prev_item())?;
6668 let last_row = last_excerpt
6669 .range
6670 .context
6671 .end
6672 .to_point(&last_excerpt.buffer)
6673 .row;
6674
6675 let first_row = last_excerpt
6676 .range
6677 .context
6678 .start
6679 .to_point(&last_excerpt.buffer)
6680 .row;
6681
6682 let expand_info = if self.is_singleton {
6683 None
6684 } else {
6685 let needs_expand_up = first_row == last_row
6686 && last_row > 0
6687 && !region.diff_hunk_status.is_some_and(|d| d.is_deleted());
6688 let needs_expand_down = last_row < last_excerpt.buffer.max_point().row;
6689
6690 if needs_expand_up && needs_expand_down {
6691 Some(ExpandExcerptDirection::UpAndDown)
6692 } else if needs_expand_up {
6693 Some(ExpandExcerptDirection::Up)
6694 } else if needs_expand_down {
6695 Some(ExpandExcerptDirection::Down)
6696 } else {
6697 None
6698 }
6699 .map(|direction| ExpandInfo {
6700 direction,
6701 excerpt_id: last_excerpt.id,
6702 })
6703 };
6704 self.point += Point::new(1, 0);
6705 return Some(RowInfo {
6706 buffer_id: Some(last_excerpt.buffer_id),
6707 buffer_row: Some(last_row),
6708 multibuffer_row: Some(multibuffer_row),
6709 diff_status: None,
6710 expand_info,
6711 });
6712 } else {
6713 return None;
6714 };
6715 }
6716
6717 let overshoot = self.point - region.range.start;
6718 let buffer_point = region.buffer_range.start + overshoot;
6719 let expand_info = if self.is_singleton {
6720 None
6721 } else {
6722 let needs_expand_up = self.point.row == region.range.start.row
6723 && self.cursor.is_at_start_of_excerpt()
6724 && buffer_point.row > 0;
6725 let needs_expand_down = (region.excerpt.has_trailing_newline
6726 && self.point.row + 1 == region.range.end.row
6727 || !region.excerpt.has_trailing_newline && self.point.row == region.range.end.row)
6728 && self.cursor.is_at_end_of_excerpt()
6729 && buffer_point.row < region.buffer.max_point().row;
6730
6731 if needs_expand_up && needs_expand_down {
6732 Some(ExpandExcerptDirection::UpAndDown)
6733 } else if needs_expand_up {
6734 Some(ExpandExcerptDirection::Up)
6735 } else if needs_expand_down {
6736 Some(ExpandExcerptDirection::Down)
6737 } else {
6738 None
6739 }
6740 .map(|direction| ExpandInfo {
6741 direction,
6742 excerpt_id: region.excerpt.id,
6743 })
6744 };
6745
6746 let result = Some(RowInfo {
6747 buffer_id: Some(region.buffer.remote_id()),
6748 buffer_row: Some(buffer_point.row),
6749 multibuffer_row: Some(MultiBufferRow(self.point.row)),
6750 diff_status: region
6751 .diff_hunk_status
6752 .filter(|_| self.point < region.range.end),
6753 expand_info,
6754 });
6755 self.point += Point::new(1, 0);
6756 result
6757 }
6758}
6759
6760impl<'a> MultiBufferChunks<'a> {
6761 pub fn offset(&self) -> usize {
6762 self.range.start
6763 }
6764
6765 pub fn seek(&mut self, range: Range<usize>) {
6766 self.diff_transforms.seek(&range.end, Bias::Right);
6767 let mut excerpt_end = self.diff_transforms.start().1;
6768 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6769 let overshoot = range.end - self.diff_transforms.start().0;
6770 excerpt_end.value += overshoot;
6771 }
6772
6773 self.diff_transforms.seek(&range.start, Bias::Right);
6774 let mut excerpt_start = self.diff_transforms.start().1;
6775 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6776 let overshoot = range.start - self.diff_transforms.start().0;
6777 excerpt_start.value += overshoot;
6778 }
6779
6780 self.seek_to_excerpt_offset_range(excerpt_start..excerpt_end);
6781 self.buffer_chunk.take();
6782 self.range = range;
6783 }
6784
6785 fn seek_to_excerpt_offset_range(&mut self, new_range: Range<ExcerptOffset>) {
6786 self.excerpt_offset_range = new_range.clone();
6787 self.excerpts.seek(&new_range.start, Bias::Right);
6788 if let Some(excerpt) = self.excerpts.item() {
6789 let excerpt_start = *self.excerpts.start();
6790 if let Some(excerpt_chunks) = self
6791 .excerpt_chunks
6792 .as_mut()
6793 .filter(|chunks| excerpt.id == chunks.excerpt_id)
6794 {
6795 excerpt.seek_chunks(
6796 excerpt_chunks,
6797 (self.excerpt_offset_range.start - excerpt_start).value
6798 ..(self.excerpt_offset_range.end - excerpt_start).value,
6799 );
6800 } else {
6801 self.excerpt_chunks = Some(excerpt.chunks_in_range(
6802 (self.excerpt_offset_range.start - excerpt_start).value
6803 ..(self.excerpt_offset_range.end - excerpt_start).value,
6804 self.language_aware,
6805 ));
6806 }
6807 } else {
6808 self.excerpt_chunks = None;
6809 }
6810 }
6811
6812 fn next_excerpt_chunk(&mut self) -> Option<Chunk<'a>> {
6813 loop {
6814 if self.excerpt_offset_range.is_empty() {
6815 return None;
6816 } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
6817 self.excerpt_offset_range.start.value += chunk.text.len();
6818 return Some(chunk);
6819 } else {
6820 self.excerpts.next();
6821 let excerpt = self.excerpts.item()?;
6822 self.excerpt_chunks = Some(excerpt.chunks_in_range(
6823 0..(self.excerpt_offset_range.end - *self.excerpts.start()).value,
6824 self.language_aware,
6825 ));
6826 }
6827 }
6828 }
6829}
6830
6831impl<'a> Iterator for ReversedMultiBufferChunks<'a> {
6832 type Item = &'a str;
6833
6834 fn next(&mut self) -> Option<Self::Item> {
6835 let mut region = self.cursor.region()?;
6836 if self.offset == region.range.start {
6837 self.cursor.prev();
6838 region = self.cursor.region()?;
6839 let start_overshoot = self.start.saturating_sub(region.range.start);
6840 self.current_chunks = Some(region.buffer.reversed_chunks_in_range(
6841 region.buffer_range.start + start_overshoot..region.buffer_range.end,
6842 ));
6843 }
6844
6845 if self.offset == region.range.end && region.has_trailing_newline {
6846 self.offset -= 1;
6847 Some("\n")
6848 } else {
6849 let chunk = self.current_chunks.as_mut().unwrap().next()?;
6850 self.offset -= chunk.len();
6851 Some(chunk)
6852 }
6853 }
6854}
6855
6856impl<'a> Iterator for MultiBufferChunks<'a> {
6857 type Item = Chunk<'a>;
6858
6859 fn next(&mut self) -> Option<Chunk<'a>> {
6860 if self.range.start >= self.range.end {
6861 return None;
6862 }
6863 if self.range.start == self.diff_transforms.end().0 {
6864 self.diff_transforms.next();
6865 }
6866
6867 let diff_transform_start = self.diff_transforms.start().0;
6868 let diff_transform_end = self.diff_transforms.end().0;
6869 debug_assert!(self.range.start < diff_transform_end);
6870
6871 let diff_transform = self.diff_transforms.item()?;
6872 match diff_transform {
6873 DiffTransform::BufferContent { .. } => {
6874 let chunk = if let Some(chunk) = &mut self.buffer_chunk {
6875 chunk
6876 } else {
6877 let chunk = self.next_excerpt_chunk().unwrap();
6878 self.buffer_chunk.insert(chunk)
6879 };
6880
6881 let chunk_end = self.range.start + chunk.text.len();
6882 let diff_transform_end = diff_transform_end.min(self.range.end);
6883
6884 if diff_transform_end < chunk_end {
6885 let split_idx = diff_transform_end - self.range.start;
6886 let (before, after) = chunk.text.split_at(split_idx);
6887 self.range.start = diff_transform_end;
6888 let mask = 1u128.unbounded_shl(split_idx as u32).wrapping_sub(1);
6889 let chars = chunk.chars & mask;
6890 let tabs = chunk.tabs & mask;
6891
6892 chunk.text = after;
6893 chunk.chars = chunk.chars >> split_idx;
6894 chunk.tabs = chunk.tabs >> split_idx;
6895
6896 Some(Chunk {
6897 text: before,
6898 chars,
6899 tabs,
6900 ..chunk.clone()
6901 })
6902 } else {
6903 self.range.start = chunk_end;
6904 self.buffer_chunk.take()
6905 }
6906 }
6907 DiffTransform::DeletedHunk {
6908 buffer_id,
6909 base_text_byte_range,
6910 has_trailing_newline,
6911 ..
6912 } => {
6913 let base_text_start =
6914 base_text_byte_range.start + self.range.start - diff_transform_start;
6915 let base_text_end =
6916 base_text_byte_range.start + self.range.end - diff_transform_start;
6917 let base_text_end = base_text_end.min(base_text_byte_range.end);
6918
6919 let mut chunks = if let Some((_, mut chunks)) = self
6920 .diff_base_chunks
6921 .take()
6922 .filter(|(id, _)| id == buffer_id)
6923 {
6924 if chunks.range().start != base_text_start || chunks.range().end < base_text_end
6925 {
6926 chunks.seek(base_text_start..base_text_end);
6927 }
6928 chunks
6929 } else {
6930 let base_buffer = &self.diffs.get(buffer_id)?.base_text();
6931 base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
6932 };
6933
6934 let chunk = if let Some(chunk) = chunks.next() {
6935 self.range.start += chunk.text.len();
6936 self.diff_base_chunks = Some((*buffer_id, chunks));
6937 chunk
6938 } else {
6939 debug_assert!(has_trailing_newline);
6940 self.range.start += "\n".len();
6941 Chunk {
6942 text: "\n",
6943 chars: 1u128,
6944 ..Default::default()
6945 }
6946 };
6947 Some(chunk)
6948 }
6949 }
6950 }
6951}
6952
6953impl MultiBufferBytes<'_> {
6954 fn consume(&mut self, len: usize) {
6955 self.range.start += len;
6956 self.chunk = &self.chunk[len..];
6957
6958 if !self.range.is_empty() && self.chunk.is_empty() {
6959 if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
6960 self.chunk = chunk;
6961 } else if self.has_trailing_newline {
6962 self.has_trailing_newline = false;
6963 self.chunk = b"\n";
6964 } else {
6965 self.cursor.next();
6966 if let Some(region) = self.cursor.region() {
6967 let mut excerpt_bytes = region.buffer.bytes_in_range(
6968 region.buffer_range.start
6969 ..(region.buffer_range.start + self.range.end - region.range.start)
6970 .min(region.buffer_range.end),
6971 );
6972 self.chunk = excerpt_bytes.next().unwrap_or(&[]);
6973 self.excerpt_bytes = Some(excerpt_bytes);
6974 self.has_trailing_newline =
6975 region.has_trailing_newline && self.range.end >= region.range.end;
6976 if self.chunk.is_empty() && self.has_trailing_newline {
6977 self.has_trailing_newline = false;
6978 self.chunk = b"\n";
6979 }
6980 }
6981 }
6982 }
6983 }
6984}
6985
6986impl<'a> Iterator for MultiBufferBytes<'a> {
6987 type Item = &'a [u8];
6988
6989 fn next(&mut self) -> Option<Self::Item> {
6990 let chunk = self.chunk;
6991 if chunk.is_empty() {
6992 None
6993 } else {
6994 self.consume(chunk.len());
6995 Some(chunk)
6996 }
6997 }
6998}
6999
7000impl io::Read for MultiBufferBytes<'_> {
7001 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
7002 let len = cmp::min(buf.len(), self.chunk.len());
7003 buf[..len].copy_from_slice(&self.chunk[..len]);
7004 if len > 0 {
7005 self.consume(len);
7006 }
7007 Ok(len)
7008 }
7009}
7010
7011impl io::Read for ReversedMultiBufferBytes<'_> {
7012 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
7013 let len = cmp::min(buf.len(), self.chunk.len());
7014 buf[..len].copy_from_slice(&self.chunk[..len]);
7015 buf[..len].reverse();
7016 if len > 0 {
7017 self.range.end -= len;
7018 self.chunk = &self.chunk[..self.chunk.len() - len];
7019 if !self.range.is_empty()
7020 && self.chunk.is_empty()
7021 && let Some(chunk) = self.chunks.next()
7022 {
7023 self.chunk = chunk.as_bytes();
7024 }
7025 }
7026 Ok(len)
7027 }
7028}
7029
7030impl<'a> Iterator for ExcerptChunks<'a> {
7031 type Item = Chunk<'a>;
7032
7033 fn next(&mut self) -> Option<Self::Item> {
7034 if let Some(chunk) = self.content_chunks.next() {
7035 return Some(chunk);
7036 }
7037
7038 if self.has_footer {
7039 let text = "\n";
7040 let chars = 0b1;
7041 self.has_footer = false;
7042 return Some(Chunk {
7043 text,
7044 chars,
7045 ..Default::default()
7046 });
7047 }
7048
7049 None
7050 }
7051}
7052
7053impl ToOffset for Point {
7054 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7055 snapshot.point_to_offset(*self)
7056 }
7057 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7058 snapshot.point_to_offset_utf16(*self)
7059 }
7060}
7061
7062impl ToOffset for usize {
7063 #[track_caller]
7064 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7065 assert!(
7066 *self <= snapshot.len(),
7067 "offset {} is greater than the snapshot.len() {}",
7068 *self,
7069 snapshot.len(),
7070 );
7071 *self
7072 }
7073 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7074 snapshot.offset_to_offset_utf16(*self)
7075 }
7076}
7077
7078impl ToOffset for OffsetUtf16 {
7079 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7080 snapshot.offset_utf16_to_offset(*self)
7081 }
7082
7083 fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7084 *self
7085 }
7086}
7087
7088impl ToOffset for PointUtf16 {
7089 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7090 snapshot.point_utf16_to_offset(*self)
7091 }
7092 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7093 snapshot.point_utf16_to_offset_utf16(*self)
7094 }
7095}
7096
7097impl ToPoint for usize {
7098 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
7099 snapshot.offset_to_point(*self)
7100 }
7101 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
7102 snapshot.offset_to_point_utf16(*self)
7103 }
7104}
7105
7106impl ToPoint for Point {
7107 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
7108 *self
7109 }
7110 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
7111 snapshot.point_to_point_utf16(*self)
7112 }
7113}
7114
7115impl ToPoint for PointUtf16 {
7116 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
7117 snapshot.point_utf16_to_point(*self)
7118 }
7119 fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 {
7120 *self
7121 }
7122}
7123
7124impl From<ExcerptId> for EntityId {
7125 fn from(id: ExcerptId) -> Self {
7126 EntityId::from(id.0 as u64)
7127 }
7128}
7129
7130#[cfg(debug_assertions)]
7131pub mod debug {
7132 use super::*;
7133
7134 pub trait ToMultiBufferDebugRanges {
7135 fn to_multi_buffer_debug_ranges(&self, snapshot: &MultiBufferSnapshot)
7136 -> Vec<Range<usize>>;
7137 }
7138
7139 impl<T: ToOffset> ToMultiBufferDebugRanges for T {
7140 fn to_multi_buffer_debug_ranges(
7141 &self,
7142 snapshot: &MultiBufferSnapshot,
7143 ) -> Vec<Range<usize>> {
7144 [self.to_offset(snapshot)].to_multi_buffer_debug_ranges(snapshot)
7145 }
7146 }
7147
7148 impl<T: ToOffset> ToMultiBufferDebugRanges for Range<T> {
7149 fn to_multi_buffer_debug_ranges(
7150 &self,
7151 snapshot: &MultiBufferSnapshot,
7152 ) -> Vec<Range<usize>> {
7153 [self.start.to_offset(snapshot)..self.end.to_offset(snapshot)]
7154 .to_multi_buffer_debug_ranges(snapshot)
7155 }
7156 }
7157
7158 impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<T> {
7159 fn to_multi_buffer_debug_ranges(
7160 &self,
7161 snapshot: &MultiBufferSnapshot,
7162 ) -> Vec<Range<usize>> {
7163 self.as_slice().to_multi_buffer_debug_ranges(snapshot)
7164 }
7165 }
7166
7167 impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<Range<T>> {
7168 fn to_multi_buffer_debug_ranges(
7169 &self,
7170 snapshot: &MultiBufferSnapshot,
7171 ) -> Vec<Range<usize>> {
7172 self.as_slice().to_multi_buffer_debug_ranges(snapshot)
7173 }
7174 }
7175
7176 impl<T: ToOffset> ToMultiBufferDebugRanges for [T] {
7177 fn to_multi_buffer_debug_ranges(
7178 &self,
7179 snapshot: &MultiBufferSnapshot,
7180 ) -> Vec<Range<usize>> {
7181 self.iter()
7182 .map(|item| {
7183 let offset = item.to_offset(snapshot);
7184 offset..offset
7185 })
7186 .collect()
7187 }
7188 }
7189
7190 impl<T: ToOffset> ToMultiBufferDebugRanges for [Range<T>] {
7191 fn to_multi_buffer_debug_ranges(
7192 &self,
7193 snapshot: &MultiBufferSnapshot,
7194 ) -> Vec<Range<usize>> {
7195 self.iter()
7196 .map(|range| range.start.to_offset(snapshot)..range.end.to_offset(snapshot))
7197 .collect()
7198 }
7199 }
7200}