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