1mod anchor;
2mod selection;
3
4use crate::{
5 buffer::{self, Buffer, Chunk, ToOffset as _, ToPoint as _},
6 BufferSnapshot, DiagnosticEntry, File, Language,
7};
8pub use anchor::{Anchor, AnchorRangeExt};
9use anyhow::Result;
10use clock::ReplicaId;
11use collections::HashMap;
12use gpui::{AppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task};
13pub use selection::SelectionSet;
14use std::{
15 cell::{Ref, RefCell},
16 cmp, io,
17 iter::Peekable,
18 ops::{Range, Sub},
19 sync::Arc,
20 time::SystemTime,
21};
22use sum_tree::{Bias, Cursor, SumTree};
23use text::{
24 locator::Locator,
25 rope::TextDimension,
26 subscription::{Subscription, Topic},
27 AnchorRangeExt as _, Edit, Point, PointUtf16, Selection, SelectionSetId, TextSummary,
28};
29use theme::SyntaxTheme;
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 selection_sets: HashMap<SelectionSetId, SelectionSet>,
40 singleton: bool,
41 replica_id: ReplicaId,
42}
43
44pub trait ToOffset: 'static {
45 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize;
46}
47
48pub trait ToPoint: 'static {
49 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point;
50}
51
52#[derive(Debug)]
53struct BufferState {
54 buffer: ModelHandle<Buffer>,
55 last_sync: clock::Global,
56 excerpts: Vec<ExcerptId>,
57}
58
59#[derive(Clone, Default)]
60pub struct MultiBufferSnapshot {
61 excerpts: SumTree<Excerpt>,
62}
63
64pub struct ExcerptProperties<'a, T> {
65 buffer: &'a ModelHandle<Buffer>,
66 range: Range<T>,
67 header_height: u8,
68}
69
70#[derive(Clone)]
71struct Excerpt {
72 id: ExcerptId,
73 buffer: buffer::BufferSnapshot,
74 range: Range<text::Anchor>,
75 text_summary: TextSummary,
76 header_height: u8,
77 has_trailing_newline: bool,
78}
79
80#[derive(Clone, Debug, Default)]
81struct ExcerptSummary {
82 excerpt_id: ExcerptId,
83 text: TextSummary,
84}
85
86pub struct MultiBufferChunks<'a> {
87 range: Range<usize>,
88 cursor: Cursor<'a, Excerpt, usize>,
89 header_height: u8,
90 has_trailing_newline: bool,
91 excerpt_chunks: Option<buffer::BufferChunks<'a>>,
92 theme: Option<&'a SyntaxTheme>,
93}
94
95pub struct MultiBufferBytes<'a> {
96 chunks: Peekable<MultiBufferChunks<'a>>,
97}
98
99impl MultiBuffer {
100 pub fn new(replica_id: ReplicaId) -> Self {
101 Self {
102 snapshot: Default::default(),
103 buffers: Default::default(),
104 subscriptions: Default::default(),
105 selection_sets: Default::default(),
106 singleton: false,
107 replica_id,
108 }
109 }
110
111 pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
112 let mut this = Self::new(buffer.read(cx).replica_id());
113 this.singleton = true;
114 this.push(
115 ExcerptProperties {
116 buffer: &buffer,
117 range: text::Anchor::min()..text::Anchor::max(),
118 header_height: 0,
119 },
120 cx,
121 );
122 this
123 }
124
125 pub fn build_simple(text: &str, cx: &mut MutableAppContext) -> ModelHandle<Self> {
126 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
127 cx.add_model(|cx| Self::singleton(buffer, cx))
128 }
129
130 pub fn replica_id(&self) -> ReplicaId {
131 self.replica_id
132 }
133
134 pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
135 self.sync(cx);
136 self.snapshot.borrow().clone()
137 }
138
139 pub fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
140 self.sync(cx);
141 self.snapshot.borrow()
142 }
143
144 pub fn as_singleton(&self) -> Option<&ModelHandle<Buffer>> {
145 if self.buffers.len() == 1 {
146 return Some(&self.buffers.values().next().unwrap().buffer);
147 } else {
148 None
149 }
150 }
151
152 pub fn subscribe(&mut self) -> Subscription {
153 self.subscriptions.subscribe()
154 }
155
156 pub fn edit<I, S, T>(&mut self, ranges_iter: I, new_text: T, cx: &mut ModelContext<Self>)
157 where
158 I: IntoIterator<Item = Range<S>>,
159 S: ToOffset,
160 T: Into<String>,
161 {
162 self.edit_internal(ranges_iter, new_text, false, cx)
163 }
164
165 pub fn edit_with_autoindent<I, S, T>(
166 &mut self,
167 ranges_iter: I,
168 new_text: T,
169 cx: &mut ModelContext<Self>,
170 ) where
171 I: IntoIterator<Item = Range<S>>,
172 S: ToOffset,
173 T: Into<String>,
174 {
175 self.edit_internal(ranges_iter, new_text, true, cx)
176 }
177
178 pub fn edit_internal<I, S, T>(
179 &mut self,
180 ranges_iter: I,
181 new_text: T,
182 autoindent: bool,
183 cx: &mut ModelContext<Self>,
184 ) where
185 I: IntoIterator<Item = Range<S>>,
186 S: ToOffset,
187 T: Into<String>,
188 {
189 // TODO
190 let snapshot = self.read(cx);
191 let ranges_iter = ranges_iter
192 .into_iter()
193 .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot));
194 self.as_singleton().unwrap().update(cx, |buffer, cx| {
195 if autoindent {
196 buffer.edit_with_autoindent(ranges_iter, new_text, cx);
197 } else {
198 buffer.edit(ranges_iter, new_text, cx);
199 }
200 });
201 }
202
203 pub fn start_transaction(
204 &mut self,
205 selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
206 cx: &mut ModelContext<Self>,
207 ) -> Result<()> {
208 // TODO
209 self.as_singleton()
210 .unwrap()
211 .update(cx, |buffer, _| buffer.start_transaction(selection_set_ids))
212 }
213
214 pub fn end_transaction(
215 &mut self,
216 selection_set_ids: impl IntoIterator<Item = SelectionSetId>,
217 cx: &mut ModelContext<Self>,
218 ) -> Result<()> {
219 // TODO
220 self.as_singleton().unwrap().update(cx, |buffer, cx| {
221 buffer.end_transaction(selection_set_ids, cx)
222 })
223 }
224
225 pub fn undo(&mut self, cx: &mut ModelContext<Self>) {
226 // TODO
227 self.as_singleton()
228 .unwrap()
229 .update(cx, |buffer, cx| buffer.undo(cx))
230 }
231
232 pub fn redo(&mut self, cx: &mut ModelContext<Self>) {
233 // TODO
234 self.as_singleton()
235 .unwrap()
236 .update(cx, |buffer, cx| buffer.redo(cx))
237 }
238
239 pub fn selection_set(&self, set_id: SelectionSetId, cx: &AppContext) -> Result<&SelectionSet> {
240 // TODO
241 let set = self
242 .as_singleton()
243 .unwrap()
244 .read(cx)
245 .selection_set(set_id)?;
246 let excerpt_id = self.snapshot.borrow().excerpts.first().unwrap().id.clone();
247
248 let selection_sets: &mut HashMap<SelectionSetId, SelectionSet> =
249 unsafe { &mut *(&self.selection_sets as *const _ as *mut _) };
250 selection_sets.insert(
251 set_id,
252 SelectionSet {
253 id: set.id,
254 active: set.active,
255 selections: set
256 .selections
257 .iter()
258 .map(|selection| Selection {
259 id: selection.id,
260 start: Anchor {
261 excerpt_id: excerpt_id.clone(),
262 text_anchor: selection.start.clone(),
263 },
264 end: Anchor {
265 excerpt_id: excerpt_id.clone(),
266 text_anchor: selection.end.clone(),
267 },
268 reversed: selection.reversed,
269 goal: selection.goal,
270 })
271 .collect(),
272 },
273 );
274 Ok(self.selection_sets.get(&set.id).unwrap())
275 }
276
277 pub fn add_selection_set<T: ToOffset>(
278 &mut self,
279 selections: &[Selection<T>],
280 cx: &mut ModelContext<Self>,
281 ) -> SelectionSetId {
282 // TODO
283 let snapshot = self.read(cx);
284 self.as_singleton().unwrap().update(cx, |buffer, cx| {
285 buffer.add_selection_set(
286 &selections
287 .iter()
288 .map(|selection| Selection {
289 id: selection.id,
290 start: selection.start.to_offset(&snapshot),
291 end: selection.end.to_offset(&snapshot),
292 reversed: selection.reversed,
293 goal: selection.goal,
294 })
295 .collect::<Vec<_>>(),
296 cx,
297 )
298 })
299 }
300
301 pub fn remove_selection_set(
302 &mut self,
303 set_id: SelectionSetId,
304 cx: &mut ModelContext<Self>,
305 ) -> Result<()> {
306 // TODO
307 self.as_singleton()
308 .unwrap()
309 .update(cx, |buffer, cx| buffer.remove_selection_set(set_id, cx))
310 }
311
312 pub fn update_selection_set<T: ToOffset>(
313 &mut self,
314 set_id: SelectionSetId,
315 selections: &[Selection<T>],
316 cx: &mut ModelContext<Self>,
317 ) -> Result<()> {
318 // TODO
319 let snapshot = self.read(cx);
320 self.as_singleton().unwrap().update(cx, |buffer, cx| {
321 buffer.update_selection_set(
322 set_id,
323 &selections
324 .iter()
325 .map(|selection| Selection {
326 id: selection.id,
327 start: selection.start.to_offset(&snapshot),
328 end: selection.end.to_offset(&snapshot),
329 reversed: selection.reversed,
330 goal: selection.goal,
331 })
332 .collect::<Vec<_>>(),
333 cx,
334 )
335 })
336 }
337
338 pub fn set_active_selection_set(
339 &mut self,
340 set_id: Option<SelectionSetId>,
341 cx: &mut ModelContext<Self>,
342 ) -> Result<()> {
343 self.as_singleton()
344 .unwrap()
345 .update(cx, |buffer, cx| buffer.set_active_selection_set(set_id, cx))
346 }
347
348 pub fn selection_sets(
349 &self,
350 cx: &AppContext,
351 ) -> impl Iterator<Item = (&SelectionSetId, &SelectionSet)> {
352 let excerpt_id = self.snapshot.borrow().excerpts.first().unwrap().id.clone();
353 let selection_sets: &mut HashMap<SelectionSetId, SelectionSet> =
354 unsafe { &mut *(&self.selection_sets as *const _ as *mut _) };
355 selection_sets.clear();
356 for (selection_set_id, set) in self.as_singleton().unwrap().read(cx).selection_sets() {
357 selection_sets.insert(
358 *selection_set_id,
359 SelectionSet {
360 id: set.id,
361 active: set.active,
362 selections: set
363 .selections
364 .iter()
365 .map(|selection| Selection {
366 id: selection.id,
367 start: Anchor {
368 excerpt_id: excerpt_id.clone(),
369 text_anchor: selection.start.clone(),
370 },
371 end: Anchor {
372 excerpt_id: excerpt_id.clone(),
373 text_anchor: selection.end.clone(),
374 },
375 reversed: selection.reversed,
376 goal: selection.goal,
377 })
378 .collect(),
379 },
380 );
381 }
382 self.selection_sets.iter()
383 }
384
385 pub fn push<O>(&mut self, props: ExcerptProperties<O>, cx: &mut ModelContext<Self>) -> ExcerptId
386 where
387 O: text::ToOffset,
388 {
389 self.sync(cx);
390
391 let buffer = props.buffer.read(cx);
392 let range = buffer.anchor_before(props.range.start)..buffer.anchor_after(props.range.end);
393 let mut snapshot = self.snapshot.borrow_mut();
394 let prev_id = snapshot.excerpts.last().map(|e| &e.id);
395 let id = ExcerptId::between(prev_id.unwrap_or(&ExcerptId::min()), &ExcerptId::max());
396
397 let edit_start = snapshot.excerpts.summary().text.bytes;
398 let excerpt = Excerpt::new(
399 id.clone(),
400 buffer.snapshot(),
401 range,
402 props.header_height,
403 !self.singleton,
404 );
405 let edit = Edit {
406 old: edit_start..edit_start,
407 new: edit_start..edit_start + excerpt.text_summary.bytes,
408 };
409 snapshot.excerpts.push(excerpt, &());
410 self.buffers
411 .entry(props.buffer.id())
412 .or_insert_with(|| BufferState {
413 buffer: props.buffer.clone(),
414 last_sync: buffer.version(),
415 excerpts: Default::default(),
416 })
417 .excerpts
418 .push(id.clone());
419
420 self.subscriptions.publish_mut([edit]);
421
422 id
423 }
424
425 pub fn save(
426 &mut self,
427 cx: &mut ModelContext<Self>,
428 ) -> Result<Task<Result<(clock::Global, SystemTime)>>> {
429 self.as_singleton()
430 .unwrap()
431 .update(cx, |buffer, cx| buffer.save(cx))
432 }
433
434 pub fn language<'a>(&self, cx: &'a AppContext) -> Option<&'a Arc<Language>> {
435 self.as_singleton().unwrap().read(cx).language()
436 }
437
438 pub fn file<'a>(&self, cx: &'a AppContext) -> Option<&'a dyn File> {
439 self.as_singleton()
440 .and_then(|buffer| buffer.read(cx).file())
441 }
442
443 pub fn is_dirty(&self, cx: &AppContext) -> bool {
444 self.as_singleton().unwrap().read(cx).is_dirty()
445 }
446
447 pub fn has_conflict(&self, cx: &AppContext) -> bool {
448 self.as_singleton().unwrap().read(cx).has_conflict()
449 }
450
451 pub fn is_parsing(&self, cx: &AppContext) -> bool {
452 self.as_singleton().unwrap().read(cx).is_parsing()
453 }
454
455 fn sync(&self, cx: &AppContext) {
456 let mut snapshot = self.snapshot.borrow_mut();
457 let mut excerpts_to_edit = Vec::new();
458 for buffer_state in self.buffers.values() {
459 if buffer_state
460 .buffer
461 .read(cx)
462 .version()
463 .gt(&buffer_state.last_sync)
464 {
465 excerpts_to_edit.extend(
466 buffer_state
467 .excerpts
468 .iter()
469 .map(|excerpt_id| (excerpt_id, buffer_state)),
470 );
471 }
472 }
473 excerpts_to_edit.sort_unstable_by_key(|(excerpt_id, _)| *excerpt_id);
474
475 let mut edits = Vec::new();
476 let mut new_excerpts = SumTree::new();
477 let mut cursor = snapshot.excerpts.cursor::<(Option<&ExcerptId>, usize)>();
478
479 for (id, buffer_state) in excerpts_to_edit {
480 new_excerpts.push_tree(cursor.slice(&Some(id), Bias::Left, &()), &());
481 let old_excerpt = cursor.item().unwrap();
482 let buffer = buffer_state.buffer.read(cx);
483
484 edits.extend(
485 buffer
486 .edits_since_in_range::<usize>(
487 old_excerpt.buffer.version(),
488 old_excerpt.range.clone(),
489 )
490 .map(|mut edit| {
491 let excerpt_old_start =
492 cursor.start().1 + old_excerpt.header_height as usize;
493 let excerpt_new_start =
494 new_excerpts.summary().text.bytes + old_excerpt.header_height as usize;
495 edit.old.start += excerpt_old_start;
496 edit.old.end += excerpt_old_start;
497 edit.new.start += excerpt_new_start;
498 edit.new.end += excerpt_new_start;
499 edit
500 }),
501 );
502
503 new_excerpts.push(
504 Excerpt::new(
505 id.clone(),
506 buffer.snapshot(),
507 old_excerpt.range.clone(),
508 old_excerpt.header_height,
509 !self.singleton,
510 ),
511 &(),
512 );
513
514 cursor.next(&());
515 }
516 new_excerpts.push_tree(cursor.suffix(&()), &());
517
518 drop(cursor);
519 snapshot.excerpts = new_excerpts;
520
521 self.subscriptions.publish(edits);
522 }
523}
524
525#[cfg(any(test, feature = "test-support"))]
526impl MultiBuffer {
527 pub fn randomly_edit<R: rand::Rng>(
528 &mut self,
529 rng: &mut R,
530 count: usize,
531 cx: &mut ModelContext<Self>,
532 ) {
533 self.as_singleton()
534 .unwrap()
535 .update(cx, |buffer, cx| buffer.randomly_edit(rng, count, cx));
536 self.sync(cx);
537 }
538
539 pub fn randomly_mutate<R: rand::Rng>(&mut self, rng: &mut R, cx: &mut ModelContext<Self>) {
540 self.as_singleton()
541 .unwrap()
542 .update(cx, |buffer, cx| buffer.randomly_mutate(rng, cx));
543 self.sync(cx);
544 }
545}
546
547impl Entity for MultiBuffer {
548 type Event = super::Event;
549}
550
551impl MultiBufferSnapshot {
552 pub fn text(&self) -> String {
553 self.chunks(0..self.len(), None)
554 .map(|chunk| chunk.text)
555 .collect()
556 }
557
558 pub fn reversed_chars_at<'a, T: ToOffset>(
559 &'a self,
560 position: T,
561 ) -> impl Iterator<Item = char> + 'a {
562 // TODO
563 let offset = position.to_offset(self);
564 self.as_singleton().unwrap().reversed_chars_at(offset)
565 }
566
567 pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
568 let offset = position.to_offset(self);
569 self.text_for_range(offset..self.len())
570 .flat_map(|chunk| chunk.chars())
571 }
572
573 pub fn text_for_range<'a, T: ToOffset>(
574 &'a self,
575 range: Range<T>,
576 ) -> impl Iterator<Item = &'a str> {
577 self.chunks(range, None).map(|chunk| chunk.text)
578 }
579
580 pub fn is_line_blank(&self, row: u32) -> bool {
581 self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
582 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
583 }
584
585 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
586 where
587 T: ToOffset,
588 {
589 let offset = position.to_offset(self);
590 self.as_singleton().unwrap().contains_str_at(offset, needle)
591 }
592
593 fn as_singleton(&self) -> Option<&BufferSnapshot> {
594 let mut excerpts = self.excerpts.iter();
595 let buffer = excerpts.next().map(|excerpt| &excerpt.buffer);
596 if excerpts.next().is_none() {
597 buffer
598 } else {
599 None
600 }
601 }
602
603 pub fn len(&self) -> usize {
604 self.excerpts.summary().text.bytes
605 }
606
607 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
608 let mut cursor = self.excerpts.cursor::<usize>();
609 cursor.seek(&offset, Bias::Right, &());
610 if let Some(excerpt) = cursor.item() {
611 let start_after_header = *cursor.start() + excerpt.header_height as usize;
612 if offset < start_after_header {
613 *cursor.start()
614 } else {
615 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
616 let buffer_offset = excerpt
617 .buffer
618 .clip_offset(excerpt_start + (offset - start_after_header), bias);
619 let offset_in_excerpt = if buffer_offset > excerpt_start {
620 buffer_offset - excerpt_start
621 } else {
622 0
623 };
624 start_after_header + offset_in_excerpt
625 }
626 } else {
627 self.excerpts.summary().text.bytes
628 }
629 }
630
631 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
632 let mut cursor = self.excerpts.cursor::<Point>();
633 cursor.seek(&point, Bias::Right, &());
634 if let Some(excerpt) = cursor.item() {
635 let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
636 if point < start_after_header {
637 *cursor.start()
638 } else {
639 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
640 let buffer_point = excerpt
641 .buffer
642 .clip_point(excerpt_start + (point - start_after_header), bias);
643 let point_in_excerpt = if buffer_point > excerpt_start {
644 buffer_point - excerpt_start
645 } else {
646 Point::zero()
647 };
648 start_after_header + point_in_excerpt
649 }
650 } else {
651 self.excerpts.summary().text.lines
652 }
653 }
654
655 pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
656 let mut cursor = self.excerpts.cursor::<PointUtf16>();
657 cursor.seek(&point, Bias::Right, &());
658 if let Some(excerpt) = cursor.item() {
659 let start_after_header =
660 *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
661 if point < start_after_header {
662 *cursor.start()
663 } else {
664 let excerpt_start = excerpt
665 .buffer
666 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
667 let buffer_point = excerpt
668 .buffer
669 .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
670 let point_in_excerpt = if buffer_point > excerpt_start {
671 buffer_point - excerpt_start
672 } else {
673 PointUtf16::new(0, 0)
674 };
675 start_after_header + point_in_excerpt
676 }
677 } else {
678 self.excerpts.summary().text.lines_utf16
679 }
680 }
681
682 pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
683 MultiBufferBytes {
684 chunks: self.chunks(range, None).peekable(),
685 }
686 }
687
688 pub fn chunks<'a, T: ToOffset>(
689 &'a self,
690 range: Range<T>,
691 theme: Option<&'a SyntaxTheme>,
692 ) -> MultiBufferChunks<'a> {
693 let mut result = MultiBufferChunks {
694 range: 0..range.end.to_offset(self),
695 cursor: self.excerpts.cursor::<usize>(),
696 header_height: 0,
697 excerpt_chunks: None,
698 has_trailing_newline: false,
699 theme,
700 };
701 result.seek(range.start.to_offset(self));
702 result
703 }
704
705 pub fn offset_to_point(&self, offset: usize) -> Point {
706 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
707 cursor.seek(&offset, Bias::Right, &());
708 if let Some(excerpt) = cursor.item() {
709 let (start_offset, start_point) = cursor.start();
710 let overshoot = offset - start_offset;
711 let header_height = excerpt.header_height as usize;
712 if overshoot < header_height {
713 *start_point
714 } else {
715 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
716 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
717 let buffer_point = excerpt
718 .buffer
719 .offset_to_point(excerpt_start_offset + (overshoot - header_height));
720 *start_point
721 + Point::new(header_height as u32, 0)
722 + (buffer_point - excerpt_start_point)
723 }
724 } else {
725 self.excerpts.summary().text.lines
726 }
727 }
728
729 pub fn point_to_offset(&self, point: Point) -> usize {
730 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
731 cursor.seek(&point, Bias::Right, &());
732 if let Some(excerpt) = cursor.item() {
733 let (start_point, start_offset) = cursor.start();
734 let overshoot = point - start_point;
735 let header_height = Point::new(excerpt.header_height as u32, 0);
736 if overshoot < header_height {
737 *start_offset
738 } else {
739 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
740 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
741 let buffer_offset = excerpt
742 .buffer
743 .point_to_offset(excerpt_start_point + (overshoot - header_height));
744 *start_offset + excerpt.header_height as usize + buffer_offset
745 - excerpt_start_offset
746 }
747 } else {
748 self.excerpts.summary().text.bytes
749 }
750 }
751
752 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
753 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
754 cursor.seek(&point, Bias::Right, &());
755 if let Some(excerpt) = cursor.item() {
756 let (start_point, start_offset) = cursor.start();
757 let overshoot = point - start_point;
758 let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
759 if overshoot < header_height {
760 *start_offset
761 } else {
762 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
763 let excerpt_start_point = excerpt
764 .buffer
765 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
766 let buffer_offset = excerpt
767 .buffer
768 .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
769 *start_offset
770 + excerpt.header_height as usize
771 + (buffer_offset - excerpt_start_offset)
772 }
773 } else {
774 self.excerpts.summary().text.bytes
775 }
776 }
777
778 pub fn indent_column_for_line(&self, row: u32) -> u32 {
779 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
780 buffer
781 .indent_column_for_line(range.start.row)
782 .min(range.end.column)
783 .saturating_sub(range.start.column)
784 } else {
785 0
786 }
787 }
788
789 pub fn line_len(&self, row: u32) -> u32 {
790 if let Some((_, range)) = self.buffer_line_for_row(row) {
791 range.end.column - range.start.column
792 } else {
793 0
794 }
795 }
796
797 fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
798 let mut cursor = self.excerpts.cursor::<Point>();
799 cursor.seek(&Point::new(row, 0), Bias::Right, &());
800 if let Some(excerpt) = cursor.item() {
801 let overshoot = row - cursor.start().row;
802 let header_height = excerpt.header_height as u32;
803 if overshoot >= header_height {
804 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
805 let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
806 let buffer_row = excerpt_start.row + overshoot - header_height;
807 let line_start = Point::new(buffer_row, 0);
808 let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
809 return Some((
810 &excerpt.buffer,
811 line_start.max(excerpt_start)..line_end.min(excerpt_end),
812 ));
813 }
814 }
815 None
816 }
817
818 pub fn max_point(&self) -> Point {
819 self.text_summary().lines
820 }
821
822 pub fn text_summary(&self) -> TextSummary {
823 self.excerpts.summary().text
824 }
825
826 pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
827 where
828 D: TextDimension,
829 O: ToOffset,
830 {
831 let mut summary = D::default();
832 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
833 let mut cursor = self.excerpts.cursor::<usize>();
834 cursor.seek(&range.start, Bias::Right, &());
835 if let Some(excerpt) = cursor.item() {
836 let start_after_header = cursor.start() + excerpt.header_height as usize;
837 if range.start < start_after_header {
838 let header_len = cmp::min(range.end, start_after_header) - range.start;
839 summary.add_assign(&D::from_text_summary(&TextSummary {
840 bytes: header_len,
841 lines: Point::new(header_len as u32, 0),
842 lines_utf16: PointUtf16::new(header_len as u32, 0),
843 first_line_chars: 0,
844 last_line_chars: 0,
845 longest_row: 0,
846 longest_row_chars: 0,
847 }));
848 range.start = start_after_header;
849 range.end = cmp::max(range.start, range.end);
850 }
851
852 let mut end_before_newline = cursor.end(&());
853 if excerpt.has_trailing_newline {
854 end_before_newline -= 1;
855 }
856
857 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
858 let start_in_excerpt = excerpt_start + (range.start - start_after_header);
859 let end_in_excerpt =
860 excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
861 summary.add_assign(
862 &excerpt
863 .buffer
864 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
865 );
866
867 if range.end > end_before_newline {
868 summary.add_assign(&D::from_text_summary(&TextSummary {
869 bytes: 1,
870 lines: Point::new(1 as u32, 0),
871 lines_utf16: PointUtf16::new(1 as u32, 0),
872 first_line_chars: 0,
873 last_line_chars: 0,
874 longest_row: 0,
875 longest_row_chars: 0,
876 }));
877 }
878
879 cursor.next(&());
880 }
881
882 if range.end > *cursor.start() {
883 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
884 &range.end,
885 Bias::Right,
886 &(),
887 )));
888 if let Some(excerpt) = cursor.item() {
889 let start_after_header = cursor.start() + excerpt.header_height as usize;
890 let header_len =
891 cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
892 summary.add_assign(&D::from_text_summary(&TextSummary {
893 bytes: header_len,
894 lines: Point::new(header_len as u32, 0),
895 lines_utf16: PointUtf16::new(header_len as u32, 0),
896 first_line_chars: 0,
897 last_line_chars: 0,
898 longest_row: 0,
899 longest_row_chars: 0,
900 }));
901 range.end = cmp::max(start_after_header, range.end);
902
903 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
904 let end_in_excerpt = excerpt_start + (range.end - start_after_header);
905 summary.add_assign(
906 &excerpt
907 .buffer
908 .text_summary_for_range(excerpt_start..end_in_excerpt),
909 );
910 cursor.next(&());
911 }
912 }
913
914 summary
915 }
916
917 fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
918 where
919 D: TextDimension + Ord + Sub<D, Output = D>,
920 {
921 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
922 cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
923 if let Some(excerpt) = cursor.item() {
924 if excerpt.id == anchor.excerpt_id {
925 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
926 excerpt_start.add_summary(&excerpt.header_summary(), &());
927 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
928 let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
929 if buffer_point > excerpt_buffer_start {
930 excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
931 }
932 return excerpt_start;
933 }
934 }
935 D::from_text_summary(&cursor.start().text)
936 }
937
938 fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
939 where
940 D: TextDimension + Ord + Sub<D, Output = D>,
941 I: 'a + IntoIterator<Item = &'a Anchor>,
942 {
943 let mut anchors = anchors.into_iter().peekable();
944 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
945 let mut summaries = Vec::new();
946 while let Some(anchor) = anchors.peek() {
947 let excerpt_id = &anchor.excerpt_id;
948 cursor.seek(&Some(excerpt_id), Bias::Left, &());
949 if let Some(excerpt) = cursor.item() {
950 let excerpt_exists = excerpt.id == *excerpt_id;
951 let excerpt_anchors = std::iter::from_fn(|| {
952 let anchor = anchors.peek()?;
953 if anchor.excerpt_id == *excerpt_id {
954 Some(&anchors.next().unwrap().text_anchor)
955 } else {
956 None
957 }
958 });
959
960 if excerpt_exists {
961 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
962 excerpt_start.add_summary(&excerpt.header_summary(), &());
963 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
964 summaries.extend(
965 excerpt
966 .buffer
967 .summaries_for_anchors::<D, _>(excerpt_anchors)
968 .map(move |summary| {
969 let mut excerpt_start = excerpt_start.clone();
970 let excerpt_buffer_start = excerpt_buffer_start.clone();
971 if summary > excerpt_buffer_start {
972 excerpt_start.add_assign(&(summary - excerpt_buffer_start));
973 }
974 excerpt_start
975 }),
976 );
977 } else {
978 excerpt_anchors.for_each(drop);
979 }
980 } else {
981 break;
982 }
983 }
984
985 summaries
986 }
987
988 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
989 self.anchor_at(position, Bias::Left)
990 }
991
992 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
993 self.anchor_at(position, Bias::Right)
994 }
995
996 pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
997 let offset = position.to_offset(self);
998 let mut cursor = self.excerpts.cursor::<(usize, Option<&ExcerptId>)>();
999 cursor.seek(&offset, bias, &());
1000 if let Some(excerpt) = cursor.item() {
1001 let overshoot =
1002 (offset - cursor.start().0).saturating_sub(excerpt.header_height as usize);
1003 let buffer_start = excerpt.range.start.to_offset(&excerpt.buffer);
1004 Anchor {
1005 excerpt_id: excerpt.id.clone(),
1006 text_anchor: excerpt.buffer.anchor_at(buffer_start + overshoot, bias),
1007 }
1008 } else if offset == 0 && bias == Bias::Left {
1009 Anchor::min()
1010 } else {
1011 Anchor::max()
1012 }
1013 }
1014
1015 pub fn parse_count(&self) -> usize {
1016 self.as_singleton().unwrap().parse_count()
1017 }
1018
1019 pub fn enclosing_bracket_ranges<T: ToOffset>(
1020 &self,
1021 range: Range<T>,
1022 ) -> Option<(Range<usize>, Range<usize>)> {
1023 let range = range.start.to_offset(self)..range.end.to_offset(self);
1024 self.as_singleton().unwrap().enclosing_bracket_ranges(range)
1025 }
1026
1027 pub fn diagnostics_update_count(&self) -> usize {
1028 self.as_singleton().unwrap().diagnostics_update_count()
1029 }
1030
1031 pub fn language(&self) -> Option<&Arc<Language>> {
1032 self.as_singleton().unwrap().language()
1033 }
1034
1035 pub fn diagnostic_group<'a, O>(
1036 &'a self,
1037 group_id: usize,
1038 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1039 where
1040 O: text::FromAnchor + 'a,
1041 {
1042 self.as_singleton().unwrap().diagnostic_group(group_id)
1043 }
1044
1045 pub fn diagnostics_in_range<'a, T, O>(
1046 &'a self,
1047 range: Range<T>,
1048 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
1049 where
1050 T: 'a + ToOffset,
1051 O: 'a + text::FromAnchor,
1052 {
1053 let range = range.start.to_offset(self)..range.end.to_offset(self);
1054 self.as_singleton().unwrap().diagnostics_in_range(range)
1055 }
1056
1057 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
1058 let range = range.start.to_offset(self)..range.end.to_offset(self);
1059 self.as_singleton()
1060 .unwrap()
1061 .range_for_syntax_ancestor(range)
1062 }
1063
1064 fn buffer_snapshot_for_excerpt<'a>(
1065 &'a self,
1066 excerpt_id: &'a ExcerptId,
1067 ) -> Option<&'a BufferSnapshot> {
1068 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
1069 cursor.seek(&Some(excerpt_id), Bias::Left, &());
1070 if let Some(excerpt) = cursor.item() {
1071 if excerpt.id == *excerpt_id {
1072 return Some(&excerpt.buffer);
1073 }
1074 }
1075 None
1076 }
1077}
1078
1079impl Excerpt {
1080 fn new(
1081 id: ExcerptId,
1082 buffer: buffer::BufferSnapshot,
1083 range: Range<text::Anchor>,
1084 header_height: u8,
1085 has_trailing_newline: bool,
1086 ) -> Self {
1087 let mut text_summary =
1088 buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1089 if header_height > 0 {
1090 text_summary.first_line_chars = 0;
1091 text_summary.lines.row += header_height as u32;
1092 text_summary.lines_utf16.row += header_height as u32;
1093 text_summary.bytes += header_height as usize;
1094 text_summary.longest_row += header_height as u32;
1095 }
1096 if has_trailing_newline {
1097 text_summary.last_line_chars = 0;
1098 text_summary.lines.row += 1;
1099 text_summary.lines.column = 0;
1100 text_summary.lines_utf16.row += 1;
1101 text_summary.lines_utf16.column = 0;
1102 text_summary.bytes += 1;
1103 }
1104
1105 Excerpt {
1106 id,
1107 buffer,
1108 range,
1109 text_summary,
1110 header_height,
1111 has_trailing_newline,
1112 }
1113 }
1114
1115 fn header_summary(&self) -> TextSummary {
1116 TextSummary {
1117 bytes: self.header_height as usize,
1118 lines: Point::new(self.header_height as u32, 0),
1119 lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1120 first_line_chars: 0,
1121 last_line_chars: 0,
1122 longest_row: 0,
1123 longest_row_chars: 0,
1124 }
1125 }
1126}
1127
1128impl sum_tree::Item for Excerpt {
1129 type Summary = ExcerptSummary;
1130
1131 fn summary(&self) -> Self::Summary {
1132 ExcerptSummary {
1133 excerpt_id: self.id.clone(),
1134 text: self.text_summary.clone(),
1135 }
1136 }
1137}
1138
1139impl sum_tree::Summary for ExcerptSummary {
1140 type Context = ();
1141
1142 fn add_summary(&mut self, summary: &Self, _: &()) {
1143 debug_assert!(summary.excerpt_id > self.excerpt_id);
1144 self.excerpt_id = summary.excerpt_id.clone();
1145 self.text.add_summary(&summary.text, &());
1146 }
1147}
1148
1149impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1150 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1151 *self += &summary.text;
1152 }
1153}
1154
1155impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1156 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1157 *self += summary.text.bytes;
1158 }
1159}
1160
1161impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1162 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1163 Ord::cmp(self, &cursor_location.text.bytes)
1164 }
1165}
1166
1167impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1168 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1169 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1170 }
1171}
1172
1173impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1174 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1175 *self += summary.text.lines;
1176 }
1177}
1178
1179impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1180 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1181 *self += summary.text.lines_utf16
1182 }
1183}
1184
1185impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1186 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1187 *self = Some(&summary.excerpt_id);
1188 }
1189}
1190
1191impl<'a> MultiBufferChunks<'a> {
1192 pub fn offset(&self) -> usize {
1193 self.range.start
1194 }
1195
1196 pub fn seek(&mut self, offset: usize) {
1197 self.range.start = offset;
1198 self.cursor.seek_forward(&offset, Bias::Right, &());
1199 self.header_height = 0;
1200 self.excerpt_chunks = None;
1201 if let Some(excerpt) = self.cursor.item() {
1202 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1203 self.header_height = excerpt.header_height;
1204 self.has_trailing_newline = excerpt.has_trailing_newline;
1205
1206 let buffer_start;
1207 let start_overshoot = self.range.start - self.cursor.start();
1208 if start_overshoot < excerpt.header_height as usize {
1209 self.header_height -= start_overshoot as u8;
1210 buffer_start = buffer_range.start;
1211 } else {
1212 buffer_start =
1213 buffer_range.start + start_overshoot - excerpt.header_height as usize;
1214 self.header_height = 0;
1215 }
1216
1217 let buffer_end;
1218 let end_overshoot = self.range.end - self.cursor.start();
1219 if end_overshoot < excerpt.header_height as usize {
1220 self.header_height -= excerpt.header_height - end_overshoot as u8;
1221 buffer_end = buffer_start;
1222 } else {
1223 buffer_end = cmp::min(
1224 buffer_range.end,
1225 buffer_range.start + end_overshoot - excerpt.header_height as usize,
1226 );
1227 }
1228
1229 self.excerpt_chunks = Some(excerpt.buffer.chunks(buffer_start..buffer_end, self.theme));
1230 }
1231 }
1232}
1233
1234impl<'a> Iterator for MultiBufferChunks<'a> {
1235 type Item = Chunk<'a>;
1236
1237 fn next(&mut self) -> Option<Self::Item> {
1238 loop {
1239 if self.header_height > 0 {
1240 let chunk = Chunk {
1241 text: unsafe {
1242 std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1243 },
1244 ..Default::default()
1245 };
1246 self.range.start += self.header_height as usize;
1247 self.header_height = 0;
1248 return Some(chunk);
1249 }
1250
1251 if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1252 if let Some(chunk) = excerpt_chunks.next() {
1253 self.range.start += chunk.text.len();
1254 return Some(chunk);
1255 }
1256 self.excerpt_chunks.take();
1257 if self.has_trailing_newline && self.cursor.end(&()) <= self.range.end {
1258 self.range.start += 1;
1259 return Some(Chunk {
1260 text: "\n",
1261 ..Default::default()
1262 });
1263 }
1264 }
1265
1266 self.cursor.next(&());
1267 if *self.cursor.start() >= self.range.end {
1268 return None;
1269 }
1270
1271 let excerpt = self.cursor.item()?;
1272 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1273
1274 let buffer_end = cmp::min(
1275 buffer_range.end,
1276 buffer_range.start + self.range.end
1277 - excerpt.header_height as usize
1278 - self.cursor.start(),
1279 );
1280
1281 self.header_height = excerpt.header_height;
1282 self.has_trailing_newline = excerpt.has_trailing_newline;
1283 self.excerpt_chunks = Some(
1284 excerpt
1285 .buffer
1286 .chunks(buffer_range.start..buffer_end, self.theme),
1287 );
1288 }
1289 }
1290}
1291
1292impl<'a> Iterator for MultiBufferBytes<'a> {
1293 type Item = &'a [u8];
1294
1295 fn next(&mut self) -> Option<Self::Item> {
1296 self.chunks.next().map(|chunk| chunk.text.as_bytes())
1297 }
1298}
1299
1300impl<'a> io::Read for MultiBufferBytes<'a> {
1301 fn read(&mut self, _: &mut [u8]) -> io::Result<usize> {
1302 todo!()
1303 }
1304}
1305
1306impl ToOffset for Point {
1307 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1308 snapshot.point_to_offset(*self)
1309 }
1310}
1311
1312impl ToOffset for PointUtf16 {
1313 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1314 snapshot.point_utf16_to_offset(*self)
1315 }
1316}
1317
1318impl ToOffset for usize {
1319 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1320 assert!(*self <= snapshot.len(), "offset is out of range");
1321 *self
1322 }
1323}
1324
1325impl ToPoint for usize {
1326 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1327 snapshot.offset_to_point(*self)
1328 }
1329}
1330
1331impl ToPoint for Point {
1332 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1333 *self
1334 }
1335}
1336
1337#[cfg(test)]
1338mod tests {
1339 use super::*;
1340 use crate::buffer::Buffer;
1341 use gpui::MutableAppContext;
1342 use rand::prelude::*;
1343 use std::env;
1344 use text::{Point, RandomCharIter};
1345 use util::test::sample_text;
1346
1347 #[gpui::test]
1348 fn test_singleton_multibuffer(cx: &mut MutableAppContext) {
1349 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1350 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
1351 assert_eq!(
1352 multibuffer.read(cx).snapshot(cx).text(),
1353 buffer.read(cx).text()
1354 );
1355
1356 buffer.update(cx, |buffer, cx| buffer.edit([1..3], "XXX", cx));
1357 assert_eq!(
1358 multibuffer.read(cx).snapshot(cx).text(),
1359 buffer.read(cx).text()
1360 );
1361 }
1362
1363 #[gpui::test]
1364 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1365 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1366 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1367
1368 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
1369
1370 let subscription = multibuffer.update(cx, |multibuffer, cx| {
1371 let subscription = multibuffer.subscribe();
1372 multibuffer.push(
1373 ExcerptProperties {
1374 buffer: &buffer_1,
1375 range: Point::new(1, 2)..Point::new(2, 5),
1376 header_height: 2,
1377 },
1378 cx,
1379 );
1380 assert_eq!(
1381 subscription.consume().into_inner(),
1382 [Edit {
1383 old: 0..0,
1384 new: 0..13
1385 }]
1386 );
1387
1388 multibuffer.push(
1389 ExcerptProperties {
1390 buffer: &buffer_1,
1391 range: Point::new(3, 3)..Point::new(4, 4),
1392 header_height: 1,
1393 },
1394 cx,
1395 );
1396 multibuffer.push(
1397 ExcerptProperties {
1398 buffer: &buffer_2,
1399 range: Point::new(3, 1)..Point::new(3, 3),
1400 header_height: 3,
1401 },
1402 cx,
1403 );
1404 assert_eq!(
1405 subscription.consume().into_inner(),
1406 [Edit {
1407 old: 13..13,
1408 new: 13..29
1409 }]
1410 );
1411
1412 subscription
1413 });
1414
1415 assert_eq!(
1416 multibuffer.read(cx).snapshot(cx).text(),
1417 concat!(
1418 "\n", // Preserve newlines
1419 "\n", //
1420 "bbbb\n", //
1421 "ccccc\n", //
1422 "\n", //
1423 "ddd\n", //
1424 "eeee\n", //
1425 "\n", //
1426 "\n", //
1427 "\n", //
1428 "jj\n" //
1429 )
1430 );
1431
1432 buffer_1.update(cx, |buffer, cx| {
1433 buffer.edit(
1434 [
1435 Point::new(0, 0)..Point::new(0, 0),
1436 Point::new(2, 1)..Point::new(2, 3),
1437 ],
1438 "\n",
1439 cx,
1440 );
1441 });
1442
1443 assert_eq!(
1444 multibuffer.read(cx).snapshot(cx).text(),
1445 concat!(
1446 "\n", // Preserve newlines
1447 "\n", //
1448 "bbbb\n", //
1449 "c\n", //
1450 "cc\n", //
1451 "\n", //
1452 "ddd\n", //
1453 "eeee\n", //
1454 "\n", //
1455 "\n", //
1456 "\n", //
1457 "jj\n" //
1458 )
1459 );
1460
1461 assert_eq!(
1462 subscription.consume().into_inner(),
1463 [Edit {
1464 old: 8..10,
1465 new: 8..9
1466 }]
1467 );
1468 }
1469
1470 #[gpui::test(iterations = 100)]
1471 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1472 let operations = env::var("OPERATIONS")
1473 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1474 .unwrap_or(10);
1475
1476 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1477 let list = cx.add_model(|_| MultiBuffer::new(0));
1478 let mut excerpt_ids = Vec::new();
1479 let mut expected_excerpts = Vec::new();
1480 let mut old_versions = Vec::new();
1481
1482 for _ in 0..operations {
1483 match rng.gen_range(0..100) {
1484 0..=19 if !buffers.is_empty() => {
1485 let buffer = buffers.choose(&mut rng).unwrap();
1486 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1487 }
1488 _ => {
1489 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1490 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1491 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1492 buffers.last().unwrap()
1493 } else {
1494 buffers.choose(&mut rng).unwrap()
1495 };
1496
1497 let buffer = buffer_handle.read(cx);
1498 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1499 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1500 let header_height = rng.gen_range(0..=5);
1501 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1502 log::info!(
1503 "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1504 header_height,
1505 buffer_handle.id(),
1506 buffer.text(),
1507 start_ix..end_ix,
1508 &buffer.text()[start_ix..end_ix]
1509 );
1510
1511 let excerpt_id = list.update(cx, |list, cx| {
1512 list.push(
1513 ExcerptProperties {
1514 buffer: &buffer_handle,
1515 range: start_ix..end_ix,
1516 header_height,
1517 },
1518 cx,
1519 )
1520 });
1521 excerpt_ids.push(excerpt_id);
1522 expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1523 }
1524 }
1525
1526 if rng.gen_bool(0.3) {
1527 list.update(cx, |list, cx| {
1528 old_versions.push((list.snapshot(cx), list.subscribe()));
1529 })
1530 }
1531
1532 let snapshot = list.read(cx).snapshot(cx);
1533
1534 let mut excerpt_starts = Vec::new();
1535 let mut expected_text = String::new();
1536 for (buffer, range, header_height) in &expected_excerpts {
1537 let buffer = buffer.read(cx);
1538 let buffer_range = range.to_offset(buffer);
1539
1540 for _ in 0..*header_height {
1541 expected_text.push('\n');
1542 }
1543
1544 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1545 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1546 expected_text.push('\n');
1547 }
1548
1549 assert_eq!(snapshot.text(), expected_text);
1550
1551 let mut excerpt_starts = excerpt_starts.into_iter();
1552 for (buffer, range, _) in &expected_excerpts {
1553 let buffer_id = buffer.id();
1554 let buffer = buffer.read(cx);
1555 let buffer_range = range.to_offset(buffer);
1556 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1557 let buffer_start_point_utf16 =
1558 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1559
1560 let excerpt_start = excerpt_starts.next().unwrap();
1561 let mut offset = excerpt_start.bytes;
1562 let mut buffer_offset = buffer_range.start;
1563 let mut point = excerpt_start.lines;
1564 let mut buffer_point = buffer_start_point;
1565 let mut point_utf16 = excerpt_start.lines_utf16;
1566 let mut buffer_point_utf16 = buffer_start_point_utf16;
1567 for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1568 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1569 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1570 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1571 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1572 assert_eq!(
1573 left_offset,
1574 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1575 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1576 offset,
1577 buffer_id,
1578 buffer_offset,
1579 );
1580 assert_eq!(
1581 right_offset,
1582 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1583 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1584 offset,
1585 buffer_id,
1586 buffer_offset,
1587 );
1588
1589 let left_point = snapshot.clip_point(point, Bias::Left);
1590 let right_point = snapshot.clip_point(point, Bias::Right);
1591 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1592 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1593 assert_eq!(
1594 left_point,
1595 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1596 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1597 point,
1598 buffer_id,
1599 buffer_point,
1600 );
1601 assert_eq!(
1602 right_point,
1603 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1604 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1605 point,
1606 buffer_id,
1607 buffer_point,
1608 );
1609
1610 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1611 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1612 let buffer_left_point_utf16 =
1613 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1614 let buffer_right_point_utf16 =
1615 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1616 assert_eq!(
1617 left_point_utf16,
1618 excerpt_start.lines_utf16
1619 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1620 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1621 point_utf16,
1622 buffer_id,
1623 buffer_point_utf16,
1624 );
1625 assert_eq!(
1626 right_point_utf16,
1627 excerpt_start.lines_utf16
1628 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1629 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1630 point_utf16,
1631 buffer_id,
1632 buffer_point_utf16,
1633 );
1634
1635 assert_eq!(
1636 snapshot.point_to_offset(left_point),
1637 left_offset,
1638 "point_to_offset({:?})",
1639 left_point,
1640 );
1641 assert_eq!(
1642 snapshot.offset_to_point(left_offset),
1643 left_point,
1644 "offset_to_point({:?})",
1645 left_offset,
1646 );
1647
1648 offset += 1;
1649 buffer_offset += 1;
1650 if *byte == b'\n' {
1651 point += Point::new(1, 0);
1652 point_utf16 += PointUtf16::new(1, 0);
1653 buffer_point += Point::new(1, 0);
1654 buffer_point_utf16 += PointUtf16::new(1, 0);
1655 } else {
1656 point += Point::new(0, 1);
1657 point_utf16 += PointUtf16::new(0, 1);
1658 buffer_point += Point::new(0, 1);
1659 buffer_point_utf16 += PointUtf16::new(0, 1);
1660 }
1661 }
1662 }
1663
1664 for (row, line) in expected_text.split('\n').enumerate() {
1665 assert_eq!(
1666 snapshot.line_len(row as u32),
1667 line.len() as u32,
1668 "line_len({}).",
1669 row
1670 );
1671 }
1672
1673 for _ in 0..10 {
1674 let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1675 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1676
1677 assert_eq!(
1678 snapshot
1679 .text_for_range(start_ix..end_ix)
1680 .collect::<String>(),
1681 &expected_text[start_ix..end_ix],
1682 "incorrect text for range {:?}",
1683 start_ix..end_ix
1684 );
1685
1686 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1687 assert_eq!(
1688 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1689 expected_summary,
1690 "incorrect summary for range {:?}",
1691 start_ix..end_ix
1692 );
1693 }
1694 }
1695
1696 let snapshot = list.read(cx).snapshot(cx);
1697 for (old_snapshot, subscription) in old_versions {
1698 let edits = subscription.consume().into_inner();
1699
1700 log::info!(
1701 "applying edits since old text: {:?}: {:?}",
1702 old_snapshot.text(),
1703 edits,
1704 );
1705
1706 let mut text = old_snapshot.text();
1707 for edit in edits {
1708 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1709 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1710 }
1711 assert_eq!(text.to_string(), snapshot.text());
1712 }
1713 }
1714}