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