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