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 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 contains_str_at<T>(&self, _: T, _: &str) -> bool
387 where
388 T: ToOffset,
389 {
390 todo!()
391 }
392
393 pub fn max_point(&self) -> Point {
394 self.snapshot.lock().max_point()
395 }
396
397 pub fn len(&self) -> usize {
398 self.snapshot.lock().len()
399 }
400
401 pub fn line_len(&self, row: u32) -> u32 {
402 self.snapshot.lock().line_len(row)
403 }
404
405 pub fn is_line_blank(&self, row: u32) -> bool {
406 self.snapshot.lock().is_line_blank(row)
407 }
408
409 pub fn indent_column_for_line(&self, row: u32) -> u32 {
410 self.snapshot.lock().indent_column_for_line(row)
411 }
412
413 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
414 self.snapshot.lock().anchor_before(position)
415 }
416
417 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
418 self.snapshot.lock().anchor_after(position)
419 }
420
421 pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
422 self.snapshot.lock().anchor_at(position, bias)
423 }
424
425 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
426 self.snapshot.lock().clip_offset(offset, bias)
427 }
428
429 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
430 self.snapshot.lock().clip_point(point, bias)
431 }
432
433 pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
434 todo!()
435 }
436
437 pub fn parse_count(&self) -> usize {
438 self.snapshot.lock().parse_count()
439 }
440
441 pub fn diagnostics_update_count(&self) -> usize {
442 self.snapshot.lock().diagnostics_update_count()
443 }
444
445 pub fn diagnostics_in_range<'a, T, O>(
446 &'a self,
447 search_range: Range<T>,
448 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
449 where
450 T: 'a + ToOffset,
451 O: 'a,
452 {
453 todo!();
454 None.into_iter()
455 }
456}
457
458#[cfg(any(test, feature = "test-support"))]
459impl MultiBuffer {
460 pub fn randomly_edit<R: rand::Rng>(&mut self, _: &mut R, _: usize, _: &mut ModelContext<Self>) {
461 todo!()
462 }
463
464 pub fn randomly_mutate<R: rand::Rng>(&mut self, rng: &mut R, cx: &mut ModelContext<Self>) {
465 todo!()
466 }
467}
468
469impl Entity for MultiBuffer {
470 type Event = super::Event;
471}
472
473impl MultiBufferSnapshot {
474 pub fn replica_id(&self) -> ReplicaId {
475 todo!()
476 }
477
478 pub fn text(&self) -> String {
479 self.chunks(0..self.len(), None)
480 .map(|chunk| chunk.text)
481 .collect()
482 }
483
484 pub fn reversed_chars_at<'a, T: ToOffset>(
485 &'a self,
486 position: T,
487 ) -> impl Iterator<Item = char> + 'a {
488 todo!();
489 None.into_iter()
490 }
491
492 pub fn chars_at<'a, T: ToOffset>(&'a self, position: T) -> impl Iterator<Item = char> + 'a {
493 let offset = position.to_offset(self);
494 self.text_for_range(offset..self.len())
495 .flat_map(|chunk| chunk.chars())
496 }
497
498 pub fn text_for_range<'a, T: ToOffset>(
499 &'a self,
500 range: Range<T>,
501 ) -> impl Iterator<Item = &'a str> {
502 self.chunks(range, None).map(|chunk| chunk.text)
503 }
504
505 pub fn is_line_blank(&self, row: u32) -> bool {
506 self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
507 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
508 }
509
510 pub fn contains_str_at<T>(&self, _: T, _: &str) -> bool
511 where
512 T: ToOffset,
513 {
514 todo!()
515 }
516
517 pub fn len(&self) -> usize {
518 self.excerpts.summary().text.bytes
519 }
520
521 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
522 let mut cursor = self.excerpts.cursor::<usize>();
523 cursor.seek(&offset, Bias::Right, &());
524 if let Some(excerpt) = cursor.item() {
525 let start_after_header = *cursor.start() + excerpt.header_height as usize;
526 if offset < start_after_header {
527 *cursor.start()
528 } else {
529 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
530 let buffer_offset = excerpt
531 .buffer
532 .clip_offset(excerpt_start + (offset - start_after_header), bias);
533 let offset_in_excerpt = if buffer_offset > excerpt_start {
534 buffer_offset - excerpt_start
535 } else {
536 0
537 };
538 start_after_header + offset_in_excerpt
539 }
540 } else {
541 self.excerpts.summary().text.bytes
542 }
543 }
544
545 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
546 let mut cursor = self.excerpts.cursor::<Point>();
547 cursor.seek(&point, Bias::Right, &());
548 if let Some(excerpt) = cursor.item() {
549 let start_after_header = *cursor.start() + Point::new(excerpt.header_height as u32, 0);
550 if point < start_after_header {
551 *cursor.start()
552 } else {
553 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
554 let buffer_point = excerpt
555 .buffer
556 .clip_point(excerpt_start + (point - start_after_header), bias);
557 let point_in_excerpt = if buffer_point > excerpt_start {
558 buffer_point - excerpt_start
559 } else {
560 Point::zero()
561 };
562 start_after_header + point_in_excerpt
563 }
564 } else {
565 self.excerpts.summary().text.lines
566 }
567 }
568
569 pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 {
570 let mut cursor = self.excerpts.cursor::<PointUtf16>();
571 cursor.seek(&point, Bias::Right, &());
572 if let Some(excerpt) = cursor.item() {
573 let start_after_header =
574 *cursor.start() + PointUtf16::new(excerpt.header_height as u32, 0);
575 if point < start_after_header {
576 *cursor.start()
577 } else {
578 let excerpt_start = excerpt
579 .buffer
580 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
581 let buffer_point = excerpt
582 .buffer
583 .clip_point_utf16(excerpt_start + (point - start_after_header), bias);
584 let point_in_excerpt = if buffer_point > excerpt_start {
585 buffer_point - excerpt_start
586 } else {
587 PointUtf16::new(0, 0)
588 };
589 start_after_header + point_in_excerpt
590 }
591 } else {
592 self.excerpts.summary().text.lines_utf16
593 }
594 }
595
596 pub fn bytes_in_range<'a, T: ToOffset>(&'a self, range: Range<T>) -> MultiBufferBytes<'a> {
597 todo!()
598 }
599
600 pub fn chunks<'a, T: ToOffset>(
601 &'a self,
602 range: Range<T>,
603 theme: Option<&'a SyntaxTheme>,
604 ) -> MultiBufferChunks<'a> {
605 let range = range.start.to_offset(self)..range.end.to_offset(self);
606 let mut cursor = self.excerpts.cursor::<usize>();
607 cursor.seek(&range.start, Bias::Right, &());
608
609 let mut header_height: u8 = 0;
610 let excerpt_chunks = cursor.item().map(|excerpt| {
611 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
612 header_height = excerpt.header_height;
613
614 let buffer_start;
615 let start_overshoot = range.start - cursor.start();
616 if start_overshoot < excerpt.header_height as usize {
617 header_height -= start_overshoot as u8;
618 buffer_start = buffer_range.start;
619 } else {
620 buffer_start =
621 buffer_range.start + start_overshoot - excerpt.header_height as usize;
622 header_height = 0;
623 }
624
625 let buffer_end;
626 let end_overshoot = range.end - cursor.start();
627 if end_overshoot < excerpt.header_height as usize {
628 header_height -= excerpt.header_height - end_overshoot as u8;
629 buffer_end = buffer_start;
630 } else {
631 buffer_end = cmp::min(
632 buffer_range.end,
633 buffer_range.start + end_overshoot - excerpt.header_height as usize,
634 );
635 }
636
637 excerpt.buffer.chunks(buffer_start..buffer_end, theme)
638 });
639
640 MultiBufferChunks {
641 range,
642 cursor,
643 header_height,
644 excerpt_chunks,
645 theme,
646 }
647 }
648
649 pub fn offset_to_point(&self, offset: usize) -> Point {
650 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
651 cursor.seek(&offset, Bias::Right, &());
652 if let Some(excerpt) = cursor.item() {
653 let (start_offset, start_point) = cursor.start();
654 let overshoot = offset - start_offset;
655 let header_height = excerpt.header_height as usize;
656 if overshoot < header_height {
657 *start_point
658 } else {
659 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
660 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
661 let buffer_point = excerpt
662 .buffer
663 .offset_to_point(excerpt_start_offset + (overshoot - header_height));
664 *start_point
665 + Point::new(header_height as u32, 0)
666 + (buffer_point - excerpt_start_point)
667 }
668 } else {
669 self.excerpts.summary().text.lines
670 }
671 }
672
673 pub fn point_to_offset(&self, point: Point) -> usize {
674 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
675 cursor.seek(&point, Bias::Right, &());
676 if let Some(excerpt) = cursor.item() {
677 let (start_point, start_offset) = cursor.start();
678 let overshoot = point - start_point;
679 let header_height = Point::new(excerpt.header_height as u32, 0);
680 if overshoot < header_height {
681 *start_offset
682 } else {
683 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
684 let excerpt_start_point = excerpt.range.start.to_point(&excerpt.buffer);
685 let buffer_offset = excerpt
686 .buffer
687 .point_to_offset(excerpt_start_point + (overshoot - header_height));
688 *start_offset + excerpt.header_height as usize + buffer_offset
689 - excerpt_start_offset
690 }
691 } else {
692 self.excerpts.summary().text.bytes
693 }
694 }
695
696 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
697 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
698 cursor.seek(&point, Bias::Right, &());
699 if let Some(excerpt) = cursor.item() {
700 let (start_point, start_offset) = cursor.start();
701 let overshoot = point - start_point;
702 let header_height = PointUtf16::new(excerpt.header_height as u32, 0);
703 if overshoot < header_height {
704 *start_offset
705 } else {
706 let excerpt_start_offset = excerpt.range.start.to_offset(&excerpt.buffer);
707 let excerpt_start_point = excerpt
708 .buffer
709 .offset_to_point_utf16(excerpt.range.start.to_offset(&excerpt.buffer));
710 let buffer_offset = excerpt
711 .buffer
712 .point_utf16_to_offset(excerpt_start_point + (overshoot - header_height));
713 *start_offset
714 + excerpt.header_height as usize
715 + (buffer_offset - excerpt_start_offset)
716 }
717 } else {
718 self.excerpts.summary().text.bytes
719 }
720 }
721
722 pub fn indent_column_for_line(&self, row: u32) -> u32 {
723 todo!()
724 }
725
726 pub fn line_len(&self, row: u32) -> u32 {
727 let mut cursor = self.excerpts.cursor::<Point>();
728 cursor.seek(&Point::new(row, 0), Bias::Right, &());
729 if let Some(excerpt) = cursor.item() {
730 let overshoot = row - cursor.start().row;
731 let header_height = excerpt.header_height as u32;
732 if overshoot < header_height {
733 0
734 } else {
735 let excerpt_start = excerpt.range.start.to_point(&excerpt.buffer);
736 let excerpt_end = excerpt.range.end.to_point(&excerpt.buffer);
737 let buffer_row = excerpt_start.row + overshoot - header_height;
738 let mut len = excerpt.buffer.line_len(buffer_row);
739 if buffer_row == excerpt_end.row {
740 len = excerpt_end.column;
741 }
742 if buffer_row == excerpt_start.row {
743 len -= excerpt_start.column
744 }
745 len
746 }
747 } else {
748 0
749 }
750 }
751
752 pub fn max_point(&self) -> Point {
753 self.text_summary().lines
754 }
755
756 pub fn text_summary(&self) -> TextSummary {
757 self.excerpts.summary().text
758 }
759
760 pub fn text_summary_for_range<'a, D, O>(&'a self, range: Range<O>) -> D
761 where
762 D: TextDimension,
763 O: ToOffset,
764 {
765 let mut summary = D::default();
766 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
767 let mut cursor = self.excerpts.cursor::<usize>();
768 cursor.seek(&range.start, Bias::Right, &());
769 if let Some(excerpt) = cursor.item() {
770 let start_after_header = cursor.start() + excerpt.header_height as usize;
771 if range.start < start_after_header {
772 let header_len = cmp::min(range.end, start_after_header) - range.start;
773 summary.add_assign(&D::from_text_summary(&TextSummary {
774 bytes: header_len,
775 lines: Point::new(header_len as u32, 0),
776 lines_utf16: PointUtf16::new(header_len as u32, 0),
777 first_line_chars: 0,
778 last_line_chars: 0,
779 longest_row: 0,
780 longest_row_chars: 0,
781 }));
782 range.start = start_after_header;
783 range.end = cmp::max(range.start, range.end);
784 }
785
786 let end_before_newline = cursor.end(&()) - 1;
787 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
788 let start_in_excerpt = excerpt_start + (range.start - start_after_header);
789 let end_in_excerpt =
790 excerpt_start + (cmp::min(end_before_newline, range.end) - start_after_header);
791 summary.add_assign(
792 &excerpt
793 .buffer
794 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
795 );
796
797 if range.end > end_before_newline {
798 summary.add_assign(&D::from_text_summary(&TextSummary {
799 bytes: 1,
800 lines: Point::new(1 as u32, 0),
801 lines_utf16: PointUtf16::new(1 as u32, 0),
802 first_line_chars: 0,
803 last_line_chars: 0,
804 longest_row: 0,
805 longest_row_chars: 0,
806 }));
807 }
808
809 cursor.next(&());
810 }
811
812 if range.end > *cursor.start() {
813 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
814 &range.end,
815 Bias::Right,
816 &(),
817 )));
818 if let Some(excerpt) = cursor.item() {
819 let start_after_header = cursor.start() + excerpt.header_height as usize;
820 let header_len =
821 cmp::min(range.end - cursor.start(), excerpt.header_height as usize);
822 summary.add_assign(&D::from_text_summary(&TextSummary {
823 bytes: header_len,
824 lines: Point::new(header_len as u32, 0),
825 lines_utf16: PointUtf16::new(header_len as u32, 0),
826 first_line_chars: 0,
827 last_line_chars: 0,
828 longest_row: 0,
829 longest_row_chars: 0,
830 }));
831 range.end = cmp::max(start_after_header, range.end);
832
833 let excerpt_start = excerpt.range.start.to_offset(&excerpt.buffer);
834 let end_in_excerpt = excerpt_start + (range.end - start_after_header);
835 summary.add_assign(
836 &excerpt
837 .buffer
838 .text_summary_for_range(excerpt_start..end_in_excerpt),
839 );
840 cursor.next(&());
841 }
842 }
843
844 summary
845 }
846
847 fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
848 where
849 D: TextDimension + Ord + Sub<D, Output = D>,
850 {
851 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
852 cursor.seek(&Some(&anchor.excerpt_id), Bias::Left, &());
853 if let Some(excerpt) = cursor.item() {
854 if excerpt.id == anchor.excerpt_id {
855 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
856 excerpt_start.add_summary(&excerpt.header_summary(), &());
857 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
858 let buffer_point = anchor.text_anchor.summary::<D>(&excerpt.buffer);
859 if buffer_point > excerpt_buffer_start {
860 excerpt_start.add_assign(&(buffer_point - excerpt_buffer_start));
861 }
862 return excerpt_start;
863 }
864 }
865 D::from_text_summary(&cursor.start().text)
866 }
867
868 fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
869 where
870 D: TextDimension + Ord + Sub<D, Output = D>,
871 I: 'a + IntoIterator<Item = &'a Anchor>,
872 {
873 let mut anchors = anchors.into_iter().peekable();
874 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
875 let mut summaries = Vec::new();
876 while let Some(anchor) = anchors.peek() {
877 let excerpt_id = &anchor.excerpt_id;
878 cursor.seek(&Some(excerpt_id), Bias::Left, &());
879 if let Some(excerpt) = cursor.item() {
880 let excerpt_exists = excerpt.id == *excerpt_id;
881 let excerpt_anchors = std::iter::from_fn(|| {
882 let anchor = anchors.peek()?;
883 if anchor.excerpt_id == *excerpt_id {
884 Some(&anchors.next().unwrap().text_anchor)
885 } else {
886 None
887 }
888 });
889
890 if excerpt_exists {
891 let mut excerpt_start = D::from_text_summary(&cursor.start().text);
892 excerpt_start.add_summary(&excerpt.header_summary(), &());
893 let excerpt_buffer_start = excerpt.range.start.summary::<D>(&excerpt.buffer);
894 summaries.extend(
895 excerpt
896 .buffer
897 .summaries_for_anchors::<D, _>(excerpt_anchors)
898 .map(move |summary| {
899 let mut excerpt_start = excerpt_start.clone();
900 let excerpt_buffer_start = excerpt_buffer_start.clone();
901 if summary > excerpt_buffer_start {
902 excerpt_start.add_assign(&(summary - excerpt_buffer_start));
903 }
904 excerpt_start
905 }),
906 );
907 } else {
908 excerpt_anchors.for_each(drop);
909 }
910 } else {
911 break;
912 }
913 }
914
915 summaries
916 }
917
918 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
919 self.anchor_at(position, Bias::Left)
920 }
921
922 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
923 self.anchor_at(position, Bias::Right)
924 }
925
926 pub fn anchor_at<T: ToOffset>(&self, position: T, bias: Bias) -> Anchor {
927 todo!()
928 }
929
930 pub fn parse_count(&self) -> usize {
931 todo!()
932 }
933
934 pub fn enclosing_bracket_ranges<T: ToOffset>(
935 &self,
936 range: Range<T>,
937 ) -> Option<(Range<usize>, Range<usize>)> {
938 todo!()
939 }
940
941 pub fn diagnostics_update_count(&self) -> usize {
942 todo!()
943 }
944
945 pub fn language<'a>(&self) -> Option<&'a Arc<Language>> {
946 todo!()
947 }
948
949 pub fn diagnostic_group<'a, O>(
950 &'a self,
951 group_id: usize,
952 ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
953 where
954 O: 'a,
955 {
956 todo!();
957 None.into_iter()
958 }
959
960 pub fn diagnostics_in_range<'a, T, O>(
961 &'a self,
962 search_range: Range<T>,
963 ) -> impl Iterator<Item = (Range<O>, &Diagnostic)> + 'a
964 where
965 T: 'a + ToOffset,
966 O: 'a,
967 {
968 todo!();
969 None.into_iter()
970 }
971
972 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
973 todo!()
974 }
975
976 fn buffer_snapshot_for_excerpt<'a>(
977 &'a self,
978 excerpt_id: &'a ExcerptId,
979 ) -> Option<&'a BufferSnapshot> {
980 let mut cursor = self.excerpts.cursor::<Option<&ExcerptId>>();
981 cursor.seek(&Some(excerpt_id), Bias::Left, &());
982 if let Some(excerpt) = cursor.item() {
983 if *cursor.start() == Some(excerpt_id) {
984 return Some(&excerpt.buffer);
985 }
986 }
987 None
988 }
989}
990
991impl Excerpt {
992 fn new(
993 id: ExcerptId,
994 buffer: buffer::BufferSnapshot,
995 range: Range<text::Anchor>,
996 header_height: u8,
997 ) -> Self {
998 let mut text_summary =
999 buffer.text_summary_for_range::<TextSummary, _>(range.to_offset(&buffer));
1000 if header_height > 0 {
1001 text_summary.first_line_chars = 0;
1002 text_summary.lines.row += header_height as u32;
1003 text_summary.lines_utf16.row += header_height as u32;
1004 text_summary.bytes += header_height as usize;
1005 text_summary.longest_row += header_height as u32;
1006 }
1007 text_summary.last_line_chars = 0;
1008 text_summary.lines.row += 1;
1009 text_summary.lines.column = 0;
1010 text_summary.lines_utf16.row += 1;
1011 text_summary.lines_utf16.column = 0;
1012 text_summary.bytes += 1;
1013
1014 Excerpt {
1015 id,
1016 buffer,
1017 range,
1018 text_summary,
1019 header_height,
1020 }
1021 }
1022
1023 fn header_summary(&self) -> TextSummary {
1024 TextSummary {
1025 bytes: self.header_height as usize,
1026 lines: Point::new(self.header_height as u32, 0),
1027 lines_utf16: PointUtf16::new(self.header_height as u32, 0),
1028 first_line_chars: 0,
1029 last_line_chars: 0,
1030 longest_row: 0,
1031 longest_row_chars: 0,
1032 }
1033 }
1034}
1035
1036impl sum_tree::Item for Excerpt {
1037 type Summary = ExcerptSummary;
1038
1039 fn summary(&self) -> Self::Summary {
1040 ExcerptSummary {
1041 excerpt_id: self.id.clone(),
1042 text: self.text_summary.clone(),
1043 }
1044 }
1045}
1046
1047impl sum_tree::Summary for ExcerptSummary {
1048 type Context = ();
1049
1050 fn add_summary(&mut self, summary: &Self, _: &()) {
1051 debug_assert!(summary.excerpt_id > self.excerpt_id);
1052 self.excerpt_id = summary.excerpt_id.clone();
1053 self.text.add_summary(&summary.text, &());
1054 }
1055}
1056
1057impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
1058 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1059 *self += &summary.text;
1060 }
1061}
1062
1063impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
1064 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1065 *self += summary.text.bytes;
1066 }
1067}
1068
1069impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
1070 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1071 Ord::cmp(self, &cursor_location.text.bytes)
1072 }
1073}
1074
1075impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Option<&'a ExcerptId> {
1076 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
1077 Ord::cmp(self, &Some(&cursor_location.excerpt_id))
1078 }
1079}
1080
1081impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
1082 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1083 *self += summary.text.lines;
1084 }
1085}
1086
1087impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
1088 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1089 *self += summary.text.lines_utf16
1090 }
1091}
1092
1093impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a ExcerptId> {
1094 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
1095 *self = Some(&summary.excerpt_id);
1096 }
1097}
1098
1099impl<'a> MultiBufferChunks<'a> {
1100 pub fn offset(&self) -> usize {
1101 todo!()
1102 }
1103
1104 pub fn seek(&mut self, offset: usize) {
1105 todo!()
1106 }
1107}
1108
1109impl<'a> Iterator for MultiBufferChunks<'a> {
1110 type Item = Chunk<'a>;
1111
1112 fn next(&mut self) -> Option<Self::Item> {
1113 loop {
1114 if self.header_height > 0 {
1115 let chunk = Chunk {
1116 text: unsafe {
1117 std::str::from_utf8_unchecked(&NEWLINES[..self.header_height as usize])
1118 },
1119 ..Default::default()
1120 };
1121 self.header_height = 0;
1122 return Some(chunk);
1123 }
1124
1125 if let Some(excerpt_chunks) = self.excerpt_chunks.as_mut() {
1126 if let Some(chunk) = excerpt_chunks.next() {
1127 return Some(chunk);
1128 }
1129 self.excerpt_chunks.take();
1130 if self.cursor.end(&()) <= self.range.end {
1131 return Some(Chunk {
1132 text: "\n",
1133 ..Default::default()
1134 });
1135 }
1136 }
1137
1138 self.cursor.next(&());
1139 if *self.cursor.start() >= self.range.end {
1140 return None;
1141 }
1142
1143 let excerpt = self.cursor.item()?;
1144 let buffer_range = excerpt.range.to_offset(&excerpt.buffer);
1145
1146 let buffer_end = cmp::min(
1147 buffer_range.end,
1148 buffer_range.start + self.range.end
1149 - excerpt.header_height as usize
1150 - self.cursor.start(),
1151 );
1152
1153 self.header_height = excerpt.header_height;
1154 self.excerpt_chunks = Some(
1155 excerpt
1156 .buffer
1157 .chunks(buffer_range.start..buffer_end, self.theme),
1158 );
1159 }
1160 }
1161}
1162
1163impl<'a> Iterator for MultiBufferBytes<'a> {
1164 type Item = &'a [u8];
1165
1166 fn next(&mut self) -> Option<Self::Item> {
1167 todo!()
1168 }
1169}
1170
1171impl<'a> io::Read for MultiBufferBytes<'a> {
1172 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
1173 todo!()
1174 }
1175}
1176
1177impl ToOffset for Point {
1178 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1179 snapshot.point_to_offset(*self)
1180 }
1181}
1182
1183impl ToOffset for PointUtf16 {
1184 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1185 snapshot.point_utf16_to_offset(*self)
1186 }
1187}
1188
1189impl ToOffset for usize {
1190 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
1191 assert!(*self <= snapshot.len(), "offset is out of range");
1192 *self
1193 }
1194}
1195
1196impl ToPoint for usize {
1197 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
1198 snapshot.offset_to_point(*self)
1199 }
1200}
1201
1202impl ToPoint for Point {
1203 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
1204 *self
1205 }
1206}
1207
1208#[cfg(test)]
1209mod tests {
1210 use super::*;
1211 use crate::buffer::Buffer;
1212 use gpui::MutableAppContext;
1213 use rand::prelude::*;
1214 use std::env;
1215 use text::{Point, RandomCharIter};
1216 use util::test::sample_text;
1217
1218 #[gpui::test]
1219 fn test_excerpt_buffer(cx: &mut MutableAppContext) {
1220 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
1221 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
1222
1223 let list = cx.add_model(|_| MultiBuffer::new());
1224
1225 let subscription = list.update(cx, |list, cx| {
1226 let subscription = list.subscribe();
1227 list.push(
1228 ExcerptProperties {
1229 buffer: &buffer_1,
1230 range: Point::new(1, 2)..Point::new(2, 5),
1231 header_height: 2,
1232 },
1233 cx,
1234 );
1235 assert_eq!(
1236 subscription.consume().into_inner(),
1237 [Edit {
1238 old: 0..0,
1239 new: 0..13
1240 }]
1241 );
1242
1243 list.push(
1244 ExcerptProperties {
1245 buffer: &buffer_1,
1246 range: Point::new(3, 3)..Point::new(4, 4),
1247 header_height: 1,
1248 },
1249 cx,
1250 );
1251 list.push(
1252 ExcerptProperties {
1253 buffer: &buffer_2,
1254 range: Point::new(3, 1)..Point::new(3, 3),
1255 header_height: 3,
1256 },
1257 cx,
1258 );
1259 assert_eq!(
1260 subscription.consume().into_inner(),
1261 [Edit {
1262 old: 13..13,
1263 new: 13..29
1264 }]
1265 );
1266
1267 subscription
1268 });
1269
1270 assert_eq!(
1271 list.read(cx).snapshot(cx).text(),
1272 concat!(
1273 "\n", // Preserve newlines
1274 "\n", //
1275 "bbbb\n", //
1276 "ccccc\n", //
1277 "\n", //
1278 "ddd\n", //
1279 "eeee\n", //
1280 "\n", //
1281 "\n", //
1282 "\n", //
1283 "jj\n" //
1284 )
1285 );
1286
1287 buffer_1.update(cx, |buffer, cx| {
1288 buffer.edit(
1289 [
1290 Point::new(0, 0)..Point::new(0, 0),
1291 Point::new(2, 1)..Point::new(2, 3),
1292 ],
1293 "\n",
1294 cx,
1295 );
1296 });
1297
1298 assert_eq!(
1299 list.read(cx).snapshot(cx).text(),
1300 concat!(
1301 "\n", // Preserve newlines
1302 "\n", //
1303 "bbbb\n", //
1304 "c\n", //
1305 "cc\n", //
1306 "\n", //
1307 "ddd\n", //
1308 "eeee\n", //
1309 "\n", //
1310 "\n", //
1311 "\n", //
1312 "jj\n" //
1313 )
1314 );
1315
1316 assert_eq!(
1317 subscription.consume().into_inner(),
1318 [Edit {
1319 old: 8..10,
1320 new: 8..9
1321 }]
1322 );
1323 }
1324
1325 #[gpui::test(iterations = 100)]
1326 fn test_random_excerpts(cx: &mut MutableAppContext, mut rng: StdRng) {
1327 let operations = env::var("OPERATIONS")
1328 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1329 .unwrap_or(10);
1330
1331 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
1332 let list = cx.add_model(|_| MultiBuffer::new());
1333 let mut excerpt_ids = Vec::new();
1334 let mut expected_excerpts = Vec::new();
1335 let mut old_versions = Vec::new();
1336
1337 for _ in 0..operations {
1338 match rng.gen_range(0..100) {
1339 0..=19 if !buffers.is_empty() => {
1340 let buffer = buffers.choose(&mut rng).unwrap();
1341 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 1, cx));
1342 }
1343 _ => {
1344 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
1345 let base_text = RandomCharIter::new(&mut rng).take(10).collect::<String>();
1346 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
1347 buffers.last().unwrap()
1348 } else {
1349 buffers.choose(&mut rng).unwrap()
1350 };
1351
1352 let buffer = buffer_handle.read(cx);
1353 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1354 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1355 let header_height = rng.gen_range(0..=5);
1356 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
1357 log::info!(
1358 "Pushing excerpt wih header {}, buffer {}: {:?}[{:?}] = {:?}",
1359 header_height,
1360 buffer_handle.id(),
1361 buffer.text(),
1362 start_ix..end_ix,
1363 &buffer.text()[start_ix..end_ix]
1364 );
1365
1366 let excerpt_id = list.update(cx, |list, cx| {
1367 list.push(
1368 ExcerptProperties {
1369 buffer: &buffer_handle,
1370 range: start_ix..end_ix,
1371 header_height,
1372 },
1373 cx,
1374 )
1375 });
1376 excerpt_ids.push(excerpt_id);
1377 expected_excerpts.push((buffer_handle.clone(), anchor_range, header_height));
1378 }
1379 }
1380
1381 if rng.gen_bool(0.3) {
1382 list.update(cx, |list, cx| {
1383 old_versions.push((list.snapshot(cx), list.subscribe()));
1384 })
1385 }
1386
1387 let snapshot = list.read(cx).snapshot(cx);
1388
1389 let mut excerpt_starts = Vec::new();
1390 let mut expected_text = String::new();
1391 for (buffer, range, header_height) in &expected_excerpts {
1392 let buffer = buffer.read(cx);
1393 let buffer_range = range.to_offset(buffer);
1394
1395 for _ in 0..*header_height {
1396 expected_text.push('\n');
1397 }
1398
1399 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
1400 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
1401 expected_text.push('\n');
1402 }
1403
1404 assert_eq!(snapshot.text(), expected_text);
1405
1406 let mut excerpt_starts = excerpt_starts.into_iter();
1407 for (buffer, range, _) in &expected_excerpts {
1408 let buffer_id = buffer.id();
1409 let buffer = buffer.read(cx);
1410 let buffer_range = range.to_offset(buffer);
1411 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
1412 let buffer_start_point_utf16 =
1413 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
1414
1415 let excerpt_start = excerpt_starts.next().unwrap();
1416 let mut offset = excerpt_start.bytes;
1417 let mut buffer_offset = buffer_range.start;
1418 let mut point = excerpt_start.lines;
1419 let mut buffer_point = buffer_start_point;
1420 let mut point_utf16 = excerpt_start.lines_utf16;
1421 let mut buffer_point_utf16 = buffer_start_point_utf16;
1422 for byte in buffer.bytes_in_range(buffer_range.clone()).flatten() {
1423 let left_offset = snapshot.clip_offset(offset, Bias::Left);
1424 let right_offset = snapshot.clip_offset(offset, Bias::Right);
1425 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
1426 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
1427 assert_eq!(
1428 left_offset,
1429 excerpt_start.bytes + (buffer_left_offset - buffer_range.start),
1430 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
1431 offset,
1432 buffer_id,
1433 buffer_offset,
1434 );
1435 assert_eq!(
1436 right_offset,
1437 excerpt_start.bytes + (buffer_right_offset - buffer_range.start),
1438 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
1439 offset,
1440 buffer_id,
1441 buffer_offset,
1442 );
1443
1444 let left_point = snapshot.clip_point(point, Bias::Left);
1445 let right_point = snapshot.clip_point(point, Bias::Right);
1446 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
1447 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
1448 assert_eq!(
1449 left_point,
1450 excerpt_start.lines + (buffer_left_point - buffer_start_point),
1451 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
1452 point,
1453 buffer_id,
1454 buffer_point,
1455 );
1456 assert_eq!(
1457 right_point,
1458 excerpt_start.lines + (buffer_right_point - buffer_start_point),
1459 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
1460 point,
1461 buffer_id,
1462 buffer_point,
1463 );
1464
1465 let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left);
1466 let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right);
1467 let buffer_left_point_utf16 =
1468 buffer.clip_point_utf16(buffer_point_utf16, Bias::Left);
1469 let buffer_right_point_utf16 =
1470 buffer.clip_point_utf16(buffer_point_utf16, Bias::Right);
1471 assert_eq!(
1472 left_point_utf16,
1473 excerpt_start.lines_utf16
1474 + (buffer_left_point_utf16 - buffer_start_point_utf16),
1475 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
1476 point_utf16,
1477 buffer_id,
1478 buffer_point_utf16,
1479 );
1480 assert_eq!(
1481 right_point_utf16,
1482 excerpt_start.lines_utf16
1483 + (buffer_right_point_utf16 - buffer_start_point_utf16),
1484 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
1485 point_utf16,
1486 buffer_id,
1487 buffer_point_utf16,
1488 );
1489
1490 assert_eq!(
1491 snapshot.point_to_offset(left_point),
1492 left_offset,
1493 "point_to_offset({:?})",
1494 left_point,
1495 );
1496 assert_eq!(
1497 snapshot.offset_to_point(left_offset),
1498 left_point,
1499 "offset_to_point({:?})",
1500 left_offset,
1501 );
1502
1503 offset += 1;
1504 buffer_offset += 1;
1505 if *byte == b'\n' {
1506 point += Point::new(1, 0);
1507 point_utf16 += PointUtf16::new(1, 0);
1508 buffer_point += Point::new(1, 0);
1509 buffer_point_utf16 += PointUtf16::new(1, 0);
1510 } else {
1511 point += Point::new(0, 1);
1512 point_utf16 += PointUtf16::new(0, 1);
1513 buffer_point += Point::new(0, 1);
1514 buffer_point_utf16 += PointUtf16::new(0, 1);
1515 }
1516 }
1517 }
1518
1519 for (row, line) in expected_text.split('\n').enumerate() {
1520 assert_eq!(
1521 snapshot.line_len(row as u32),
1522 line.len() as u32,
1523 "line_len({}).",
1524 row
1525 );
1526 }
1527
1528 for _ in 0..10 {
1529 let end_ix = snapshot.clip_offset(rng.gen_range(0..=snapshot.len()), Bias::Right);
1530 let start_ix = snapshot.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1531
1532 assert_eq!(
1533 snapshot
1534 .text_for_range(start_ix..end_ix)
1535 .collect::<String>(),
1536 &expected_text[start_ix..end_ix],
1537 "incorrect text for range {:?}",
1538 start_ix..end_ix
1539 );
1540
1541 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
1542 assert_eq!(
1543 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
1544 expected_summary,
1545 "incorrect summary for range {:?}",
1546 start_ix..end_ix
1547 );
1548 }
1549 }
1550
1551 let snapshot = list.read(cx).snapshot(cx);
1552 for (old_snapshot, subscription) in old_versions {
1553 let edits = subscription.consume().into_inner();
1554
1555 log::info!(
1556 "applying edits since old text: {:?}: {:?}",
1557 old_snapshot.text(),
1558 edits,
1559 );
1560
1561 let mut text = old_snapshot.text();
1562 for edit in edits {
1563 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
1564 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
1565 }
1566 assert_eq!(text.to_string(), snapshot.text());
1567 }
1568 }
1569}