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