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