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