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 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, &());
1154 if let Some(excerpt) = cursor.item() {
1155 let start_after_header = cursor.start().0 + excerpt.header_height as usize;
1156 let mut end_before_newline = cursor.end(&()).0;
1157 if excerpt.has_trailing_newline {
1158 end_before_newline -= 1;
1159 }
1160
1161 let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1162 let overshoot = cmp::min(offset, end_before_newline).saturating_sub(start_after_header);
1163 Anchor {
1164 excerpt_id: excerpt.id.clone(),
1165 text_anchor: excerpt.buffer.anchor_at(buffer_start + overshoot, bias),
1166 }
1167 } else if offset == 0 && bias == Bias::Left {
1168 Anchor::min()
1169 } else {
1170 Anchor::max()
1171 }
1172 }
1173
1174 pub fn parse_count(&self) -> usize {
1175 self.parse_count
1176 }
1177
1178 pub fn enclosing_bracket_ranges<T: ToOffset>(
1179 &self,
1180 range: Range<T>,
1181 ) -> Option<(Range<usize>, Range<usize>)> {
1182 let range = range.start.to_offset(self)..range.end.to_offset(self);
1183 self.as_singleton().unwrap().enclosing_bracket_ranges(range)
1184 }
1185
1186 pub fn diagnostics_update_count(&self) -> usize {
1187 self.diagnostics_update_count
1188 }
1189
1190 pub fn language(&self) -> Option<&Arc<Language>> {
1191 self.excerpts
1192 .iter()
1193 .next()
1194 .and_then(|excerpt| excerpt.buffer.language())
1195 }
1196
1197 pub fn diagnostic_group<'a, O>(
1198 &'a self,
1199 group_id: usize,
1200 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1201 where
1202 O: text::FromAnchor + 'a,
1203 {
1204 self.as_singleton().unwrap().diagnostic_group(group_id)
1205 }
1206
1207 pub fn diagnostics_in_range<'a, T, O>(
1208 &'a self,
1209 range: Range<T>,
1210 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1211 where
1212 T: 'a + ToOffset,
1213 O: 'a + text::FromAnchor,
1214 {
1215 let range = range.start.to_offset(self)..range.end.to_offset(self);
1216 self.as_singleton().unwrap().diagnostics_in_range(range)
1217 }
1218
1219 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1220 let range = range.start.to_offset(self)..range.end.to_offset(self);
1221 self.as_singleton()
1222 .unwrap()
1223 .range_for_syntax_ancestor(range)
1224 }
1225
1226 fn buffer_snapshot_for_excerpt<'a>(
1227 &'a self,
1228 excerpt_id: &'a ExcerptId,
1229 ) -> Option<&'a BufferSnapshot> {
1230 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1231 cursor.seek(&Some(excerpt_id), Bias::Left, &());
1232 if let Some(excerpt) = cursor.item() {
1233 if excerpt.id == *excerpt_id {
1234 return Some(&excerpt.buffer);
1235 }
1236 }
1237 None
1238 }
1239
1240 pub fn remote_selections_in_range<'a>(
1241 &'a self,
1242 range: &'a Range<Anchor>,
1243 ) -> impl 'a + Iterator<Item = (ReplicaId, Selection<Anchor>)> {
1244 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1245 cursor.seek(&Some(&range.start.excerpt_id), Bias::Left, &());
1246 cursor
1247 .take_while(move |excerpt| excerpt.id <= range.end.excerpt_id)
1248 .flat_map(move |excerpt| {
1249 let mut query_range = excerpt.range.start.clone()..excerpt.range.end.clone();
1250 if excerpt.id == range.start.excerpt_id {
1251 query_range.start = range.start.text_anchor.clone();
1252 }
1253 if excerpt.id == range.end.excerpt_id {
1254 query_range.end = range.end.text_anchor.clone();
1255 }
1256
1257 excerpt
1258 .buffer
1259 .remote_selections_in_range(query_range)
1260 .flat_map(move |(replica_id, selections)| {
1261 selections.map(move |selection| {
1262 let mut start = Anchor {
1263 excerpt_id: excerpt.id.clone(),
1264 text_anchor: selection.start.clone(),
1265 };
1266 let mut end = Anchor {
1267 excerpt_id: excerpt.id.clone(),
1268 text_anchor: selection.end.clone(),
1269 };
1270 if range.start.cmp(&start, self).unwrap().is_gt() {
1271 start = range.start.clone();
1272 }
1273 if range.end.cmp(&end, self).unwrap().is_lt() {
1274 end = range.end.clone();
1275 }
1276
1277 (
1278 replica_id,
1279 Selection {
1280 id: selection.id,
1281 start,
1282 end,
1283 reversed: selection.reversed,
1284 goal: selection.goal,
1285 },
1286 )
1287 })
1288 })
1289 })
1290 }
1291}
1292
1293impl History {
1294 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
1295 self.transaction_depth += 1;
1296 if self.transaction_depth == 1 {
1297 let id = post_inc(&mut self.next_transaction_id);
1298 self.undo_stack.push(Transaction {
1299 id,
1300 buffer_transactions: Default::default(),
1301 first_edit_at: now,
1302 last_edit_at: now,
1303 });
1304 Some(id)
1305 } else {
1306 None
1307 }
1308 }
1309
1310 fn end_transaction(
1311 &mut self,
1312 now: Instant,
1313 buffer_transactions: HashSet<(usize, TransactionId)>,
1314 ) -> bool {
1315 assert_ne!(self.transaction_depth, 0);
1316 self.transaction_depth -= 1;
1317 if self.transaction_depth == 0 {
1318 if buffer_transactions.is_empty() {
1319 self.undo_stack.pop();
1320 false
1321 } else {
1322 let transaction = self.undo_stack.last_mut().unwrap();
1323 transaction.last_edit_at = now;
1324 transaction.buffer_transactions.extend(buffer_transactions);
1325 true
1326 }
1327 } else {
1328 false
1329 }
1330 }
1331
1332 fn pop_undo(&mut self) -> Option<&Transaction> {
1333 assert_eq!(self.transaction_depth, 0);
1334 if let Some(transaction) = self.undo_stack.pop() {
1335 self.redo_stack.push(transaction);
1336 self.redo_stack.last()
1337 } else {
1338 None
1339 }
1340 }
1341
1342 fn pop_redo(&mut self) -> Option<&Transaction> {
1343 assert_eq!(self.transaction_depth, 0);
1344 if let Some(transaction) = self.redo_stack.pop() {
1345 self.undo_stack.push(transaction);
1346 self.undo_stack.last()
1347 } else {
1348 None
1349 }
1350 }
1351
1352 fn group(&mut self) -> Option<TransactionId> {
1353 let mut new_len = self.undo_stack.len();
1354 let mut transactions = self.undo_stack.iter_mut();
1355
1356 if let Some(mut transaction) = transactions.next_back() {
1357 while let Some(prev_transaction) = transactions.next_back() {
1358 if transaction.first_edit_at - prev_transaction.last_edit_at <= self.group_interval
1359 {
1360 transaction = prev_transaction;
1361 new_len -= 1;
1362 } else {
1363 break;
1364 }
1365 }
1366 }
1367
1368 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
1369 if let Some(last_transaction) = transactions_to_keep.last_mut() {
1370 if let Some(transaction) = transactions_to_merge.last() {
1371 last_transaction.last_edit_at = transaction.last_edit_at;
1372 }
1373 }
1374
1375 self.undo_stack.truncate(new_len);
1376 self.undo_stack.last().map(|t| t.id)
1377 }
1378}
1379
1380impl Excerpt {
1381 fn new(
1382 id: ExcerptId,
1383 buffer_id: usize,
1384 buffer: BufferSnapshot,
1385 range: Range<text::Anchor>,
1386 header_height: u8,
1387 has_trailing_newline: bool,
1388 ) -> Self {
1389 let mut text_summary =
1390 buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1391 if header_height > 0 {
1392 text_summary.first_line_chars = 0;
1393 text_summary.lines.row += header_height as u32;
1394 text_summary.lines_utf16.row += header_height as u32;
1395 text_summary.bytes += header_height as usize;
1396 text_summary.longest_row += header_height as u32;
1397 }
1398 if has_trailing_newline {
1399 text_summary.last_line_chars = 0;
1400 text_summary.lines.row += 1;
1401 text_summary.lines.column = 0;
1402 text_summary.lines_utf16.row += 1;
1403 text_summary.lines_utf16.column = 0;
1404 text_summary.bytes += 1;
1405 }
1406
1407 Excerpt {
1408 id,
1409 buffer_id,
1410 buffer,
1411 range,
1412 text_summary,
1413 header_height,
1414 has_trailing_newline,
1415 }
1416 }
1417
1418 fn header_summary(&self) -> TextSummary {
1419 TextSummary {
1420 bytes: self.header_height as usize,
1421 lines: Point::new(self.header_height as u32, 0),
1422 lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1423 first_line_chars: 0,
1424 last_line_chars: 0,
1425 longest_row: 0,
1426 longest_row_chars: 0,
1427 }
1428 }
1429}
1430
1431impl sum_tree::Item for Excerpt {
1432 type Summary = ExcerptSummary;
1433
1434 fn summary(&self) -> Self::Summary {
1435 ExcerptSummary {
1436 excerpt_id: self.id.clone(),
1437 text: self.text_summary.clone(),
1438 }
1439 }
1440}
1441
1442impl sum_tree::Summary for ExcerptSummary {
1443 type Context = ();
1444
1445 fn add_summary(&mut self, summary: &Self, _: &()) {
1446 debug_assert!(summary.excerpt_id > self.excerpt_id);
1447 self.excerpt_id = summary.excerpt_id.clone();
1448 self.text.add_summary(&summary.text, &());
1449 }
1450}
1451
1452impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1453 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1454 *self += &summary.text;
1455 }
1456}
1457
1458impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1459 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1460 *self += summary.text.bytes;
1461 }
1462}
1463
1464impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1465 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1466 Ord::cmp(self, &cursor_location.text.bytes)
1467 }
1468}
1469
1470impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1471 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1472 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1473 }
1474}
1475
1476impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1477 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1478 *self += summary.text.lines;
1479 }
1480}
1481
1482impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1483 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1484 *self += summary.text.lines_utf16
1485 }
1486}
1487
1488impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1489 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1490 *self = Some(&summary.excerpt_id);
1491 }
1492}
1493
1494impl<'a> MultiBufferChunks<'a> {
1495 pub fn offset(&self) -> usize {
1496 self.range.start
1497 }
1498
1499 pub fn seek(&mut self, offset: usize) {
1500 self.range.start = offset;
1501 self.cursor.seek_forward(&offset, Bias::Right, &());
1502 self.header_height = 0;
1503 self.excerpt_chunks = None;
1504 if let Some(excerpt) = self.cursor.item() {
1505 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1506 self.header_height = excerpt.header_height;
1507 self.has_trailing_newline = excerpt.has_trailing_newline;
1508
1509 let buffer_start;
1510 let start_overshoot = self.range.start - self.cursor.start();
1511 if start_overshoot < excerpt.header_height as usize {
1512 self.header_height -= start_overshoot as u8;
1513 buffer_start = buffer_range.start;
1514 } else {
1515 buffer_start =
1516 buffer_range.start + start_overshoot - excerpt.header_height as usize;
1517 self.header_height = 0;
1518 }
1519
1520 let buffer_end;
1521 let end_overshoot = self.range.end - self.cursor.start();
1522 if end_overshoot < excerpt.header_height as usize {
1523 self.header_height -= excerpt.header_height - end_overshoot as u8;
1524 buffer_end = buffer_start;
1525 } else {
1526 buffer_end = cmp::min(
1527 buffer_range.end,
1528 buffer_range.start + end_overshoot - excerpt.header_height as usize,
1529 );
1530 }
1531
1532 self.excerpt_chunks = Some(excerpt.buffer.chunks(buffer_start..buffer_end, self.theme));
1533 }
1534 }
1535}
1536
1537impl<'a> Iterator for MultiBufferChunks<'a> {
1538 type Item = Chunk<'a>;
1539
1540 fn next(&mut self) -> Option<Self::Item> {
1541 loop {
1542 if self.header_height > 0 {
1543 let chunk = Chunk {
1544 text: unsafe {
1545 std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1546 },
1547 ..Default::default()
1548 };
1549 self.range.start += self.header_height as usize;
1550 self.header_height = 0;
1551 return Some(chunk);
1552 }
1553
1554 if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1555 if let Some(chunk) = excerpt_chunks.next() {
1556 self.range.start += chunk.text.len();
1557 return Some(chunk);
1558 }
1559 self.excerpt_chunks.take();
1560 if self.has_trailing_newline && self.cursor.end(&()) <= self.range.end {
1561 self.range.start += 1;
1562 return Some(Chunk {
1563 text: "\n",
1564 ..Default::default()
1565 });
1566 }
1567 }
1568
1569 self.cursor.next(&());
1570 if *self.cursor.start() >= self.range.end {
1571 return None;
1572 }
1573
1574 let excerpt = self.cursor.item()?;
1575 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1576
1577 let buffer_end = cmp::min(
1578 buffer_range.end,
1579 buffer_range.start + self.range.end
1580 - excerpt.header_height as usize
1581 - self.cursor.start(),
1582 );
1583
1584 self.header_height = excerpt.header_height;
1585 self.has_trailing_newline = excerpt.has_trailing_newline;
1586 self.excerpt_chunks = Some(
1587 excerpt
1588 .buffer
1589 .chunks(buffer_range.start..buffer_end, self.theme),
1590 );
1591 }
1592 }
1593}
1594
1595impl<'a> Iterator for MultiBufferBytes<'a> {
1596 type Item = &'a [u8];
1597
1598 fn next(&mut self) -> Option<Self::Item> {
1599 self.chunks.next().map(|chunk| chunk.text.as_bytes())
1600 }
1601}
1602
1603impl<'a> io::Read for MultiBufferBytes<'a> {
1604 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
1605 todo!()
1606 }
1607}
1608
1609impl ToOffset for Point {
1610 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1611 snapshot.point_to_offset(*self)
1612 }
1613}
1614
1615impl ToOffset for PointUtf16 {
1616 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1617 snapshot.point_utf16_to_offset(*self)
1618 }
1619}
1620
1621impl ToOffset for usize {
1622 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1623 assert!(*self <= snapshot.len(), "offset is out of range");
1624 *self
1625 }
1626}
1627
1628impl ToPoint for usize {
1629 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1630 snapshot.offset_to_point(*self)
1631 }
1632}
1633
1634impl ToPoint for Point {
1635 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1636 *self
1637 }
1638}
1639
1640#[cfg(test)]
1641mod tests {
1642 use super::*;
1643 use gpui::MutableAppContext;
1644 use language::Buffer;
1645 use rand::prelude::*;
1646 use std::env;
1647 use text::{Point, RandomCharIter};
1648 use util::test::sample_text;
1649
1650 #[gpui::test]
1651 fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
1652 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1653 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
1654 assert_eq!(
1655 multibuffer.read(cx).snapshot(cx).text(),
1656 buffer.read(cx).text()
1657 );
1658
1659 buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX", cx));
1660 assert_eq!(
1661 multibuffer.read(cx).snapshot(cx).text(),
1662 buffer.read(cx).text()
1663 );
1664 }
1665
1666 #[gpui::test]
1667 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1668 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1669 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1670
1671 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
1672
1673 let subscription = multibuffer.update(cx, |multibuffer, cx| {
1674 let subscription = multibuffer.subscribe();
1675 multibuffer.push_excerpt(
1676 ExcerptProperties {
1677 buffer: &buffer_1,
1678 range: Point::new(1, 2)..Point::new(2, 5),
1679 header_height: 2,
1680 },
1681 cx,
1682 );
1683 assert_eq!(
1684 subscription.consume().into_inner(),
1685 [Edit {
1686 old: 0..0,
1687 new: 0..13
1688 }]
1689 );
1690
1691 multibuffer.push_excerpt(
1692 ExcerptProperties {
1693 buffer: &buffer_1,
1694 range: Point::new(3, 3)..Point::new(4, 4),
1695 header_height: 1,
1696 },
1697 cx,
1698 );
1699 multibuffer.push_excerpt(
1700 ExcerptProperties {
1701 buffer: &buffer_2,
1702 range: Point::new(3, 1)..Point::new(3, 3),
1703 header_height: 3,
1704 },
1705 cx,
1706 );
1707 assert_eq!(
1708 subscription.consume().into_inner(),
1709 [Edit {
1710 old: 13..13,
1711 new: 13..29
1712 }]
1713 );
1714
1715 subscription
1716 });
1717
1718 assert_eq!(
1719 multibuffer.read(cx).snapshot(cx).text(),
1720 concat!(
1721 "\n", // Preserve newlines
1722 "\n", //
1723 "bbbb\n", //
1724 "ccccc\n", //
1725 "\n", //
1726 "ddd\n", //
1727 "eeee\n", //
1728 "\n", //
1729 "\n", //
1730 "\n", //
1731 "jj\n" //
1732 )
1733 );
1734
1735 buffer_1.update(cx, |buffer, cx| {
1736 buffer.edit(
1737 [
1738 Point::new(0, 0)..Point::new(0, 0),
1739 Point::new(2, 1)..Point::new(2, 3),
1740 ],
1741 "\n",
1742 cx,
1743 );
1744 });
1745
1746 assert_eq!(
1747 multibuffer.read(cx).snapshot(cx).text(),
1748 concat!(
1749 "\n", // Preserve newlines
1750 "\n", //
1751 "bbbb\n", //
1752 "c\n", //
1753 "cc\n", //
1754 "\n", //
1755 "ddd\n", //
1756 "eeee\n", //
1757 "\n", //
1758 "\n", //
1759 "\n", //
1760 "jj\n" //
1761 )
1762 );
1763
1764 assert_eq!(
1765 subscription.consume().into_inner(),
1766 [Edit {
1767 old: 8..10,
1768 new: 8..9
1769 }]
1770 );
1771 }
1772
1773 #[gpui::test(iterations = 100)]
1774 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1775 let operations = env::var("OPERATIONS")
1776 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1777 .unwrap_or(10);
1778
1779 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1780 let list = cx.add_model(|_| MultiBuffer::new(0));
1781 let mut excerpt_ids = Vec::new();
1782 let mut expected_excerpts = Vec::new();
1783 let mut old_versions = Vec::new();
1784
1785 for _ in 0..operations {
1786 match rng.gen_range(0..100) {
1787 0..=19 if !buffers.is_empty() => {
1788 let buffer = buffers.choose(&mut rng).unwrap();
1789 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1790 }
1791 _ => {
1792 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1793 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1794 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1795 buffers.last().unwrap()
1796 } else {
1797 buffers.choose(&mut rng).unwrap()
1798 };
1799
1800 let buffer = buffer_handle.read(cx);
1801 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1802 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1803 let header_height = rng.gen_range(0..=5);
1804 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1805 log::info!(
1806 "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1807 header_height,
1808 buffer_handle.id(),
1809 buffer.text(),
1810 start_ix..end_ix,
1811 &buffer.text()[start_ix..end_ix]
1812 );
1813
1814 let excerpt_id = list.update(cx, |list, cx| {
1815 list.push_excerpt(
1816 ExcerptProperties {
1817 buffer: &buffer_handle,
1818 range: start_ix..end_ix,
1819 header_height,
1820 },
1821 cx,
1822 )
1823 });
1824 excerpt_ids.push(excerpt_id);
1825 expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1826 }
1827 }
1828
1829 if rng.gen_bool(0.3) {
1830 list.update(cx, |list, cx| {
1831 old_versions.push((list.snapshot(cx), list.subscribe()));
1832 })
1833 }
1834
1835 let snapshot = list.read(cx).snapshot(cx);
1836
1837 let mut excerpt_starts = Vec::new();
1838 let mut expected_text = String::new();
1839 for (buffer, range, header_height) in &expected_excerpts {
1840 let buffer = buffer.read(cx);
1841 let buffer_range = range.to_offset(buffer);
1842
1843 for _ in 0..*header_height {
1844 expected_text.push('\n');
1845 }
1846
1847 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1848 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1849 expected_text.push('\n');
1850 }
1851
1852 assert_eq!(snapshot.text(), expected_text);
1853
1854 let mut excerpt_starts = excerpt_starts.into_iter();
1855 for (buffer, range, _) in &expected_excerpts {
1856 let buffer_id = buffer.id();
1857 let buffer = buffer.read(cx);
1858 let buffer_range = range.to_offset(buffer);
1859 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1860 let buffer_start_point_utf16 =
1861 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1862
1863 let excerpt_start = excerpt_starts.next().unwrap();
1864 let mut offset = excerpt_start.bytes;
1865 let mut buffer_offset = buffer_range.start;
1866 let mut point = excerpt_start.lines;
1867 let mut buffer_point = buffer_start_point;
1868 let mut point_utf16 = excerpt_start.lines_utf16;
1869 let mut buffer_point_utf16 = buffer_start_point_utf16;
1870 for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1871 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1872 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1873 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1874 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1875 assert_eq!(
1876 left_offset,
1877 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1878 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1879 offset,
1880 buffer_id,
1881 buffer_offset,
1882 );
1883 assert_eq!(
1884 right_offset,
1885 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1886 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1887 offset,
1888 buffer_id,
1889 buffer_offset,
1890 );
1891
1892 let left_point = snapshot.clip_point(point, Bias::Left);
1893 let right_point = snapshot.clip_point(point, Bias::Right);
1894 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1895 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1896 assert_eq!(
1897 left_point,
1898 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1899 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1900 point,
1901 buffer_id,
1902 buffer_point,
1903 );
1904 assert_eq!(
1905 right_point,
1906 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1907 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1908 point,
1909 buffer_id,
1910 buffer_point,
1911 );
1912
1913 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1914 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1915 let buffer_left_point_utf16 =
1916 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1917 let buffer_right_point_utf16 =
1918 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1919 assert_eq!(
1920 left_point_utf16,
1921 excerpt_start.lines_utf16
1922 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1923 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1924 point_utf16,
1925 buffer_id,
1926 buffer_point_utf16,
1927 );
1928 assert_eq!(
1929 right_point_utf16,
1930 excerpt_start.lines_utf16
1931 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1932 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1933 point_utf16,
1934 buffer_id,
1935 buffer_point_utf16,
1936 );
1937
1938 assert_eq!(
1939 snapshot.point_to_offset(left_point),
1940 left_offset,
1941 "point_to_offset({:?})",
1942 left_point,
1943 );
1944 assert_eq!(
1945 snapshot.offset_to_point(left_offset),
1946 left_point,
1947 "offset_to_point({:?})",
1948 left_offset,
1949 );
1950
1951 offset += 1;
1952 buffer_offset += 1;
1953 if *byte == b'\n' {
1954 point += Point::new(1, 0);
1955 point_utf16 += PointUtf16::new(1, 0);
1956 buffer_point += Point::new(1, 0);
1957 buffer_point_utf16 += PointUtf16::new(1, 0);
1958 } else {
1959 point += Point::new(0, 1);
1960 point_utf16 += PointUtf16::new(0, 1);
1961 buffer_point += Point::new(0, 1);
1962 buffer_point_utf16 += PointUtf16::new(0, 1);
1963 }
1964 }
1965 }
1966
1967 for (row, line) in expected_text.split('\n').enumerate() {
1968 assert_eq!(
1969 snapshot.line_len(row as u32),
1970 line.len() as u32,
1971 "line_len({}).",
1972 row
1973 );
1974 }
1975
1976 for _ in 0..10 {
1977 let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1978 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1979
1980 assert_eq!(
1981 snapshot
1982 .text_for_range(start_ix..end_ix)
1983 .collect::<String>(),
1984 &expected_text[start_ix..end_ix],
1985 "incorrect text for range {:?}",
1986 start_ix..end_ix
1987 );
1988
1989 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1990 assert_eq!(
1991 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1992 expected_summary,
1993 "incorrect summary for range {:?}",
1994 start_ix..end_ix
1995 );
1996 }
1997 }
1998
1999 let snapshot = list.read(cx).snapshot(cx);
2000 for (old_snapshot, subscription) in old_versions {
2001 let edits = subscription.consume().into_inner();
2002
2003 log::info!(
2004 "applying edits since old text: {:?}: {:?}",
2005 old_snapshot.text(),
2006 edits,
2007 );
2008
2009 let mut text = old_snapshot.text();
2010 for edit in edits {
2011 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
2012 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
2013 }
2014 assert_eq!(text.to_string(), snapshot.text());
2015 }
2016 }
2017
2018 #[gpui::test]
2019 fn test_history(cx: &mut MutableAppContext) {
2020 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
2021 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
2022 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2023 let group_interval = multibuffer.read(cx).history.group_interval;
2024 multibuffer.update(cx, |multibuffer, cx| {
2025 multibuffer.push_excerpt(
2026 ExcerptProperties {
2027 buffer: &buffer_1,
2028 range: 0..buffer_1.read(cx).len(),
2029 header_height: 0,
2030 },
2031 cx,
2032 );
2033 multibuffer.push_excerpt(
2034 ExcerptProperties {
2035 buffer: &buffer_2,
2036 range: 0..buffer_2.read(cx).len(),
2037 header_height: 0,
2038 },
2039 cx,
2040 );
2041 });
2042
2043 let mut now = Instant::now();
2044
2045 multibuffer.update(cx, |multibuffer, cx| {
2046 multibuffer.start_transaction_at(now, cx);
2047 multibuffer.edit(
2048 [
2049 Point::new(0, 0)..Point::new(0, 0),
2050 Point::new(1, 0)..Point::new(1, 0),
2051 ],
2052 "A",
2053 cx,
2054 );
2055 multibuffer.edit(
2056 [
2057 Point::new(0, 1)..Point::new(0, 1),
2058 Point::new(1, 1)..Point::new(1, 1),
2059 ],
2060 "B",
2061 cx,
2062 );
2063 multibuffer.end_transaction_at(now, cx);
2064 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2065
2066 now += 2 * group_interval;
2067 multibuffer.start_transaction_at(now, cx);
2068 multibuffer.edit([2..2], "C", cx);
2069 multibuffer.end_transaction_at(now, cx);
2070 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2071
2072 multibuffer.undo(cx);
2073 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2074
2075 multibuffer.undo(cx);
2076 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2077
2078 multibuffer.redo(cx);
2079 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2080
2081 multibuffer.redo(cx);
2082 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2083
2084 buffer_1.update(cx, |buffer_1, cx| buffer_1.undo(cx));
2085 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2086
2087 multibuffer.undo(cx);
2088 assert_eq!(multibuffer.read(cx).text(), "1234\n5678\n");
2089
2090 multibuffer.redo(cx);
2091 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2092
2093 multibuffer.redo(cx);
2094 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2095
2096 multibuffer.undo(cx);
2097 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678\n");
2098
2099 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
2100 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2101
2102 multibuffer.undo(cx);
2103 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678\n");
2104 });
2105 }
2106}