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