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