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