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