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