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