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