1mod anchor;
2
3pub use anchor::{Anchor, AnchorRangeExt};
4use anyhow::Result;
5use clock::ReplicaId;
6use collections::{HashMap, HashSet};
7use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, 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, io,
15 iter::{self, FromIterator, Peekable},
16 ops::{Range, Sub},
17 sync::Arc,
18 time::{Duration, Instant, SystemTime},
19};
20use sum_tree::{Bias, Cursor, SumTree};
21use text::{
22 locator::Locator,
23 rope::TextDimension,
24 subscription::{Subscription, Topic},
25 AnchorRangeExt as _, Edit, Point, PointUtf16, TextSummary,
26};
27use theme::SyntaxTheme;
28use util::post_inc;
29
30const NEWLINES: &'static [u8] = &[b'\n'; u8::MAX as usize];
31
32pub type ExcerptId = Locator;
33
34pub struct MultiBuffer {
35 snapshot: RefCell<MultiBufferSnapshot>,
36 buffers: HashMap<usize, BufferState>,
37 subscriptions: Topic,
38 singleton: bool,
39 replica_id: ReplicaId,
40 history: History,
41}
42
43struct History {
44 next_transaction_id: usize,
45 undo_stack: Vec<Transaction>,
46 redo_stack: Vec<Transaction>,
47 transaction_depth: usize,
48 group_interval: Duration,
49}
50
51struct Transaction {
52 id: usize,
53 buffer_transactions: HashSet<(usize, text::TransactionId)>,
54 first_edit_at: Instant,
55 last_edit_at: Instant,
56}
57
58pub trait ToOffset: 'static + std::fmt::Debug {
59 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
60}
61
62pub trait ToPoint: 'static + std::fmt::Debug {
63 fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
64}
65
66pub trait FromAnchor: 'static {
67 fn from_anchor(anchor: &Anchor, snapshot: &MultiBufferSnapshot) -> Self;
68}
69
70#[derive(Debug)]
71struct BufferState {
72 buffer: ModelHandle<Buffer>,
73 last_version: clock::Global,
74 last_parse_count: usize,
75 last_diagnostics_update_count: usize,
76 excerpts: Vec<ExcerptId>,
77}
78
79#[derive(Clone, Default)]
80pub struct MultiBufferSnapshot {
81 excerpts: SumTree<Excerpt>,
82 parse_count: usize,
83 diagnostics_update_count: usize,
84}
85
86pub struct ExcerptProperties<'a, T> {
87 pub buffer: &'a ModelHandle<Buffer>,
88 pub range: Range<T>,
89 pub header_height: u8,
90}
91
92#[derive(Clone)]
93struct Excerpt {
94 id: ExcerptId,
95 buffer_id: usize,
96 buffer: BufferSnapshot,
97 range: Range<text::Anchor>,
98 text_summary: TextSummary,
99 header_height: u8,
100 has_trailing_newline: bool,
101}
102
103#[derive(Clone, Debug, Default)]
104struct ExcerptSummary {
105 excerpt_id: ExcerptId,
106 text: TextSummary,
107}
108
109pub struct MultiBufferChunks<'a> {
110 range: Range<usize>,
111 cursor: Cursor<'a, Excerpt, usize>,
112 header_height: u8,
113 has_trailing_newline: bool,
114 excerpt_chunks: Option<BufferChunks<'a>>,
115 theme: Option<&'a SyntaxTheme>,
116}
117
118pub struct MultiBufferBytes<'a> {
119 chunks: Peekable<MultiBufferChunks<'a>>,
120}
121
122impl MultiBuffer {
123 pub fn new(replica_id: ReplicaId) -> Self {
124 Self {
125 snapshot: Default::default(),
126 buffers: Default::default(),
127 subscriptions: Default::default(),
128 singleton: false,
129 replica_id,
130 history: History {
131 next_transaction_id: Default::default(),
132 undo_stack: Default::default(),
133 redo_stack: Default::default(),
134 transaction_depth: 0,
135 group_interval: Duration::from_millis(300),
136 },
137 }
138 }
139
140 pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
141 let mut this = Self::new(buffer.read(cx).replica_id());
142 this.singleton = true;
143 this.push_excerpt(
144 ExcerptProperties {
145 buffer: &buffer,
146 range: text::Anchor::min()..text::Anchor::max(),
147 header_height: 0,
148 },
149 cx,
150 );
151 this
152 }
153
154 pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle<Self> {
155 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
156 cx.add_model(|cx| Self::singleton(buffer, cx))
157 }
158
159 pub fn replica_id(&self) -> ReplicaId {
160 self.replica_id
161 }
162
163 pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
164 self.sync(cx);
165 self.snapshot.borrow().clone()
166 }
167
168 pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
169 self.sync(cx);
170 self.snapshot.borrow()
171 }
172
173 pub fn as_singleton(&self) -> Option<&ModelHandle<Buffer>> {
174 if self.singleton {
175 return Some(&self.buffers.values().next().unwrap().buffer);
176 } else {
177 None
178 }
179 }
180
181 pub fn subscribe(&mut self) -> Subscription {
182 self.subscriptions.subscribe()
183 }
184
185 pub fn edit<I, S, T>(&mut self, ranges: I, new_text: T, cx: &mut ModelContext<Self>)
186 where
187 I: IntoIterator<Item = Range<S>>,
188 S: ToOffset,
189 T: Into<String>,
190 {
191 self.edit_internal(ranges, new_text, false, cx)
192 }
193
194 pub fn edit_with_autoindent<I, S, T>(
195 &mut self,
196 ranges: I,
197 new_text: T,
198 cx: &mut ModelContext<Self>,
199 ) where
200 I: IntoIterator<Item = Range<S>>,
201 S: ToOffset,
202 T: Into<String>,
203 {
204 self.edit_internal(ranges, new_text, true, cx)
205 }
206
207 pub fn edit_internal<I, S, T>(
208 &mut self,
209 ranges_iter: I,
210 new_text: T,
211 autoindent: bool,
212 cx: &mut ModelContext<Self>,
213 ) where
214 I: IntoIterator<Item = Range<S>>,
215 S: ToOffset,
216 T: Into<String>,
217 {
218 if let Some(buffer) = self.as_singleton() {
219 let snapshot = self.read(cx);
220 let ranges = ranges_iter
221 .into_iter()
222 .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
223 return buffer.update(cx, |buffer, cx| {
224 if autoindent {
225 buffer.edit_with_autoindent(ranges, new_text, cx)
226 } else {
227 buffer.edit(ranges, new_text, cx)
228 }
229 });
230 }
231
232 let snapshot = self.read(cx);
233 let mut buffer_edits: HashMap<usize, Vec<(Range<usize>, bool)>> = Default::default();
234 let mut cursor = snapshot.excerpts.cursor::<usize>();
235 for range in ranges_iter {
236 let start = range.start.to_offset(&snapshot);
237 let end = range.end.to_offset(&snapshot);
238 cursor.seek(&start, Bias::Right, &());
239 let start_excerpt = cursor.item().expect("start offset out of bounds");
240 let start_overshoot =
241 (start - cursor.start()).saturating_sub(start_excerpt.header_height as usize);
242 let buffer_start =
243 start_excerpt.range.start.to_offset(&start_excerpt.buffer) + start_overshoot;
244
245 cursor.seek(&end, Bias::Right, &());
246 let end_excerpt = cursor.item().expect("end offset out of bounds");
247 let end_overshoot =
248 (end - cursor.start()).saturating_sub(end_excerpt.header_height as usize);
249 let buffer_end = end_excerpt.range.start.to_offset(&end_excerpt.buffer) + end_overshoot;
250
251 if start_excerpt.id == end_excerpt.id {
252 buffer_edits
253 .entry(start_excerpt.buffer_id)
254 .or_insert(Vec::new())
255 .push((buffer_start..buffer_end, true));
256 } else {
257 let start_excerpt_range =
258 buffer_start..start_excerpt.range.end.to_offset(&start_excerpt.buffer);
259 let end_excerpt_range =
260 end_excerpt.range.start.to_offset(&end_excerpt.buffer)..buffer_end;
261 buffer_edits
262 .entry(start_excerpt.buffer_id)
263 .or_insert(Vec::new())
264 .push((start_excerpt_range, true));
265 buffer_edits
266 .entry(end_excerpt.buffer_id)
267 .or_insert(Vec::new())
268 .push((end_excerpt_range, false));
269
270 cursor.seek(&start, Bias::Right, &());
271 cursor.next(&());
272 while let Some(excerpt) = cursor.item() {
273 if excerpt.id == end_excerpt.id {
274 break;
275 }
276
277 let excerpt_range = start_excerpt.range.end.to_offset(&start_excerpt.buffer)
278 ..start_excerpt.range.end.to_offset(&start_excerpt.buffer);
279 buffer_edits
280 .entry(excerpt.buffer_id)
281 .or_insert(Vec::new())
282 .push((excerpt_range, false));
283 cursor.next(&());
284 }
285 }
286 }
287
288 let new_text = new_text.into();
289 for (buffer_id, mut edits) in buffer_edits {
290 edits.sort_unstable_by_key(|(range, _)| range.start);
291 self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
292 let mut edits = edits.into_iter().peekable();
293 let mut insertions = Vec::new();
294 let mut deletions = Vec::new();
295 while let Some((mut range, mut is_insertion)) = edits.next() {
296 while let Some((next_range, next_is_insertion)) = edits.peek() {
297 if range.end >= next_range.start {
298 range.end = cmp::max(next_range.end, range.end);
299 is_insertion |= *next_is_insertion;
300 edits.next();
301 } else {
302 break;
303 }
304 }
305
306 if is_insertion {
307 insertions.push(
308 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
309 );
310 } else {
311 deletions.push(
312 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
313 );
314 }
315 }
316
317 if autoindent {
318 buffer.edit_with_autoindent(deletions, "", cx);
319 buffer.edit_with_autoindent(insertions, new_text.clone(), cx);
320 } else {
321 buffer.edit(deletions, "", cx);
322 buffer.edit(insertions, new_text.clone(), cx);
323 }
324 })
325 }
326 }
327
328 pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
329 self.start_transaction_at(Instant::now(), cx)
330 }
331
332 pub(crate) fn start_transaction_at(
333 &mut self,
334 now: Instant,
335 cx: &mut ModelContext<Self>,
336 ) -> Option<TransactionId> {
337 if let Some(buffer) = self.as_singleton() {
338 return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
339 }
340
341 for BufferState { buffer, .. } in self.buffers.values() {
342 buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
343 }
344 self.history.start_transaction(now)
345 }
346
347 pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
348 self.end_transaction_at(Instant::now(), cx)
349 }
350
351 pub(crate) fn end_transaction_at(
352 &mut self,
353 now: Instant,
354 cx: &mut ModelContext<Self>,
355 ) -> Option<TransactionId> {
356 if let Some(buffer) = self.as_singleton() {
357 return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
358 }
359
360 let mut buffer_transactions = HashSet::default();
361 for BufferState { buffer, .. } in self.buffers.values() {
362 if let Some(transaction_id) =
363 buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
364 {
365 buffer_transactions.insert((buffer.id(), transaction_id));
366 }
367 }
368
369 if self.history.end_transaction(now, buffer_transactions) {
370 let transaction_id = self.history.group().unwrap();
371 Some(transaction_id)
372 } else {
373 None
374 }
375 }
376
377 pub fn set_active_selections(
378 &mut self,
379 selections: &[Selection<Anchor>],
380 cx: &mut ModelContext<Self>,
381 ) {
382 let mut selections_by_buffer: HashMap<usize, Vec<Selection<text::Anchor>>> =
383 Default::default();
384 let snapshot = self.read(cx);
385 let mut cursor = snapshot.excerpts.cursor::<Option<&ExcerptId>>();
386 for selection in selections {
387 cursor.seek(&Some(&selection.start.excerpt_id), Bias::Left, &());
388 while let Some(excerpt) = cursor.item() {
389 if excerpt.id > selection.end.excerpt_id {
390 break;
391 }
392
393 let mut start = excerpt.range.start.clone();
394 let mut end = excerpt.range.end.clone();
395 if excerpt.id == selection.start.excerpt_id {
396 start = selection.start.text_anchor.clone();
397 }
398 if excerpt.id == selection.end.excerpt_id {
399 end = selection.end.text_anchor.clone();
400 }
401 selections_by_buffer
402 .entry(excerpt.buffer_id)
403 .or_default()
404 .push(Selection {
405 id: selection.id,
406 start,
407 end,
408 reversed: selection.reversed,
409 goal: selection.goal,
410 });
411
412 cursor.next(&());
413 }
414 }
415
416 for (buffer_id, mut selections) in selections_by_buffer {
417 self.buffers[&buffer_id].buffer.update(cx, |buffer, cx| {
418 selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer).unwrap());
419 let mut selections = selections.into_iter().peekable();
420 let merged_selections = Arc::from_iter(iter::from_fn(|| {
421 let mut selection = selections.next()?;
422 while let Some(next_selection) = selections.peek() {
423 if selection
424 .end
425 .cmp(&next_selection.start, buffer)
426 .unwrap()
427 .is_ge()
428 {
429 let next_selection = selections.next().unwrap();
430 if next_selection
431 .end
432 .cmp(&selection.end, buffer)
433 .unwrap()
434 .is_ge()
435 {
436 selection.end = next_selection.end;
437 }
438 } else {
439 break;
440 }
441 }
442 Some(selection)
443 }));
444 buffer.set_active_selections(merged_selections, cx);
445 });
446 }
447 }
448
449 pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
450 for buffer in self.buffers.values() {
451 buffer
452 .buffer
453 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
454 }
455 }
456
457 pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
458 if let Some(buffer) = self.as_singleton() {
459 return buffer.update(cx, |buffer, cx| buffer.undo(cx));
460 }
461
462 while let Some(transaction) = self.history.pop_undo() {
463 let mut undone = false;
464 for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
465 if let Some(BufferState { buffer, .. }) = self.buffers.get(&buffer_id) {
466 undone |= buffer.update(cx, |buf, cx| {
467 buf.undo_transaction(*buffer_transaction_id, cx)
468 });
469 }
470 }
471
472 if undone {
473 return Some(transaction.id);
474 }
475 }
476
477 None
478 }
479
480 pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
481 if let Some(buffer) = self.as_singleton() {
482 return buffer.update(cx, |buffer, cx| buffer.redo(cx));
483 }
484
485 while let Some(transaction) = self.history.pop_redo() {
486 let mut redone = false;
487 for (buffer_id, buffer_transaction_id) in &transaction.buffer_transactions {
488 if let Some(BufferState { buffer, .. }) = self.buffers.get(&buffer_id) {
489 redone |= buffer.update(cx, |buf, cx| {
490 buf.redo_transaction(*buffer_transaction_id, cx)
491 });
492 }
493 }
494
495 if redone {
496 return Some(transaction.id);
497 }
498 }
499
500 None
501 }
502
503 pub fn push_excerpt<O>(
504 &mut self,
505 props: ExcerptProperties<O>,
506 cx: &mut ModelContext<Self>,
507 ) -> ExcerptId
508 where
509 O: text::ToOffset,
510 {
511 assert_eq!(self.history.transaction_depth, 0);
512 self.sync(cx);
513
514 let buffer = &props.buffer;
515 cx.subscribe(buffer, Self::on_buffer_event).detach();
516
517 let buffer = props.buffer.read(cx);
518 let range = buffer.anchor_before(&props.range.start)..buffer.anchor_after(&props.range.end);
519 let mut snapshot = self.snapshot.borrow_mut();
520 let prev_id = snapshot.excerpts.last().map(|e| &e.id);
521 let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
522
523 let edit_start = snapshot.excerpts.summary().text.bytes;
524 let excerpt = Excerpt::new(
525 id.clone(),
526 props.buffer.id(),
527 buffer.snapshot(),
528 range,
529 props.header_height,
530 !self.singleton,
531 );
532 let edit = Edit {
533 old: edit_start..edit_start,
534 new: edit_start..edit_start + excerpt.text_summary.bytes,
535 };
536 snapshot.excerpts.push(excerpt, &());
537 self.buffers
538 .entry(props.buffer.id())
539 .or_insert_with(|| BufferState {
540 buffer: props.buffer.clone(),
541 last_version: buffer.version(),
542 last_parse_count: buffer.parse_count(),
543 last_diagnostics_update_count: buffer.diagnostics_update_count(),
544 excerpts: Default::default(),
545 })
546 .excerpts
547 .push(id.clone());
548
549 self.subscriptions.publish_mut([edit]);
550
551 id
552 }
553
554 fn on_buffer_event(
555 &mut self,
556 _: ModelHandle<Buffer>,
557 event: &Event,
558 cx: &mut ModelContext<Self>,
559 ) {
560 cx.emit(event.clone());
561 }
562
563 pub fn save(
564 &mut self,
565 cx: &mut ModelContext<Self>,
566 ) -> Result<Task<Result<(clock::Global, SystemTime)>>> {
567 self.as_singleton()
568 .unwrap()
569 .update(cx, |buffer, cx| buffer.save(cx))
570 }
571
572 pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
573 self.buffers
574 .values()
575 .next()
576 .and_then(|state| state.buffer.read(cx).language())
577 }
578
579 pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
580 self.as_singleton().unwrap().read(cx).file()
581 }
582
583 pub fn is_dirty(&self, cx: &AppContext) -> bool {
584 self.as_singleton().unwrap().read(cx).is_dirty()
585 }
586
587 pub fn has_conflict(&self, cx: &AppContext) -> bool {
588 self.as_singleton().unwrap().read(cx).has_conflict()
589 }
590
591 pub fn is_parsing(&self, cx: &AppContext) -> bool {
592 self.as_singleton().unwrap().read(cx).is_parsing()
593 }
594
595 fn sync(&self, cx: &AppContext) {
596 let mut snapshot = self.snapshot.borrow_mut();
597 let mut excerpts_to_edit = Vec::new();
598 let mut reparsed = false;
599 let mut diagnostics_updated = false;
600 for buffer_state in self.buffers.values() {
601 let buffer = buffer_state.buffer.read(cx);
602 let buffer_edited = buffer.version().gt(&buffer_state.last_version);
603 let buffer_reparsed = buffer.parse_count() > buffer_state.last_parse_count;
604 let buffer_diagnostics_updated =
605 buffer.diagnostics_update_count() > buffer_state.last_diagnostics_update_count;
606 if buffer_edited || buffer_reparsed || buffer_diagnostics_updated {
607 excerpts_to_edit.extend(
608 buffer_state
609 .excerpts
610 .iter()
611 .map(|excerpt_id| (excerpt_id, buffer_state, buffer_edited)),
612 );
613 }
614
615 reparsed |= buffer_reparsed;
616 diagnostics_updated |= buffer_diagnostics_updated;
617 }
618 if reparsed {
619 snapshot.parse_count += 1;
620 }
621 if diagnostics_updated {
622 snapshot.diagnostics_update_count += 1;
623 }
624 excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _, _)| *excerpt_id);
625
626 let mut edits = Vec::new();
627 let mut new_excerpts = SumTree::new();
628 let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
629
630 for (id, buffer_state, buffer_edited) in excerpts_to_edit {
631 new_excerpts.push_tree(cursor.slice(&Some(id), Bias::Left, &()), &());
632 let old_excerpt = cursor.item().unwrap();
633 let buffer = buffer_state.buffer.read(cx);
634
635 let mut new_excerpt;
636 if buffer_edited {
637 edits.extend(
638 buffer
639 .edits_since_in_range::<usize>(
640 old_excerpt.buffer.version(),
641 old_excerpt.range.clone(),
642 )
643 .map(|mut edit| {
644 let excerpt_old_start =
645 cursor.start().1 + old_excerpt.header_height as usize;
646 let excerpt_new_start = new_excerpts.summary().text.bytes
647 + old_excerpt.header_height as usize;
648 edit.old.start += excerpt_old_start;
649 edit.old.end += excerpt_old_start;
650 edit.new.start += excerpt_new_start;
651 edit.new.end += excerpt_new_start;
652 edit
653 }),
654 );
655
656 new_excerpt = Excerpt::new(
657 id.clone(),
658 buffer_state.buffer.id(),
659 buffer.snapshot(),
660 old_excerpt.range.clone(),
661 old_excerpt.header_height,
662 !self.singleton,
663 );
664 } else {
665 new_excerpt = old_excerpt.clone();
666 new_excerpt.buffer = buffer.snapshot();
667 }
668
669 new_excerpts.push(new_excerpt, &());
670 cursor.next(&());
671 }
672 new_excerpts.push_tree(cursor.suffix(&()), &());
673
674 drop(cursor);
675 snapshot.excerpts = new_excerpts;
676
677 self.subscriptions.publish(edits);
678 }
679}
680
681#[cfg(any(test, feature = "test-support"))]
682impl MultiBuffer {
683 pub fn randomly_edit<R: rand::Rng>(
684 &mut self,
685 rng: &mut R,
686 count: usize,
687 cx: &mut ModelContext<Self>,
688 ) {
689 self.as_singleton()
690 .unwrap()
691 .update(cx, |buffer, cx| buffer.randomly_edit(rng, count, cx));
692 self.sync(cx);
693 }
694}
695
696impl Entity for MultiBuffer {
697 type Event = language::Event;
698}
699
700impl MultiBufferSnapshot {
701 pub fn text(&self) -> String {
702 self.chunks(0..self.len(), None)
703 .map(|chunk| chunk.text)
704 .collect()
705 }
706
707 pub fn reversed_chars_at<'a, T: ToOffset>(
708 &'a self,
709 position: T,
710 ) -> impl Iterator<Item = char> + 'a {
711 // TODO
712 let offset = position.to_offset(self);
713 self.as_singleton().unwrap().reversed_chars_at(offset)
714 }
715
716 pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
717 let offset = position.to_offset(self);
718 self.text_for_range(offset..self.len())
719 .flat_map(|chunk| chunk.chars())
720 }
721
722 pub fn text_for_range<'a, T: ToOffset>(
723 &'a self,
724 range: Range<T>,
725 ) -> impl Iterator<Item = &'a str> {
726 self.chunks(range, None).map(|chunk| chunk.text)
727 }
728
729 pub fn is_line_blank(&self, row: u32) -> bool {
730 self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
731 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
732 }
733
734 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
735 where
736 T: ToOffset,
737 {
738 let position = position.to_offset(self);
739 position == self.clip_offset(position, Bias::Left)
740 && self
741 .bytes_in_range(position..self.len())
742 .flatten()
743 .copied()
744 .take(needle.len())
745 .eq(needle.bytes())
746 }
747
748 fn as_singleton(&self) -> Option<&BufferSnapshot> {
749 let mut excerpts = self.excerpts.iter();
750 let buffer = excerpts.next().map(|excerpt| &excerpt.buffer);
751 if excerpts.next().is_none() {
752 buffer
753 } else {
754 None
755 }
756 }
757
758 pub fn len(&self) -> usize {
759 self.excerpts.summary().text.bytes
760 }
761
762 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
763 let mut cursor = self.excerpts.cursor::<usize>();
764 cursor.seek(&offset, Bias::Right, &());
765 if let Some(excerpt) = cursor.item() {
766 let start_after_header = *cursor.start() + excerpt.header_height as usize;
767 if offset < start_after_header {
768 *cursor.start()
769 } else {
770 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
771 let buffer_offset = excerpt
772 .buffer
773 .clip_offset(excerpt_start + (offset - start_after_header), bias);
774 let offset_in_excerpt = if buffer_offset > excerpt_start {
775 buffer_offset - excerpt_start
776 } else {
777 0
778 };
779 start_after_header + offset_in_excerpt
780 }
781 } else {
782 self.excerpts.summary().text.bytes
783 }
784 }
785
786 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
787 let mut cursor = self.excerpts.cursor::<Point>();
788 cursor.seek(&point, Bias::Right, &());
789 if let Some(excerpt) = cursor.item() {
790 let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
791 if point < start_after_header {
792 *cursor.start()
793 } else {
794 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
795 let buffer_point = excerpt
796 .buffer
797 .clip_point(excerpt_start + (point - start_after_header), bias);
798 let point_in_excerpt = if buffer_point > excerpt_start {
799 buffer_point - excerpt_start
800 } else {
801 Point::zero()
802 };
803 start_after_header + point_in_excerpt
804 }
805 } else {
806 self.excerpts.summary().text.lines
807 }
808 }
809
810 pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
811 let mut cursor = self.excerpts.cursor::<PointUtf16>();
812 cursor.seek(&point, Bias::Right, &());
813 if let Some(excerpt) = cursor.item() {
814 let start_after_header =
815 *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
816 if point < start_after_header {
817 *cursor.start()
818 } else {
819 let excerpt_start = excerpt
820 .buffer
821 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
822 let buffer_point = excerpt
823 .buffer
824 .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
825 let point_in_excerpt = if buffer_point > excerpt_start {
826 buffer_point - excerpt_start
827 } else {
828 PointUtf16::new(0, 0)
829 };
830 start_after_header + point_in_excerpt
831 }
832 } else {
833 self.excerpts.summary().text.lines_utf16
834 }
835 }
836
837 pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
838 MultiBufferBytes {
839 chunks: self.chunks(range, None).peekable(),
840 }
841 }
842
843 pub fn chunks<'a, T: ToOffset>(
844 &'a self,
845 range: Range<T>,
846 theme: Option<&'a SyntaxTheme>,
847 ) -> MultiBufferChunks<'a> {
848 let mut result = MultiBufferChunks {
849 range: 0..range.end.to_offset(self),
850 cursor: self.excerpts.cursor::<usize>(),
851 header_height: 0,
852 excerpt_chunks: None,
853 has_trailing_newline: false,
854 theme,
855 };
856 result.seek(range.start.to_offset(self));
857 result
858 }
859
860 pub fn offset_to_point(&self, offset: usize) -> Point {
861 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
862 cursor.seek(&offset, Bias::Right, &());
863 if let Some(excerpt) = cursor.item() {
864 let (start_offset, start_point) = cursor.start();
865 let overshoot = offset - start_offset;
866 let header_height = excerpt.header_height as usize;
867 if overshoot < header_height {
868 *start_point
869 } else {
870 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
871 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
872 let buffer_point = excerpt
873 .buffer
874 .offset_to_point(excerpt_start_offset + (overshoot - header_height));
875 *start_point
876 + Point::new(header_height as u32, 0)
877 + (buffer_point - excerpt_start_point)
878 }
879 } else {
880 self.excerpts.summary().text.lines
881 }
882 }
883
884 pub fn point_to_offset(&self, point: Point) -> usize {
885 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
886 cursor.seek(&point, Bias::Right, &());
887 if let Some(excerpt) = cursor.item() {
888 let (start_point, start_offset) = cursor.start();
889 let overshoot = point - start_point;
890 let header_height = Point::new(excerpt.header_height as u32, 0);
891 if overshoot < header_height {
892 *start_offset
893 } else {
894 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
895 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
896 let buffer_offset = excerpt
897 .buffer
898 .point_to_offset(excerpt_start_point + (overshoot - header_height));
899 *start_offset + excerpt.header_height as usize + buffer_offset
900 - excerpt_start_offset
901 }
902 } else {
903 self.excerpts.summary().text.bytes
904 }
905 }
906
907 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
908 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
909 cursor.seek(&point, Bias::Right, &());
910 if let Some(excerpt) = cursor.item() {
911 let (start_point, start_offset) = cursor.start();
912 let overshoot = point - start_point;
913 let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
914 if overshoot < header_height {
915 *start_offset
916 } else {
917 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
918 let excerpt_start_point = excerpt
919 .buffer
920 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
921 let buffer_offset = excerpt
922 .buffer
923 .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
924 *start_offset
925 + excerpt.header_height as usize
926 + (buffer_offset - excerpt_start_offset)
927 }
928 } else {
929 self.excerpts.summary().text.bytes
930 }
931 }
932
933 pub fn indent_column_for_line(&self, row: u32) -> u32 {
934 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
935 buffer
936 .indent_column_for_line(range.start.row)
937 .min(range.end.column)
938 .saturating_sub(range.start.column)
939 } else {
940 0
941 }
942 }
943
944 pub fn line_len(&self, row: u32) -> u32 {
945 if let Some((_, range)) = self.buffer_line_for_row(row) {
946 range.end.column - range.start.column
947 } else {
948 0
949 }
950 }
951
952 fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
953 let mut cursor = self.excerpts.cursor::<Point>();
954 cursor.seek(&Point::new(row, 0), Bias::Right, &());
955 if let Some(excerpt) = cursor.item() {
956 let overshoot = row - cursor.start().row;
957 let header_height = excerpt.header_height as u32;
958 if overshoot >= header_height {
959 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
960 let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
961 let buffer_row = excerpt_start.row + overshoot - header_height;
962 let line_start = Point::new(buffer_row, 0);
963 let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
964 return Some((
965 &excerpt.buffer,
966 line_start.max(excerpt_start)..line_end.min(excerpt_end),
967 ));
968 }
969 }
970 None
971 }
972
973 pub fn max_point(&self) -> Point {
974 self.text_summary().lines
975 }
976
977 pub fn text_summary(&self) -> TextSummary {
978 self.excerpts.summary().text
979 }
980
981 pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
982 where
983 D: TextDimension,
984 O: ToOffset,
985 {
986 let mut summary = D::default();
987 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
988 let mut cursor = self.excerpts.cursor::<usize>();
989 cursor.seek(&range.start, Bias::Right, &());
990 if let Some(excerpt) = cursor.item() {
991 let start_after_header = cursor.start() + excerpt.header_height as usize;
992 if range.start < start_after_header {
993 let header_len = cmp::min(range.end, start_after_header) - range.start;
994 summary.add_assign(&D::from_text_summary(&TextSummary {
995 bytes: header_len,
996 lines: Point::new(header_len as u32, 0),
997 lines_utf16: PointUtf16::new(header_len as u32, 0),
998 first_line_chars: 0,
999 last_line_chars: 0,
1000 longest_row: 0,
1001 longest_row_chars: 0,
1002 }));
1003 range.start = start_after_header;
1004 range.end = cmp::max(range.start, range.end);
1005 }
1006
1007 let mut end_before_newline = cursor.end(&());
1008 if excerpt.has_trailing_newline {
1009 end_before_newline -= 1;
1010 }
1011
1012 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1013 let start_in_excerpt = excerpt_start + (range.start - start_after_header);
1014 let end_in_excerpt =
1015 excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
1016 summary.add_assign(
1017 &excerpt
1018 .buffer
1019 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
1020 );
1021
1022 if range.end > end_before_newline {
1023 summary.add_assign(&D::from_text_summary(&TextSummary {
1024 bytes: 1,
1025 lines: Point::new(1 as u32, 0),
1026 lines_utf16: PointUtf16::new(1 as u32, 0),
1027 first_line_chars: 0,
1028 last_line_chars: 0,
1029 longest_row: 0,
1030 longest_row_chars: 0,
1031 }));
1032 }
1033
1034 cursor.next(&());
1035 }
1036
1037 if range.end > *cursor.start() {
1038 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
1039 &range.end,
1040 Bias::Right,
1041 &(),
1042 )));
1043 if let Some(excerpt) = cursor.item() {
1044 let start_after_header = cursor.start() + excerpt.header_height as usize;
1045 let header_len =
1046 cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
1047 summary.add_assign(&D::from_text_summary(&TextSummary {
1048 bytes: header_len,
1049 lines: Point::new(header_len as u32, 0),
1050 lines_utf16: PointUtf16::new(header_len as u32, 0),
1051 first_line_chars: 0,
1052 last_line_chars: 0,
1053 longest_row: 0,
1054 longest_row_chars: 0,
1055 }));
1056 range.end = cmp::max(start_after_header, range.end);
1057
1058 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1059 let end_in_excerpt = excerpt_start + (range.end - start_after_header);
1060 summary.add_assign(
1061 &excerpt
1062 .buffer
1063 .text_summary_for_range(excerpt_start..end_in_excerpt),
1064 );
1065 cursor.next(&());
1066 }
1067 }
1068
1069 summary
1070 }
1071
1072 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
1073 where
1074 D: TextDimension + Ord + Sub<D, Output = D>,
1075 {
1076 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1077 cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
1078 if let Some(excerpt) = cursor.item() {
1079 if excerpt.id == anchor.excerpt_id {
1080 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1081 excerpt_start.add_summary(&excerpt.header_summary(), &());
1082 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1083 let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
1084 if buffer_point > excerpt_buffer_start {
1085 excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
1086 }
1087 return excerpt_start;
1088 }
1089 }
1090 D::from_text_summary(&cursor.start().text)
1091 }
1092
1093 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
1094 where
1095 D: TextDimension + Ord + Sub<D, Output = D>,
1096 I: 'a + IntoIterator<Item = &'a Anchor>,
1097 {
1098 let mut anchors = anchors.into_iter().peekable();
1099 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1100 let mut summaries = Vec::new();
1101 while let Some(anchor) = anchors.peek() {
1102 let excerpt_id = &anchor.excerpt_id;
1103 let excerpt_anchors = iter::from_fn(|| {
1104 let anchor = anchors.peek()?;
1105 if anchor.excerpt_id == *excerpt_id {
1106 Some(&anchors.next().unwrap().text_anchor)
1107 } else {
1108 None
1109 }
1110 });
1111
1112 cursor.seek_forward(&Some(excerpt_id), Bias::Left, &());
1113 if let Some(excerpt) = cursor.item() {
1114 if excerpt.id == *excerpt_id {
1115 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1116 excerpt_start.add_summary(&excerpt.header_summary(), &());
1117 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1118 summaries.extend(
1119 excerpt
1120 .buffer
1121 .summaries_for_anchors::<D, _>(excerpt_anchors)
1122 .map(move |summary| {
1123 let mut excerpt_start = excerpt_start.clone();
1124 let excerpt_buffer_start = excerpt_buffer_start.clone();
1125 if summary > excerpt_buffer_start {
1126 excerpt_start.add_assign(&(summary - excerpt_buffer_start));
1127 }
1128 excerpt_start
1129 }),
1130 );
1131 continue;
1132 }
1133 }
1134
1135 let summary = D::from_text_summary(&cursor.start().text);
1136 summaries.extend(excerpt_anchors.map(|_| summary.clone()));
1137 }
1138
1139 summaries
1140 }
1141
1142 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
1143 self.anchor_at(position, Bias::Left)
1144 }
1145
1146 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
1147 self.anchor_at(position, Bias::Right)
1148 }
1149
1150 pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
1151 let offset = position.to_offset(self);
1152 let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
1153 cursor.seek(&offset, Bias::Right, &());
1154 if let Some(excerpt) = cursor.item() {
1155 let start_after_header = cursor.start().0 + excerpt.header_height as usize;
1156 let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1157 let overshoot = offset.saturating_sub(start_after_header);
1158 Anchor {
1159 excerpt_id: excerpt.id.clone(),
1160 text_anchor: excerpt.buffer.anchor_at(buffer_start + overshoot, bias),
1161 }
1162 } else if offset == 0 && bias == Bias::Left {
1163 Anchor::min()
1164 } else {
1165 Anchor::max()
1166 }
1167 }
1168
1169 pub fn parse_count(&self) -> usize {
1170 self.parse_count
1171 }
1172
1173 pub fn enclosing_bracket_ranges<T: ToOffset>(
1174 &self,
1175 range: Range<T>,
1176 ) -> Option<(Range<usize>, Range<usize>)> {
1177 let range = range.start.to_offset(self)..range.end.to_offset(self);
1178 self.as_singleton().unwrap().enclosing_bracket_ranges(range)
1179 }
1180
1181 pub fn diagnostics_update_count(&self) -> usize {
1182 self.diagnostics_update_count
1183 }
1184
1185 pub fn language(&self) -> Option<&Arc<Language>> {
1186 self.excerpts
1187 .iter()
1188 .next()
1189 .and_then(|excerpt| excerpt.buffer.language())
1190 }
1191
1192 pub fn diagnostic_group<'a, O>(
1193 &'a self,
1194 group_id: usize,
1195 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1196 where
1197 O: text::FromAnchor + 'a,
1198 {
1199 self.as_singleton().unwrap().diagnostic_group(group_id)
1200 }
1201
1202 pub fn diagnostics_in_range<'a, T, O>(
1203 &'a self,
1204 range: Range<T>,
1205 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1206 where
1207 T: 'a + ToOffset,
1208 O: 'a + text::FromAnchor,
1209 {
1210 let range = range.start.to_offset(self)..range.end.to_offset(self);
1211 self.as_singleton().unwrap().diagnostics_in_range(range)
1212 }
1213
1214 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1215 let range = range.start.to_offset(self)..range.end.to_offset(self);
1216 self.as_singleton()
1217 .unwrap()
1218 .range_for_syntax_ancestor(range)
1219 }
1220
1221 fn buffer_snapshot_for_excerpt<'a>(
1222 &'a self,
1223 excerpt_id: &'a ExcerptId,
1224 ) -> Option<&'a BufferSnapshot> {
1225 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1226 cursor.seek(&Some(excerpt_id), Bias::Left, &());
1227 if let Some(excerpt) = cursor.item() {
1228 if excerpt.id == *excerpt_id {
1229 return Some(&excerpt.buffer);
1230 }
1231 }
1232 None
1233 }
1234
1235 pub fn remote_selections_in_range<'a>(
1236 &'a self,
1237 range: &'a Range<Anchor>,
1238 ) -> impl 'a + Iterator<Item = (ReplicaId, Selection<Anchor>)> {
1239 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1240 cursor.seek(&Some(&range.start.excerpt_id), Bias::Left, &());
1241 cursor
1242 .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id)
1243 .flat_map(move |excerpt| {
1244 let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone();
1245 if excerpt.id == range.start.excerpt_id {
1246 query_range.start = range.start.text_anchor.clone();
1247 }
1248 if excerpt.id == range.end.excerpt_id {
1249 query_range.end = range.end.text_anchor.clone();
1250 }
1251
1252 excerpt
1253 .buffer
1254 .remote_selections_in_range(query_range)
1255 .flat_map(move |(replica_id, selections)| {
1256 selections.map(move |selection| {
1257 let mut start = Anchor {
1258 excerpt_id: excerpt.id.clone(),
1259 text_anchor: selection.start.clone(),
1260 };
1261 let mut end = Anchor {
1262 excerpt_id: excerpt.id.clone(),
1263 text_anchor: selection.end.clone(),
1264 };
1265 if range.start.cmp(&start, self).unwrap().is_gt() {
1266 start = range.start.clone();
1267 }
1268 if range.end.cmp(&end, self).unwrap().is_lt() {
1269 end = range.end.clone();
1270 }
1271
1272 (
1273 replica_id,
1274 Selection {
1275 id: selection.id,
1276 start,
1277 end,
1278 reversed: selection.reversed,
1279 goal: selection.goal,
1280 },
1281 )
1282 })
1283 })
1284 })
1285 }
1286}
1287
1288impl History {
1289 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
1290 self.transaction_depth += 1;
1291 if self.transaction_depth == 1 {
1292 let id = post_inc(&mut self.next_transaction_id);
1293 self.undo_stack.push(Transaction {
1294 id,
1295 buffer_transactions: Default::default(),
1296 first_edit_at: now,
1297 last_edit_at: now,
1298 });
1299 Some(id)
1300 } else {
1301 None
1302 }
1303 }
1304
1305 fn end_transaction(
1306 &mut self,
1307 now: Instant,
1308 buffer_transactions: HashSet<(usize, TransactionId)>,
1309 ) -> bool {
1310 assert_ne!(self.transaction_depth, 0);
1311 self.transaction_depth -= 1;
1312 if self.transaction_depth == 0 {
1313 if buffer_transactions.is_empty() {
1314 self.undo_stack.pop();
1315 false
1316 } else {
1317 let transaction = self.undo_stack.last_mut().unwrap();
1318 transaction.last_edit_at = now;
1319 transaction.buffer_transactions.extend(buffer_transactions);
1320 true
1321 }
1322 } else {
1323 false
1324 }
1325 }
1326
1327 fn pop_undo(&mut self) -> Option<&Transaction> {
1328 assert_eq!(self.transaction_depth, 0);
1329 if let Some(transaction) = self.undo_stack.pop() {
1330 self.redo_stack.push(transaction);
1331 self.redo_stack.last()
1332 } else {
1333 None
1334 }
1335 }
1336
1337 fn pop_redo(&mut self) -> Option<&Transaction> {
1338 assert_eq!(self.transaction_depth, 0);
1339 if let Some(transaction) = self.redo_stack.pop() {
1340 self.undo_stack.push(transaction);
1341 self.undo_stack.last()
1342 } else {
1343 None
1344 }
1345 }
1346
1347 fn group(&mut self) -> Option<TransactionId> {
1348 let mut new_len = self.undo_stack.len();
1349 let mut transactions = self.undo_stack.iter_mut();
1350
1351 if let Some(mut transaction) = transactions.next_back() {
1352 while let Some(prev_transaction) = transactions.next_back() {
1353 if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval
1354 {
1355 transaction = prev_transaction;
1356 new_len -= 1;
1357 } else {
1358 break;
1359 }
1360 }
1361 }
1362
1363 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
1364 if let Some(last_transaction) = transactions_to_keep.last_mut() {
1365 if let Some(transaction) = transactions_to_merge.last() {
1366 last_transaction.last_edit_at = transaction.last_edit_at;
1367 }
1368 }
1369
1370 self.undo_stack.truncate(new_len);
1371 self.undo_stack.last().map(|t| t.id)
1372 }
1373}
1374
1375impl Excerpt {
1376 fn new(
1377 id: ExcerptId,
1378 buffer_id: usize,
1379 buffer: BufferSnapshot,
1380 range: Range<text::Anchor>,
1381 header_height: u8,
1382 has_trailing_newline: bool,
1383 ) -> Self {
1384 let mut text_summary =
1385 buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1386 if header_height > 0 {
1387 text_summary.first_line_chars = 0;
1388 text_summary.lines.row += header_height as u32;
1389 text_summary.lines_utf16.row += header_height as u32;
1390 text_summary.bytes += header_height as usize;
1391 text_summary.longest_row += header_height as u32;
1392 }
1393 if has_trailing_newline {
1394 text_summary.last_line_chars = 0;
1395 text_summary.lines.row += 1;
1396 text_summary.lines.column = 0;
1397 text_summary.lines_utf16.row += 1;
1398 text_summary.lines_utf16.column = 0;
1399 text_summary.bytes += 1;
1400 }
1401
1402 Excerpt {
1403 id,
1404 buffer_id,
1405 buffer,
1406 range,
1407 text_summary,
1408 header_height,
1409 has_trailing_newline,
1410 }
1411 }
1412
1413 fn header_summary(&self) -> TextSummary {
1414 TextSummary {
1415 bytes: self.header_height as usize,
1416 lines: Point::new(self.header_height as u32, 0),
1417 lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1418 first_line_chars: 0,
1419 last_line_chars: 0,
1420 longest_row: 0,
1421 longest_row_chars: 0,
1422 }
1423 }
1424}
1425
1426impl sum_tree::Item for Excerpt {
1427 type Summary = ExcerptSummary;
1428
1429 fn summary(&self) -> Self::Summary {
1430 ExcerptSummary {
1431 excerpt_id: self.id.clone(),
1432 text: self.text_summary.clone(),
1433 }
1434 }
1435}
1436
1437impl sum_tree::Summary for ExcerptSummary {
1438 type Context = ();
1439
1440 fn add_summary(&mut self, summary: &Self, _: &()) {
1441 debug_assert!(summary.excerpt_id > self.excerpt_id);
1442 self.excerpt_id = summary.excerpt_id.clone();
1443 self.text.add_summary(&summary.text, &());
1444 }
1445}
1446
1447impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1448 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1449 *self += &summary.text;
1450 }
1451}
1452
1453impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1454 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1455 *self += summary.text.bytes;
1456 }
1457}
1458
1459impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1460 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1461 Ord::cmp(self, &cursor_location.text.bytes)
1462 }
1463}
1464
1465impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1466 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1467 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1468 }
1469}
1470
1471impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1472 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1473 *self += summary.text.lines;
1474 }
1475}
1476
1477impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1478 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1479 *self += summary.text.lines_utf16
1480 }
1481}
1482
1483impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1484 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1485 *self = Some(&summary.excerpt_id);
1486 }
1487}
1488
1489impl<'a> MultiBufferChunks<'a> {
1490 pub fn offset(&self) -> usize {
1491 self.range.start
1492 }
1493
1494 pub fn seek(&mut self, offset: usize) {
1495 self.range.start = offset;
1496 self.cursor.seek_forward(&offset, Bias::Right, &());
1497 self.header_height = 0;
1498 self.excerpt_chunks = None;
1499 if let Some(excerpt) = self.cursor.item() {
1500 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1501 self.header_height = excerpt.header_height;
1502 self.has_trailing_newline = excerpt.has_trailing_newline;
1503
1504 let buffer_start;
1505 let start_overshoot = self.range.start - self.cursor.start();
1506 if start_overshoot < excerpt.header_height as usize {
1507 self.header_height -= start_overshoot as u8;
1508 buffer_start = buffer_range.start;
1509 } else {
1510 buffer_start =
1511 buffer_range.start + start_overshoot - excerpt.header_height as usize;
1512 self.header_height = 0;
1513 }
1514
1515 let buffer_end;
1516 let end_overshoot = self.range.end - self.cursor.start();
1517 if end_overshoot < excerpt.header_height as usize {
1518 self.header_height -= excerpt.header_height - end_overshoot as u8;
1519 buffer_end = buffer_start;
1520 } else {
1521 buffer_end = cmp::min(
1522 buffer_range.end,
1523 buffer_range.start + end_overshoot - excerpt.header_height as usize,
1524 );
1525 }
1526
1527 self.excerpt_chunks = Some(excerpt.buffer.chunks(buffer_start..buffer_end, self.theme));
1528 }
1529 }
1530}
1531
1532impl<'a> Iterator for MultiBufferChunks<'a> {
1533 type Item = Chunk<'a>;
1534
1535 fn next(&mut self) -> Option<Self::Item> {
1536 loop {
1537 if self.header_height > 0 {
1538 let chunk = Chunk {
1539 text: unsafe {
1540 std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1541 },
1542 ..Default::default()
1543 };
1544 self.range.start += self.header_height as usize;
1545 self.header_height = 0;
1546 return Some(chunk);
1547 }
1548
1549 if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1550 if let Some(chunk) = excerpt_chunks.next() {
1551 self.range.start += chunk.text.len();
1552 return Some(chunk);
1553 }
1554 self.excerpt_chunks.take();
1555 if self.has_trailing_newline && self.cursor.end(&()) <= self.range.end {
1556 self.range.start += 1;
1557 return Some(Chunk {
1558 text: "\n",
1559 ..Default::default()
1560 });
1561 }
1562 }
1563
1564 self.cursor.next(&());
1565 if *self.cursor.start() >= self.range.end {
1566 return None;
1567 }
1568
1569 let excerpt = self.cursor.item()?;
1570 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1571
1572 let buffer_end = cmp::min(
1573 buffer_range.end,
1574 buffer_range.start + self.range.end
1575 - excerpt.header_height as usize
1576 - self.cursor.start(),
1577 );
1578
1579 self.header_height = excerpt.header_height;
1580 self.has_trailing_newline = excerpt.has_trailing_newline;
1581 self.excerpt_chunks = Some(
1582 excerpt
1583 .buffer
1584 .chunks(buffer_range.start..buffer_end, self.theme),
1585 );
1586 }
1587 }
1588}
1589
1590impl<'a> Iterator for MultiBufferBytes<'a> {
1591 type Item = &'a [u8];
1592
1593 fn next(&mut self) -> Option<Self::Item> {
1594 self.chunks.next().map(|chunk| chunk.text.as_bytes())
1595 }
1596}
1597
1598impl<'a> io::Read for MultiBufferBytes<'a> {
1599 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
1600 todo!()
1601 }
1602}
1603
1604impl ToOffset for Point {
1605 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1606 snapshot.point_to_offset(*self)
1607 }
1608}
1609
1610impl ToOffset for PointUtf16 {
1611 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1612 snapshot.point_utf16_to_offset(*self)
1613 }
1614}
1615
1616impl ToOffset for usize {
1617 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1618 assert!(*self <= snapshot.len(), "offset is out of range");
1619 *self
1620 }
1621}
1622
1623impl ToPoint for usize {
1624 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1625 snapshot.offset_to_point(*self)
1626 }
1627}
1628
1629impl ToPoint for Point {
1630 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1631 *self
1632 }
1633}
1634
1635#[cfg(test)]
1636mod tests {
1637 use super::*;
1638 use gpui::MutableAppContext;
1639 use language::Buffer;
1640 use rand::prelude::*;
1641 use std::env;
1642 use text::{Point, RandomCharIter};
1643 use util::test::sample_text;
1644
1645 #[gpui::test]
1646 fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
1647 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1648 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
1649 assert_eq!(
1650 multibuffer.read(cx).snapshot(cx).text(),
1651 buffer.read(cx).text()
1652 );
1653
1654 buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX", cx));
1655 assert_eq!(
1656 multibuffer.read(cx).snapshot(cx).text(),
1657 buffer.read(cx).text()
1658 );
1659 }
1660
1661 #[gpui::test]
1662 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1663 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1664 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1665
1666 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
1667
1668 let subscription = multibuffer.update(cx, |multibuffer, cx| {
1669 let subscription = multibuffer.subscribe();
1670 multibuffer.push_excerpt(
1671 ExcerptProperties {
1672 buffer: &buffer_1,
1673 range: Point::new(1, 2)..Point::new(2, 5),
1674 header_height: 2,
1675 },
1676 cx,
1677 );
1678 assert_eq!(
1679 subscription.consume().into_inner(),
1680 [Edit {
1681 old: 0..0,
1682 new: 0..13
1683 }]
1684 );
1685
1686 multibuffer.push_excerpt(
1687 ExcerptProperties {
1688 buffer: &buffer_1,
1689 range: Point::new(3, 3)..Point::new(4, 4),
1690 header_height: 1,
1691 },
1692 cx,
1693 );
1694 multibuffer.push_excerpt(
1695 ExcerptProperties {
1696 buffer: &buffer_2,
1697 range: Point::new(3, 1)..Point::new(3, 3),
1698 header_height: 3,
1699 },
1700 cx,
1701 );
1702 assert_eq!(
1703 subscription.consume().into_inner(),
1704 [Edit {
1705 old: 13..13,
1706 new: 13..29
1707 }]
1708 );
1709
1710 subscription
1711 });
1712
1713 assert_eq!(
1714 multibuffer.read(cx).snapshot(cx).text(),
1715 concat!(
1716 "\n", // Preserve newlines
1717 "\n", //
1718 "bbbb\n", //
1719 "ccccc\n", //
1720 "\n", //
1721 "ddd\n", //
1722 "eeee\n", //
1723 "\n", //
1724 "\n", //
1725 "\n", //
1726 "jj\n" //
1727 )
1728 );
1729
1730 buffer_1.update(cx, |buffer, cx| {
1731 buffer.edit(
1732 [
1733 Point::new(0, 0)..Point::new(0, 0),
1734 Point::new(2, 1)..Point::new(2, 3),
1735 ],
1736 "\n",
1737 cx,
1738 );
1739 });
1740
1741 assert_eq!(
1742 multibuffer.read(cx).snapshot(cx).text(),
1743 concat!(
1744 "\n", // Preserve newlines
1745 "\n", //
1746 "bbbb\n", //
1747 "c\n", //
1748 "cc\n", //
1749 "\n", //
1750 "ddd\n", //
1751 "eeee\n", //
1752 "\n", //
1753 "\n", //
1754 "\n", //
1755 "jj\n" //
1756 )
1757 );
1758
1759 assert_eq!(
1760 subscription.consume().into_inner(),
1761 [Edit {
1762 old: 8..10,
1763 new: 8..9
1764 }]
1765 );
1766 }
1767
1768 #[gpui::test(iterations = 100)]
1769 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1770 let operations = env::var("OPERATIONS")
1771 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1772 .unwrap_or(10);
1773
1774 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1775 let list = cx.add_model(|_| MultiBuffer::new(0));
1776 let mut excerpt_ids = Vec::new();
1777 let mut expected_excerpts = Vec::new();
1778 let mut old_versions = Vec::new();
1779
1780 for _ in 0..operations {
1781 match rng.gen_range(0..100) {
1782 0..=19 if !buffers.is_empty() => {
1783 let buffer = buffers.choose(&mut rng).unwrap();
1784 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1785 }
1786 _ => {
1787 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1788 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1789 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1790 buffers.last().unwrap()
1791 } else {
1792 buffers.choose(&mut rng).unwrap()
1793 };
1794
1795 let buffer = buffer_handle.read(cx);
1796 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1797 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1798 let header_height = rng.gen_range(0..=5);
1799 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1800 log::info!(
1801 "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1802 header_height,
1803 buffer_handle.id(),
1804 buffer.text(),
1805 start_ix..end_ix,
1806 &buffer.text()[start_ix..end_ix]
1807 );
1808
1809 let excerpt_id = list.update(cx, |list, cx| {
1810 list.push_excerpt(
1811 ExcerptProperties {
1812 buffer: &buffer_handle,
1813 range: start_ix..end_ix,
1814 header_height,
1815 },
1816 cx,
1817 )
1818 });
1819 excerpt_ids.push(excerpt_id);
1820 expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1821 }
1822 }
1823
1824 if rng.gen_bool(0.3) {
1825 list.update(cx, |list, cx| {
1826 old_versions.push((list.snapshot(cx), list.subscribe()));
1827 })
1828 }
1829
1830 let snapshot = list.read(cx).snapshot(cx);
1831
1832 let mut excerpt_starts = Vec::new();
1833 let mut expected_text = String::new();
1834 for (buffer, range, header_height) in &expected_excerpts {
1835 let buffer = buffer.read(cx);
1836 let buffer_range = range.to_offset(buffer);
1837
1838 for _ in 0..*header_height {
1839 expected_text.push('\n');
1840 }
1841
1842 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1843 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1844 expected_text.push('\n');
1845 }
1846
1847 assert_eq!(snapshot.text(), expected_text);
1848
1849 let mut excerpt_starts = excerpt_starts.into_iter();
1850 for (buffer, range, _) in &expected_excerpts {
1851 let buffer_id = buffer.id();
1852 let buffer = buffer.read(cx);
1853 let buffer_range = range.to_offset(buffer);
1854 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1855 let buffer_start_point_utf16 =
1856 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1857
1858 let excerpt_start = excerpt_starts.next().unwrap();
1859 let mut offset = excerpt_start.bytes;
1860 let mut buffer_offset = buffer_range.start;
1861 let mut point = excerpt_start.lines;
1862 let mut buffer_point = buffer_start_point;
1863 let mut point_utf16 = excerpt_start.lines_utf16;
1864 let mut buffer_point_utf16 = buffer_start_point_utf16;
1865 for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1866 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1867 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1868 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1869 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1870 assert_eq!(
1871 left_offset,
1872 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1873 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1874 offset,
1875 buffer_id,
1876 buffer_offset,
1877 );
1878 assert_eq!(
1879 right_offset,
1880 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1881 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1882 offset,
1883 buffer_id,
1884 buffer_offset,
1885 );
1886
1887 let left_point = snapshot.clip_point(point, Bias::Left);
1888 let right_point = snapshot.clip_point(point, Bias::Right);
1889 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1890 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1891 assert_eq!(
1892 left_point,
1893 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1894 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1895 point,
1896 buffer_id,
1897 buffer_point,
1898 );
1899 assert_eq!(
1900 right_point,
1901 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1902 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1903 point,
1904 buffer_id,
1905 buffer_point,
1906 );
1907
1908 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1909 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1910 let buffer_left_point_utf16 =
1911 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1912 let buffer_right_point_utf16 =
1913 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1914 assert_eq!(
1915 left_point_utf16,
1916 excerpt_start.lines_utf16
1917 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1918 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1919 point_utf16,
1920 buffer_id,
1921 buffer_point_utf16,
1922 );
1923 assert_eq!(
1924 right_point_utf16,
1925 excerpt_start.lines_utf16
1926 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1927 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1928 point_utf16,
1929 buffer_id,
1930 buffer_point_utf16,
1931 );
1932
1933 assert_eq!(
1934 snapshot.point_to_offset(left_point),
1935 left_offset,
1936 "point_to_offset({:?})",
1937 left_point,
1938 );
1939 assert_eq!(
1940 snapshot.offset_to_point(left_offset),
1941 left_point,
1942 "offset_to_point({:?})",
1943 left_offset,
1944 );
1945
1946 offset += 1;
1947 buffer_offset += 1;
1948 if *byte == b'\n' {
1949 point += Point::new(1, 0);
1950 point_utf16 += PointUtf16::new(1, 0);
1951 buffer_point += Point::new(1, 0);
1952 buffer_point_utf16 += PointUtf16::new(1, 0);
1953 } else {
1954 point += Point::new(0, 1);
1955 point_utf16 += PointUtf16::new(0, 1);
1956 buffer_point += Point::new(0, 1);
1957 buffer_point_utf16 += PointUtf16::new(0, 1);
1958 }
1959 }
1960 }
1961
1962 for (row, line) in expected_text.split('\n').enumerate() {
1963 assert_eq!(
1964 snapshot.line_len(row as u32),
1965 line.len() as u32,
1966 "line_len({}).",
1967 row
1968 );
1969 }
1970
1971 for _ in 0..10 {
1972 let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1973 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1974
1975 assert_eq!(
1976 snapshot
1977 .text_for_range(start_ix..end_ix)
1978 .collect::<String>(),
1979 &expected_text[start_ix..end_ix],
1980 "incorrect text for range {:?}",
1981 start_ix..end_ix
1982 );
1983
1984 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1985 assert_eq!(
1986 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1987 expected_summary,
1988 "incorrect summary for range {:?}",
1989 start_ix..end_ix
1990 );
1991 }
1992 }
1993
1994 let snapshot = list.read(cx).snapshot(cx);
1995 for (old_snapshot, subscription) in old_versions {
1996 let edits = subscription.consume().into_inner();
1997
1998 log::info!(
1999 "applying edits since old text: {:?}: {:?}",
2000 old_snapshot.text(),
2001 edits,
2002 );
2003
2004 let mut text = old_snapshot.text();
2005 for edit in edits {
2006 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2007 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2008 }
2009 assert_eq!(text.to_string(), snapshot.text());
2010 }
2011 }
2012
2013 #[gpui::test]
2014 fn test_history(cx: &mut MutableAppContext) {
2015 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
2016 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
2017 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2018 let group_interval = multibuffer.read(cx).history.group_interval;
2019 multibuffer.update(cx, |multibuffer, cx| {
2020 multibuffer.push_excerpt(
2021 ExcerptProperties {
2022 buffer: &buffer_1,
2023 range: 0..buffer_1.read(cx).len(),
2024 header_height: 0,
2025 },
2026 cx,
2027 );
2028 multibuffer.push_excerpt(
2029 ExcerptProperties {
2030 buffer: &buffer_2,
2031 range: 0..buffer_2.read(cx).len(),
2032 header_height: 0,
2033 },
2034 cx,
2035 );
2036 });
2037
2038 let mut now = Instant::now();
2039
2040 multibuffer.update(cx, |multibuffer, cx| {
2041 multibuffer.start_transaction_at(now, cx);
2042 multibuffer.edit(
2043 [
2044 Point::new(0, 0)..Point::new(0, 0),
2045 Point::new(1, 0)..Point::new(1, 0),
2046 ],
2047 "A",
2048 cx,
2049 );
2050 multibuffer.edit(
2051 [
2052 Point::new(0, 1)..Point::new(0, 1),
2053 Point::new(1, 1)..Point::new(1, 1),
2054 ],
2055 "B",
2056 cx,
2057 );
2058 multibuffer.end_transaction_at(now, cx);
2059 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2060
2061 now += 2 * group_interval;
2062 multibuffer.start_transaction_at(now, cx);
2063 multibuffer.edit([2..2], "C", cx);
2064 multibuffer.end_transaction_at(now, cx);
2065 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2066
2067 multibuffer.undo(cx);
2068 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2069
2070 multibuffer.undo(cx);
2071 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2072
2073 multibuffer.redo(cx);
2074 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2075
2076 multibuffer.redo(cx);
2077 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2078
2079 buffer_1.update(cx, |buffer_1, cx| buffer_1.undo(cx));
2080 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2081
2082 multibuffer.undo(cx);
2083 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2084
2085 multibuffer.redo(cx);
2086 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2087
2088 multibuffer.redo(cx);
2089 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2090
2091 multibuffer.undo(cx);
2092 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2093
2094 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
2095 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2096
2097 multibuffer.undo(cx);
2098 assert_eq!(multibuffer.read(cx).text(), "C1234\n5678\n");
2099 });
2100 }
2101}