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