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