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