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