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