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