1mod anchor;
2
3pub use anchor::{Anchor, AnchorRangeExt};
4use anyhow::Result;
5use clock::ReplicaId;
6use collections::{HashMap, HashSet};
7use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
8use language::{
9 Buffer, BufferChunks, BufferSnapshot, Chunk, DiagnosticEntry, Event, File, Language, Selection,
10 ToOffset as _, ToPoint as _, TransactionId,
11};
12use std::{
13 cell::{Ref, RefCell},
14 cmp, fmt, io,
15 iter::{self, FromIterator},
16 ops::{Range, Sub},
17 str,
18 sync::Arc,
19 time::{Duration, Instant},
20};
21use sum_tree::{Bias, Cursor, SumTree};
22use text::{
23 locator::Locator,
24 rope::TextDimension,
25 subscription::{Subscription, Topic},
26 AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
27};
28use theme::SyntaxTheme;
29use util::post_inc;
30
31const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
32
33pub type ExcerptId = Locator;
34
35pub struct MultiBuffer {
36 snapshot: RefCell<MultiBufferSnapshot>,
37 buffers: RefCell<HashMap<usize, BufferState>>,
38 subscriptions: Topic,
39 singleton: bool,
40 replica_id: ReplicaId,
41 history: History,
42}
43
44struct History {
45 next_transaction_id: usize,
46 undo_stack: Vec<Transaction>,
47 redo_stack: Vec<Transaction>,
48 transaction_depth: usize,
49 group_interval: Duration,
50}
51
52struct Transaction {
53 id: usize,
54 buffer_transactions: HashSet<(usize, text::TransactionId)>,
55 first_edit_at: Instant,
56 last_edit_at: Instant,
57}
58
59pub trait ToOffset: 'static + fmt::Debug {
60 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
61}
62
63pub trait ToPoint: 'static + fmt::Debug {
64 fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
65}
66
67struct BufferState {
68 buffer: ModelHandle<Buffer>,
69 last_version: clock::Global,
70 last_parse_count: usize,
71 last_diagnostics_update_count: usize,
72 excerpts: Vec<ExcerptId>,
73 _subscriptions: [gpui::Subscription; 2],
74}
75
76#[derive(Clone, Default)]
77pub struct MultiBufferSnapshot {
78 excerpts: SumTree<Excerpt>,
79 parse_count: usize,
80 diagnostics_update_count: usize,
81 is_dirty: bool,
82 has_conflict: bool,
83}
84
85pub struct ExcerptProperties<'a, T> {
86 pub buffer: &'a ModelHandle<Buffer>,
87 pub range: Range<T>,
88}
89
90#[derive(Clone)]
91struct Excerpt {
92 id: ExcerptId,
93 buffer_id: usize,
94 buffer: BufferSnapshot,
95 range: Range<text::Anchor>,
96 max_buffer_row: u32,
97 text_summary: TextSummary,
98 has_trailing_newline: bool,
99}
100
101#[derive(Clone, Debug, Default)]
102struct ExcerptSummary {
103 excerpt_id: ExcerptId,
104 max_buffer_row: u32,
105 text: TextSummary,
106}
107
108pub struct MultiBufferRows<'a> {
109 buffer_row_range: Range<u32>,
110 excerpts: Cursor<'a, Excerpt, Point>,
111}
112
113pub struct MultiBufferChunks<'a> {
114 range: Range<usize>,
115 excerpts: Cursor<'a, Excerpt, usize>,
116 excerpt_chunks: Option<ExcerptChunks<'a>>,
117 theme: Option<&'a SyntaxTheme>,
118}
119
120pub struct MultiBufferBytes<'a> {
121 range: Range<usize>,
122 excerpts: Cursor<'a, Excerpt, usize>,
123 excerpt_bytes: Option<ExcerptBytes<'a>>,
124 chunk: &'a [u8],
125}
126
127struct ExcerptChunks<'a> {
128 content_chunks: BufferChunks<'a>,
129 footer_height: usize,
130}
131
132struct ExcerptBytes<'a> {
133 content_bytes: language::rope::Bytes<'a>,
134 footer_height: usize,
135}
136
137impl MultiBuffer {
138 pub fn new(replica_id: ReplicaId) -> Self {
139 Self {
140 snapshot: Default::default(),
141 buffers: Default::default(),
142 subscriptions: Default::default(),
143 singleton: false,
144 replica_id,
145 history: History {
146 next_transaction_id: Default::default(),
147 undo_stack: Default::default(),
148 redo_stack: Default::default(),
149 transaction_depth: 0,
150 group_interval: Duration::from_millis(300),
151 },
152 }
153 }
154
155 pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
156 let mut this = Self::new(buffer.read(cx).replica_id());
157 this.singleton = true;
158 this.push_excerpt(
159 ExcerptProperties {
160 buffer: &buffer,
161 range: text::Anchor::min()..text::Anchor::max(),
162 },
163 cx,
164 );
165 this
166 }
167
168 #[cfg(any(test, feature = "test-support"))]
169 pub fn build_simple(text: &str, cx: &mut gpui::MutableAppContext) -> ModelHandle<Self> {
170 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
171 cx.add_model(|cx| Self::singleton(buffer, cx))
172 }
173
174 #[cfg(any(test, feature = "test-support"))]
175 pub fn build_random(
176 mut rng: &mut impl rand::Rng,
177 cx: &mut gpui::MutableAppContext,
178 ) -> ModelHandle<Self> {
179 use rand::prelude::*;
180 use std::env;
181 use text::RandomCharIter;
182
183 let max_excerpts = env::var("MAX_EXCERPTS")
184 .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
185 .unwrap_or(5);
186 let excerpts = rng.gen_range(1..=max_excerpts);
187
188 cx.add_model(|cx| {
189 let mut multibuffer = MultiBuffer::new(0);
190 let mut buffers = Vec::new();
191 for _ in 0..excerpts {
192 let buffer_handle = if rng.gen() || buffers.is_empty() {
193 let text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
194 buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx)));
195 let buffer = buffers.last().unwrap();
196 log::info!(
197 "Creating new buffer {} with text: {:?}",
198 buffer.id(),
199 buffer.read(cx).text()
200 );
201 buffers.last().unwrap()
202 } else {
203 buffers.choose(rng).unwrap()
204 };
205
206 let buffer = buffer_handle.read(cx);
207 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
208 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
209 let header_height = rng.gen_range(0..=5);
210 log::info!(
211 "Inserting excerpt from buffer {} with header height {} and range {:?}: {:?}",
212 buffer_handle.id(),
213 header_height,
214 start_ix..end_ix,
215 &buffer.text()[start_ix..end_ix]
216 );
217
218 multibuffer.push_excerpt(
219 ExcerptProperties {
220 buffer: buffer_handle,
221 range: start_ix..end_ix,
222 },
223 cx,
224 );
225 }
226 multibuffer
227 })
228 }
229
230 pub fn replica_id(&self) -> ReplicaId {
231 self.replica_id
232 }
233
234 pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
235 self.sync(cx);
236 self.snapshot.borrow().clone()
237 }
238
239 pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
240 self.sync(cx);
241 self.snapshot.borrow()
242 }
243
244 pub fn as_singleton(&self) -> Option<ModelHandle<Buffer>> {
245 if self.singleton {
246 return Some(
247 self.buffers
248 .borrow()
249 .values()
250 .next()
251 .unwrap()
252 .buffer
253 .clone(),
254 );
255 } else {
256 None
257 }
258 }
259
260 pub fn subscribe(&mut self) -> Subscription {
261 self.subscriptions.subscribe()
262 }
263
264 pub fn edit<I, S, T>(&mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>)
265 where
266 I: IntoIterator<Item = Range<S>>,
267 S: ToOffset,
268 T: Into<String>,
269 {
270 self.edit_internal(ranges, new_text, false, cx)
271 }
272
273 pub fn edit_with_autoindent<I, S, T>(
274 &mut self,
275 ranges: I,
276 new_text: T,
277 cx: &mut ModelContext<Self>,
278 ) where
279 I: IntoIterator<Item = Range<S>>,
280 S: ToOffset,
281 T: Into<String>,
282 {
283 self.edit_internal(ranges, new_text, true, cx)
284 }
285
286 pub fn edit_internal<I, S, T>(
287 &mut self,
288 ranges_iter: I,
289 new_text: T,
290 autoindent: bool,
291 cx: &mut ModelContext<Self>,
292 ) where
293 I: IntoIterator<Item = Range<S>>,
294 S: ToOffset,
295 T: Into<String>,
296 {
297 if let Some(buffer) = self.as_singleton() {
298 let snapshot = self.read(cx);
299 let ranges = ranges_iter
300 .into_iter()
301 .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
302 return buffer.update(cx, |buffer, cx| {
303 if autoindent {
304 buffer.edit_with_autoindent(ranges, new_text, cx)
305 } else {
306 buffer.edit(ranges, new_text, cx)
307 }
308 });
309 }
310
311 let snapshot = self.read(cx);
312 let mut buffer_edits: HashMap<usize, Vec<(Range<usize>, bool)>> = Default::default();
313 let mut cursor = snapshot.excerpts.cursor::<usize>();
314 for range in ranges_iter {
315 let start = range.start.to_offset(&snapshot);
316 let end = range.end.to_offset(&snapshot);
317 cursor.seek(&start, Bias::Right, &());
318 if cursor.item().is_none() && start == *cursor.start() {
319 cursor.prev(&());
320 }
321 let start_excerpt = cursor.item().expect("start offset out of bounds");
322 let start_overshoot = start - cursor.start();
323 let buffer_start =
324 start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot;
325
326 cursor.seek(&end, Bias::Right, &());
327 if cursor.item().is_none() && end == *cursor.start() {
328 cursor.prev(&());
329 }
330 let end_excerpt = cursor.item().expect("end offset out of bounds");
331 let end_overshoot = end - cursor.start();
332 let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot;
333
334 if start_excerpt.id == end_excerpt.id {
335 buffer_edits
336 .entry(start_excerpt.buffer_id)
337 .or_insert(Vec::new())
338 .push((buffer_start..buffer_end, true));
339 } else {
340 let start_excerpt_range =
341 buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer);
342 let end_excerpt_range =
343 end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end;
344 buffer_edits
345 .entry(start_excerpt.buffer_id)
346 .or_insert(Vec::new())
347 .push((start_excerpt_range, true));
348 buffer_edits
349 .entry(end_excerpt.buffer_id)
350 .or_insert(Vec::new())
351 .push((end_excerpt_range, false));
352
353 cursor.seek(&start, Bias::Right, &());
354 cursor.next(&());
355 while let Some(excerpt) = cursor.item() {
356 if excerpt.id == end_excerpt.id {
357 break;
358 }
359 buffer_edits
360 .entry(excerpt.buffer_id)
361 .or_insert(Vec::new())
362 .push((excerpt.range.to_offset(&excerpt.buffer), false));
363 cursor.next(&());
364 }
365 }
366 }
367
368 let new_text = new_text.into();
369 for (buffer_id, mut edits) in buffer_edits {
370 edits.sort_unstable_by_key(|(range, _)| range.start);
371 self.buffers.borrow()[&buffer_id]
372 .buffer
373 .update(cx, |buffer, cx| {
374 let mut edits = edits.into_iter().peekable();
375 let mut insertions = Vec::new();
376 let mut deletions = Vec::new();
377 while let Some((mut range, mut is_insertion)) = edits.next() {
378 while let Some((next_range, next_is_insertion)) = edits.peek() {
379 if range.end >= next_range.start {
380 range.end = cmp::max(next_range.end, range.end);
381 is_insertion |= *next_is_insertion;
382 edits.next();
383 } else {
384 break;
385 }
386 }
387
388 if is_insertion {
389 insertions.push(
390 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
391 );
392 } else if !range.is_empty() {
393 deletions.push(
394 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
395 );
396 }
397 }
398
399 if autoindent {
400 buffer.edit_with_autoindent(deletions, "", cx);
401 buffer.edit_with_autoindent(insertions, new_text.clone(), cx);
402 } else {
403 buffer.edit(deletions, "", cx);
404 buffer.edit(insertions, new_text.clone(), cx);
405 }
406 })
407 }
408 }
409
410 pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
411 self.start_transaction_at(Instant::now(), cx)
412 }
413
414 pub(crate) fn start_transaction_at(
415 &mut self,
416 now: Instant,
417 cx: &mut ModelContext<Self>,
418 ) -> Option<TransactionId> {
419 if let Some(buffer) = self.as_singleton() {
420 return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
421 }
422
423 for BufferState { buffer, .. } in self.buffers.borrow().values() {
424 buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
425 }
426 self.history.start_transaction(now)
427 }
428
429 pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
430 self.end_transaction_at(Instant::now(), cx)
431 }
432
433 pub(crate) fn end_transaction_at(
434 &mut self,
435 now: Instant,
436 cx: &mut ModelContext<Self>,
437 ) -> Option<TransactionId> {
438 if let Some(buffer) = self.as_singleton() {
439 return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
440 }
441
442 let mut buffer_transactions = HashSet::default();
443 for BufferState { buffer, .. } in self.buffers.borrow().values() {
444 if let Some(transaction_id) =
445 buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
446 {
447 buffer_transactions.insert((buffer.id(), transaction_id));
448 }
449 }
450
451 if self.history.end_transaction(now, buffer_transactions) {
452 let transaction_id = self.history.group().unwrap();
453 Some(transaction_id)
454 } else {
455 None
456 }
457 }
458
459 pub fn set_active_selections(
460 &mut self,
461 selections: &[Selection<Anchor>],
462 cx: &mut ModelContext<Self>,
463 ) {
464 let mut selections_by_buffer: HashMap<usize, Vec<Selection<text::Anchor>>> =
465 Default::default();
466 let snapshot = self.read(cx);
467 let mut cursor = snapshot.excerpts.cursor::<Option<&ExcerptId>>();
468 for selection in selections {
469 cursor.seek(&Some(&selection.start.excerpt_id), Bias::Left, &());
470 while let Some(excerpt) = cursor.item() {
471 if excerpt.id > selection.end.excerpt_id {
472 break;
473 }
474
475 let mut start = excerpt.range.start.clone();
476 let mut end = excerpt.range.end.clone();
477 if excerpt.id == selection.start.excerpt_id {
478 start = selection.start.text_anchor.clone();
479 }
480 if excerpt.id == selection.end.excerpt_id {
481 end = selection.end.text_anchor.clone();
482 }
483 selections_by_buffer
484 .entry(excerpt.buffer_id)
485 .or_default()
486 .push(Selection {
487 id: selection.id,
488 start,
489 end,
490 reversed: selection.reversed,
491 goal: selection.goal,
492 });
493
494 cursor.next(&());
495 }
496 }
497
498 for (buffer_id, mut selections) in selections_by_buffer {
499 self.buffers.borrow()[&buffer_id]
500 .buffer
501 .update(cx, |buffer, cx| {
502 selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer).unwrap());
503 let mut selections = selections.into_iter().peekable();
504 let merged_selections = Arc::from_iter(iter::from_fn(|| {
505 let mut selection = selections.next()?;
506 while let Some(next_selection) = selections.peek() {
507 if selection
508 .end
509 .cmp(&next_selection.start, buffer)
510 .unwrap()
511 .is_ge()
512 {
513 let next_selection = selections.next().unwrap();
514 if next_selection
515 .end
516 .cmp(&selection.end, buffer)
517 .unwrap()
518 .is_ge()
519 {
520 selection.end = next_selection.end;
521 }
522 } else {
523 break;
524 }
525 }
526 Some(selection)
527 }));
528 buffer.set_active_selections(merged_selections, cx);
529 });
530 }
531 }
532
533 pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
534 for buffer in self.buffers.borrow().values() {
535 buffer
536 .buffer
537 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
538 }
539 }
540
541 pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
542 if let Some(buffer) = self.as_singleton() {
543 return buffer.update(cx, |buffer, cx| buffer.undo(cx));
544 }
545
546 while let Some(transaction) = self.history.pop_undo() {
547 let mut undone = false;
548 for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
549 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(&buffer_id) {
550 undone |= buffer.update(cx, |buf, cx| {
551 buf.undo_transaction(*buffer_transaction_id, cx)
552 });
553 }
554 }
555
556 if undone {
557 return Some(transaction.id);
558 }
559 }
560
561 None
562 }
563
564 pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
565 if let Some(buffer) = self.as_singleton() {
566 return buffer.update(cx, |buffer, cx| buffer.redo(cx));
567 }
568
569 while let Some(transaction) = self.history.pop_redo() {
570 let mut redone = false;
571 for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
572 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(&buffer_id) {
573 redone |= buffer.update(cx, |buf, cx| {
574 buf.redo_transaction(*buffer_transaction_id, cx)
575 });
576 }
577 }
578
579 if redone {
580 return Some(transaction.id);
581 }
582 }
583
584 None
585 }
586
587 pub fn push_excerpt<O>(
588 &mut self,
589 props: ExcerptProperties<O>,
590 cx: &mut ModelContext<Self>,
591 ) -> ExcerptId
592 where
593 O: text::ToOffset,
594 {
595 assert_eq!(self.history.transaction_depth, 0);
596 self.sync(cx);
597
598 let buffer_snapshot = props.buffer.read(cx).snapshot();
599 let range = buffer_snapshot.anchor_before(&props.range.start)
600 ..buffer_snapshot.anchor_after(&props.range.end);
601 let mut snapshot = self.snapshot.borrow_mut();
602 let mut prev_id = None;
603 let edit_start = snapshot.excerpts.summary().text.bytes;
604 snapshot.excerpts.update_last(
605 |excerpt| {
606 excerpt.has_trailing_newline = true;
607 prev_id = Some(excerpt.id.clone());
608 },
609 &(),
610 );
611
612 let id = ExcerptId::between(&prev_id.unwrap_or(ExcerptId::min()), &ExcerptId::max());
613 self.buffers
614 .borrow_mut()
615 .entry(props.buffer.id())
616 .or_insert_with(|| BufferState {
617 last_version: buffer_snapshot.version().clone(),
618 last_parse_count: buffer_snapshot.parse_count(),
619 last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
620 excerpts: Default::default(),
621 _subscriptions: [
622 cx.observe(&props.buffer, |_, _, cx| cx.notify()),
623 cx.subscribe(&props.buffer, Self::on_buffer_event),
624 ],
625 buffer: props.buffer.clone(),
626 })
627 .excerpts
628 .push(id.clone());
629 let excerpt = Excerpt::new(id.clone(), props.buffer.id(), buffer_snapshot, range, false);
630 snapshot.excerpts.push(excerpt, &());
631 self.subscriptions.publish_mut([Edit {
632 old: edit_start..edit_start,
633 new: edit_start..snapshot.excerpts.summary().text.bytes,
634 }]);
635
636 cx.notify();
637 id
638 }
639
640 pub fn excerpt_ids_for_buffer(&self, buffer: &ModelHandle<Buffer>) -> Vec<ExcerptId> {
641 self.buffers
642 .borrow()
643 .get(&buffer.id())
644 .map_or(Vec::new(), |state| state.excerpts.clone())
645 }
646
647 pub fn remove_excerpts<'a>(
648 &mut self,
649 excerpt_ids: impl IntoIterator<Item = &'a ExcerptId>,
650 cx: &mut ModelContext<Self>,
651 ) {
652 let mut buffers = self.buffers.borrow_mut();
653 let mut snapshot = self.snapshot.borrow_mut();
654 let mut new_excerpts = SumTree::new();
655 let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
656 let mut edits = Vec::new();
657 for excerpt_id in excerpt_ids {
658 new_excerpts.push_tree(cursor.slice(&Some(excerpt_id), Bias::Left, &()), &());
659 if let Some(excerpt) = cursor.item() {
660 if excerpt.id == *excerpt_id {
661 let mut old_start = cursor.start().1;
662 let old_end = cursor.end(&()).1;
663 cursor.next(&());
664
665 if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
666 buffer_state.excerpts.retain(|id| id != excerpt_id);
667 }
668
669 // When removing the last excerpt, remove the trailing newline from
670 // the previous excerpt.
671 if cursor.item().is_none() && old_start > 0 {
672 old_start -= 1;
673 new_excerpts.update_last(|e| e.has_trailing_newline = false, &());
674 }
675
676 let new_start = new_excerpts.summary().text.bytes;
677 edits.push(Edit {
678 old: old_start..old_end,
679 new: new_start..new_start,
680 });
681 }
682 }
683 }
684 new_excerpts.push_tree(cursor.suffix(&()), &());
685 drop(cursor);
686 snapshot.excerpts = new_excerpts;
687 self.subscriptions.publish_mut(edits);
688 cx.notify();
689 }
690
691 fn on_buffer_event(
692 &mut self,
693 _: ModelHandle<Buffer>,
694 event: &Event,
695 cx: &mut ModelContext<Self>,
696 ) {
697 cx.emit(event.clone());
698 }
699
700 pub fn save(&mut self, cx: &mut ModelContext<Self>) -> Result<Task<Result<()>>> {
701 let mut save_tasks = Vec::new();
702 for BufferState { buffer, .. } in self.buffers.borrow().values() {
703 save_tasks.push(buffer.update(cx, |buffer, cx| buffer.save(cx))?);
704 }
705
706 Ok(cx.spawn(|_, _| async move {
707 for save in save_tasks {
708 save.await?;
709 }
710 Ok(())
711 }))
712 }
713
714 pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
715 self.buffers
716 .borrow()
717 .values()
718 .next()
719 .and_then(|state| state.buffer.read(cx).language())
720 }
721
722 pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
723 self.as_singleton()?.read(cx).file()
724 }
725
726 #[cfg(test)]
727 pub fn is_parsing(&self, cx: &AppContext) -> bool {
728 self.as_singleton().unwrap().read(cx).is_parsing()
729 }
730
731 fn sync(&self, cx: &AppContext) {
732 let mut snapshot = self.snapshot.borrow_mut();
733 let mut excerpts_to_edit = Vec::new();
734 let mut reparsed = false;
735 let mut diagnostics_updated = false;
736 let mut is_dirty = false;
737 let mut has_conflict = false;
738 let mut buffers = self.buffers.borrow_mut();
739 for buffer_state in buffers.values_mut() {
740 let buffer = buffer_state.buffer.read(cx);
741 let version = buffer.version();
742 let parse_count = buffer.parse_count();
743 let diagnostics_update_count = buffer.diagnostics_update_count();
744
745 let buffer_edited = version.gt(&buffer_state.last_version);
746 let buffer_reparsed = parse_count > buffer_state.last_parse_count;
747 let buffer_diagnostics_updated =
748 diagnostics_update_count > buffer_state.last_diagnostics_update_count;
749 if buffer_edited || buffer_reparsed || buffer_diagnostics_updated {
750 buffer_state.last_version = version;
751 buffer_state.last_parse_count = parse_count;
752 buffer_state.last_diagnostics_update_count = diagnostics_update_count;
753 excerpts_to_edit.extend(
754 buffer_state
755 .excerpts
756 .iter()
757 .map(|excerpt_id| (excerpt_id, buffer_state.buffer.clone(), buffer_edited)),
758 );
759 }
760
761 reparsed |= buffer_reparsed;
762 diagnostics_updated |= buffer_diagnostics_updated;
763 is_dirty |= buffer.is_dirty();
764 has_conflict |= buffer.has_conflict();
765 }
766 if reparsed {
767 snapshot.parse_count += 1;
768 }
769 if diagnostics_updated {
770 snapshot.diagnostics_update_count += 1;
771 }
772 snapshot.is_dirty = is_dirty;
773 snapshot.has_conflict = has_conflict;
774
775 excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _, _)| *excerpt_id);
776
777 let mut edits = Vec::new();
778 let mut new_excerpts = SumTree::new();
779 let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
780
781 for (id, buffer, buffer_edited) in excerpts_to_edit {
782 new_excerpts.push_tree(cursor.slice(&Some(id), Bias::Left, &()), &());
783 let old_excerpt = cursor.item().unwrap();
784 let buffer_id = buffer.id();
785 let buffer = buffer.read(cx);
786
787 let mut new_excerpt;
788 if buffer_edited {
789 edits.extend(
790 buffer
791 .edits_since_in_range::<usize>(
792 old_excerpt.buffer.version(),
793 old_excerpt.range.clone(),
794 )
795 .map(|mut edit| {
796 let excerpt_old_start = cursor.start().1;
797 let excerpt_new_start = new_excerpts.summary().text.bytes;
798 edit.old.start += excerpt_old_start;
799 edit.old.end += excerpt_old_start;
800 edit.new.start += excerpt_new_start;
801 edit.new.end += excerpt_new_start;
802 edit
803 }),
804 );
805
806 new_excerpt = Excerpt::new(
807 id.clone(),
808 buffer_id,
809 buffer.snapshot(),
810 old_excerpt.range.clone(),
811 old_excerpt.has_trailing_newline,
812 );
813 } else {
814 new_excerpt = old_excerpt.clone();
815 new_excerpt.buffer = buffer.snapshot();
816 }
817
818 new_excerpts.push(new_excerpt, &());
819 cursor.next(&());
820 }
821 new_excerpts.push_tree(cursor.suffix(&()), &());
822
823 drop(cursor);
824 snapshot.excerpts = new_excerpts;
825
826 self.subscriptions.publish(edits);
827 }
828}
829
830#[cfg(any(test, feature = "test-support"))]
831impl MultiBuffer {
832 pub fn randomly_edit(
833 &mut self,
834 rng: &mut impl rand::Rng,
835 count: usize,
836 cx: &mut ModelContext<Self>,
837 ) {
838 use text::RandomCharIter;
839
840 let snapshot = self.read(cx);
841 let mut old_ranges: Vec<Range<usize>> = Vec::new();
842 for _ in 0..count {
843 let last_end = old_ranges.last().map_or(0, |last_range| last_range.end + 1);
844 if last_end > snapshot.len() {
845 break;
846 }
847 let end_ix = snapshot.clip_offset(rng.gen_range(0..=last_end), Bias::Right);
848 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
849 old_ranges.push(start_ix..end_ix);
850 }
851 let new_text_len = rng.gen_range(0..10);
852 let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
853 log::info!("mutating multi-buffer at {:?}: {:?}", old_ranges, new_text);
854 drop(snapshot);
855
856 self.edit(old_ranges.iter().cloned(), new_text.as_str(), cx);
857 }
858}
859
860impl Entity for MultiBuffer {
861 type Event = language::Event;
862}
863
864impl MultiBufferSnapshot {
865 pub fn text(&self) -> String {
866 self.chunks(0..self.len(), None)
867 .map(|chunk| chunk.text)
868 .collect()
869 }
870
871 pub fn reversed_chars_at<'a, T: ToOffset>(
872 &'a self,
873 position: T,
874 ) -> impl Iterator<Item = char> + 'a {
875 let mut offset = position.to_offset(self);
876 let mut cursor = self.excerpts.cursor::<usize>();
877 cursor.seek(&offset, Bias::Left, &());
878 let mut excerpt_chunks = cursor.item().map(|excerpt| {
879 let end_before_footer = cursor.start() + excerpt.text_summary.bytes;
880 let start = excerpt.range.start.to_offset(&excerpt.buffer);
881 let end = start + (cmp::min(offset, end_before_footer) - cursor.start());
882 excerpt.buffer.reversed_chunks_in_range(start..end)
883 });
884 iter::from_fn(move || {
885 if offset == *cursor.start() {
886 cursor.prev(&());
887 let excerpt = cursor.item()?;
888 excerpt_chunks = Some(
889 excerpt
890 .buffer
891 .reversed_chunks_in_range(excerpt.range.clone()),
892 );
893 }
894
895 let excerpt = cursor.item().unwrap();
896 if offset == cursor.end(&()) && excerpt.has_trailing_newline {
897 offset -= 1;
898 Some("\n")
899 } else {
900 let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap();
901 offset -= chunk.len();
902 Some(chunk)
903 }
904 })
905 .flat_map(|c| c.chars().rev())
906 }
907
908 pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
909 let offset = position.to_offset(self);
910 self.text_for_range(offset..self.len())
911 .flat_map(|chunk| chunk.chars())
912 }
913
914 pub fn text_for_range<'a, T: ToOffset>(
915 &'a self,
916 range: Range<T>,
917 ) -> impl Iterator<Item = &'a str> {
918 self.chunks(range, None).map(|chunk| chunk.text)
919 }
920
921 pub fn is_line_blank(&self, row: u32) -> bool {
922 self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
923 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
924 }
925
926 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
927 where
928 T: ToOffset,
929 {
930 let position = position.to_offset(self);
931 position == self.clip_offset(position, Bias::Left)
932 && self
933 .bytes_in_range(position..self.len())
934 .flatten()
935 .copied()
936 .take(needle.len())
937 .eq(needle.bytes())
938 }
939
940 fn as_singleton(&self) -> Option<&BufferSnapshot> {
941 let mut excerpts = self.excerpts.iter();
942 let buffer = excerpts.next().map(|excerpt| &excerpt.buffer);
943 if excerpts.next().is_none() {
944 buffer
945 } else {
946 None
947 }
948 }
949
950 pub fn len(&self) -> usize {
951 self.excerpts.summary().text.bytes
952 }
953
954 pub fn max_buffer_row(&self) -> u32 {
955 self.excerpts.summary().max_buffer_row
956 }
957
958 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
959 let mut cursor = self.excerpts.cursor::<usize>();
960 cursor.seek(&offset, Bias::Right, &());
961 let overshoot = if let Some(excerpt) = cursor.item() {
962 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
963 let buffer_offset = excerpt
964 .buffer
965 .clip_offset(excerpt_start + (offset - cursor.start()), bias);
966 buffer_offset.saturating_sub(excerpt_start)
967 } else {
968 0
969 };
970 cursor.start() + overshoot
971 }
972
973 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
974 let mut cursor = self.excerpts.cursor::<Point>();
975 cursor.seek(&point, Bias::Right, &());
976 let overshoot = if let Some(excerpt) = cursor.item() {
977 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
978 let buffer_point = excerpt
979 .buffer
980 .clip_point(excerpt_start + (point - cursor.start()), bias);
981 buffer_point.saturating_sub(excerpt_start)
982 } else {
983 Point::zero()
984 };
985 *cursor.start() + overshoot
986 }
987
988 pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
989 let mut cursor = self.excerpts.cursor::<PointUtf16>();
990 cursor.seek(&point, Bias::Right, &());
991 let overshoot = if let Some(excerpt) = cursor.item() {
992 let excerpt_start = excerpt
993 .buffer
994 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
995 let buffer_point = excerpt
996 .buffer
997 .clip_point_utf16(excerpt_start + (point - cursor.start()), bias);
998 buffer_point.saturating_sub(excerpt_start)
999 } else {
1000 PointUtf16::zero()
1001 };
1002 *cursor.start() + overshoot
1003 }
1004
1005 pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
1006 let range = range.start.to_offset(self)..range.end.to_offset(self);
1007 let mut excerpts = self.excerpts.cursor::<usize>();
1008 excerpts.seek(&range.start, Bias::Right, &());
1009
1010 let mut chunk = &[][..];
1011 let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
1012 let mut excerpt_bytes = excerpt
1013 .bytes_in_range(range.start - excerpts.start()..range.end - excerpts.start());
1014 chunk = excerpt_bytes.next().unwrap_or(&[][..]);
1015 Some(excerpt_bytes)
1016 } else {
1017 None
1018 };
1019
1020 MultiBufferBytes {
1021 range,
1022 excerpts,
1023 excerpt_bytes,
1024 chunk,
1025 }
1026 }
1027
1028 pub fn buffer_rows<'a>(&'a self, start_row: u32) -> MultiBufferRows<'a> {
1029 let mut result = MultiBufferRows {
1030 buffer_row_range: 0..0,
1031 excerpts: self.excerpts.cursor(),
1032 };
1033 result.seek(start_row);
1034 result
1035 }
1036
1037 pub fn chunks<'a, T: ToOffset>(
1038 &'a self,
1039 range: Range<T>,
1040 theme: Option<&'a SyntaxTheme>,
1041 ) -> MultiBufferChunks<'a> {
1042 let range = range.start.to_offset(self)..range.end.to_offset(self);
1043 let mut chunks = MultiBufferChunks {
1044 range: range.clone(),
1045 excerpts: self.excerpts.cursor(),
1046 excerpt_chunks: None,
1047 theme,
1048 };
1049 chunks.seek(range.start);
1050 chunks
1051 }
1052
1053 pub fn offset_to_point(&self, offset: usize) -> Point {
1054 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
1055 cursor.seek(&offset, Bias::Right, &());
1056 if let Some(excerpt) = cursor.item() {
1057 let (start_offset, start_point) = cursor.start();
1058 let overshoot = offset - start_offset;
1059 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1060 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
1061 let buffer_point = excerpt
1062 .buffer
1063 .offset_to_point(excerpt_start_offset + overshoot);
1064 *start_point + (buffer_point - excerpt_start_point)
1065 } else {
1066 self.excerpts.summary().text.lines
1067 }
1068 }
1069
1070 pub fn point_to_offset(&self, point: Point) -> usize {
1071 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
1072 cursor.seek(&point, Bias::Right, &());
1073 if let Some(excerpt) = cursor.item() {
1074 let (start_point, start_offset) = cursor.start();
1075 let overshoot = point - start_point;
1076 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1077 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
1078 let buffer_offset = excerpt
1079 .buffer
1080 .point_to_offset(excerpt_start_point + overshoot);
1081 *start_offset + buffer_offset - excerpt_start_offset
1082 } else {
1083 self.excerpts.summary().text.bytes
1084 }
1085 }
1086
1087 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
1088 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
1089 cursor.seek(&point, Bias::Right, &());
1090 if let Some(excerpt) = cursor.item() {
1091 let (start_point, start_offset) = cursor.start();
1092 let overshoot = point - start_point;
1093 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
1094 let excerpt_start_point = excerpt
1095 .buffer
1096 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
1097 let buffer_offset = excerpt
1098 .buffer
1099 .point_utf16_to_offset(excerpt_start_point + overshoot);
1100 *start_offset + (buffer_offset - excerpt_start_offset)
1101 } else {
1102 self.excerpts.summary().text.bytes
1103 }
1104 }
1105
1106 pub fn indent_column_for_line(&self, row: u32) -> u32 {
1107 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
1108 buffer
1109 .indent_column_for_line(range.start.row)
1110 .min(range.end.column)
1111 .saturating_sub(range.start.column)
1112 } else {
1113 0
1114 }
1115 }
1116
1117 pub fn line_len(&self, row: u32) -> u32 {
1118 if let Some((_, range)) = self.buffer_line_for_row(row) {
1119 range.end.column - range.start.column
1120 } else {
1121 0
1122 }
1123 }
1124
1125 fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
1126 let mut cursor = self.excerpts.cursor::<Point>();
1127 cursor.seek(&Point::new(row, 0), Bias::Right, &());
1128 if let Some(excerpt) = cursor.item() {
1129 let overshoot = row - cursor.start().row;
1130 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
1131 let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
1132 let buffer_row = excerpt_start.row + overshoot;
1133 let line_start = Point::new(buffer_row, 0);
1134 let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
1135 return Some((
1136 &excerpt.buffer,
1137 line_start.max(excerpt_start)..line_end.min(excerpt_end),
1138 ));
1139 }
1140 None
1141 }
1142
1143 pub fn max_point(&self) -> Point {
1144 self.text_summary().lines
1145 }
1146
1147 pub fn text_summary(&self) -> TextSummary {
1148 self.excerpts.summary().text
1149 }
1150
1151 pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
1152 where
1153 D: TextDimension,
1154 O: ToOffset,
1155 {
1156 let mut summary = D::default();
1157 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
1158 let mut cursor = self.excerpts.cursor::<usize>();
1159 cursor.seek(&range.start, Bias::Right, &());
1160 if let Some(excerpt) = cursor.item() {
1161 let mut end_before_newline = cursor.end(&());
1162 if excerpt.has_trailing_newline {
1163 end_before_newline -= 1;
1164 }
1165
1166 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1167 let start_in_excerpt = excerpt_start + (range.start - cursor.start());
1168 let end_in_excerpt =
1169 excerpt_start + (cmp::min(end_before_newline, range.end) - cursor.start());
1170 summary.add_assign(
1171 &excerpt
1172 .buffer
1173 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
1174 );
1175
1176 if range.end > end_before_newline {
1177 summary.add_assign(&D::from_text_summary(&TextSummary {
1178 bytes: 1,
1179 lines: Point::new(1 as u32, 0),
1180 lines_utf16: PointUtf16::new(1 as u32, 0),
1181 first_line_chars: 0,
1182 last_line_chars: 0,
1183 longest_row: 0,
1184 longest_row_chars: 0,
1185 }));
1186 }
1187
1188 cursor.next(&());
1189 }
1190
1191 if range.end > *cursor.start() {
1192 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
1193 &range.end,
1194 Bias::Right,
1195 &(),
1196 )));
1197 if let Some(excerpt) = cursor.item() {
1198 range.end = cmp::max(*cursor.start(), range.end);
1199
1200 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1201 let end_in_excerpt = excerpt_start + (range.end - cursor.start());
1202 summary.add_assign(
1203 &excerpt
1204 .buffer
1205 .text_summary_for_range(excerpt_start..end_in_excerpt),
1206 );
1207 }
1208 }
1209
1210 summary
1211 }
1212
1213 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
1214 where
1215 D: TextDimension + Ord + Sub<D, Output = D>,
1216 {
1217 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1218 cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
1219 if cursor.item().is_none() {
1220 cursor.next(&());
1221 }
1222
1223 let mut position = D::from_text_summary(&cursor.start().text);
1224 if let Some(excerpt) = cursor.item() {
1225 if excerpt.id == anchor.excerpt_id {
1226 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1227 let buffer_position = anchor.text_anchor.summary::<D>(&excerpt.buffer);
1228 if buffer_position > excerpt_buffer_start {
1229 position.add_assign(&(buffer_position - excerpt_buffer_start));
1230 }
1231 }
1232 }
1233 position
1234 }
1235
1236 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
1237 where
1238 D: TextDimension + Ord + Sub<D, Output = D>,
1239 I: 'a + IntoIterator<Item = &'a Anchor>,
1240 {
1241 let mut anchors = anchors.into_iter().peekable();
1242 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1243 let mut summaries = Vec::new();
1244 while let Some(anchor) = anchors.peek() {
1245 let excerpt_id = &anchor.excerpt_id;
1246 let excerpt_anchors = iter::from_fn(|| {
1247 let anchor = anchors.peek()?;
1248 if anchor.excerpt_id == *excerpt_id {
1249 Some(&anchors.next().unwrap().text_anchor)
1250 } else {
1251 None
1252 }
1253 });
1254
1255 cursor.seek_forward(&Some(excerpt_id), Bias::Left, &());
1256 if cursor.item().is_none() {
1257 cursor.next(&());
1258 }
1259
1260 let position = D::from_text_summary(&cursor.start().text);
1261 if let Some(excerpt) = cursor.item() {
1262 if excerpt.id == *excerpt_id {
1263 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1264 summaries.extend(
1265 excerpt
1266 .buffer
1267 .summaries_for_anchors::<D, _>(excerpt_anchors)
1268 .map(move |summary| {
1269 let mut position = position.clone();
1270 let excerpt_buffer_start = excerpt_buffer_start.clone();
1271 if summary > excerpt_buffer_start {
1272 position.add_assign(&(summary - excerpt_buffer_start));
1273 }
1274 position
1275 }),
1276 );
1277 continue;
1278 }
1279 }
1280
1281 summaries.extend(excerpt_anchors.map(|_| position.clone()));
1282 }
1283
1284 summaries
1285 }
1286
1287 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
1288 self.anchor_at(position, Bias::Left)
1289 }
1290
1291 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
1292 self.anchor_at(position, Bias::Right)
1293 }
1294
1295 pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
1296 let offset = position.to_offset(self);
1297 let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
1298 cursor.seek(&offset, Bias::Right, &());
1299 if cursor.item().is_none() && offset == cursor.start().0 && bias == Bias::Left {
1300 cursor.prev(&());
1301 }
1302 if let Some(excerpt) = cursor.item() {
1303 let mut overshoot = offset.saturating_sub(cursor.start().0);
1304 if excerpt.has_trailing_newline && offset == cursor.end(&()).0 {
1305 overshoot -= 1;
1306 bias = Bias::Right;
1307 }
1308
1309 let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1310 let text_anchor =
1311 excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
1312 Anchor {
1313 excerpt_id: excerpt.id.clone(),
1314 text_anchor,
1315 }
1316 } else if offset == 0 && bias == Bias::Left {
1317 Anchor::min()
1318 } else {
1319 Anchor::max()
1320 }
1321 }
1322
1323 pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor {
1324 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1325 cursor.seek(&Some(&excerpt_id), Bias::Left, &());
1326 if let Some(excerpt) = cursor.item() {
1327 if excerpt.id == excerpt_id {
1328 let text_anchor = excerpt.clip_anchor(text_anchor);
1329 drop(cursor);
1330 return Anchor {
1331 excerpt_id,
1332 text_anchor,
1333 };
1334 }
1335 }
1336 panic!("excerpt not found");
1337 }
1338
1339 pub fn parse_count(&self) -> usize {
1340 self.parse_count
1341 }
1342
1343 pub fn enclosing_bracket_ranges<T: ToOffset>(
1344 &self,
1345 range: Range<T>,
1346 ) -> Option<(Range<usize>, Range<usize>)> {
1347 let range = range.start.to_offset(self)..range.end.to_offset(self);
1348
1349 let mut cursor = self.excerpts.cursor::<usize>();
1350 cursor.seek(&range.start, Bias::Right, &());
1351 let start_excerpt = cursor.item();
1352
1353 cursor.seek(&range.end, Bias::Right, &());
1354 let end_excerpt = cursor.item();
1355
1356 start_excerpt
1357 .zip(end_excerpt)
1358 .and_then(|(start_excerpt, end_excerpt)| {
1359 if start_excerpt.id != end_excerpt.id {
1360 return None;
1361 }
1362
1363 let excerpt_buffer_start =
1364 start_excerpt.range.start.to_offset(&start_excerpt.buffer);
1365 let excerpt_buffer_end = excerpt_buffer_start + start_excerpt.text_summary.bytes;
1366
1367 let start_in_buffer =
1368 excerpt_buffer_start + range.start.saturating_sub(*cursor.start());
1369 let end_in_buffer =
1370 excerpt_buffer_start + range.end.saturating_sub(*cursor.start());
1371 let (mut start_bracket_range, mut end_bracket_range) = start_excerpt
1372 .buffer
1373 .enclosing_bracket_ranges(start_in_buffer..end_in_buffer)?;
1374
1375 if start_bracket_range.start >= excerpt_buffer_start
1376 && end_bracket_range.end < excerpt_buffer_end
1377 {
1378 start_bracket_range.start =
1379 cursor.start() + (start_bracket_range.start - excerpt_buffer_start);
1380 start_bracket_range.end =
1381 cursor.start() + (start_bracket_range.end - excerpt_buffer_start);
1382 end_bracket_range.start =
1383 cursor.start() + (end_bracket_range.start - excerpt_buffer_start);
1384 end_bracket_range.end =
1385 cursor.start() + (end_bracket_range.end - excerpt_buffer_start);
1386 Some((start_bracket_range, end_bracket_range))
1387 } else {
1388 None
1389 }
1390 })
1391 }
1392
1393 pub fn diagnostics_update_count(&self) -> usize {
1394 self.diagnostics_update_count
1395 }
1396
1397 pub fn language(&self) -> Option<&Arc<Language>> {
1398 self.excerpts
1399 .iter()
1400 .next()
1401 .and_then(|excerpt| excerpt.buffer.language())
1402 }
1403
1404 pub fn is_dirty(&self) -> bool {
1405 self.is_dirty
1406 }
1407
1408 pub fn has_conflict(&self) -> bool {
1409 self.has_conflict
1410 }
1411
1412 pub fn diagnostic_group<'a, O>(
1413 &'a self,
1414 group_id: usize,
1415 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1416 where
1417 O: text::FromAnchor + 'a,
1418 {
1419 self.as_singleton()
1420 .into_iter()
1421 .flat_map(move |buffer| buffer.diagnostic_group(group_id))
1422 }
1423
1424 pub fn diagnostics_in_range<'a, T, O>(
1425 &'a self,
1426 range: Range<T>,
1427 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1428 where
1429 T: 'a + ToOffset,
1430 O: 'a + text::FromAnchor,
1431 {
1432 self.as_singleton().into_iter().flat_map(move |buffer| {
1433 buffer.diagnostics_in_range(range.start.to_offset(self)..range.end.to_offset(self))
1434 })
1435 }
1436
1437 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1438 let range = range.start.to_offset(self)..range.end.to_offset(self);
1439
1440 let mut cursor = self.excerpts.cursor::<usize>();
1441 cursor.seek(&range.start, Bias::Right, &());
1442 let start_excerpt = cursor.item();
1443
1444 cursor.seek(&range.end, Bias::Right, &());
1445 let end_excerpt = cursor.item();
1446
1447 start_excerpt
1448 .zip(end_excerpt)
1449 .and_then(|(start_excerpt, end_excerpt)| {
1450 if start_excerpt.id != end_excerpt.id {
1451 return None;
1452 }
1453
1454 let excerpt_buffer_start =
1455 start_excerpt.range.start.to_offset(&start_excerpt.buffer);
1456 let excerpt_buffer_end = excerpt_buffer_start + start_excerpt.text_summary.bytes;
1457
1458 let start_in_buffer =
1459 excerpt_buffer_start + range.start.saturating_sub(*cursor.start());
1460 let end_in_buffer =
1461 excerpt_buffer_start + range.end.saturating_sub(*cursor.start());
1462 let mut ancestor_buffer_range = start_excerpt
1463 .buffer
1464 .range_for_syntax_ancestor(start_in_buffer..end_in_buffer)?;
1465 ancestor_buffer_range.start =
1466 cmp::max(ancestor_buffer_range.start, excerpt_buffer_start);
1467 ancestor_buffer_range.end = cmp::min(ancestor_buffer_range.end, excerpt_buffer_end);
1468
1469 let start = cursor.start() + (ancestor_buffer_range.start - excerpt_buffer_start);
1470 let end = cursor.start() + (ancestor_buffer_range.end - excerpt_buffer_start);
1471 Some(start..end)
1472 })
1473 }
1474
1475 fn buffer_snapshot_for_excerpt<'a>(
1476 &'a self,
1477 excerpt_id: &'a ExcerptId,
1478 ) -> Option<&'a BufferSnapshot> {
1479 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1480 cursor.seek(&Some(excerpt_id), Bias::Left, &());
1481 if let Some(excerpt) = cursor.item() {
1482 if excerpt.id == *excerpt_id {
1483 return Some(&excerpt.buffer);
1484 }
1485 }
1486 None
1487 }
1488
1489 pub fn remote_selections_in_range<'a>(
1490 &'a self,
1491 range: &'a Range<Anchor>,
1492 ) -> impl 'a + Iterator<Item = (ReplicaId, Selection<Anchor>)> {
1493 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1494 cursor.seek(&Some(&range.start.excerpt_id), Bias::Left, &());
1495 cursor
1496 .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id)
1497 .flat_map(move |excerpt| {
1498 let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone();
1499 if excerpt.id == range.start.excerpt_id {
1500 query_range.start = range.start.text_anchor.clone();
1501 }
1502 if excerpt.id == range.end.excerpt_id {
1503 query_range.end = range.end.text_anchor.clone();
1504 }
1505
1506 excerpt
1507 .buffer
1508 .remote_selections_in_range(query_range)
1509 .flat_map(move |(replica_id, selections)| {
1510 selections.map(move |selection| {
1511 let mut start = Anchor {
1512 excerpt_id: excerpt.id.clone(),
1513 text_anchor: selection.start.clone(),
1514 };
1515 let mut end = Anchor {
1516 excerpt_id: excerpt.id.clone(),
1517 text_anchor: selection.end.clone(),
1518 };
1519 if range.start.cmp(&start, self).unwrap().is_gt() {
1520 start = range.start.clone();
1521 }
1522 if range.end.cmp(&end, self).unwrap().is_lt() {
1523 end = range.end.clone();
1524 }
1525
1526 (
1527 replica_id,
1528 Selection {
1529 id: selection.id,
1530 start,
1531 end,
1532 reversed: selection.reversed,
1533 goal: selection.goal,
1534 },
1535 )
1536 })
1537 })
1538 })
1539 }
1540}
1541
1542impl History {
1543 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
1544 self.transaction_depth += 1;
1545 if self.transaction_depth == 1 {
1546 let id = post_inc(&mut self.next_transaction_id);
1547 self.undo_stack.push(Transaction {
1548 id,
1549 buffer_transactions: Default::default(),
1550 first_edit_at: now,
1551 last_edit_at: now,
1552 });
1553 Some(id)
1554 } else {
1555 None
1556 }
1557 }
1558
1559 fn end_transaction(
1560 &mut self,
1561 now: Instant,
1562 buffer_transactions: HashSet<(usize, TransactionId)>,
1563 ) -> bool {
1564 assert_ne!(self.transaction_depth, 0);
1565 self.transaction_depth -= 1;
1566 if self.transaction_depth == 0 {
1567 if buffer_transactions.is_empty() {
1568 self.undo_stack.pop();
1569 false
1570 } else {
1571 let transaction = self.undo_stack.last_mut().unwrap();
1572 transaction.last_edit_at = now;
1573 transaction.buffer_transactions.extend(buffer_transactions);
1574 true
1575 }
1576 } else {
1577 false
1578 }
1579 }
1580
1581 fn pop_undo(&mut self) -> Option<&Transaction> {
1582 assert_eq!(self.transaction_depth, 0);
1583 if let Some(transaction) = self.undo_stack.pop() {
1584 self.redo_stack.push(transaction);
1585 self.redo_stack.last()
1586 } else {
1587 None
1588 }
1589 }
1590
1591 fn pop_redo(&mut self) -> Option<&Transaction> {
1592 assert_eq!(self.transaction_depth, 0);
1593 if let Some(transaction) = self.redo_stack.pop() {
1594 self.undo_stack.push(transaction);
1595 self.undo_stack.last()
1596 } else {
1597 None
1598 }
1599 }
1600
1601 fn group(&mut self) -> Option<TransactionId> {
1602 let mut new_len = self.undo_stack.len();
1603 let mut transactions = self.undo_stack.iter_mut();
1604
1605 if let Some(mut transaction) = transactions.next_back() {
1606 while let Some(prev_transaction) = transactions.next_back() {
1607 if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval
1608 {
1609 transaction = prev_transaction;
1610 new_len -= 1;
1611 } else {
1612 break;
1613 }
1614 }
1615 }
1616
1617 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
1618 if let Some(last_transaction) = transactions_to_keep.last_mut() {
1619 if let Some(transaction) = transactions_to_merge.last() {
1620 last_transaction.last_edit_at = transaction.last_edit_at;
1621 }
1622 }
1623
1624 self.undo_stack.truncate(new_len);
1625 self.undo_stack.last().map(|t| t.id)
1626 }
1627}
1628
1629impl Excerpt {
1630 fn new(
1631 id: ExcerptId,
1632 buffer_id: usize,
1633 buffer: BufferSnapshot,
1634 range: Range<text::Anchor>,
1635 has_trailing_newline: bool,
1636 ) -> Self {
1637 Excerpt {
1638 id,
1639 max_buffer_row: range.end.to_point(&buffer).row,
1640 text_summary: buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer)),
1641 buffer_id,
1642 buffer,
1643 range,
1644 has_trailing_newline,
1645 }
1646 }
1647
1648 fn chunks_in_range<'a>(
1649 &'a self,
1650 range: Range<usize>,
1651 theme: Option<&'a SyntaxTheme>,
1652 ) -> ExcerptChunks<'a> {
1653 let content_start = self.range.start.to_offset(&self.buffer);
1654 let chunks_start = content_start + range.start;
1655 let chunks_end = content_start + cmp::min(range.end, self.text_summary.bytes);
1656
1657 let footer_height = if self.has_trailing_newline
1658 && range.start <= self.text_summary.bytes
1659 && range.end > self.text_summary.bytes
1660 {
1661 1
1662 } else {
1663 0
1664 };
1665
1666 let content_chunks = self.buffer.chunks(chunks_start..chunks_end, theme);
1667
1668 ExcerptChunks {
1669 content_chunks,
1670 footer_height,
1671 }
1672 }
1673
1674 fn bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
1675 let content_start = self.range.start.to_offset(&self.buffer);
1676 let bytes_start = content_start + range.start;
1677 let bytes_end = content_start + cmp::min(range.end, self.text_summary.bytes);
1678 let footer_height = if self.has_trailing_newline
1679 && range.start <= self.text_summary.bytes
1680 && range.end > self.text_summary.bytes
1681 {
1682 1
1683 } else {
1684 0
1685 };
1686 let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end);
1687
1688 ExcerptBytes {
1689 content_bytes,
1690 footer_height,
1691 }
1692 }
1693
1694 fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
1695 if text_anchor
1696 .cmp(&self.range.start, &self.buffer)
1697 .unwrap()
1698 .is_lt()
1699 {
1700 self.range.start.clone()
1701 } else if text_anchor
1702 .cmp(&self.range.end, &self.buffer)
1703 .unwrap()
1704 .is_gt()
1705 {
1706 self.range.end.clone()
1707 } else {
1708 text_anchor
1709 }
1710 }
1711}
1712
1713impl fmt::Debug for Excerpt {
1714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1715 f.debug_struct("Excerpt")
1716 .field("id", &self.id)
1717 .field("buffer_id", &self.buffer_id)
1718 .field("range", &self.range)
1719 .field("text_summary", &self.text_summary)
1720 .field("has_trailing_newline", &self.has_trailing_newline)
1721 .finish()
1722 }
1723}
1724
1725impl sum_tree::Item for Excerpt {
1726 type Summary = ExcerptSummary;
1727
1728 fn summary(&self) -> Self::Summary {
1729 let mut text = self.text_summary.clone();
1730 if self.has_trailing_newline {
1731 text += TextSummary::from("\n");
1732 }
1733 ExcerptSummary {
1734 excerpt_id: self.id.clone(),
1735 max_buffer_row: self.max_buffer_row,
1736 text,
1737 }
1738 }
1739}
1740
1741impl sum_tree::Summary for ExcerptSummary {
1742 type Context = ();
1743
1744 fn add_summary(&mut self, summary: &Self, _: &()) {
1745 debug_assert!(summary.excerpt_id > self.excerpt_id);
1746 self.excerpt_id = summary.excerpt_id.clone();
1747 self.text.add_summary(&summary.text, &());
1748 self.max_buffer_row = cmp::max(self.max_buffer_row, summary.max_buffer_row);
1749 }
1750}
1751
1752impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1753 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1754 *self += &summary.text;
1755 }
1756}
1757
1758impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1759 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1760 *self += summary.text.bytes;
1761 }
1762}
1763
1764impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1765 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1766 Ord::cmp(self, &cursor_location.text.bytes)
1767 }
1768}
1769
1770impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1771 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1772 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1773 }
1774}
1775
1776impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1777 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1778 *self += summary.text.lines;
1779 }
1780}
1781
1782impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1783 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1784 *self += summary.text.lines_utf16
1785 }
1786}
1787
1788impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1789 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1790 *self = Some(&summary.excerpt_id);
1791 }
1792}
1793
1794impl<'a> MultiBufferRows<'a> {
1795 pub fn seek(&mut self, row: u32) {
1796 self.buffer_row_range = 0..0;
1797
1798 self.excerpts
1799 .seek_forward(&Point::new(row, 0), Bias::Right, &());
1800 if self.excerpts.item().is_none() {
1801 self.excerpts.prev(&());
1802
1803 if self.excerpts.item().is_none() && row == 0 {
1804 self.buffer_row_range = 0..1;
1805 return;
1806 }
1807 }
1808
1809 if let Some(excerpt) = self.excerpts.item() {
1810 let overshoot = row - self.excerpts.start().row;
1811 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer).row;
1812 self.buffer_row_range.start = excerpt_start + overshoot;
1813 self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1;
1814 }
1815 }
1816}
1817
1818impl<'a> Iterator for MultiBufferRows<'a> {
1819 type Item = Option<u32>;
1820
1821 fn next(&mut self) -> Option<Self::Item> {
1822 loop {
1823 if !self.buffer_row_range.is_empty() {
1824 let row = Some(self.buffer_row_range.start);
1825 self.buffer_row_range.start += 1;
1826 return Some(row);
1827 }
1828 self.excerpts.item()?;
1829 self.excerpts.next(&());
1830 let excerpt = self.excerpts.item()?;
1831 self.buffer_row_range.start = excerpt.range.start.to_point(&excerpt.buffer).row;
1832 self.buffer_row_range.end =
1833 self.buffer_row_range.start + excerpt.text_summary.lines.row + 1;
1834 }
1835 }
1836}
1837
1838impl<'a> MultiBufferChunks<'a> {
1839 pub fn offset(&self) -> usize {
1840 self.range.start
1841 }
1842
1843 pub fn seek(&mut self, offset: usize) {
1844 self.range.start = offset;
1845 self.excerpts.seek(&offset, Bias::Right, &());
1846 if let Some(excerpt) = self.excerpts.item() {
1847 self.excerpt_chunks = Some(excerpt.chunks_in_range(
1848 self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(),
1849 self.theme,
1850 ));
1851 } else {
1852 self.excerpt_chunks = None;
1853 }
1854 }
1855}
1856
1857impl<'a> Iterator for MultiBufferChunks<'a> {
1858 type Item = Chunk<'a>;
1859
1860 fn next(&mut self) -> Option<Self::Item> {
1861 if self.range.is_empty() {
1862 None
1863 } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
1864 self.range.start += chunk.text.len();
1865 Some(chunk)
1866 } else {
1867 self.excerpts.next(&());
1868 let excerpt = self.excerpts.item()?;
1869 self.excerpt_chunks = Some(
1870 excerpt.chunks_in_range(0..self.range.end - self.excerpts.start(), self.theme),
1871 );
1872 self.next()
1873 }
1874 }
1875}
1876
1877impl<'a> MultiBufferBytes<'a> {
1878 fn consume(&mut self, len: usize) {
1879 self.range.start += len;
1880 self.chunk = &self.chunk[len..];
1881
1882 if !self.range.is_empty() && self.chunk.is_empty() {
1883 if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
1884 self.chunk = chunk;
1885 } else {
1886 self.excerpts.next(&());
1887 if let Some(excerpt) = self.excerpts.item() {
1888 let mut excerpt_bytes =
1889 excerpt.bytes_in_range(0..self.range.end - self.excerpts.start());
1890 self.chunk = excerpt_bytes.next().unwrap();
1891 self.excerpt_bytes = Some(excerpt_bytes);
1892 }
1893 }
1894 }
1895 }
1896}
1897
1898impl<'a> Iterator for MultiBufferBytes<'a> {
1899 type Item = &'a [u8];
1900
1901 fn next(&mut self) -> Option<Self::Item> {
1902 let chunk = self.chunk;
1903 if chunk.is_empty() {
1904 None
1905 } else {
1906 self.consume(chunk.len());
1907 Some(chunk)
1908 }
1909 }
1910}
1911
1912impl<'a> io::Read for MultiBufferBytes<'a> {
1913 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1914 let len = cmp::min(buf.len(), self.chunk.len());
1915 buf[..len].copy_from_slice(&self.chunk[..len]);
1916 if len > 0 {
1917 self.consume(len);
1918 }
1919 Ok(len)
1920 }
1921}
1922
1923impl<'a> Iterator for ExcerptBytes<'a> {
1924 type Item = &'a [u8];
1925
1926 fn next(&mut self) -> Option<Self::Item> {
1927 if let Some(chunk) = self.content_bytes.next() {
1928 if !chunk.is_empty() {
1929 return Some(chunk);
1930 }
1931 }
1932
1933 if self.footer_height > 0 {
1934 let result = &NEWLINES[..self.footer_height];
1935 self.footer_height = 0;
1936 return Some(result);
1937 }
1938
1939 None
1940 }
1941}
1942
1943impl<'a> Iterator for ExcerptChunks<'a> {
1944 type Item = Chunk<'a>;
1945
1946 fn next(&mut self) -> Option<Self::Item> {
1947 if let Some(chunk) = self.content_chunks.next() {
1948 return Some(chunk);
1949 }
1950
1951 if self.footer_height > 0 {
1952 let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) };
1953 self.footer_height = 0;
1954 return Some(Chunk {
1955 text,
1956 ..Default::default()
1957 });
1958 }
1959
1960 None
1961 }
1962}
1963
1964impl ToOffset for Point {
1965 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1966 snapshot.point_to_offset(*self)
1967 }
1968}
1969
1970impl ToOffset for PointUtf16 {
1971 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1972 snapshot.point_utf16_to_offset(*self)
1973 }
1974}
1975
1976impl ToOffset for usize {
1977 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1978 assert!(*self <= snapshot.len(), "offset is out of range");
1979 *self
1980 }
1981}
1982
1983impl ToPoint for usize {
1984 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1985 snapshot.offset_to_point(*self)
1986 }
1987}
1988
1989impl ToPoint for Point {
1990 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1991 *self
1992 }
1993}
1994
1995#[cfg(test)]
1996mod tests {
1997 use super::*;
1998 use gpui::MutableAppContext;
1999 use language::{Buffer, Rope};
2000 use rand::prelude::*;
2001 use std::env;
2002 use text::{Point, RandomCharIter};
2003 use util::test::sample_text;
2004
2005 #[gpui::test]
2006 fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
2007 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
2008 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
2009
2010 let snapshot = multibuffer.read(cx).snapshot(cx);
2011 assert_eq!(snapshot.text(), buffer.read(cx).text());
2012
2013 assert_eq!(
2014 snapshot.buffer_rows(0).collect::<Vec<_>>(),
2015 (0..buffer.read(cx).row_count())
2016 .map(Some)
2017 .collect::<Vec<_>>()
2018 );
2019
2020 buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX\n", cx));
2021 let snapshot = multibuffer.read(cx).snapshot(cx);
2022
2023 assert_eq!(snapshot.text(), buffer.read(cx).text());
2024 assert_eq!(
2025 snapshot.buffer_rows(0).collect::<Vec<_>>(),
2026 (0..buffer.read(cx).row_count())
2027 .map(Some)
2028 .collect::<Vec<_>>()
2029 );
2030 }
2031
2032 #[gpui::test]
2033 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
2034 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
2035 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
2036 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2037
2038 let subscription = multibuffer.update(cx, |multibuffer, cx| {
2039 let subscription = multibuffer.subscribe();
2040 multibuffer.push_excerpt(
2041 ExcerptProperties {
2042 buffer: &buffer_1,
2043 range: Point::new(1, 2)..Point::new(2, 5),
2044 },
2045 cx,
2046 );
2047 assert_eq!(
2048 subscription.consume().into_inner(),
2049 [Edit {
2050 old: 0..0,
2051 new: 0..10
2052 }]
2053 );
2054
2055 multibuffer.push_excerpt(
2056 ExcerptProperties {
2057 buffer: &buffer_1,
2058 range: Point::new(3, 3)..Point::new(4, 4),
2059 },
2060 cx,
2061 );
2062 multibuffer.push_excerpt(
2063 ExcerptProperties {
2064 buffer: &buffer_2,
2065 range: Point::new(3, 1)..Point::new(3, 3),
2066 },
2067 cx,
2068 );
2069 assert_eq!(
2070 subscription.consume().into_inner(),
2071 [Edit {
2072 old: 10..10,
2073 new: 10..22
2074 }]
2075 );
2076
2077 subscription
2078 });
2079
2080 let snapshot = multibuffer.read(cx).snapshot(cx);
2081 assert_eq!(
2082 snapshot.text(),
2083 concat!(
2084 "bbbb\n", // Preserve newlines
2085 "ccccc\n", //
2086 "ddd\n", //
2087 "eeee\n", //
2088 "jj" //
2089 )
2090 );
2091 assert_eq!(
2092 snapshot.buffer_rows(0).collect::<Vec<_>>(),
2093 [Some(1), Some(2), Some(3), Some(4), Some(3)]
2094 );
2095 assert_eq!(
2096 snapshot.buffer_rows(2).collect::<Vec<_>>(),
2097 [Some(3), Some(4), Some(3)]
2098 );
2099 assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]);
2100 assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []);
2101
2102 buffer_1.update(cx, |buffer, cx| {
2103 buffer.edit(
2104 [
2105 Point::new(0, 0)..Point::new(0, 0),
2106 Point::new(2, 1)..Point::new(2, 3),
2107 ],
2108 "\n",
2109 cx,
2110 );
2111 });
2112
2113 let snapshot = multibuffer.read(cx).snapshot(cx);
2114 assert_eq!(
2115 snapshot.text(),
2116 concat!(
2117 "bbbb\n", // Preserve newlines
2118 "c\n", //
2119 "cc\n", //
2120 "ddd\n", //
2121 "eeee\n", //
2122 "jj" //
2123 )
2124 );
2125
2126 assert_eq!(
2127 subscription.consume().into_inner(),
2128 [Edit {
2129 old: 6..8,
2130 new: 6..7
2131 }]
2132 );
2133
2134 let snapshot = multibuffer.read(cx).snapshot(cx);
2135 assert_eq!(
2136 snapshot.clip_point(Point::new(0, 5), Bias::Left),
2137 Point::new(0, 4)
2138 );
2139 assert_eq!(
2140 snapshot.clip_point(Point::new(0, 5), Bias::Right),
2141 Point::new(0, 4)
2142 );
2143 assert_eq!(
2144 snapshot.clip_point(Point::new(5, 1), Bias::Right),
2145 Point::new(5, 1)
2146 );
2147 assert_eq!(
2148 snapshot.clip_point(Point::new(5, 2), Bias::Right),
2149 Point::new(5, 2)
2150 );
2151 assert_eq!(
2152 snapshot.clip_point(Point::new(5, 3), Bias::Right),
2153 Point::new(5, 2)
2154 );
2155
2156 let snapshot = multibuffer.update(cx, |multibuffer, cx| {
2157 let buffer_2_excerpt_id = multibuffer.excerpt_ids_for_buffer(&buffer_2)[0].clone();
2158 multibuffer.remove_excerpts(&[buffer_2_excerpt_id], cx);
2159 multibuffer.snapshot(cx)
2160 });
2161
2162 assert_eq!(
2163 snapshot.text(),
2164 concat!(
2165 "bbbb\n", // Preserve newlines
2166 "c\n", //
2167 "cc\n", //
2168 "ddd\n", //
2169 "eeee", //
2170 )
2171 );
2172 }
2173
2174 #[gpui::test]
2175 fn test_empty_excerpt_buffer(cx: &mut MutableAppContext) {
2176 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2177
2178 let snapshot = multibuffer.read(cx).snapshot(cx);
2179 assert_eq!(snapshot.text(), "");
2180 assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), &[Some(0)]);
2181 assert_eq!(snapshot.buffer_rows(1).collect::<Vec<_>>(), &[]);
2182 }
2183
2184 #[gpui::test]
2185 fn test_singleton_multibuffer_anchors(cx: &mut MutableAppContext) {
2186 let buffer = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
2187 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
2188 let old_snapshot = multibuffer.read(cx).snapshot(cx);
2189 buffer.update(cx, |buffer, cx| {
2190 buffer.edit([0..0], "X", cx);
2191 buffer.edit([5..5], "Y", cx);
2192 });
2193 let new_snapshot = multibuffer.read(cx).snapshot(cx);
2194
2195 assert_eq!(old_snapshot.text(), "abcd");
2196 assert_eq!(new_snapshot.text(), "XabcdY");
2197
2198 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
2199 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
2200 assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
2201 assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
2202 }
2203
2204 #[gpui::test]
2205 fn test_multibuffer_anchors(cx: &mut MutableAppContext) {
2206 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
2207 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "efghi", cx));
2208 let multibuffer = cx.add_model(|cx| {
2209 let mut multibuffer = MultiBuffer::new(0);
2210 multibuffer.push_excerpt(
2211 ExcerptProperties {
2212 buffer: &buffer_1,
2213 range: 0..4,
2214 },
2215 cx,
2216 );
2217 multibuffer.push_excerpt(
2218 ExcerptProperties {
2219 buffer: &buffer_2,
2220 range: 0..5,
2221 },
2222 cx,
2223 );
2224 multibuffer
2225 });
2226 let old_snapshot = multibuffer.read(cx).snapshot(cx);
2227
2228 assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0);
2229 assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0);
2230 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
2231 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
2232 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
2233 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
2234
2235 buffer_1.update(cx, |buffer, cx| {
2236 buffer.edit([0..0], "W", cx);
2237 buffer.edit([5..5], "X", cx);
2238 });
2239 buffer_2.update(cx, |buffer, cx| {
2240 buffer.edit([0..0], "Y", cx);
2241 buffer.edit([6..0], "Z", cx);
2242 });
2243 let new_snapshot = multibuffer.read(cx).snapshot(cx);
2244
2245 assert_eq!(old_snapshot.text(), "abcd\nefghi");
2246 assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ");
2247
2248 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
2249 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
2250 assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2);
2251 assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
2252 assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
2253 assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
2254 assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7);
2255 assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8);
2256 assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13);
2257 assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
2258 }
2259
2260 #[gpui::test(iterations = 100)]
2261 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
2262 let operations = env::var("OPERATIONS")
2263 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2264 .unwrap_or(10);
2265
2266 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
2267 let list = cx.add_model(|_| MultiBuffer::new(0));
2268 let mut excerpt_ids = Vec::new();
2269 let mut expected_excerpts = Vec::<(ModelHandle<Buffer>, Range<text::Anchor>)>::new();
2270 let mut old_versions = Vec::new();
2271
2272 for _ in 0..operations {
2273 match rng.gen_range(0..100) {
2274 0..=19 if !buffers.is_empty() => {
2275 let buffer = buffers.choose(&mut rng).unwrap();
2276 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
2277 }
2278 20..=29 if !expected_excerpts.is_empty() => {
2279 let ix = rng.gen_range(0..expected_excerpts.len());
2280 let id = excerpt_ids.remove(ix);
2281 let (buffer, range) = expected_excerpts.remove(ix);
2282 let buffer = buffer.read(cx);
2283 log::info!(
2284 "Removing excerpt {}: {:?}",
2285 ix,
2286 buffer
2287 .text_for_range(range.to_offset(&buffer))
2288 .collect::<String>(),
2289 );
2290 list.update(cx, |list, cx| list.remove_excerpts(&[id], cx));
2291 }
2292 _ => {
2293 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
2294 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
2295 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
2296 buffers.last().unwrap()
2297 } else {
2298 buffers.choose(&mut rng).unwrap()
2299 };
2300
2301 let buffer = buffer_handle.read(cx);
2302 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
2303 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
2304 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
2305 log::info!(
2306 "Pushing excerpt for buffer {}: {:?}[{:?}] = {:?}",
2307 buffer_handle.id(),
2308 buffer.text(),
2309 start_ix..end_ix,
2310 &buffer.text()[start_ix..end_ix]
2311 );
2312
2313 let excerpt_id = list.update(cx, |list, cx| {
2314 list.push_excerpt(
2315 ExcerptProperties {
2316 buffer: &buffer_handle,
2317 range: start_ix..end_ix,
2318 },
2319 cx,
2320 )
2321 });
2322 excerpt_ids.push(excerpt_id);
2323 expected_excerpts.push((buffer_handle.clone(), anchor_range));
2324 }
2325 }
2326
2327 if rng.gen_bool(0.3) {
2328 list.update(cx, |list, cx| {
2329 old_versions.push((list.snapshot(cx), list.subscribe()));
2330 })
2331 }
2332
2333 let snapshot = list.read(cx).snapshot(cx);
2334
2335 let mut excerpt_starts = Vec::new();
2336 let mut expected_text = String::new();
2337 let mut expected_buffer_rows = Vec::new();
2338 for (buffer, range) in &expected_excerpts {
2339 let buffer = buffer.read(cx);
2340 let buffer_range = range.to_offset(buffer);
2341
2342 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
2343 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
2344 expected_text.push('\n');
2345
2346 let buffer_row_range = buffer.offset_to_point(buffer_range.start).row
2347 ..=buffer.offset_to_point(buffer_range.end).row;
2348 for row in buffer_row_range {
2349 expected_buffer_rows.push(Some(row));
2350 }
2351 }
2352 // Remove final trailing newline.
2353 if !expected_excerpts.is_empty() {
2354 expected_text.pop();
2355 }
2356
2357 // Always report one buffer row
2358 if expected_buffer_rows.is_empty() {
2359 expected_buffer_rows.push(Some(0));
2360 }
2361
2362 assert_eq!(snapshot.text(), expected_text);
2363 log::info!("MultiBuffer text: {:?}", expected_text);
2364
2365 assert_eq!(
2366 snapshot.buffer_rows(0).collect::<Vec<_>>(),
2367 expected_buffer_rows,
2368 );
2369
2370 for _ in 0..5 {
2371 let start_row = rng.gen_range(0..=expected_buffer_rows.len());
2372 assert_eq!(
2373 snapshot.buffer_rows(start_row as u32).collect::<Vec<_>>(),
2374 &expected_buffer_rows[start_row..],
2375 "buffer_rows({})",
2376 start_row
2377 );
2378 }
2379
2380 assert_eq!(
2381 snapshot.max_buffer_row(),
2382 expected_buffer_rows
2383 .into_iter()
2384 .filter_map(|r| r)
2385 .max()
2386 .unwrap()
2387 );
2388
2389 let mut excerpt_starts = excerpt_starts.into_iter();
2390 for (buffer, range) in &expected_excerpts {
2391 let buffer_id = buffer.id();
2392 let buffer = buffer.read(cx);
2393 let buffer_range = range.to_offset(buffer);
2394 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
2395 let buffer_start_point_utf16 =
2396 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
2397
2398 let excerpt_start = excerpt_starts.next().unwrap();
2399 let mut offset = excerpt_start.bytes;
2400 let mut buffer_offset = buffer_range.start;
2401 let mut point = excerpt_start.lines;
2402 let mut buffer_point = buffer_start_point;
2403 let mut point_utf16 = excerpt_start.lines_utf16;
2404 let mut buffer_point_utf16 = buffer_start_point_utf16;
2405 for ch in buffer
2406 .snapshot()
2407 .chunks(buffer_range.clone(), None)
2408 .flat_map(|c| c.text.chars())
2409 {
2410 for _ in 0..ch.len_utf8() {
2411 let left_offset = snapshot.clip_offset(offset, Bias::Left);
2412 let right_offset = snapshot.clip_offset(offset, Bias::Right);
2413 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
2414 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
2415 assert_eq!(
2416 left_offset,
2417 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
2418 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
2419 offset,
2420 buffer_id,
2421 buffer_offset,
2422 );
2423 assert_eq!(
2424 right_offset,
2425 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
2426 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
2427 offset,
2428 buffer_id,
2429 buffer_offset,
2430 );
2431
2432 let left_point = snapshot.clip_point(point, Bias::Left);
2433 let right_point = snapshot.clip_point(point, Bias::Right);
2434 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
2435 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
2436 assert_eq!(
2437 left_point,
2438 excerpt_start.lines + (buffer_left_point - buffer_start_point),
2439 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
2440 point,
2441 buffer_id,
2442 buffer_point,
2443 );
2444 assert_eq!(
2445 right_point,
2446 excerpt_start.lines + (buffer_right_point - buffer_start_point),
2447 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
2448 point,
2449 buffer_id,
2450 buffer_point,
2451 );
2452
2453 assert_eq!(
2454 snapshot.point_to_offset(left_point),
2455 left_offset,
2456 "point_to_offset({:?})",
2457 left_point,
2458 );
2459 assert_eq!(
2460 snapshot.offset_to_point(left_offset),
2461 left_point,
2462 "offset_to_point({:?})",
2463 left_offset,
2464 );
2465
2466 offset += 1;
2467 buffer_offset += 1;
2468 if ch == '\n' {
2469 point += Point::new(1, 0);
2470 buffer_point += Point::new(1, 0);
2471 } else {
2472 point += Point::new(0, 1);
2473 buffer_point += Point::new(0, 1);
2474 }
2475 }
2476
2477 for _ in 0..ch.len_utf16() {
2478 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
2479 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
2480 let buffer_left_point_utf16 =
2481 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
2482 let buffer_right_point_utf16 =
2483 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
2484 assert_eq!(
2485 left_point_utf16,
2486 excerpt_start.lines_utf16
2487 + (buffer_left_point_utf16 - buffer_start_point_utf16),
2488 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
2489 point_utf16,
2490 buffer_id,
2491 buffer_point_utf16,
2492 );
2493 assert_eq!(
2494 right_point_utf16,
2495 excerpt_start.lines_utf16
2496 + (buffer_right_point_utf16 - buffer_start_point_utf16),
2497 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
2498 point_utf16,
2499 buffer_id,
2500 buffer_point_utf16,
2501 );
2502
2503 if ch == '\n' {
2504 point_utf16 += PointUtf16::new(1, 0);
2505 buffer_point_utf16 += PointUtf16::new(1, 0);
2506 } else {
2507 point_utf16 += PointUtf16::new(0, 1);
2508 buffer_point_utf16 += PointUtf16::new(0, 1);
2509 }
2510 }
2511 }
2512 }
2513
2514 for (row, line) in expected_text.split('\n').enumerate() {
2515 assert_eq!(
2516 snapshot.line_len(row as u32),
2517 line.len() as u32,
2518 "line_len({}).",
2519 row
2520 );
2521 }
2522
2523 let text_rope = Rope::from(expected_text.as_str());
2524 for _ in 0..10 {
2525 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
2526 let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
2527
2528 assert_eq!(
2529 snapshot
2530 .text_for_range(start_ix..end_ix)
2531 .collect::<String>(),
2532 &expected_text[start_ix..end_ix],
2533 "incorrect text for range {:?}",
2534 start_ix..end_ix
2535 );
2536
2537 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
2538 assert_eq!(
2539 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
2540 expected_summary,
2541 "incorrect summary for range {:?}",
2542 start_ix..end_ix
2543 );
2544 }
2545
2546 for _ in 0..10 {
2547 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
2548 assert_eq!(
2549 snapshot.reversed_chars_at(end_ix).collect::<String>(),
2550 expected_text[..end_ix].chars().rev().collect::<String>(),
2551 );
2552 }
2553
2554 for _ in 0..10 {
2555 let end_ix = rng.gen_range(0..=text_rope.len());
2556 let start_ix = rng.gen_range(0..=end_ix);
2557 assert_eq!(
2558 snapshot
2559 .bytes_in_range(start_ix..end_ix)
2560 .flatten()
2561 .copied()
2562 .collect::<Vec<_>>(),
2563 expected_text.as_bytes()[start_ix..end_ix].to_vec(),
2564 "bytes_in_range({:?})",
2565 start_ix..end_ix,
2566 );
2567 }
2568 }
2569
2570 let snapshot = list.read(cx).snapshot(cx);
2571 for (old_snapshot, subscription) in old_versions {
2572 let edits = subscription.consume().into_inner();
2573
2574 log::info!(
2575 "applying subscription edits to old text: {:?}: {:?}",
2576 old_snapshot.text(),
2577 edits,
2578 );
2579
2580 let mut text = old_snapshot.text();
2581 for edit in edits {
2582 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2583 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2584 }
2585 assert_eq!(text.to_string(), snapshot.text());
2586 }
2587 }
2588
2589 #[gpui::test]
2590 fn test_history(cx: &mut MutableAppContext) {
2591 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
2592 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
2593 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2594 let group_interval = multibuffer.read(cx).history.group_interval;
2595 multibuffer.update(cx, |multibuffer, cx| {
2596 multibuffer.push_excerpt(
2597 ExcerptProperties {
2598 buffer: &buffer_1,
2599 range: 0..buffer_1.read(cx).len(),
2600 },
2601 cx,
2602 );
2603 multibuffer.push_excerpt(
2604 ExcerptProperties {
2605 buffer: &buffer_2,
2606 range: 0..buffer_2.read(cx).len(),
2607 },
2608 cx,
2609 );
2610 });
2611
2612 let mut now = Instant::now();
2613
2614 multibuffer.update(cx, |multibuffer, cx| {
2615 multibuffer.start_transaction_at(now, cx);
2616 multibuffer.edit(
2617 [
2618 Point::new(0, 0)..Point::new(0, 0),
2619 Point::new(1, 0)..Point::new(1, 0),
2620 ],
2621 "A",
2622 cx,
2623 );
2624 multibuffer.edit(
2625 [
2626 Point::new(0, 1)..Point::new(0, 1),
2627 Point::new(1, 1)..Point::new(1, 1),
2628 ],
2629 "B",
2630 cx,
2631 );
2632 multibuffer.end_transaction_at(now, cx);
2633 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2634
2635 now += 2 * group_interval;
2636 multibuffer.start_transaction_at(now, cx);
2637 multibuffer.edit([2..2], "C", cx);
2638 multibuffer.end_transaction_at(now, cx);
2639 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2640
2641 multibuffer.undo(cx);
2642 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2643
2644 multibuffer.undo(cx);
2645 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
2646
2647 multibuffer.redo(cx);
2648 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2649
2650 multibuffer.redo(cx);
2651 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2652
2653 buffer_1.update(cx, |buffer_1, cx| buffer_1.undo(cx));
2654 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2655
2656 multibuffer.undo(cx);
2657 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
2658
2659 multibuffer.redo(cx);
2660 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2661
2662 multibuffer.redo(cx);
2663 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2664
2665 multibuffer.undo(cx);
2666 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
2667
2668 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
2669 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
2670
2671 multibuffer.undo(cx);
2672 assert_eq!(multibuffer.read(cx).text(), "C1234\n5678");
2673 });
2674 }
2675}