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