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