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