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