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