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