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