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