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 {
59 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
60}
61
62pub trait ToPoint: 'static {
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.buffers.len() == 1 {
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 offset = position.to_offset(self);
739 self.as_singleton().unwrap().contains_str_at(offset, needle)
740 }
741
742 fn as_singleton(&self) -> Option<&BufferSnapshot> {
743 let mut excerpts = self.excerpts.iter();
744 let buffer = excerpts.next().map(|excerpt| &excerpt.buffer);
745 if excerpts.next().is_none() {
746 buffer
747 } else {
748 None
749 }
750 }
751
752 pub fn len(&self) -> usize {
753 self.excerpts.summary().text.bytes
754 }
755
756 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
757 let mut cursor = self.excerpts.cursor::<usize>();
758 cursor.seek(&offset, Bias::Right, &());
759 if let Some(excerpt) = cursor.item() {
760 let start_after_header = *cursor.start() + excerpt.header_height as usize;
761 if offset < start_after_header {
762 *cursor.start()
763 } else {
764 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
765 let buffer_offset = excerpt
766 .buffer
767 .clip_offset(excerpt_start + (offset - start_after_header), bias);
768 let offset_in_excerpt = if buffer_offset > excerpt_start {
769 buffer_offset - excerpt_start
770 } else {
771 0
772 };
773 start_after_header + offset_in_excerpt
774 }
775 } else {
776 self.excerpts.summary().text.bytes
777 }
778 }
779
780 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
781 let mut cursor = self.excerpts.cursor::<Point>();
782 cursor.seek(&point, Bias::Right, &());
783 if let Some(excerpt) = cursor.item() {
784 let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
785 if point < start_after_header {
786 *cursor.start()
787 } else {
788 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
789 let buffer_point = excerpt
790 .buffer
791 .clip_point(excerpt_start + (point - start_after_header), bias);
792 let point_in_excerpt = if buffer_point > excerpt_start {
793 buffer_point - excerpt_start
794 } else {
795 Point::zero()
796 };
797 start_after_header + point_in_excerpt
798 }
799 } else {
800 self.excerpts.summary().text.lines
801 }
802 }
803
804 pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
805 let mut cursor = self.excerpts.cursor::<PointUtf16>();
806 cursor.seek(&point, Bias::Right, &());
807 if let Some(excerpt) = cursor.item() {
808 let start_after_header =
809 *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
810 if point < start_after_header {
811 *cursor.start()
812 } else {
813 let excerpt_start = excerpt
814 .buffer
815 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
816 let buffer_point = excerpt
817 .buffer
818 .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
819 let point_in_excerpt = if buffer_point > excerpt_start {
820 buffer_point - excerpt_start
821 } else {
822 PointUtf16::new(0, 0)
823 };
824 start_after_header + point_in_excerpt
825 }
826 } else {
827 self.excerpts.summary().text.lines_utf16
828 }
829 }
830
831 pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
832 MultiBufferBytes {
833 chunks: self.chunks(range, None).peekable(),
834 }
835 }
836
837 pub fn chunks<'a, T: ToOffset>(
838 &'a self,
839 range: Range<T>,
840 theme: Option<&'a SyntaxTheme>,
841 ) -> MultiBufferChunks<'a> {
842 let mut result = MultiBufferChunks {
843 range: 0..range.end.to_offset(self),
844 cursor: self.excerpts.cursor::<usize>(),
845 header_height: 0,
846 excerpt_chunks: None,
847 has_trailing_newline: false,
848 theme,
849 };
850 result.seek(range.start.to_offset(self));
851 result
852 }
853
854 pub fn offset_to_point(&self, offset: usize) -> Point {
855 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
856 cursor.seek(&offset, Bias::Right, &());
857 if let Some(excerpt) = cursor.item() {
858 let (start_offset, start_point) = cursor.start();
859 let overshoot = offset - start_offset;
860 let header_height = excerpt.header_height as usize;
861 if overshoot < header_height {
862 *start_point
863 } else {
864 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
865 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
866 let buffer_point = excerpt
867 .buffer
868 .offset_to_point(excerpt_start_offset + (overshoot - header_height));
869 *start_point
870 + Point::new(header_height as u32, 0)
871 + (buffer_point - excerpt_start_point)
872 }
873 } else {
874 self.excerpts.summary().text.lines
875 }
876 }
877
878 pub fn point_to_offset(&self, point: Point) -> usize {
879 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
880 cursor.seek(&point, Bias::Right, &());
881 if let Some(excerpt) = cursor.item() {
882 let (start_point, start_offset) = cursor.start();
883 let overshoot = point - start_point;
884 let header_height = Point::new(excerpt.header_height as u32, 0);
885 if overshoot < header_height {
886 *start_offset
887 } else {
888 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
889 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
890 let buffer_offset = excerpt
891 .buffer
892 .point_to_offset(excerpt_start_point + (overshoot - header_height));
893 *start_offset + excerpt.header_height as usize + buffer_offset
894 - excerpt_start_offset
895 }
896 } else {
897 self.excerpts.summary().text.bytes
898 }
899 }
900
901 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
902 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
903 cursor.seek(&point, Bias::Right, &());
904 if let Some(excerpt) = cursor.item() {
905 let (start_point, start_offset) = cursor.start();
906 let overshoot = point - start_point;
907 let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
908 if overshoot < header_height {
909 *start_offset
910 } else {
911 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
912 let excerpt_start_point = excerpt
913 .buffer
914 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
915 let buffer_offset = excerpt
916 .buffer
917 .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
918 *start_offset
919 + excerpt.header_height as usize
920 + (buffer_offset - excerpt_start_offset)
921 }
922 } else {
923 self.excerpts.summary().text.bytes
924 }
925 }
926
927 pub fn indent_column_for_line(&self, row: u32) -> u32 {
928 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
929 buffer
930 .indent_column_for_line(range.start.row)
931 .min(range.end.column)
932 .saturating_sub(range.start.column)
933 } else {
934 0
935 }
936 }
937
938 pub fn line_len(&self, row: u32) -> u32 {
939 if let Some((_, range)) = self.buffer_line_for_row(row) {
940 range.end.column - range.start.column
941 } else {
942 0
943 }
944 }
945
946 fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
947 let mut cursor = self.excerpts.cursor::<Point>();
948 cursor.seek(&Point::new(row, 0), Bias::Right, &());
949 if let Some(excerpt) = cursor.item() {
950 let overshoot = row - cursor.start().row;
951 let header_height = excerpt.header_height as u32;
952 if overshoot >= header_height {
953 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
954 let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
955 let buffer_row = excerpt_start.row + overshoot - header_height;
956 let line_start = Point::new(buffer_row, 0);
957 let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
958 return Some((
959 &excerpt.buffer,
960 line_start.max(excerpt_start)..line_end.min(excerpt_end),
961 ));
962 }
963 }
964 None
965 }
966
967 pub fn max_point(&self) -> Point {
968 self.text_summary().lines
969 }
970
971 pub fn text_summary(&self) -> TextSummary {
972 self.excerpts.summary().text
973 }
974
975 pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
976 where
977 D: TextDimension,
978 O: ToOffset,
979 {
980 let mut summary = D::default();
981 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
982 let mut cursor = self.excerpts.cursor::<usize>();
983 cursor.seek(&range.start, Bias::Right, &());
984 if let Some(excerpt) = cursor.item() {
985 let start_after_header = cursor.start() + excerpt.header_height as usize;
986 if range.start < start_after_header {
987 let header_len = cmp::min(range.end, start_after_header) - range.start;
988 summary.add_assign(&D::from_text_summary(&TextSummary {
989 bytes: header_len,
990 lines: Point::new(header_len as u32, 0),
991 lines_utf16: PointUtf16::new(header_len as u32, 0),
992 first_line_chars: 0,
993 last_line_chars: 0,
994 longest_row: 0,
995 longest_row_chars: 0,
996 }));
997 range.start = start_after_header;
998 range.end = cmp::max(range.start, range.end);
999 }
1000
1001 let mut end_before_newline = cursor.end(&());
1002 if excerpt.has_trailing_newline {
1003 end_before_newline -= 1;
1004 }
1005
1006 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1007 let start_in_excerpt = excerpt_start + (range.start - start_after_header);
1008 let end_in_excerpt =
1009 excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
1010 summary.add_assign(
1011 &excerpt
1012 .buffer
1013 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
1014 );
1015
1016 if range.end > end_before_newline {
1017 summary.add_assign(&D::from_text_summary(&TextSummary {
1018 bytes: 1,
1019 lines: Point::new(1 as u32, 0),
1020 lines_utf16: PointUtf16::new(1 as u32, 0),
1021 first_line_chars: 0,
1022 last_line_chars: 0,
1023 longest_row: 0,
1024 longest_row_chars: 0,
1025 }));
1026 }
1027
1028 cursor.next(&());
1029 }
1030
1031 if range.end > *cursor.start() {
1032 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
1033 &range.end,
1034 Bias::Right,
1035 &(),
1036 )));
1037 if let Some(excerpt) = cursor.item() {
1038 let start_after_header = cursor.start() + excerpt.header_height as usize;
1039 let header_len =
1040 cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
1041 summary.add_assign(&D::from_text_summary(&TextSummary {
1042 bytes: header_len,
1043 lines: Point::new(header_len as u32, 0),
1044 lines_utf16: PointUtf16::new(header_len as u32, 0),
1045 first_line_chars: 0,
1046 last_line_chars: 0,
1047 longest_row: 0,
1048 longest_row_chars: 0,
1049 }));
1050 range.end = cmp::max(start_after_header, range.end);
1051
1052 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
1053 let end_in_excerpt = excerpt_start + (range.end - start_after_header);
1054 summary.add_assign(
1055 &excerpt
1056 .buffer
1057 .text_summary_for_range(excerpt_start..end_in_excerpt),
1058 );
1059 cursor.next(&());
1060 }
1061 }
1062
1063 summary
1064 }
1065
1066 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
1067 where
1068 D: TextDimension + Ord + Sub<D, Output = D>,
1069 {
1070 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1071 cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
1072 if let Some(excerpt) = cursor.item() {
1073 if excerpt.id == anchor.excerpt_id {
1074 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1075 excerpt_start.add_summary(&excerpt.header_summary(), &());
1076 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1077 let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
1078 if buffer_point > excerpt_buffer_start {
1079 excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
1080 }
1081 return excerpt_start;
1082 }
1083 }
1084 D::from_text_summary(&cursor.start().text)
1085 }
1086
1087 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
1088 where
1089 D: TextDimension + Ord + Sub<D, Output = D>,
1090 I: 'a + IntoIterator<Item = &'a Anchor>,
1091 {
1092 let mut anchors = anchors.into_iter().peekable();
1093 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
1094 let mut summaries = Vec::new();
1095 while let Some(anchor) = anchors.peek() {
1096 let excerpt_id = &anchor.excerpt_id;
1097 let excerpt_anchors = iter::from_fn(|| {
1098 let anchor = anchors.peek()?;
1099 if anchor.excerpt_id == *excerpt_id {
1100 Some(&anchors.next().unwrap().text_anchor)
1101 } else {
1102 None
1103 }
1104 });
1105
1106 cursor.seek_forward(&Some(excerpt_id), Bias::Left, &());
1107 if let Some(excerpt) = cursor.item() {
1108 if excerpt.id == *excerpt_id {
1109 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
1110 excerpt_start.add_summary(&excerpt.header_summary(), &());
1111 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
1112 summaries.extend(
1113 excerpt
1114 .buffer
1115 .summaries_for_anchors::<D, _>(excerpt_anchors)
1116 .map(move |summary| {
1117 let mut excerpt_start = excerpt_start.clone();
1118 let excerpt_buffer_start = excerpt_buffer_start.clone();
1119 if summary > excerpt_buffer_start {
1120 excerpt_start.add_assign(&(summary - excerpt_buffer_start));
1121 }
1122 excerpt_start
1123 }),
1124 );
1125 continue;
1126 }
1127 }
1128
1129 let summary = D::from_text_summary(&cursor.start().text);
1130 summaries.extend(excerpt_anchors.map(|_| summary.clone()));
1131 }
1132
1133 summaries
1134 }
1135
1136 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
1137 self.anchor_at(position, Bias::Left)
1138 }
1139
1140 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
1141 self.anchor_at(position, Bias::Right)
1142 }
1143
1144 pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
1145 let offset = position.to_offset(self);
1146 let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
1147 cursor.seek(&offset, bias, &());
1148 if let Some(excerpt) = cursor.item() {
1149 let start_after_header = cursor.start().0 + excerpt.header_height as usize;
1150 let mut end_before_newline = cursor.end(&()).0;
1151 if excerpt.has_trailing_newline {
1152 end_before_newline -= 1;
1153 }
1154
1155 let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1156 let overshoot = cmp::min(offset, end_before_newline).saturating_sub(start_after_header);
1157 Anchor {
1158 excerpt_id: excerpt.id.clone(),
1159 text_anchor: excerpt.buffer.anchor_at(buffer_start + overshoot, bias),
1160 }
1161 } else if offset == 0 && bias == Bias::Left {
1162 Anchor::min()
1163 } else {
1164 Anchor::max()
1165 }
1166 }
1167
1168 pub fn parse_count(&self) -> usize {
1169 self.parse_count
1170 }
1171
1172 pub fn enclosing_bracket_ranges<T: ToOffset>(
1173 &self,
1174 range: Range<T>,
1175 ) -> Option<(Range<usize>, Range<usize>)> {
1176 let range = range.start.to_offset(self)..range.end.to_offset(self);
1177 self.as_singleton().unwrap().enclosing_bracket_ranges(range)
1178 }
1179
1180 pub fn diagnostics_update_count(&self) -> usize {
1181 self.diagnostics_update_count
1182 }
1183
1184 pub fn language(&self) -> Option<&Arc<Language>> {
1185 self.excerpts
1186 .iter()
1187 .next()
1188 .and_then(|excerpt| excerpt.buffer.language())
1189 }
1190
1191 pub fn diagnostic_group<'a, O>(
1192 &'a self,
1193 group_id: usize,
1194 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1195 where
1196 O: text::FromAnchor + 'a,
1197 {
1198 self.as_singleton().unwrap().diagnostic_group(group_id)
1199 }
1200
1201 pub fn diagnostics_in_range<'a, T, O>(
1202 &'a self,
1203 range: Range<T>,
1204 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1205 where
1206 T: 'a + ToOffset,
1207 O: 'a + text::FromAnchor,
1208 {
1209 let range = range.start.to_offset(self)..range.end.to_offset(self);
1210 self.as_singleton().unwrap().diagnostics_in_range(range)
1211 }
1212
1213 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1214 let range = range.start.to_offset(self)..range.end.to_offset(self);
1215 self.as_singleton()
1216 .unwrap()
1217 .range_for_syntax_ancestor(range)
1218 }
1219
1220 fn buffer_snapshot_for_excerpt<'a>(
1221 &'a self,
1222 excerpt_id: &'a ExcerptId,
1223 ) -> Option<&'a BufferSnapshot> {
1224 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1225 cursor.seek(&Some(excerpt_id), Bias::Left, &());
1226 if let Some(excerpt) = cursor.item() {
1227 if excerpt.id == *excerpt_id {
1228 return Some(&excerpt.buffer);
1229 }
1230 }
1231 None
1232 }
1233
1234 pub fn remote_selections_in_range<'a>(
1235 &'a self,
1236 range: &'a Range<Anchor>,
1237 ) -> impl 'a + Iterator<Item = (ReplicaId, Selection<Anchor>)> {
1238 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1239 cursor.seek(&Some(&range.start.excerpt_id), Bias::Left, &());
1240 cursor
1241 .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id)
1242 .flat_map(move |excerpt| {
1243 let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone();
1244 if excerpt.id == range.start.excerpt_id {
1245 query_range.start = range.start.text_anchor.clone();
1246 }
1247 if excerpt.id == range.end.excerpt_id {
1248 query_range.end = range.end.text_anchor.clone();
1249 }
1250
1251 excerpt
1252 .buffer
1253 .remote_selections_in_range(query_range)
1254 .flat_map(move |(replica_id, selections)| {
1255 selections.map(move |selection| {
1256 let mut start = Anchor {
1257 excerpt_id: excerpt.id.clone(),
1258 text_anchor: selection.start.clone(),
1259 };
1260 let mut end = Anchor {
1261 excerpt_id: excerpt.id.clone(),
1262 text_anchor: selection.end.clone(),
1263 };
1264 if range.start.cmp(&start, self).unwrap().is_gt() {
1265 start = range.start.clone();
1266 }
1267 if range.end.cmp(&end, self).unwrap().is_lt() {
1268 end = range.end.clone();
1269 }
1270
1271 (
1272 replica_id,
1273 Selection {
1274 id: selection.id,
1275 start,
1276 end,
1277 reversed: selection.reversed,
1278 goal: selection.goal,
1279 },
1280 )
1281 })
1282 })
1283 })
1284 }
1285}
1286
1287impl History {
1288 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
1289 self.transaction_depth += 1;
1290 if self.transaction_depth == 1 {
1291 let id = post_inc(&mut self.next_transaction_id);
1292 self.undo_stack.push(Transaction {
1293 id,
1294 buffer_transactions: Default::default(),
1295 first_edit_at: now,
1296 last_edit_at: now,
1297 });
1298 Some(id)
1299 } else {
1300 None
1301 }
1302 }
1303
1304 fn end_transaction(
1305 &mut self,
1306 now: Instant,
1307 buffer_transactions: HashSet<(usize, TransactionId)>,
1308 ) -> bool {
1309 assert_ne!(self.transaction_depth, 0);
1310 self.transaction_depth -= 1;
1311 if self.transaction_depth == 0 {
1312 if buffer_transactions.is_empty() {
1313 self.undo_stack.pop();
1314 false
1315 } else {
1316 let transaction = self.undo_stack.last_mut().unwrap();
1317 transaction.last_edit_at = now;
1318 transaction.buffer_transactions.extend(buffer_transactions);
1319 true
1320 }
1321 } else {
1322 false
1323 }
1324 }
1325
1326 fn pop_undo(&mut self) -> Option<&Transaction> {
1327 assert_eq!(self.transaction_depth, 0);
1328 if let Some(transaction) = self.undo_stack.pop() {
1329 self.redo_stack.push(transaction);
1330 self.redo_stack.last()
1331 } else {
1332 None
1333 }
1334 }
1335
1336 fn pop_redo(&mut self) -> Option<&Transaction> {
1337 assert_eq!(self.transaction_depth, 0);
1338 if let Some(transaction) = self.redo_stack.pop() {
1339 self.undo_stack.push(transaction);
1340 self.undo_stack.last()
1341 } else {
1342 None
1343 }
1344 }
1345
1346 fn group(&mut self) -> Option<TransactionId> {
1347 let mut new_len = self.undo_stack.len();
1348 let mut transactions = self.undo_stack.iter_mut();
1349
1350 if let Some(mut transaction) = transactions.next_back() {
1351 while let Some(prev_transaction) = transactions.next_back() {
1352 if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval
1353 {
1354 transaction = prev_transaction;
1355 new_len -= 1;
1356 } else {
1357 break;
1358 }
1359 }
1360 }
1361
1362 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
1363 if let Some(last_transaction) = transactions_to_keep.last_mut() {
1364 if let Some(transaction) = transactions_to_merge.last() {
1365 last_transaction.last_edit_at = transaction.last_edit_at;
1366 }
1367 }
1368
1369 self.undo_stack.truncate(new_len);
1370 self.undo_stack.last().map(|t| t.id)
1371 }
1372}
1373
1374impl Excerpt {
1375 fn new(
1376 id: ExcerptId,
1377 buffer_id: usize,
1378 buffer: BufferSnapshot,
1379 range: Range<text::Anchor>,
1380 header_height: u8,
1381 has_trailing_newline: bool,
1382 ) -> Self {
1383 let mut text_summary =
1384 buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1385 if header_height > 0 {
1386 text_summary.first_line_chars = 0;
1387 text_summary.lines.row += header_height as u32;
1388 text_summary.lines_utf16.row += header_height as u32;
1389 text_summary.bytes += header_height as usize;
1390 text_summary.longest_row += header_height as u32;
1391 }
1392 if has_trailing_newline {
1393 text_summary.last_line_chars = 0;
1394 text_summary.lines.row += 1;
1395 text_summary.lines.column = 0;
1396 text_summary.lines_utf16.row += 1;
1397 text_summary.lines_utf16.column = 0;
1398 text_summary.bytes += 1;
1399 }
1400
1401 Excerpt {
1402 id,
1403 buffer_id,
1404 buffer,
1405 range,
1406 text_summary,
1407 header_height,
1408 has_trailing_newline,
1409 }
1410 }
1411
1412 fn header_summary(&self) -> TextSummary {
1413 TextSummary {
1414 bytes: self.header_height as usize,
1415 lines: Point::new(self.header_height as u32, 0),
1416 lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1417 first_line_chars: 0,
1418 last_line_chars: 0,
1419 longest_row: 0,
1420 longest_row_chars: 0,
1421 }
1422 }
1423}
1424
1425impl sum_tree::Item for Excerpt {
1426 type Summary = ExcerptSummary;
1427
1428 fn summary(&self) -> Self::Summary {
1429 ExcerptSummary {
1430 excerpt_id: self.id.clone(),
1431 text: self.text_summary.clone(),
1432 }
1433 }
1434}
1435
1436impl sum_tree::Summary for ExcerptSummary {
1437 type Context = ();
1438
1439 fn add_summary(&mut self, summary: &Self, _: &()) {
1440 debug_assert!(summary.excerpt_id > self.excerpt_id);
1441 self.excerpt_id = summary.excerpt_id.clone();
1442 self.text.add_summary(&summary.text, &());
1443 }
1444}
1445
1446impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1447 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1448 *self += &summary.text;
1449 }
1450}
1451
1452impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1453 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1454 *self += summary.text.bytes;
1455 }
1456}
1457
1458impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1459 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1460 Ord::cmp(self, &cursor_location.text.bytes)
1461 }
1462}
1463
1464impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1465 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1466 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1467 }
1468}
1469
1470impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1471 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1472 *self += summary.text.lines;
1473 }
1474}
1475
1476impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1477 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1478 *self += summary.text.lines_utf16
1479 }
1480}
1481
1482impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1483 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1484 *self = Some(&summary.excerpt_id);
1485 }
1486}
1487
1488impl<'a> MultiBufferChunks<'a> {
1489 pub fn offset(&self) -> usize {
1490 self.range.start
1491 }
1492
1493 pub fn seek(&mut self, offset: usize) {
1494 self.range.start = offset;
1495 self.cursor.seek_forward(&offset, Bias::Right, &());
1496 self.header_height = 0;
1497 self.excerpt_chunks = None;
1498 if let Some(excerpt) = self.cursor.item() {
1499 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1500 self.header_height = excerpt.header_height;
1501 self.has_trailing_newline = excerpt.has_trailing_newline;
1502
1503 let buffer_start;
1504 let start_overshoot = self.range.start - self.cursor.start();
1505 if start_overshoot < excerpt.header_height as usize {
1506 self.header_height -= start_overshoot as u8;
1507 buffer_start = buffer_range.start;
1508 } else {
1509 buffer_start =
1510 buffer_range.start + start_overshoot - excerpt.header_height as usize;
1511 self.header_height = 0;
1512 }
1513
1514 let buffer_end;
1515 let end_overshoot = self.range.end - self.cursor.start();
1516 if end_overshoot < excerpt.header_height as usize {
1517 self.header_height -= excerpt.header_height - end_overshoot as u8;
1518 buffer_end = buffer_start;
1519 } else {
1520 buffer_end = cmp::min(
1521 buffer_range.end,
1522 buffer_range.start + end_overshoot - excerpt.header_height as usize,
1523 );
1524 }
1525
1526 self.excerpt_chunks = Some(excerpt.buffer.chunks(buffer_start..buffer_end, self.theme));
1527 }
1528 }
1529}
1530
1531impl<'a> Iterator for MultiBufferChunks<'a> {
1532 type Item = Chunk<'a>;
1533
1534 fn next(&mut self) -> Option<Self::Item> {
1535 loop {
1536 if self.header_height > 0 {
1537 let chunk = Chunk {
1538 text: unsafe {
1539 std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1540 },
1541 ..Default::default()
1542 };
1543 self.range.start += self.header_height as usize;
1544 self.header_height = 0;
1545 return Some(chunk);
1546 }
1547
1548 if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1549 if let Some(chunk) = excerpt_chunks.next() {
1550 self.range.start += chunk.text.len();
1551 return Some(chunk);
1552 }
1553 self.excerpt_chunks.take();
1554 if self.has_trailing_newline && self.cursor.end(&()) <= self.range.end {
1555 self.range.start += 1;
1556 return Some(Chunk {
1557 text: "\n",
1558 ..Default::default()
1559 });
1560 }
1561 }
1562
1563 self.cursor.next(&());
1564 if *self.cursor.start() >= self.range.end {
1565 return None;
1566 }
1567
1568 let excerpt = self.cursor.item()?;
1569 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1570
1571 let buffer_end = cmp::min(
1572 buffer_range.end,
1573 buffer_range.start + self.range.end
1574 - excerpt.header_height as usize
1575 - self.cursor.start(),
1576 );
1577
1578 self.header_height = excerpt.header_height;
1579 self.has_trailing_newline = excerpt.has_trailing_newline;
1580 self.excerpt_chunks = Some(
1581 excerpt
1582 .buffer
1583 .chunks(buffer_range.start..buffer_end, self.theme),
1584 );
1585 }
1586 }
1587}
1588
1589impl<'a> Iterator for MultiBufferBytes<'a> {
1590 type Item = &'a [u8];
1591
1592 fn next(&mut self) -> Option<Self::Item> {
1593 self.chunks.next().map(|chunk| chunk.text.as_bytes())
1594 }
1595}
1596
1597impl<'a> io::Read for MultiBufferBytes<'a> {
1598 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
1599 todo!()
1600 }
1601}
1602
1603impl ToOffset for Point {
1604 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1605 snapshot.point_to_offset(*self)
1606 }
1607}
1608
1609impl ToOffset for PointUtf16 {
1610 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1611 snapshot.point_utf16_to_offset(*self)
1612 }
1613}
1614
1615impl ToOffset for usize {
1616 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1617 assert!(*self <= snapshot.len(), "offset is out of range");
1618 *self
1619 }
1620}
1621
1622impl ToPoint for usize {
1623 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1624 snapshot.offset_to_point(*self)
1625 }
1626}
1627
1628impl ToPoint for Point {
1629 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1630 *self
1631 }
1632}
1633
1634#[cfg(test)]
1635mod tests {
1636 use super::*;
1637 use gpui::MutableAppContext;
1638 use language::Buffer;
1639 use rand::prelude::*;
1640 use std::env;
1641 use text::{Point, RandomCharIter};
1642 use util::test::sample_text;
1643
1644 #[gpui::test]
1645 fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
1646 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1647 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
1648 assert_eq!(
1649 multibuffer.read(cx).snapshot(cx).text(),
1650 buffer.read(cx).text()
1651 );
1652
1653 buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX", cx));
1654 assert_eq!(
1655 multibuffer.read(cx).snapshot(cx).text(),
1656 buffer.read(cx).text()
1657 );
1658 }
1659
1660 #[gpui::test]
1661 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1662 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1663 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1664
1665 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
1666
1667 let subscription = multibuffer.update(cx, |multibuffer, cx| {
1668 let subscription = multibuffer.subscribe();
1669 multibuffer.push_excerpt(
1670 ExcerptProperties {
1671 buffer: &buffer_1,
1672 range: Point::new(1, 2)..Point::new(2, 5),
1673 header_height: 2,
1674 },
1675 cx,
1676 );
1677 assert_eq!(
1678 subscription.consume().into_inner(),
1679 [Edit {
1680 old: 0..0,
1681 new: 0..13
1682 }]
1683 );
1684
1685 multibuffer.push_excerpt(
1686 ExcerptProperties {
1687 buffer: &buffer_1,
1688 range: Point::new(3, 3)..Point::new(4, 4),
1689 header_height: 1,
1690 },
1691 cx,
1692 );
1693 multibuffer.push_excerpt(
1694 ExcerptProperties {
1695 buffer: &buffer_2,
1696 range: Point::new(3, 1)..Point::new(3, 3),
1697 header_height: 3,
1698 },
1699 cx,
1700 );
1701 assert_eq!(
1702 subscription.consume().into_inner(),
1703 [Edit {
1704 old: 13..13,
1705 new: 13..29
1706 }]
1707 );
1708
1709 subscription
1710 });
1711
1712 assert_eq!(
1713 multibuffer.read(cx).snapshot(cx).text(),
1714 concat!(
1715 "\n", // Preserve newlines
1716 "\n", //
1717 "bbbb\n", //
1718 "ccccc\n", //
1719 "\n", //
1720 "ddd\n", //
1721 "eeee\n", //
1722 "\n", //
1723 "\n", //
1724 "\n", //
1725 "jj\n" //
1726 )
1727 );
1728
1729 buffer_1.update(cx, |buffer, cx| {
1730 buffer.edit(
1731 [
1732 Point::new(0, 0)..Point::new(0, 0),
1733 Point::new(2, 1)..Point::new(2, 3),
1734 ],
1735 "\n",
1736 cx,
1737 );
1738 });
1739
1740 assert_eq!(
1741 multibuffer.read(cx).snapshot(cx).text(),
1742 concat!(
1743 "\n", // Preserve newlines
1744 "\n", //
1745 "bbbb\n", //
1746 "c\n", //
1747 "cc\n", //
1748 "\n", //
1749 "ddd\n", //
1750 "eeee\n", //
1751 "\n", //
1752 "\n", //
1753 "\n", //
1754 "jj\n" //
1755 )
1756 );
1757
1758 assert_eq!(
1759 subscription.consume().into_inner(),
1760 [Edit {
1761 old: 8..10,
1762 new: 8..9
1763 }]
1764 );
1765 }
1766
1767 #[gpui::test(iterations = 100)]
1768 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1769 let operations = env::var("OPERATIONS")
1770 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1771 .unwrap_or(10);
1772
1773 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1774 let list = cx.add_model(|_| MultiBuffer::new(0));
1775 let mut excerpt_ids = Vec::new();
1776 let mut expected_excerpts = Vec::new();
1777 let mut old_versions = Vec::new();
1778
1779 for _ in 0..operations {
1780 match rng.gen_range(0..100) {
1781 0..=19 if !buffers.is_empty() => {
1782 let buffer = buffers.choose(&mut rng).unwrap();
1783 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1784 }
1785 _ => {
1786 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1787 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1788 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1789 buffers.last().unwrap()
1790 } else {
1791 buffers.choose(&mut rng).unwrap()
1792 };
1793
1794 let buffer = buffer_handle.read(cx);
1795 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1796 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1797 let header_height = rng.gen_range(0..=5);
1798 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1799 log::info!(
1800 "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1801 header_height,
1802 buffer_handle.id(),
1803 buffer.text(),
1804 start_ix..end_ix,
1805 &buffer.text()[start_ix..end_ix]
1806 );
1807
1808 let excerpt_id = list.update(cx, |list, cx| {
1809 list.push_excerpt(
1810 ExcerptProperties {
1811 buffer: &buffer_handle,
1812 range: start_ix..end_ix,
1813 header_height,
1814 },
1815 cx,
1816 )
1817 });
1818 excerpt_ids.push(excerpt_id);
1819 expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1820 }
1821 }
1822
1823 if rng.gen_bool(0.3) {
1824 list.update(cx, |list, cx| {
1825 old_versions.push((list.snapshot(cx), list.subscribe()));
1826 })
1827 }
1828
1829 let snapshot = list.read(cx).snapshot(cx);
1830
1831 let mut excerpt_starts = Vec::new();
1832 let mut expected_text = String::new();
1833 for (buffer, range, header_height) in &expected_excerpts {
1834 let buffer = buffer.read(cx);
1835 let buffer_range = range.to_offset(buffer);
1836
1837 for _ in 0..*header_height {
1838 expected_text.push('\n');
1839 }
1840
1841 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1842 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1843 expected_text.push('\n');
1844 }
1845
1846 assert_eq!(snapshot.text(), expected_text);
1847
1848 let mut excerpt_starts = excerpt_starts.into_iter();
1849 for (buffer, range, _) in &expected_excerpts {
1850 let buffer_id = buffer.id();
1851 let buffer = buffer.read(cx);
1852 let buffer_range = range.to_offset(buffer);
1853 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1854 let buffer_start_point_utf16 =
1855 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1856
1857 let excerpt_start = excerpt_starts.next().unwrap();
1858 let mut offset = excerpt_start.bytes;
1859 let mut buffer_offset = buffer_range.start;
1860 let mut point = excerpt_start.lines;
1861 let mut buffer_point = buffer_start_point;
1862 let mut point_utf16 = excerpt_start.lines_utf16;
1863 let mut buffer_point_utf16 = buffer_start_point_utf16;
1864 for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1865 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1866 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1867 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1868 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1869 assert_eq!(
1870 left_offset,
1871 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1872 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1873 offset,
1874 buffer_id,
1875 buffer_offset,
1876 );
1877 assert_eq!(
1878 right_offset,
1879 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1880 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1881 offset,
1882 buffer_id,
1883 buffer_offset,
1884 );
1885
1886 let left_point = snapshot.clip_point(point, Bias::Left);
1887 let right_point = snapshot.clip_point(point, Bias::Right);
1888 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1889 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1890 assert_eq!(
1891 left_point,
1892 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1893 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1894 point,
1895 buffer_id,
1896 buffer_point,
1897 );
1898 assert_eq!(
1899 right_point,
1900 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1901 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1902 point,
1903 buffer_id,
1904 buffer_point,
1905 );
1906
1907 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1908 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1909 let buffer_left_point_utf16 =
1910 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1911 let buffer_right_point_utf16 =
1912 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1913 assert_eq!(
1914 left_point_utf16,
1915 excerpt_start.lines_utf16
1916 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1917 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1918 point_utf16,
1919 buffer_id,
1920 buffer_point_utf16,
1921 );
1922 assert_eq!(
1923 right_point_utf16,
1924 excerpt_start.lines_utf16
1925 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1926 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1927 point_utf16,
1928 buffer_id,
1929 buffer_point_utf16,
1930 );
1931
1932 assert_eq!(
1933 snapshot.point_to_offset(left_point),
1934 left_offset,
1935 "point_to_offset({:?})",
1936 left_point,
1937 );
1938 assert_eq!(
1939 snapshot.offset_to_point(left_offset),
1940 left_point,
1941 "offset_to_point({:?})",
1942 left_offset,
1943 );
1944
1945 offset += 1;
1946 buffer_offset += 1;
1947 if *byte == b'\n' {
1948 point += Point::new(1, 0);
1949 point_utf16 += PointUtf16::new(1, 0);
1950 buffer_point += Point::new(1, 0);
1951 buffer_point_utf16 += PointUtf16::new(1, 0);
1952 } else {
1953 point += Point::new(0, 1);
1954 point_utf16 += PointUtf16::new(0, 1);
1955 buffer_point += Point::new(0, 1);
1956 buffer_point_utf16 += PointUtf16::new(0, 1);
1957 }
1958 }
1959 }
1960
1961 for (row, line) in expected_text.split('\n').enumerate() {
1962 assert_eq!(
1963 snapshot.line_len(row as u32),
1964 line.len() as u32,
1965 "line_len({}).",
1966 row
1967 );
1968 }
1969
1970 for _ in 0..10 {
1971 let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1972 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1973
1974 assert_eq!(
1975 snapshot
1976 .text_for_range(start_ix..end_ix)
1977 .collect::<String>(),
1978 &expected_text[start_ix..end_ix],
1979 "incorrect text for range {:?}",
1980 start_ix..end_ix
1981 );
1982
1983 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1984 assert_eq!(
1985 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1986 expected_summary,
1987 "incorrect summary for range {:?}",
1988 start_ix..end_ix
1989 );
1990 }
1991 }
1992
1993 let snapshot = list.read(cx).snapshot(cx);
1994 for (old_snapshot, subscription) in old_versions {
1995 let edits = subscription.consume().into_inner();
1996
1997 log::info!(
1998 "applying edits since old text: {:?}: {:?}",
1999 old_snapshot.text(),
2000 edits,
2001 );
2002
2003 let mut text = old_snapshot.text();
2004 for edit in edits {
2005 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2006 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2007 }
2008 assert_eq!(text.to_string(), snapshot.text());
2009 }
2010 }
2011
2012 #[gpui::test]
2013 fn test_history(cx: &mut MutableAppContext) {
2014 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
2015 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
2016 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2017 let group_interval = multibuffer.read(cx).history.group_interval;
2018 multibuffer.update(cx, |multibuffer, cx| {
2019 multibuffer.push_excerpt(
2020 ExcerptProperties {
2021 buffer: &buffer_1,
2022 range: 0..buffer_1.read(cx).len(),
2023 header_height: 0,
2024 },
2025 cx,
2026 );
2027 multibuffer.push_excerpt(
2028 ExcerptProperties {
2029 buffer: &buffer_2,
2030 range: 0..buffer_2.read(cx).len(),
2031 header_height: 0,
2032 },
2033 cx,
2034 );
2035 });
2036
2037 let mut now = Instant::now();
2038
2039 multibuffer.update(cx, |multibuffer, cx| {
2040 multibuffer.start_transaction_at(now, cx);
2041 multibuffer.edit(
2042 [
2043 Point::new(0, 0)..Point::new(0, 0),
2044 Point::new(1, 0)..Point::new(1, 0),
2045 ],
2046 "A",
2047 cx,
2048 );
2049 multibuffer.edit(
2050 [
2051 Point::new(0, 1)..Point::new(0, 1),
2052 Point::new(1, 1)..Point::new(1, 1),
2053 ],
2054 "B",
2055 cx,
2056 );
2057 multibuffer.end_transaction_at(now, cx);
2058 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2059
2060 now += 2 * group_interval;
2061 multibuffer.start_transaction_at(now, cx);
2062 multibuffer.edit([2..2], "C", cx);
2063 multibuffer.end_transaction_at(now, cx);
2064 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2065
2066 multibuffer.undo(cx);
2067 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2068
2069 multibuffer.undo(cx);
2070 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2071
2072 multibuffer.redo(cx);
2073 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2074
2075 multibuffer.redo(cx);
2076 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2077
2078 buffer_1.update(cx, |buffer_1, cx| buffer_1.undo(cx));
2079 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2080
2081 multibuffer.undo(cx);
2082 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2083
2084 multibuffer.redo(cx);
2085 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2086
2087 multibuffer.redo(cx);
2088 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2089
2090 multibuffer.undo(cx);
2091 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2092
2093 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
2094 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2095
2096 multibuffer.undo(cx);
2097 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2098 });
2099 }
2100}