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