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