1mod anchor;
2
3pub use anchor::{Anchor, AnchorRangeExt};
4use anyhow::{anyhow, Result};
5use clock::ReplicaId;
6use collections::{BTreeMap, Bound, HashMap, HashSet};
7use futures::{channel::mpsc, SinkExt};
8use git::diff::DiffHunk;
9use gpui::{AppContext, Entity, ModelContext, ModelHandle, Task};
10pub use language::Completion;
11use language::{
12 char_kind,
13 language_settings::{language_settings, LanguageSettings},
14 AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape,
15 DiagnosticEntry, File, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16,
16 Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _,
17 ToOffsetUtf16 as _, ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped,
18};
19use std::{
20 borrow::Cow,
21 cell::{Ref, RefCell},
22 cmp, fmt,
23 future::Future,
24 io,
25 iter::{self, FromIterator},
26 mem,
27 ops::{Range, RangeBounds, Sub},
28 str,
29 sync::Arc,
30 time::{Duration, Instant},
31};
32use sum_tree::{Bias, Cursor, SumTree};
33use text::{
34 locator::Locator,
35 subscription::{Subscription, Topic},
36 Edit, TextSummary,
37};
38use theme::SyntaxTheme;
39use util::post_inc;
40
41const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
42
43#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
44pub struct ExcerptId(usize);
45
46pub struct MultiBuffer {
47 snapshot: RefCell<MultiBufferSnapshot>,
48 buffers: RefCell<HashMap<u64, BufferState>>,
49 next_excerpt_id: usize,
50 subscriptions: Topic,
51 singleton: bool,
52 replica_id: ReplicaId,
53 history: History,
54 title: Option<String>,
55}
56
57#[derive(Clone, Debug, PartialEq, Eq)]
58pub enum Event {
59 ExcerptsAdded {
60 buffer: ModelHandle<Buffer>,
61 predecessor: ExcerptId,
62 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
63 },
64 ExcerptsRemoved {
65 ids: Vec<ExcerptId>,
66 },
67 Edited,
68 Reloaded,
69 LanguageChanged,
70 Reparsed,
71 Saved,
72 FileHandleChanged,
73 Closed,
74 DirtyChanged,
75 DiagnosticsUpdated,
76}
77
78#[derive(Clone)]
79struct History {
80 next_transaction_id: TransactionId,
81 undo_stack: Vec<Transaction>,
82 redo_stack: Vec<Transaction>,
83 transaction_depth: usize,
84 group_interval: Duration,
85}
86
87#[derive(Clone)]
88struct Transaction {
89 id: TransactionId,
90 buffer_transactions: HashMap<u64, text::TransactionId>,
91 first_edit_at: Instant,
92 last_edit_at: Instant,
93 suppress_grouping: bool,
94}
95
96pub trait ToOffset: 'static + fmt::Debug {
97 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
98}
99
100pub trait ToOffsetUtf16: 'static + fmt::Debug {
101 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;
102}
103
104pub trait ToPoint: 'static + fmt::Debug {
105 fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
106}
107
108pub trait ToPointUtf16: 'static + fmt::Debug {
109 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;
110}
111
112struct BufferState {
113 buffer: ModelHandle<Buffer>,
114 last_version: clock::Global,
115 last_parse_count: usize,
116 last_selections_update_count: usize,
117 last_diagnostics_update_count: usize,
118 last_file_update_count: usize,
119 last_git_diff_update_count: usize,
120 excerpts: Vec<Locator>,
121 _subscriptions: [gpui::Subscription; 2],
122}
123
124#[derive(Clone, Default)]
125pub struct MultiBufferSnapshot {
126 singleton: bool,
127 excerpts: SumTree<Excerpt>,
128 excerpt_ids: SumTree<ExcerptIdMapping>,
129 parse_count: usize,
130 diagnostics_update_count: usize,
131 trailing_excerpt_update_count: usize,
132 git_diff_update_count: usize,
133 edit_count: usize,
134 is_dirty: bool,
135 has_conflict: bool,
136}
137
138pub struct ExcerptBoundary {
139 pub id: ExcerptId,
140 pub row: u32,
141 pub buffer: BufferSnapshot,
142 pub range: ExcerptRange<text::Anchor>,
143 pub starts_new_buffer: bool,
144}
145
146#[derive(Clone)]
147struct Excerpt {
148 id: ExcerptId,
149 locator: Locator,
150 buffer_id: u64,
151 buffer: BufferSnapshot,
152 range: ExcerptRange<text::Anchor>,
153 max_buffer_row: u32,
154 text_summary: TextSummary,
155 has_trailing_newline: bool,
156}
157
158#[derive(Clone, Debug)]
159struct ExcerptIdMapping {
160 id: ExcerptId,
161 locator: Locator,
162}
163
164#[derive(Clone, Debug, Eq, PartialEq)]
165pub struct ExcerptRange<T> {
166 pub context: Range<T>,
167 pub primary: Option<Range<T>>,
168}
169
170#[derive(Clone, Debug, Default)]
171struct ExcerptSummary {
172 excerpt_id: ExcerptId,
173 excerpt_locator: Locator,
174 max_buffer_row: u32,
175 text: TextSummary,
176}
177
178#[derive(Clone)]
179pub struct MultiBufferRows<'a> {
180 buffer_row_range: Range<u32>,
181 excerpts: Cursor<'a, Excerpt, Point>,
182}
183
184pub struct MultiBufferChunks<'a> {
185 range: Range<usize>,
186 excerpts: Cursor<'a, Excerpt, usize>,
187 excerpt_chunks: Option<ExcerptChunks<'a>>,
188 language_aware: bool,
189}
190
191pub struct MultiBufferBytes<'a> {
192 range: Range<usize>,
193 excerpts: Cursor<'a, Excerpt, usize>,
194 excerpt_bytes: Option<ExcerptBytes<'a>>,
195 chunk: &'a [u8],
196}
197
198struct ExcerptChunks<'a> {
199 content_chunks: BufferChunks<'a>,
200 footer_height: usize,
201}
202
203struct ExcerptBytes<'a> {
204 content_bytes: text::Bytes<'a>,
205 footer_height: usize,
206}
207
208impl MultiBuffer {
209 pub fn new(replica_id: ReplicaId) -> Self {
210 Self {
211 snapshot: Default::default(),
212 buffers: Default::default(),
213 next_excerpt_id: 1,
214 subscriptions: Default::default(),
215 singleton: false,
216 replica_id,
217 history: History {
218 next_transaction_id: Default::default(),
219 undo_stack: Default::default(),
220 redo_stack: Default::default(),
221 transaction_depth: 0,
222 group_interval: Duration::from_millis(300),
223 },
224 title: Default::default(),
225 }
226 }
227
228 pub fn clone(&self, new_cx: &mut ModelContext<Self>) -> Self {
229 let mut buffers = HashMap::default();
230 for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
231 buffers.insert(
232 *buffer_id,
233 BufferState {
234 buffer: buffer_state.buffer.clone(),
235 last_version: buffer_state.last_version.clone(),
236 last_parse_count: buffer_state.last_parse_count,
237 last_selections_update_count: buffer_state.last_selections_update_count,
238 last_diagnostics_update_count: buffer_state.last_diagnostics_update_count,
239 last_file_update_count: buffer_state.last_file_update_count,
240 last_git_diff_update_count: buffer_state.last_git_diff_update_count,
241 excerpts: buffer_state.excerpts.clone(),
242 _subscriptions: [
243 new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
244 new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),
245 ],
246 },
247 );
248 }
249 Self {
250 snapshot: RefCell::new(self.snapshot.borrow().clone()),
251 buffers: RefCell::new(buffers),
252 next_excerpt_id: 1,
253 subscriptions: Default::default(),
254 singleton: self.singleton,
255 replica_id: self.replica_id,
256 history: self.history.clone(),
257 title: self.title.clone(),
258 }
259 }
260
261 pub fn with_title(mut self, title: String) -> Self {
262 self.title = Some(title);
263 self
264 }
265
266 pub fn singleton(buffer: ModelHandle<Buffer>, cx: &mut ModelContext<Self>) -> Self {
267 let mut this = Self::new(buffer.read(cx).replica_id());
268 this.singleton = true;
269 this.push_excerpts(
270 buffer,
271 [ExcerptRange {
272 context: text::Anchor::MIN..text::Anchor::MAX,
273 primary: None,
274 }],
275 cx,
276 );
277 this.snapshot.borrow_mut().singleton = true;
278 this
279 }
280
281 pub fn replica_id(&self) -> ReplicaId {
282 self.replica_id
283 }
284
285 pub fn snapshot(&self, cx: &AppContext) -> MultiBufferSnapshot {
286 self.sync(cx);
287 self.snapshot.borrow().clone()
288 }
289
290 pub(crate) fn read(&self, cx: &AppContext) -> Ref<MultiBufferSnapshot> {
291 self.sync(cx);
292 self.snapshot.borrow()
293 }
294
295 pub fn as_singleton(&self) -> Option<ModelHandle<Buffer>> {
296 if self.singleton {
297 return Some(
298 self.buffers
299 .borrow()
300 .values()
301 .next()
302 .unwrap()
303 .buffer
304 .clone(),
305 );
306 } else {
307 None
308 }
309 }
310
311 pub fn is_singleton(&self) -> bool {
312 self.singleton
313 }
314
315 pub fn subscribe(&mut self) -> Subscription {
316 self.subscriptions.subscribe()
317 }
318
319 pub fn is_dirty(&self, cx: &AppContext) -> bool {
320 self.read(cx).is_dirty()
321 }
322
323 pub fn has_conflict(&self, cx: &AppContext) -> bool {
324 self.read(cx).has_conflict()
325 }
326
327 // The `is_empty` signature doesn't match what clippy expects
328 #[allow(clippy::len_without_is_empty)]
329 pub fn len(&self, cx: &AppContext) -> usize {
330 self.read(cx).len()
331 }
332
333 pub fn is_empty(&self, cx: &AppContext) -> bool {
334 self.len(cx) != 0
335 }
336
337 pub fn symbols_containing<T: ToOffset>(
338 &self,
339 offset: T,
340 theme: Option<&SyntaxTheme>,
341 cx: &AppContext,
342 ) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
343 self.read(cx).symbols_containing(offset, theme)
344 }
345
346 pub fn edit<I, S, T>(
347 &mut self,
348 edits: I,
349 mut autoindent_mode: Option<AutoindentMode>,
350 cx: &mut ModelContext<Self>,
351 ) where
352 I: IntoIterator<Item = (Range<S>, T)>,
353 S: ToOffset,
354 T: Into<Arc<str>>,
355 {
356 if self.buffers.borrow().is_empty() {
357 return;
358 }
359
360 let snapshot = self.read(cx);
361 let edits = edits.into_iter().map(|(range, new_text)| {
362 let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);
363 if range.start > range.end {
364 mem::swap(&mut range.start, &mut range.end);
365 }
366 (range, new_text)
367 });
368
369 if let Some(buffer) = self.as_singleton() {
370 return buffer.update(cx, |buffer, cx| {
371 buffer.edit(edits, autoindent_mode, cx);
372 });
373 }
374
375 let original_indent_columns = match &mut autoindent_mode {
376 Some(AutoindentMode::Block {
377 original_indent_columns,
378 }) => mem::take(original_indent_columns),
379 _ => Default::default(),
380 };
381
382 struct BufferEdit {
383 range: Range<usize>,
384 new_text: Arc<str>,
385 is_insertion: bool,
386 original_indent_column: u32,
387 }
388 let mut buffer_edits: HashMap<u64, Vec<BufferEdit>> = Default::default();
389 let mut cursor = snapshot.excerpts.cursor::<usize>();
390 for (ix, (range, new_text)) in edits.enumerate() {
391 let new_text: Arc<str> = new_text.into();
392 let original_indent_column = original_indent_columns.get(ix).copied().unwrap_or(0);
393 cursor.seek(&range.start, Bias::Right, &());
394 if cursor.item().is_none() && range.start == *cursor.start() {
395 cursor.prev(&());
396 }
397 let start_excerpt = cursor.item().expect("start offset out of bounds");
398 let start_overshoot = range.start - cursor.start();
399 let buffer_start = start_excerpt
400 .range
401 .context
402 .start
403 .to_offset(&start_excerpt.buffer)
404 + start_overshoot;
405
406 cursor.seek(&range.end, Bias::Right, &());
407 if cursor.item().is_none() && range.end == *cursor.start() {
408 cursor.prev(&());
409 }
410 let end_excerpt = cursor.item().expect("end offset out of bounds");
411 let end_overshoot = range.end - cursor.start();
412 let buffer_end = end_excerpt
413 .range
414 .context
415 .start
416 .to_offset(&end_excerpt.buffer)
417 + end_overshoot;
418
419 if start_excerpt.id == end_excerpt.id {
420 buffer_edits
421 .entry(start_excerpt.buffer_id)
422 .or_insert(Vec::new())
423 .push(BufferEdit {
424 range: buffer_start..buffer_end,
425 new_text,
426 is_insertion: true,
427 original_indent_column,
428 });
429 } else {
430 let start_excerpt_range = buffer_start
431 ..start_excerpt
432 .range
433 .context
434 .end
435 .to_offset(&start_excerpt.buffer);
436 let end_excerpt_range = end_excerpt
437 .range
438 .context
439 .start
440 .to_offset(&end_excerpt.buffer)
441 ..buffer_end;
442 buffer_edits
443 .entry(start_excerpt.buffer_id)
444 .or_insert(Vec::new())
445 .push(BufferEdit {
446 range: start_excerpt_range,
447 new_text: new_text.clone(),
448 is_insertion: true,
449 original_indent_column,
450 });
451 buffer_edits
452 .entry(end_excerpt.buffer_id)
453 .or_insert(Vec::new())
454 .push(BufferEdit {
455 range: end_excerpt_range,
456 new_text: new_text.clone(),
457 is_insertion: false,
458 original_indent_column,
459 });
460
461 cursor.seek(&range.start, Bias::Right, &());
462 cursor.next(&());
463 while let Some(excerpt) = cursor.item() {
464 if excerpt.id == end_excerpt.id {
465 break;
466 }
467 buffer_edits
468 .entry(excerpt.buffer_id)
469 .or_insert(Vec::new())
470 .push(BufferEdit {
471 range: excerpt.range.context.to_offset(&excerpt.buffer),
472 new_text: new_text.clone(),
473 is_insertion: false,
474 original_indent_column,
475 });
476 cursor.next(&());
477 }
478 }
479 }
480
481 for (buffer_id, mut edits) in buffer_edits {
482 edits.sort_unstable_by_key(|edit| edit.range.start);
483 self.buffers.borrow()[&buffer_id]
484 .buffer
485 .update(cx, |buffer, cx| {
486 let mut edits = edits.into_iter().peekable();
487 let mut insertions = Vec::new();
488 let mut original_indent_columns = Vec::new();
489 let mut deletions = Vec::new();
490 let empty_str: Arc<str> = "".into();
491 while let Some(BufferEdit {
492 mut range,
493 new_text,
494 mut is_insertion,
495 original_indent_column,
496 }) = edits.next()
497 {
498 while let Some(BufferEdit {
499 range: next_range,
500 is_insertion: next_is_insertion,
501 ..
502 }) = edits.peek()
503 {
504 if range.end >= next_range.start {
505 range.end = cmp::max(next_range.end, range.end);
506 is_insertion |= *next_is_insertion;
507 edits.next();
508 } else {
509 break;
510 }
511 }
512
513 if is_insertion {
514 original_indent_columns.push(original_indent_column);
515 insertions.push((
516 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
517 new_text.clone(),
518 ));
519 } else if !range.is_empty() {
520 deletions.push((
521 buffer.anchor_before(range.start)..buffer.anchor_before(range.end),
522 empty_str.clone(),
523 ));
524 }
525 }
526
527 let deletion_autoindent_mode =
528 if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
529 Some(AutoindentMode::Block {
530 original_indent_columns: Default::default(),
531 })
532 } else {
533 None
534 };
535 let insertion_autoindent_mode =
536 if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
537 Some(AutoindentMode::Block {
538 original_indent_columns,
539 })
540 } else {
541 None
542 };
543
544 buffer.edit(deletions, deletion_autoindent_mode, cx);
545 buffer.edit(insertions, insertion_autoindent_mode, cx);
546 })
547 }
548 }
549
550 pub fn start_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
551 self.start_transaction_at(Instant::now(), cx)
552 }
553
554 pub(crate) fn start_transaction_at(
555 &mut self,
556 now: Instant,
557 cx: &mut ModelContext<Self>,
558 ) -> Option<TransactionId> {
559 if let Some(buffer) = self.as_singleton() {
560 return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
561 }
562
563 for BufferState { buffer, .. } in self.buffers.borrow().values() {
564 buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
565 }
566 self.history.start_transaction(now)
567 }
568
569 pub fn end_transaction(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
570 self.end_transaction_at(Instant::now(), cx)
571 }
572
573 pub(crate) fn end_transaction_at(
574 &mut self,
575 now: Instant,
576 cx: &mut ModelContext<Self>,
577 ) -> Option<TransactionId> {
578 if let Some(buffer) = self.as_singleton() {
579 return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
580 }
581
582 let mut buffer_transactions = HashMap::default();
583 for BufferState { buffer, .. } in self.buffers.borrow().values() {
584 if let Some(transaction_id) =
585 buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
586 {
587 buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id);
588 }
589 }
590
591 if self.history.end_transaction(now, buffer_transactions) {
592 let transaction_id = self.history.group().unwrap();
593 Some(transaction_id)
594 } else {
595 None
596 }
597 }
598
599 pub fn finalize_last_transaction(&mut self, cx: &mut ModelContext<Self>) {
600 self.history.finalize_last_transaction();
601 for BufferState { buffer, .. } in self.buffers.borrow().values() {
602 buffer.update(cx, |buffer, _| {
603 buffer.finalize_last_transaction();
604 });
605 }
606 }
607
608 pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &mut ModelContext<Self>)
609 where
610 T: IntoIterator<Item = (&'a ModelHandle<Buffer>, &'a language::Transaction)>,
611 {
612 self.history
613 .push_transaction(buffer_transactions, Instant::now(), cx);
614 self.history.finalize_last_transaction();
615 }
616
617 pub fn group_until_transaction(
618 &mut self,
619 transaction_id: TransactionId,
620 cx: &mut ModelContext<Self>,
621 ) {
622 if let Some(buffer) = self.as_singleton() {
623 buffer.update(cx, |buffer, _| {
624 buffer.group_until_transaction(transaction_id)
625 });
626 } else {
627 self.history.group_until(transaction_id);
628 }
629 }
630
631 pub fn set_active_selections(
632 &mut self,
633 selections: &[Selection<Anchor>],
634 line_mode: bool,
635 cursor_shape: CursorShape,
636 cx: &mut ModelContext<Self>,
637 ) {
638 let mut selections_by_buffer: HashMap<u64, Vec<Selection<text::Anchor>>> =
639 Default::default();
640 let snapshot = self.read(cx);
641 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
642 for selection in selections {
643 let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id);
644 let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id);
645
646 cursor.seek(&Some(start_locator), Bias::Left, &());
647 while let Some(excerpt) = cursor.item() {
648 if excerpt.locator > *end_locator {
649 break;
650 }
651
652 let mut start = excerpt.range.context.start;
653 let mut end = excerpt.range.context.end;
654 if excerpt.id == selection.start.excerpt_id {
655 start = selection.start.text_anchor;
656 }
657 if excerpt.id == selection.end.excerpt_id {
658 end = selection.end.text_anchor;
659 }
660 selections_by_buffer
661 .entry(excerpt.buffer_id)
662 .or_default()
663 .push(Selection {
664 id: selection.id,
665 start,
666 end,
667 reversed: selection.reversed,
668 goal: selection.goal,
669 });
670
671 cursor.next(&());
672 }
673 }
674
675 for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
676 if !selections_by_buffer.contains_key(buffer_id) {
677 buffer_state
678 .buffer
679 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
680 }
681 }
682
683 for (buffer_id, mut selections) in selections_by_buffer {
684 self.buffers.borrow()[&buffer_id]
685 .buffer
686 .update(cx, |buffer, cx| {
687 selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer));
688 let mut selections = selections.into_iter().peekable();
689 let merged_selections = Arc::from_iter(iter::from_fn(|| {
690 let mut selection = selections.next()?;
691 while let Some(next_selection) = selections.peek() {
692 if selection.end.cmp(&next_selection.start, buffer).is_ge() {
693 let next_selection = selections.next().unwrap();
694 if next_selection.end.cmp(&selection.end, buffer).is_ge() {
695 selection.end = next_selection.end;
696 }
697 } else {
698 break;
699 }
700 }
701 Some(selection)
702 }));
703 buffer.set_active_selections(merged_selections, line_mode, cursor_shape, cx);
704 });
705 }
706 }
707
708 pub fn remove_active_selections(&mut self, cx: &mut ModelContext<Self>) {
709 for buffer in self.buffers.borrow().values() {
710 buffer
711 .buffer
712 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
713 }
714 }
715
716 pub fn undo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
717 if let Some(buffer) = self.as_singleton() {
718 return buffer.update(cx, |buffer, cx| buffer.undo(cx));
719 }
720
721 while let Some(transaction) = self.history.pop_undo() {
722 let mut undone = false;
723 for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
724 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
725 undone |= buffer.update(cx, |buffer, cx| {
726 let undo_to = *buffer_transaction_id;
727 if let Some(entry) = buffer.peek_undo_stack() {
728 *buffer_transaction_id = entry.transaction_id();
729 }
730 buffer.undo_to_transaction(undo_to, cx)
731 });
732 }
733 }
734
735 if undone {
736 return Some(transaction.id);
737 }
738 }
739
740 None
741 }
742
743 pub fn redo(&mut self, cx: &mut ModelContext<Self>) -> Option<TransactionId> {
744 if let Some(buffer) = self.as_singleton() {
745 return buffer.update(cx, |buffer, cx| buffer.redo(cx));
746 }
747
748 while let Some(transaction) = self.history.pop_redo() {
749 let mut redone = false;
750 for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
751 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
752 redone |= buffer.update(cx, |buffer, cx| {
753 let redo_to = *buffer_transaction_id;
754 if let Some(entry) = buffer.peek_redo_stack() {
755 *buffer_transaction_id = entry.transaction_id();
756 }
757 buffer.redo_to_transaction(redo_to, cx)
758 });
759 }
760 }
761
762 if redone {
763 return Some(transaction.id);
764 }
765 }
766
767 None
768 }
769
770 pub fn stream_excerpts_with_context_lines(
771 &mut self,
772 excerpts: Vec<(ModelHandle<Buffer>, Vec<Range<text::Anchor>>)>,
773 context_line_count: u32,
774 cx: &mut ModelContext<Self>,
775 ) -> (Task<()>, mpsc::Receiver<Range<Anchor>>) {
776 let (mut tx, rx) = mpsc::channel(256);
777 let task = cx.spawn(|this, mut cx| async move {
778 for (buffer, ranges) in excerpts {
779 let (buffer_id, buffer_snapshot) =
780 buffer.read_with(&cx, |buffer, _| (buffer.remote_id(), buffer.snapshot()));
781
782 let mut excerpt_ranges = Vec::new();
783 let mut range_counts = Vec::new();
784 cx.background()
785 .scoped(|scope| {
786 scope.spawn(async {
787 let (ranges, counts) =
788 build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
789 excerpt_ranges = ranges;
790 range_counts = counts;
791 });
792 })
793 .await;
794
795 let mut ranges = ranges.into_iter();
796 let mut range_counts = range_counts.into_iter();
797 for excerpt_ranges in excerpt_ranges.chunks(100) {
798 let excerpt_ids = this.update(&mut cx, |this, cx| {
799 this.push_excerpts(buffer.clone(), excerpt_ranges.iter().cloned(), cx)
800 });
801
802 for (excerpt_id, range_count) in
803 excerpt_ids.into_iter().zip(range_counts.by_ref())
804 {
805 for range in ranges.by_ref().take(range_count) {
806 let start = Anchor {
807 buffer_id: Some(buffer_id),
808 excerpt_id: excerpt_id.clone(),
809 text_anchor: range.start,
810 };
811 let end = Anchor {
812 buffer_id: Some(buffer_id),
813 excerpt_id: excerpt_id.clone(),
814 text_anchor: range.end,
815 };
816 if tx.send(start..end).await.is_err() {
817 break;
818 }
819 }
820 }
821 }
822 }
823 });
824 (task, rx)
825 }
826
827 pub fn push_excerpts<O>(
828 &mut self,
829 buffer: ModelHandle<Buffer>,
830 ranges: impl IntoIterator<Item = ExcerptRange<O>>,
831 cx: &mut ModelContext<Self>,
832 ) -> Vec<ExcerptId>
833 where
834 O: text::ToOffset,
835 {
836 self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx)
837 }
838
839 pub fn push_excerpts_with_context_lines<O>(
840 &mut self,
841 buffer: ModelHandle<Buffer>,
842 ranges: Vec<Range<O>>,
843 context_line_count: u32,
844 cx: &mut ModelContext<Self>,
845 ) -> Vec<Range<Anchor>>
846 where
847 O: text::ToPoint + text::ToOffset,
848 {
849 let buffer_id = buffer.read(cx).remote_id();
850 let buffer_snapshot = buffer.read(cx).snapshot();
851 let (excerpt_ranges, range_counts) =
852 build_excerpt_ranges(&buffer_snapshot, &ranges, context_line_count);
853
854 let excerpt_ids = self.push_excerpts(buffer, excerpt_ranges, cx);
855
856 let mut anchor_ranges = Vec::new();
857 let mut ranges = ranges.into_iter();
858 for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(range_counts.into_iter()) {
859 anchor_ranges.extend(ranges.by_ref().take(range_count).map(|range| {
860 let start = Anchor {
861 buffer_id: Some(buffer_id),
862 excerpt_id: excerpt_id.clone(),
863 text_anchor: buffer_snapshot.anchor_after(range.start),
864 };
865 let end = Anchor {
866 buffer_id: Some(buffer_id),
867 excerpt_id: excerpt_id.clone(),
868 text_anchor: buffer_snapshot.anchor_after(range.end),
869 };
870 start..end
871 }))
872 }
873 anchor_ranges
874 }
875
876 pub fn insert_excerpts_after<O>(
877 &mut self,
878 prev_excerpt_id: ExcerptId,
879 buffer: ModelHandle<Buffer>,
880 ranges: impl IntoIterator<Item = ExcerptRange<O>>,
881 cx: &mut ModelContext<Self>,
882 ) -> Vec<ExcerptId>
883 where
884 O: text::ToOffset,
885 {
886 let mut ids = Vec::new();
887 let mut next_excerpt_id = self.next_excerpt_id;
888 self.insert_excerpts_with_ids_after(
889 prev_excerpt_id,
890 buffer,
891 ranges.into_iter().map(|range| {
892 let id = ExcerptId(post_inc(&mut next_excerpt_id));
893 ids.push(id);
894 (id, range)
895 }),
896 cx,
897 );
898 ids
899 }
900
901 pub fn insert_excerpts_with_ids_after<O>(
902 &mut self,
903 prev_excerpt_id: ExcerptId,
904 buffer: ModelHandle<Buffer>,
905 ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
906 cx: &mut ModelContext<Self>,
907 ) where
908 O: text::ToOffset,
909 {
910 assert_eq!(self.history.transaction_depth, 0);
911 let mut ranges = ranges.into_iter().peekable();
912 if ranges.peek().is_none() {
913 return Default::default();
914 }
915
916 self.sync(cx);
917
918 let buffer_id = buffer.read(cx).remote_id();
919 let buffer_snapshot = buffer.read(cx).snapshot();
920
921 let mut buffers = self.buffers.borrow_mut();
922 let buffer_state = buffers.entry(buffer_id).or_insert_with(|| BufferState {
923 last_version: buffer_snapshot.version().clone(),
924 last_parse_count: buffer_snapshot.parse_count(),
925 last_selections_update_count: buffer_snapshot.selections_update_count(),
926 last_diagnostics_update_count: buffer_snapshot.diagnostics_update_count(),
927 last_file_update_count: buffer_snapshot.file_update_count(),
928 last_git_diff_update_count: buffer_snapshot.git_diff_update_count(),
929 excerpts: Default::default(),
930 _subscriptions: [
931 cx.observe(&buffer, |_, _, cx| cx.notify()),
932 cx.subscribe(&buffer, Self::on_buffer_event),
933 ],
934 buffer: buffer.clone(),
935 });
936
937 let mut snapshot = self.snapshot.borrow_mut();
938
939 let mut prev_locator = snapshot.excerpt_locator_for_id(prev_excerpt_id).clone();
940 let mut new_excerpt_ids = mem::take(&mut snapshot.excerpt_ids);
941 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
942 let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right, &());
943 prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone();
944
945 let edit_start = new_excerpts.summary().text.len;
946 new_excerpts.update_last(
947 |excerpt| {
948 excerpt.has_trailing_newline = true;
949 },
950 &(),
951 );
952
953 let next_locator = if let Some(excerpt) = cursor.item() {
954 excerpt.locator.clone()
955 } else {
956 Locator::max()
957 };
958
959 let mut excerpts = Vec::new();
960 while let Some((id, range)) = ranges.next() {
961 let locator = Locator::between(&prev_locator, &next_locator);
962 if let Err(ix) = buffer_state.excerpts.binary_search(&locator) {
963 buffer_state.excerpts.insert(ix, locator.clone());
964 }
965 let range = ExcerptRange {
966 context: buffer_snapshot.anchor_before(&range.context.start)
967 ..buffer_snapshot.anchor_after(&range.context.end),
968 primary: range.primary.map(|primary| {
969 buffer_snapshot.anchor_before(&primary.start)
970 ..buffer_snapshot.anchor_after(&primary.end)
971 }),
972 };
973 if id.0 >= self.next_excerpt_id {
974 self.next_excerpt_id = id.0 + 1;
975 }
976 excerpts.push((id, range.clone()));
977 let excerpt = Excerpt::new(
978 id,
979 locator.clone(),
980 buffer_id,
981 buffer_snapshot.clone(),
982 range,
983 ranges.peek().is_some() || cursor.item().is_some(),
984 );
985 new_excerpts.push(excerpt, &());
986 prev_locator = locator.clone();
987 new_excerpt_ids.push(ExcerptIdMapping { id, locator }, &());
988 }
989
990 let edit_end = new_excerpts.summary().text.len;
991
992 let suffix = cursor.suffix(&());
993 let changed_trailing_excerpt = suffix.is_empty();
994 new_excerpts.push_tree(suffix, &());
995 drop(cursor);
996 snapshot.excerpts = new_excerpts;
997 snapshot.excerpt_ids = new_excerpt_ids;
998 if changed_trailing_excerpt {
999 snapshot.trailing_excerpt_update_count += 1;
1000 }
1001
1002 self.subscriptions.publish_mut([Edit {
1003 old: edit_start..edit_start,
1004 new: edit_start..edit_end,
1005 }]);
1006 cx.emit(Event::Edited);
1007 cx.emit(Event::ExcerptsAdded {
1008 buffer,
1009 predecessor: prev_excerpt_id,
1010 excerpts,
1011 });
1012 cx.notify();
1013 }
1014
1015 pub fn clear(&mut self, cx: &mut ModelContext<Self>) {
1016 self.sync(cx);
1017 let ids = self.excerpt_ids();
1018 self.buffers.borrow_mut().clear();
1019 let mut snapshot = self.snapshot.borrow_mut();
1020 let prev_len = snapshot.len();
1021 snapshot.excerpts = Default::default();
1022 snapshot.trailing_excerpt_update_count += 1;
1023 snapshot.is_dirty = false;
1024 snapshot.has_conflict = false;
1025
1026 self.subscriptions.publish_mut([Edit {
1027 old: 0..prev_len,
1028 new: 0..0,
1029 }]);
1030 cx.emit(Event::Edited);
1031 cx.emit(Event::ExcerptsRemoved { ids });
1032 cx.notify();
1033 }
1034
1035 pub fn excerpts_for_buffer(
1036 &self,
1037 buffer: &ModelHandle<Buffer>,
1038 cx: &AppContext,
1039 ) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
1040 let mut excerpts = Vec::new();
1041 let snapshot = self.read(cx);
1042 let buffers = self.buffers.borrow();
1043 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>();
1044 for locator in buffers
1045 .get(&buffer.read(cx).remote_id())
1046 .map(|state| &state.excerpts)
1047 .into_iter()
1048 .flatten()
1049 {
1050 cursor.seek_forward(&Some(locator), Bias::Left, &());
1051 if let Some(excerpt) = cursor.item() {
1052 if excerpt.locator == *locator {
1053 excerpts.push((excerpt.id.clone(), excerpt.range.clone()));
1054 }
1055 }
1056 }
1057
1058 excerpts
1059 }
1060
1061 pub fn excerpt_ids(&self) -> Vec<ExcerptId> {
1062 self.snapshot
1063 .borrow()
1064 .excerpts
1065 .iter()
1066 .map(|entry| entry.id)
1067 .collect()
1068 }
1069
1070 pub fn excerpt_containing(
1071 &self,
1072 position: impl ToOffset,
1073 cx: &AppContext,
1074 ) -> Option<(ExcerptId, ModelHandle<Buffer>, Range<text::Anchor>)> {
1075 let snapshot = self.read(cx);
1076 let position = position.to_offset(&snapshot);
1077
1078 let mut cursor = snapshot.excerpts.cursor::<usize>();
1079 cursor.seek(&position, Bias::Right, &());
1080 cursor
1081 .item()
1082 .or_else(|| snapshot.excerpts.last())
1083 .map(|excerpt| {
1084 (
1085 excerpt.id.clone(),
1086 self.buffers
1087 .borrow()
1088 .get(&excerpt.buffer_id)
1089 .unwrap()
1090 .buffer
1091 .clone(),
1092 excerpt.range.context.clone(),
1093 )
1094 })
1095 }
1096
1097 // If point is at the end of the buffer, the last excerpt is returned
1098 pub fn point_to_buffer_offset<T: ToOffset>(
1099 &self,
1100 point: T,
1101 cx: &AppContext,
1102 ) -> Option<(ModelHandle<Buffer>, usize)> {
1103 let snapshot = self.read(cx);
1104 let offset = point.to_offset(&snapshot);
1105 let mut cursor = snapshot.excerpts.cursor::<usize>();
1106 cursor.seek(&offset, Bias::Right, &());
1107 if cursor.item().is_none() {
1108 cursor.prev(&());
1109 }
1110
1111 cursor.item().map(|excerpt| {
1112 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
1113 let buffer_point = excerpt_start + offset - *cursor.start();
1114 let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
1115
1116 (buffer, buffer_point)
1117 })
1118 }
1119
1120 pub fn range_to_buffer_ranges<T: ToOffset>(
1121 &self,
1122 range: Range<T>,
1123 cx: &AppContext,
1124 ) -> Vec<(ModelHandle<Buffer>, Range<usize>)> {
1125 let snapshot = self.read(cx);
1126 let start = range.start.to_offset(&snapshot);
1127 let end = range.end.to_offset(&snapshot);
1128
1129 let mut result = Vec::new();
1130 let mut cursor = snapshot.excerpts.cursor::<usize>();
1131 cursor.seek(&start, Bias::Right, &());
1132 if cursor.item().is_none() {
1133 cursor.prev(&());
1134 }
1135
1136 while let Some(excerpt) = cursor.item() {
1137 if *cursor.start() > end {
1138 break;
1139 }
1140
1141 let mut end_before_newline = cursor.end(&());
1142 if excerpt.has_trailing_newline {
1143 end_before_newline -= 1;
1144 }
1145 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
1146 let start = excerpt_start + (cmp::max(start, *cursor.start()) - *cursor.start());
1147 let end = excerpt_start + (cmp::min(end, end_before_newline) - *cursor.start());
1148 let buffer = self.buffers.borrow()[&excerpt.buffer_id].buffer.clone();
1149 result.push((buffer, start..end));
1150 cursor.next(&());
1151 }
1152
1153 result
1154 }
1155
1156 pub fn remove_excerpts(
1157 &mut self,
1158 excerpt_ids: impl IntoIterator<Item = ExcerptId>,
1159 cx: &mut ModelContext<Self>,
1160 ) {
1161 self.sync(cx);
1162 let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
1163 if ids.is_empty() {
1164 return;
1165 }
1166
1167 let mut buffers = self.buffers.borrow_mut();
1168 let mut snapshot = self.snapshot.borrow_mut();
1169 let mut new_excerpts = SumTree::new();
1170 let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
1171 let mut edits = Vec::new();
1172 let mut excerpt_ids = ids.iter().copied().peekable();
1173
1174 while let Some(excerpt_id) = excerpt_ids.next() {
1175 // Seek to the next excerpt to remove, preserving any preceding excerpts.
1176 let locator = snapshot.excerpt_locator_for_id(excerpt_id);
1177 new_excerpts.push_tree(cursor.slice(&Some(locator), Bias::Left, &()), &());
1178
1179 if let Some(mut excerpt) = cursor.item() {
1180 if excerpt.id != excerpt_id {
1181 continue;
1182 }
1183 let mut old_start = cursor.start().1;
1184
1185 // Skip over the removed excerpt.
1186 'remove_excerpts: loop {
1187 if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
1188 buffer_state.excerpts.retain(|l| l != &excerpt.locator);
1189 if buffer_state.excerpts.is_empty() {
1190 buffers.remove(&excerpt.buffer_id);
1191 }
1192 }
1193 cursor.next(&());
1194
1195 // Skip over any subsequent excerpts that are also removed.
1196 while let Some(&next_excerpt_id) = excerpt_ids.peek() {
1197 let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id);
1198 if let Some(next_excerpt) = cursor.item() {
1199 if next_excerpt.locator == *next_locator {
1200 excerpt_ids.next();
1201 excerpt = next_excerpt;
1202 continue 'remove_excerpts;
1203 }
1204 }
1205 break;
1206 }
1207
1208 break;
1209 }
1210
1211 // When removing the last excerpt, remove the trailing newline from
1212 // the previous excerpt.
1213 if cursor.item().is_none() && old_start > 0 {
1214 old_start -= 1;
1215 new_excerpts.update_last(|e| e.has_trailing_newline = false, &());
1216 }
1217
1218 // Push an edit for the removal of this run of excerpts.
1219 let old_end = cursor.start().1;
1220 let new_start = new_excerpts.summary().text.len;
1221 edits.push(Edit {
1222 old: old_start..old_end,
1223 new: new_start..new_start,
1224 });
1225 }
1226 }
1227 let suffix = cursor.suffix(&());
1228 let changed_trailing_excerpt = suffix.is_empty();
1229 new_excerpts.push_tree(suffix, &());
1230 drop(cursor);
1231 snapshot.excerpts = new_excerpts;
1232
1233 if changed_trailing_excerpt {
1234 snapshot.trailing_excerpt_update_count += 1;
1235 }
1236
1237 self.subscriptions.publish_mut(edits);
1238 cx.emit(Event::Edited);
1239 cx.emit(Event::ExcerptsRemoved { ids });
1240 cx.notify();
1241 }
1242
1243 pub fn wait_for_anchors<'a>(
1244 &self,
1245 anchors: impl 'a + Iterator<Item = Anchor>,
1246 cx: &mut ModelContext<Self>,
1247 ) -> impl 'static + Future<Output = Result<()>> {
1248 let borrow = self.buffers.borrow();
1249 let mut error = None;
1250 let mut futures = Vec::new();
1251 for anchor in anchors {
1252 if let Some(buffer_id) = anchor.buffer_id {
1253 if let Some(buffer) = borrow.get(&buffer_id) {
1254 buffer.buffer.update(cx, |buffer, _| {
1255 futures.push(buffer.wait_for_anchors([anchor.text_anchor]))
1256 });
1257 } else {
1258 error = Some(anyhow!(
1259 "buffer {buffer_id} is not part of this multi-buffer"
1260 ));
1261 break;
1262 }
1263 }
1264 }
1265 async move {
1266 if let Some(error) = error {
1267 Err(error)?;
1268 }
1269 for future in futures {
1270 future.await?;
1271 }
1272 Ok(())
1273 }
1274 }
1275
1276 pub fn text_anchor_for_position<T: ToOffset>(
1277 &self,
1278 position: T,
1279 cx: &AppContext,
1280 ) -> Option<(ModelHandle<Buffer>, language::Anchor)> {
1281 let snapshot = self.read(cx);
1282 let anchor = snapshot.anchor_before(position);
1283 let buffer = self
1284 .buffers
1285 .borrow()
1286 .get(&anchor.buffer_id?)?
1287 .buffer
1288 .clone();
1289 Some((buffer, anchor.text_anchor))
1290 }
1291
1292 fn on_buffer_event(
1293 &mut self,
1294 _: ModelHandle<Buffer>,
1295 event: &language::Event,
1296 cx: &mut ModelContext<Self>,
1297 ) {
1298 cx.emit(match event {
1299 language::Event::Edited => Event::Edited,
1300 language::Event::DirtyChanged => Event::DirtyChanged,
1301 language::Event::Saved => Event::Saved,
1302 language::Event::FileHandleChanged => Event::FileHandleChanged,
1303 language::Event::Reloaded => Event::Reloaded,
1304 language::Event::LanguageChanged => Event::LanguageChanged,
1305 language::Event::Reparsed => Event::Reparsed,
1306 language::Event::DiagnosticsUpdated => Event::DiagnosticsUpdated,
1307 language::Event::Closed => Event::Closed,
1308
1309 //
1310 language::Event::Operation(_) => return,
1311 });
1312 }
1313
1314 pub fn all_buffers(&self) -> HashSet<ModelHandle<Buffer>> {
1315 self.buffers
1316 .borrow()
1317 .values()
1318 .map(|state| state.buffer.clone())
1319 .collect()
1320 }
1321
1322 pub fn buffer(&self, buffer_id: u64) -> Option<ModelHandle<Buffer>> {
1323 self.buffers
1324 .borrow()
1325 .get(&buffer_id)
1326 .map(|state| state.buffer.clone())
1327 }
1328
1329 pub fn is_completion_trigger<T>(&self, position: T, text: &str, cx: &AppContext) -> bool
1330 where
1331 T: ToOffset,
1332 {
1333 let mut chars = text.chars();
1334 let char = if let Some(char) = chars.next() {
1335 char
1336 } else {
1337 return false;
1338 };
1339 if chars.next().is_some() {
1340 return false;
1341 }
1342
1343 if char.is_alphanumeric() || char == '_' {
1344 return true;
1345 }
1346
1347 let snapshot = self.snapshot(cx);
1348 let anchor = snapshot.anchor_before(position);
1349 anchor
1350 .buffer_id
1351 .and_then(|buffer_id| {
1352 let buffer = self.buffers.borrow().get(&buffer_id)?.buffer.clone();
1353 Some(
1354 buffer
1355 .read(cx)
1356 .completion_triggers()
1357 .iter()
1358 .any(|string| string == text),
1359 )
1360 })
1361 .unwrap_or(false)
1362 }
1363
1364 pub fn language_at<'a, T: ToOffset>(
1365 &self,
1366 point: T,
1367 cx: &'a AppContext,
1368 ) -> Option<Arc<Language>> {
1369 self.point_to_buffer_offset(point, cx)
1370 .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
1371 }
1372
1373 pub fn settings_at<'a, T: ToOffset>(
1374 &self,
1375 point: T,
1376 cx: &'a AppContext,
1377 ) -> &'a LanguageSettings {
1378 let language = self.language_at(point, cx);
1379 language_settings(language.map(|l| l.name()).as_deref(), cx)
1380 }
1381
1382 pub fn for_each_buffer(&self, mut f: impl FnMut(&ModelHandle<Buffer>)) {
1383 self.buffers
1384 .borrow()
1385 .values()
1386 .for_each(|state| f(&state.buffer))
1387 }
1388
1389 pub fn title<'a>(&'a self, cx: &'a AppContext) -> Cow<'a, str> {
1390 if let Some(title) = self.title.as_ref() {
1391 return title.into();
1392 }
1393
1394 if let Some(buffer) = self.as_singleton() {
1395 if let Some(file) = buffer.read(cx).file() {
1396 return file.file_name(cx).to_string_lossy();
1397 }
1398 }
1399
1400 "untitled".into()
1401 }
1402
1403 #[cfg(test)]
1404 pub fn is_parsing(&self, cx: &AppContext) -> bool {
1405 self.as_singleton().unwrap().read(cx).is_parsing()
1406 }
1407
1408 fn sync(&self, cx: &AppContext) {
1409 let mut snapshot = self.snapshot.borrow_mut();
1410 let mut excerpts_to_edit = Vec::new();
1411 let mut reparsed = false;
1412 let mut diagnostics_updated = false;
1413 let mut git_diff_updated = false;
1414 let mut is_dirty = false;
1415 let mut has_conflict = false;
1416 let mut edited = false;
1417 let mut buffers = self.buffers.borrow_mut();
1418 for buffer_state in buffers.values_mut() {
1419 let buffer = buffer_state.buffer.read(cx);
1420 let version = buffer.version();
1421 let parse_count = buffer.parse_count();
1422 let selections_update_count = buffer.selections_update_count();
1423 let diagnostics_update_count = buffer.diagnostics_update_count();
1424 let file_update_count = buffer.file_update_count();
1425 let git_diff_update_count = buffer.git_diff_update_count();
1426
1427 let buffer_edited = version.changed_since(&buffer_state.last_version);
1428 let buffer_reparsed = parse_count > buffer_state.last_parse_count;
1429 let buffer_selections_updated =
1430 selections_update_count > buffer_state.last_selections_update_count;
1431 let buffer_diagnostics_updated =
1432 diagnostics_update_count > buffer_state.last_diagnostics_update_count;
1433 let buffer_file_updated = file_update_count > buffer_state.last_file_update_count;
1434 let buffer_git_diff_updated =
1435 git_diff_update_count > buffer_state.last_git_diff_update_count;
1436 if buffer_edited
1437 || buffer_reparsed
1438 || buffer_selections_updated
1439 || buffer_diagnostics_updated
1440 || buffer_file_updated
1441 || buffer_git_diff_updated
1442 {
1443 buffer_state.last_version = version;
1444 buffer_state.last_parse_count = parse_count;
1445 buffer_state.last_selections_update_count = selections_update_count;
1446 buffer_state.last_diagnostics_update_count = diagnostics_update_count;
1447 buffer_state.last_file_update_count = file_update_count;
1448 buffer_state.last_git_diff_update_count = git_diff_update_count;
1449 excerpts_to_edit.extend(
1450 buffer_state
1451 .excerpts
1452 .iter()
1453 .map(|locator| (locator, buffer_state.buffer.clone(), buffer_edited)),
1454 );
1455 }
1456
1457 edited |= buffer_edited;
1458 reparsed |= buffer_reparsed;
1459 diagnostics_updated |= buffer_diagnostics_updated;
1460 git_diff_updated |= buffer_git_diff_updated;
1461 is_dirty |= buffer.is_dirty();
1462 has_conflict |= buffer.has_conflict();
1463 }
1464 if edited {
1465 snapshot.edit_count += 1;
1466 }
1467 if reparsed {
1468 snapshot.parse_count += 1;
1469 }
1470 if diagnostics_updated {
1471 snapshot.diagnostics_update_count += 1;
1472 }
1473 if git_diff_updated {
1474 snapshot.git_diff_update_count += 1;
1475 }
1476 snapshot.is_dirty = is_dirty;
1477 snapshot.has_conflict = has_conflict;
1478
1479 excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
1480
1481 let mut edits = Vec::new();
1482 let mut new_excerpts = SumTree::new();
1483 let mut cursor = snapshot.excerpts.cursor::<(Option<&Locator>, usize)>();
1484
1485 for (locator, buffer, buffer_edited) in excerpts_to_edit {
1486 new_excerpts.push_tree(cursor.slice(&Some(locator), Bias::Left, &()), &());
1487 let old_excerpt = cursor.item().unwrap();
1488 let buffer = buffer.read(cx);
1489 let buffer_id = buffer.remote_id();
1490
1491 let mut new_excerpt;
1492 if buffer_edited {
1493 edits.extend(
1494 buffer
1495 .edits_since_in_range::<usize>(
1496 old_excerpt.buffer.version(),
1497 old_excerpt.range.context.clone(),
1498 )
1499 .map(|mut edit| {
1500 let excerpt_old_start = cursor.start().1;
1501 let excerpt_new_start = new_excerpts.summary().text.len;
1502 edit.old.start += excerpt_old_start;
1503 edit.old.end += excerpt_old_start;
1504 edit.new.start += excerpt_new_start;
1505 edit.new.end += excerpt_new_start;
1506 edit
1507 }),
1508 );
1509
1510 new_excerpt = Excerpt::new(
1511 old_excerpt.id,
1512 locator.clone(),
1513 buffer_id,
1514 buffer.snapshot(),
1515 old_excerpt.range.clone(),
1516 old_excerpt.has_trailing_newline,
1517 );
1518 } else {
1519 new_excerpt = old_excerpt.clone();
1520 new_excerpt.buffer = buffer.snapshot();
1521 }
1522
1523 new_excerpts.push(new_excerpt, &());
1524 cursor.next(&());
1525 }
1526 new_excerpts.push_tree(cursor.suffix(&()), &());
1527
1528 drop(cursor);
1529 snapshot.excerpts = new_excerpts;
1530
1531 self.subscriptions.publish(edits);
1532 }
1533}
1534
1535#[cfg(any(test, feature = "test-support"))]
1536impl MultiBuffer {
1537 pub fn build_simple(text: &str, cx: &mut gpui::AppContext) -> ModelHandle<Self> {
1538 let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
1539 cx.add_model(|cx| Self::singleton(buffer, cx))
1540 }
1541
1542 pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::AppContext) -> ModelHandle<Self> {
1543 cx.add_model(|cx| {
1544 let mut multibuffer = MultiBuffer::new(0);
1545 let mutation_count = rng.gen_range(1..=5);
1546 multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
1547 multibuffer
1548 })
1549 }
1550
1551 pub fn randomly_edit(
1552 &mut self,
1553 rng: &mut impl rand::Rng,
1554 edit_count: usize,
1555 cx: &mut ModelContext<Self>,
1556 ) {
1557 use util::RandomCharIter;
1558
1559 let snapshot = self.read(cx);
1560 let mut edits: Vec<(Range<usize>, Arc<str>)> = Vec::new();
1561 let mut last_end = None;
1562 for _ in 0..edit_count {
1563 if last_end.map_or(false, |last_end| last_end >= snapshot.len()) {
1564 break;
1565 }
1566
1567 let new_start = last_end.map_or(0, |last_end| last_end + 1);
1568 let end = snapshot.clip_offset(rng.gen_range(new_start..=snapshot.len()), Bias::Right);
1569 let start = snapshot.clip_offset(rng.gen_range(new_start..=end), Bias::Right);
1570 last_end = Some(end);
1571
1572 let mut range = start..end;
1573 if rng.gen_bool(0.2) {
1574 mem::swap(&mut range.start, &mut range.end);
1575 }
1576
1577 let new_text_len = rng.gen_range(0..10);
1578 let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
1579
1580 edits.push((range, new_text.into()));
1581 }
1582 log::info!("mutating multi-buffer with {:?}", edits);
1583 drop(snapshot);
1584
1585 self.edit(edits, None, cx);
1586 }
1587
1588 pub fn randomly_edit_excerpts(
1589 &mut self,
1590 rng: &mut impl rand::Rng,
1591 mutation_count: usize,
1592 cx: &mut ModelContext<Self>,
1593 ) {
1594 use rand::prelude::*;
1595 use std::env;
1596 use util::RandomCharIter;
1597
1598 let max_excerpts = env::var("MAX_EXCERPTS")
1599 .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
1600 .unwrap_or(5);
1601
1602 let mut buffers = Vec::new();
1603 for _ in 0..mutation_count {
1604 if rng.gen_bool(0.05) {
1605 log::info!("Clearing multi-buffer");
1606 self.clear(cx);
1607 continue;
1608 }
1609
1610 let excerpt_ids = self.excerpt_ids();
1611 if excerpt_ids.is_empty() || (rng.gen() && excerpt_ids.len() < max_excerpts) {
1612 let buffer_handle = if rng.gen() || self.buffers.borrow().is_empty() {
1613 let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
1614 buffers.push(cx.add_model(|cx| Buffer::new(0, text, cx)));
1615 let buffer = buffers.last().unwrap().read(cx);
1616 log::info!(
1617 "Creating new buffer {} with text: {:?}",
1618 buffer.remote_id(),
1619 buffer.text()
1620 );
1621 buffers.last().unwrap().clone()
1622 } else {
1623 self.buffers
1624 .borrow()
1625 .values()
1626 .choose(rng)
1627 .unwrap()
1628 .buffer
1629 .clone()
1630 };
1631
1632 let buffer = buffer_handle.read(cx);
1633 let buffer_text = buffer.text();
1634 let ranges = (0..rng.gen_range(0..5))
1635 .map(|_| {
1636 let end_ix =
1637 buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
1638 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
1639 ExcerptRange {
1640 context: start_ix..end_ix,
1641 primary: None,
1642 }
1643 })
1644 .collect::<Vec<_>>();
1645 log::info!(
1646 "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
1647 buffer_handle.read(cx).remote_id(),
1648 ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
1649 ranges
1650 .iter()
1651 .map(|r| &buffer_text[r.context.clone()])
1652 .collect::<Vec<_>>()
1653 );
1654
1655 let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
1656 log::info!("Inserted with ids: {:?}", excerpt_id);
1657 } else {
1658 let remove_count = rng.gen_range(1..=excerpt_ids.len());
1659 let mut excerpts_to_remove = excerpt_ids
1660 .choose_multiple(rng, remove_count)
1661 .cloned()
1662 .collect::<Vec<_>>();
1663 let snapshot = self.snapshot.borrow();
1664 excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &*snapshot));
1665 drop(snapshot);
1666 log::info!("Removing excerpts {:?}", excerpts_to_remove);
1667 self.remove_excerpts(excerpts_to_remove, cx);
1668 }
1669 }
1670 }
1671
1672 pub fn randomly_mutate(
1673 &mut self,
1674 rng: &mut impl rand::Rng,
1675 mutation_count: usize,
1676 cx: &mut ModelContext<Self>,
1677 ) {
1678 use rand::prelude::*;
1679
1680 if rng.gen_bool(0.7) || self.singleton {
1681 let buffer = self
1682 .buffers
1683 .borrow()
1684 .values()
1685 .choose(rng)
1686 .map(|state| state.buffer.clone());
1687
1688 if let Some(buffer) = buffer {
1689 buffer.update(cx, |buffer, cx| {
1690 if rng.gen() {
1691 buffer.randomly_edit(rng, mutation_count, cx);
1692 } else {
1693 buffer.randomly_undo_redo(rng, cx);
1694 }
1695 });
1696 } else {
1697 self.randomly_edit(rng, mutation_count, cx);
1698 }
1699 } else {
1700 self.randomly_edit_excerpts(rng, mutation_count, cx);
1701 }
1702
1703 self.check_invariants(cx);
1704 }
1705
1706 fn check_invariants(&self, cx: &mut ModelContext<Self>) {
1707 let snapshot = self.read(cx);
1708 let excerpts = snapshot.excerpts.items(&());
1709 let excerpt_ids = snapshot.excerpt_ids.items(&());
1710
1711 for (ix, excerpt) in excerpts.iter().enumerate() {
1712 if ix == 0 {
1713 if excerpt.locator <= Locator::min() {
1714 panic!("invalid first excerpt locator {:?}", excerpt.locator);
1715 }
1716 } else {
1717 if excerpt.locator <= excerpts[ix - 1].locator {
1718 panic!("excerpts are out-of-order: {:?}", excerpts);
1719 }
1720 }
1721 }
1722
1723 for (ix, entry) in excerpt_ids.iter().enumerate() {
1724 if ix == 0 {
1725 if entry.id.cmp(&ExcerptId::min(), &*snapshot).is_le() {
1726 panic!("invalid first excerpt id {:?}", entry.id);
1727 }
1728 } else {
1729 if entry.id <= excerpt_ids[ix - 1].id {
1730 panic!("excerpt ids are out-of-order: {:?}", excerpt_ids);
1731 }
1732 }
1733 }
1734 }
1735}
1736
1737impl Entity for MultiBuffer {
1738 type Event = Event;
1739}
1740
1741impl MultiBufferSnapshot {
1742 pub fn text(&self) -> String {
1743 self.chunks(0..self.len(), false)
1744 .map(|chunk| chunk.text)
1745 .collect()
1746 }
1747
1748 pub fn reversed_chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
1749 let mut offset = position.to_offset(self);
1750 let mut cursor = self.excerpts.cursor::<usize>();
1751 cursor.seek(&offset, Bias::Left, &());
1752 let mut excerpt_chunks = cursor.item().map(|excerpt| {
1753 let end_before_footer = cursor.start() + excerpt.text_summary.len;
1754 let start = excerpt.range.context.start.to_offset(&excerpt.buffer);
1755 let end = start + (cmp::min(offset, end_before_footer) - cursor.start());
1756 excerpt.buffer.reversed_chunks_in_range(start..end)
1757 });
1758 iter::from_fn(move || {
1759 if offset == *cursor.start() {
1760 cursor.prev(&());
1761 let excerpt = cursor.item()?;
1762 excerpt_chunks = Some(
1763 excerpt
1764 .buffer
1765 .reversed_chunks_in_range(excerpt.range.context.clone()),
1766 );
1767 }
1768
1769 let excerpt = cursor.item().unwrap();
1770 if offset == cursor.end(&()) && excerpt.has_trailing_newline {
1771 offset -= 1;
1772 Some("\n")
1773 } else {
1774 let chunk = excerpt_chunks.as_mut().unwrap().next().unwrap();
1775 offset -= chunk.len();
1776 Some(chunk)
1777 }
1778 })
1779 .flat_map(|c| c.chars().rev())
1780 }
1781
1782 pub fn chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
1783 let offset = position.to_offset(self);
1784 self.text_for_range(offset..self.len())
1785 .flat_map(|chunk| chunk.chars())
1786 }
1787
1788 pub fn text_for_range<T: ToOffset>(&self, range: Range<T>) -> impl Iterator<Item = &str> + '_ {
1789 self.chunks(range, false).map(|chunk| chunk.text)
1790 }
1791
1792 pub fn is_line_blank(&self, row: u32) -> bool {
1793 self.text_for_range(Point::new(row, 0)..Point::new(row, self.line_len(row)))
1794 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
1795 }
1796
1797 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
1798 where
1799 T: ToOffset,
1800 {
1801 let position = position.to_offset(self);
1802 position == self.clip_offset(position, Bias::Left)
1803 && self
1804 .bytes_in_range(position..self.len())
1805 .flatten()
1806 .copied()
1807 .take(needle.len())
1808 .eq(needle.bytes())
1809 }
1810
1811 pub fn surrounding_word<T: ToOffset>(&self, start: T) -> (Range<usize>, Option<CharKind>) {
1812 let mut start = start.to_offset(self);
1813 let mut end = start;
1814 let mut next_chars = self.chars_at(start).peekable();
1815 let mut prev_chars = self.reversed_chars_at(start).peekable();
1816 let word_kind = cmp::max(
1817 prev_chars.peek().copied().map(char_kind),
1818 next_chars.peek().copied().map(char_kind),
1819 );
1820
1821 for ch in prev_chars {
1822 if Some(char_kind(ch)) == word_kind && ch != '\n' {
1823 start -= ch.len_utf8();
1824 } else {
1825 break;
1826 }
1827 }
1828
1829 for ch in next_chars {
1830 if Some(char_kind(ch)) == word_kind && ch != '\n' {
1831 end += ch.len_utf8();
1832 } else {
1833 break;
1834 }
1835 }
1836
1837 (start..end, word_kind)
1838 }
1839
1840 pub fn as_singleton(&self) -> Option<(&ExcerptId, u64, &BufferSnapshot)> {
1841 if self.singleton {
1842 self.excerpts
1843 .iter()
1844 .next()
1845 .map(|e| (&e.id, e.buffer_id, &e.buffer))
1846 } else {
1847 None
1848 }
1849 }
1850
1851 pub fn len(&self) -> usize {
1852 self.excerpts.summary().text.len
1853 }
1854
1855 pub fn is_empty(&self) -> bool {
1856 self.excerpts.summary().text.len == 0
1857 }
1858
1859 pub fn max_buffer_row(&self) -> u32 {
1860 self.excerpts.summary().max_buffer_row
1861 }
1862
1863 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
1864 if let Some((_, _, buffer)) = self.as_singleton() {
1865 return buffer.clip_offset(offset, bias);
1866 }
1867
1868 let mut cursor = self.excerpts.cursor::<usize>();
1869 cursor.seek(&offset, Bias::Right, &());
1870 let overshoot = if let Some(excerpt) = cursor.item() {
1871 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
1872 let buffer_offset = excerpt
1873 .buffer
1874 .clip_offset(excerpt_start + (offset - cursor.start()), bias);
1875 buffer_offset.saturating_sub(excerpt_start)
1876 } else {
1877 0
1878 };
1879 cursor.start() + overshoot
1880 }
1881
1882 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
1883 if let Some((_, _, buffer)) = self.as_singleton() {
1884 return buffer.clip_point(point, bias);
1885 }
1886
1887 let mut cursor = self.excerpts.cursor::<Point>();
1888 cursor.seek(&point, Bias::Right, &());
1889 let overshoot = if let Some(excerpt) = cursor.item() {
1890 let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
1891 let buffer_point = excerpt
1892 .buffer
1893 .clip_point(excerpt_start + (point - cursor.start()), bias);
1894 buffer_point.saturating_sub(excerpt_start)
1895 } else {
1896 Point::zero()
1897 };
1898 *cursor.start() + overshoot
1899 }
1900
1901 pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 {
1902 if let Some((_, _, buffer)) = self.as_singleton() {
1903 return buffer.clip_offset_utf16(offset, bias);
1904 }
1905
1906 let mut cursor = self.excerpts.cursor::<OffsetUtf16>();
1907 cursor.seek(&offset, Bias::Right, &());
1908 let overshoot = if let Some(excerpt) = cursor.item() {
1909 let excerpt_start = excerpt.range.context.start.to_offset_utf16(&excerpt.buffer);
1910 let buffer_offset = excerpt
1911 .buffer
1912 .clip_offset_utf16(excerpt_start + (offset - cursor.start()), bias);
1913 OffsetUtf16(buffer_offset.0.saturating_sub(excerpt_start.0))
1914 } else {
1915 OffsetUtf16(0)
1916 };
1917 *cursor.start() + overshoot
1918 }
1919
1920 pub fn clip_point_utf16(&self, point: Unclipped<PointUtf16>, bias: Bias) -> PointUtf16 {
1921 if let Some((_, _, buffer)) = self.as_singleton() {
1922 return buffer.clip_point_utf16(point, bias);
1923 }
1924
1925 let mut cursor = self.excerpts.cursor::<PointUtf16>();
1926 cursor.seek(&point.0, Bias::Right, &());
1927 let overshoot = if let Some(excerpt) = cursor.item() {
1928 let excerpt_start = excerpt
1929 .buffer
1930 .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer));
1931 let buffer_point = excerpt
1932 .buffer
1933 .clip_point_utf16(Unclipped(excerpt_start + (point.0 - cursor.start())), bias);
1934 buffer_point.saturating_sub(excerpt_start)
1935 } else {
1936 PointUtf16::zero()
1937 };
1938 *cursor.start() + overshoot
1939 }
1940
1941 pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes {
1942 let range = range.start.to_offset(self)..range.end.to_offset(self);
1943 let mut excerpts = self.excerpts.cursor::<usize>();
1944 excerpts.seek(&range.start, Bias::Right, &());
1945
1946 let mut chunk = &[][..];
1947 let excerpt_bytes = if let Some(excerpt) = excerpts.item() {
1948 let mut excerpt_bytes = excerpt
1949 .bytes_in_range(range.start - excerpts.start()..range.end - excerpts.start());
1950 chunk = excerpt_bytes.next().unwrap_or(&[][..]);
1951 Some(excerpt_bytes)
1952 } else {
1953 None
1954 };
1955
1956 MultiBufferBytes {
1957 range,
1958 excerpts,
1959 excerpt_bytes,
1960 chunk,
1961 }
1962 }
1963
1964 pub fn buffer_rows(&self, start_row: u32) -> MultiBufferRows {
1965 let mut result = MultiBufferRows {
1966 buffer_row_range: 0..0,
1967 excerpts: self.excerpts.cursor(),
1968 };
1969 result.seek(start_row);
1970 result
1971 }
1972
1973 pub fn chunks<T: ToOffset>(&self, range: Range<T>, language_aware: bool) -> MultiBufferChunks {
1974 let range = range.start.to_offset(self)..range.end.to_offset(self);
1975 let mut chunks = MultiBufferChunks {
1976 range: range.clone(),
1977 excerpts: self.excerpts.cursor(),
1978 excerpt_chunks: None,
1979 language_aware,
1980 };
1981 chunks.seek(range.start);
1982 chunks
1983 }
1984
1985 pub fn offset_to_point(&self, offset: usize) -> Point {
1986 if let Some((_, _, buffer)) = self.as_singleton() {
1987 return buffer.offset_to_point(offset);
1988 }
1989
1990 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
1991 cursor.seek(&offset, Bias::Right, &());
1992 if let Some(excerpt) = cursor.item() {
1993 let (start_offset, start_point) = cursor.start();
1994 let overshoot = offset - start_offset;
1995 let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
1996 let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
1997 let buffer_point = excerpt
1998 .buffer
1999 .offset_to_point(excerpt_start_offset + overshoot);
2000 *start_point + (buffer_point - excerpt_start_point)
2001 } else {
2002 self.excerpts.summary().text.lines
2003 }
2004 }
2005
2006 pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 {
2007 if let Some((_, _, buffer)) = self.as_singleton() {
2008 return buffer.offset_to_point_utf16(offset);
2009 }
2010
2011 let mut cursor = self.excerpts.cursor::<(usize, PointUtf16)>();
2012 cursor.seek(&offset, Bias::Right, &());
2013 if let Some(excerpt) = cursor.item() {
2014 let (start_offset, start_point) = cursor.start();
2015 let overshoot = offset - start_offset;
2016 let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
2017 let excerpt_start_point = excerpt.range.context.start.to_point_utf16(&excerpt.buffer);
2018 let buffer_point = excerpt
2019 .buffer
2020 .offset_to_point_utf16(excerpt_start_offset + overshoot);
2021 *start_point + (buffer_point - excerpt_start_point)
2022 } else {
2023 self.excerpts.summary().text.lines_utf16()
2024 }
2025 }
2026
2027 pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
2028 if let Some((_, _, buffer)) = self.as_singleton() {
2029 return buffer.point_to_point_utf16(point);
2030 }
2031
2032 let mut cursor = self.excerpts.cursor::<(Point, PointUtf16)>();
2033 cursor.seek(&point, Bias::Right, &());
2034 if let Some(excerpt) = cursor.item() {
2035 let (start_offset, start_point) = cursor.start();
2036 let overshoot = point - start_offset;
2037 let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
2038 let excerpt_start_point_utf16 =
2039 excerpt.range.context.start.to_point_utf16(&excerpt.buffer);
2040 let buffer_point = excerpt
2041 .buffer
2042 .point_to_point_utf16(excerpt_start_point + overshoot);
2043 *start_point + (buffer_point - excerpt_start_point_utf16)
2044 } else {
2045 self.excerpts.summary().text.lines_utf16()
2046 }
2047 }
2048
2049 pub fn point_to_offset(&self, point: Point) -> usize {
2050 if let Some((_, _, buffer)) = self.as_singleton() {
2051 return buffer.point_to_offset(point);
2052 }
2053
2054 let mut cursor = self.excerpts.cursor::<(Point, usize)>();
2055 cursor.seek(&point, Bias::Right, &());
2056 if let Some(excerpt) = cursor.item() {
2057 let (start_point, start_offset) = cursor.start();
2058 let overshoot = point - start_point;
2059 let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
2060 let excerpt_start_point = excerpt.range.context.start.to_point(&excerpt.buffer);
2061 let buffer_offset = excerpt
2062 .buffer
2063 .point_to_offset(excerpt_start_point + overshoot);
2064 *start_offset + buffer_offset - excerpt_start_offset
2065 } else {
2066 self.excerpts.summary().text.len
2067 }
2068 }
2069
2070 pub fn offset_utf16_to_offset(&self, offset_utf16: OffsetUtf16) -> usize {
2071 if let Some((_, _, buffer)) = self.as_singleton() {
2072 return buffer.offset_utf16_to_offset(offset_utf16);
2073 }
2074
2075 let mut cursor = self.excerpts.cursor::<(OffsetUtf16, usize)>();
2076 cursor.seek(&offset_utf16, Bias::Right, &());
2077 if let Some(excerpt) = cursor.item() {
2078 let (start_offset_utf16, start_offset) = cursor.start();
2079 let overshoot = offset_utf16 - start_offset_utf16;
2080 let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
2081 let excerpt_start_offset_utf16 =
2082 excerpt.buffer.offset_to_offset_utf16(excerpt_start_offset);
2083 let buffer_offset = excerpt
2084 .buffer
2085 .offset_utf16_to_offset(excerpt_start_offset_utf16 + overshoot);
2086 *start_offset + (buffer_offset - excerpt_start_offset)
2087 } else {
2088 self.excerpts.summary().text.len
2089 }
2090 }
2091
2092 pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 {
2093 if let Some((_, _, buffer)) = self.as_singleton() {
2094 return buffer.offset_to_offset_utf16(offset);
2095 }
2096
2097 let mut cursor = self.excerpts.cursor::<(usize, OffsetUtf16)>();
2098 cursor.seek(&offset, Bias::Right, &());
2099 if let Some(excerpt) = cursor.item() {
2100 let (start_offset, start_offset_utf16) = cursor.start();
2101 let overshoot = offset - start_offset;
2102 let excerpt_start_offset_utf16 =
2103 excerpt.range.context.start.to_offset_utf16(&excerpt.buffer);
2104 let excerpt_start_offset = excerpt
2105 .buffer
2106 .offset_utf16_to_offset(excerpt_start_offset_utf16);
2107 let buffer_offset_utf16 = excerpt
2108 .buffer
2109 .offset_to_offset_utf16(excerpt_start_offset + overshoot);
2110 *start_offset_utf16 + (buffer_offset_utf16 - excerpt_start_offset_utf16)
2111 } else {
2112 self.excerpts.summary().text.len_utf16
2113 }
2114 }
2115
2116 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
2117 if let Some((_, _, buffer)) = self.as_singleton() {
2118 return buffer.point_utf16_to_offset(point);
2119 }
2120
2121 let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>();
2122 cursor.seek(&point, Bias::Right, &());
2123 if let Some(excerpt) = cursor.item() {
2124 let (start_point, start_offset) = cursor.start();
2125 let overshoot = point - start_point;
2126 let excerpt_start_offset = excerpt.range.context.start.to_offset(&excerpt.buffer);
2127 let excerpt_start_point = excerpt
2128 .buffer
2129 .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer));
2130 let buffer_offset = excerpt
2131 .buffer
2132 .point_utf16_to_offset(excerpt_start_point + overshoot);
2133 *start_offset + (buffer_offset - excerpt_start_offset)
2134 } else {
2135 self.excerpts.summary().text.len
2136 }
2137 }
2138
2139 pub fn point_to_buffer_offset<T: ToOffset>(
2140 &self,
2141 point: T,
2142 ) -> Option<(&BufferSnapshot, usize)> {
2143 let offset = point.to_offset(&self);
2144 let mut cursor = self.excerpts.cursor::<usize>();
2145 cursor.seek(&offset, Bias::Right, &());
2146 if cursor.item().is_none() {
2147 cursor.prev(&());
2148 }
2149
2150 cursor.item().map(|excerpt| {
2151 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2152 let buffer_point = excerpt_start + offset - *cursor.start();
2153 (&excerpt.buffer, buffer_point)
2154 })
2155 }
2156
2157 pub fn suggested_indents(
2158 &self,
2159 rows: impl IntoIterator<Item = u32>,
2160 cx: &AppContext,
2161 ) -> BTreeMap<u32, IndentSize> {
2162 let mut result = BTreeMap::new();
2163
2164 let mut rows_for_excerpt = Vec::new();
2165 let mut cursor = self.excerpts.cursor::<Point>();
2166 let mut rows = rows.into_iter().peekable();
2167 let mut prev_row = u32::MAX;
2168 let mut prev_language_indent_size = IndentSize::default();
2169
2170 while let Some(row) = rows.next() {
2171 cursor.seek(&Point::new(row, 0), Bias::Right, &());
2172 let excerpt = match cursor.item() {
2173 Some(excerpt) => excerpt,
2174 _ => continue,
2175 };
2176
2177 // Retrieve the language and indent size once for each disjoint region being indented.
2178 let single_indent_size = if row.saturating_sub(1) == prev_row {
2179 prev_language_indent_size
2180 } else {
2181 excerpt
2182 .buffer
2183 .language_indent_size_at(Point::new(row, 0), cx)
2184 };
2185 prev_language_indent_size = single_indent_size;
2186 prev_row = row;
2187
2188 let start_buffer_row = excerpt.range.context.start.to_point(&excerpt.buffer).row;
2189 let start_multibuffer_row = cursor.start().row;
2190
2191 rows_for_excerpt.push(row);
2192 while let Some(next_row) = rows.peek().copied() {
2193 if cursor.end(&()).row > next_row {
2194 rows_for_excerpt.push(next_row);
2195 rows.next();
2196 } else {
2197 break;
2198 }
2199 }
2200
2201 let buffer_rows = rows_for_excerpt
2202 .drain(..)
2203 .map(|row| start_buffer_row + row - start_multibuffer_row);
2204 let buffer_indents = excerpt
2205 .buffer
2206 .suggested_indents(buffer_rows, single_indent_size);
2207 let multibuffer_indents = buffer_indents
2208 .into_iter()
2209 .map(|(row, indent)| (start_multibuffer_row + row - start_buffer_row, indent));
2210 result.extend(multibuffer_indents);
2211 }
2212
2213 result
2214 }
2215
2216 pub fn indent_size_for_line(&self, row: u32) -> IndentSize {
2217 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
2218 let mut size = buffer.indent_size_for_line(range.start.row);
2219 size.len = size
2220 .len
2221 .min(range.end.column)
2222 .saturating_sub(range.start.column);
2223 size
2224 } else {
2225 IndentSize::spaces(0)
2226 }
2227 }
2228
2229 pub fn line_len(&self, row: u32) -> u32 {
2230 if let Some((_, range)) = self.buffer_line_for_row(row) {
2231 range.end.column - range.start.column
2232 } else {
2233 0
2234 }
2235 }
2236
2237 pub fn buffer_line_for_row(&self, row: u32) -> Option<(&BufferSnapshot, Range<Point>)> {
2238 let mut cursor = self.excerpts.cursor::<Point>();
2239 let point = Point::new(row, 0);
2240 cursor.seek(&point, Bias::Right, &());
2241 if cursor.item().is_none() && *cursor.start() == point {
2242 cursor.prev(&());
2243 }
2244 if let Some(excerpt) = cursor.item() {
2245 let overshoot = row - cursor.start().row;
2246 let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer);
2247 let excerpt_end = excerpt.range.context.end.to_point(&excerpt.buffer);
2248 let buffer_row = excerpt_start.row + overshoot;
2249 let line_start = Point::new(buffer_row, 0);
2250 let line_end = Point::new(buffer_row, excerpt.buffer.line_len(buffer_row));
2251 return Some((
2252 &excerpt.buffer,
2253 line_start.max(excerpt_start)..line_end.min(excerpt_end),
2254 ));
2255 }
2256 None
2257 }
2258
2259 pub fn max_point(&self) -> Point {
2260 self.text_summary().lines
2261 }
2262
2263 pub fn text_summary(&self) -> TextSummary {
2264 self.excerpts.summary().text.clone()
2265 }
2266
2267 pub fn text_summary_for_range<D, O>(&self, range: Range<O>) -> D
2268 where
2269 D: TextDimension,
2270 O: ToOffset,
2271 {
2272 let mut summary = D::default();
2273 let mut range = range.start.to_offset(self)..range.end.to_offset(self);
2274 let mut cursor = self.excerpts.cursor::<usize>();
2275 cursor.seek(&range.start, Bias::Right, &());
2276 if let Some(excerpt) = cursor.item() {
2277 let mut end_before_newline = cursor.end(&());
2278 if excerpt.has_trailing_newline {
2279 end_before_newline -= 1;
2280 }
2281
2282 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2283 let start_in_excerpt = excerpt_start + (range.start - cursor.start());
2284 let end_in_excerpt =
2285 excerpt_start + (cmp::min(end_before_newline, range.end) - cursor.start());
2286 summary.add_assign(
2287 &excerpt
2288 .buffer
2289 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
2290 );
2291
2292 if range.end > end_before_newline {
2293 summary.add_assign(&D::from_text_summary(&TextSummary::from("\n")));
2294 }
2295
2296 cursor.next(&());
2297 }
2298
2299 if range.end > *cursor.start() {
2300 summary.add_assign(&D::from_text_summary(&cursor.summary::<_, TextSummary>(
2301 &range.end,
2302 Bias::Right,
2303 &(),
2304 )));
2305 if let Some(excerpt) = cursor.item() {
2306 range.end = cmp::max(*cursor.start(), range.end);
2307
2308 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2309 let end_in_excerpt = excerpt_start + (range.end - cursor.start());
2310 summary.add_assign(
2311 &excerpt
2312 .buffer
2313 .text_summary_for_range(excerpt_start..end_in_excerpt),
2314 );
2315 }
2316 }
2317
2318 summary
2319 }
2320
2321 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
2322 where
2323 D: TextDimension + Ord + Sub<D, Output = D>,
2324 {
2325 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
2326 let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
2327
2328 cursor.seek(locator, Bias::Left, &());
2329 if cursor.item().is_none() {
2330 cursor.next(&());
2331 }
2332
2333 let mut position = D::from_text_summary(&cursor.start().text);
2334 if let Some(excerpt) = cursor.item() {
2335 if excerpt.id == anchor.excerpt_id {
2336 let excerpt_buffer_start =
2337 excerpt.range.context.start.summary::<D>(&excerpt.buffer);
2338 let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
2339 let buffer_position = cmp::min(
2340 excerpt_buffer_end,
2341 anchor.text_anchor.summary::<D>(&excerpt.buffer),
2342 );
2343 if buffer_position > excerpt_buffer_start {
2344 position.add_assign(&(buffer_position - excerpt_buffer_start));
2345 }
2346 }
2347 }
2348 position
2349 }
2350
2351 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
2352 where
2353 D: TextDimension + Ord + Sub<D, Output = D>,
2354 I: 'a + IntoIterator<Item = &'a Anchor>,
2355 {
2356 if let Some((_, _, buffer)) = self.as_singleton() {
2357 return buffer
2358 .summaries_for_anchors(anchors.into_iter().map(|a| &a.text_anchor))
2359 .collect();
2360 }
2361
2362 let mut anchors = anchors.into_iter().peekable();
2363 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
2364 let mut summaries = Vec::new();
2365 while let Some(anchor) = anchors.peek() {
2366 let excerpt_id = anchor.excerpt_id;
2367 let excerpt_anchors = iter::from_fn(|| {
2368 let anchor = anchors.peek()?;
2369 if anchor.excerpt_id == excerpt_id {
2370 Some(&anchors.next().unwrap().text_anchor)
2371 } else {
2372 None
2373 }
2374 });
2375
2376 let locator = self.excerpt_locator_for_id(excerpt_id);
2377 cursor.seek_forward(locator, Bias::Left, &());
2378 if cursor.item().is_none() {
2379 cursor.next(&());
2380 }
2381
2382 let position = D::from_text_summary(&cursor.start().text);
2383 if let Some(excerpt) = cursor.item() {
2384 if excerpt.id == excerpt_id {
2385 let excerpt_buffer_start =
2386 excerpt.range.context.start.summary::<D>(&excerpt.buffer);
2387 let excerpt_buffer_end =
2388 excerpt.range.context.end.summary::<D>(&excerpt.buffer);
2389 summaries.extend(
2390 excerpt
2391 .buffer
2392 .summaries_for_anchors::<D, _>(excerpt_anchors)
2393 .map(move |summary| {
2394 let summary = cmp::min(excerpt_buffer_end.clone(), summary);
2395 let mut position = position.clone();
2396 let excerpt_buffer_start = excerpt_buffer_start.clone();
2397 if summary > excerpt_buffer_start {
2398 position.add_assign(&(summary - excerpt_buffer_start));
2399 }
2400 position
2401 }),
2402 );
2403 continue;
2404 }
2405 }
2406
2407 summaries.extend(excerpt_anchors.map(|_| position.clone()));
2408 }
2409
2410 summaries
2411 }
2412
2413 pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
2414 where
2415 I: 'a + IntoIterator<Item = &'a Anchor>,
2416 {
2417 let mut anchors = anchors.into_iter().enumerate().peekable();
2418 let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
2419 cursor.next(&());
2420
2421 let mut result = Vec::new();
2422
2423 while let Some((_, anchor)) = anchors.peek() {
2424 let old_excerpt_id = anchor.excerpt_id;
2425
2426 // Find the location where this anchor's excerpt should be.
2427 let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
2428 cursor.seek_forward(&Some(old_locator), Bias::Left, &());
2429
2430 if cursor.item().is_none() {
2431 cursor.next(&());
2432 }
2433
2434 let next_excerpt = cursor.item();
2435 let prev_excerpt = cursor.prev_item();
2436
2437 // Process all of the anchors for this excerpt.
2438 while let Some((_, anchor)) = anchors.peek() {
2439 if anchor.excerpt_id != old_excerpt_id {
2440 break;
2441 }
2442 let (anchor_ix, anchor) = anchors.next().unwrap();
2443 let mut anchor = *anchor;
2444
2445 // Leave min and max anchors unchanged if invalid or
2446 // if the old excerpt still exists at this location
2447 let mut kept_position = next_excerpt
2448 .map_or(false, |e| e.id == old_excerpt_id && e.contains(&anchor))
2449 || old_excerpt_id == ExcerptId::max()
2450 || old_excerpt_id == ExcerptId::min();
2451
2452 // If the old excerpt no longer exists at this location, then attempt to
2453 // find an equivalent position for this anchor in an adjacent excerpt.
2454 if !kept_position {
2455 for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
2456 if excerpt.contains(&anchor) {
2457 anchor.excerpt_id = excerpt.id.clone();
2458 kept_position = true;
2459 break;
2460 }
2461 }
2462 }
2463
2464 // If there's no adjacent excerpt that contains the anchor's position,
2465 // then report that the anchor has lost its position.
2466 if !kept_position {
2467 anchor = if let Some(excerpt) = next_excerpt {
2468 let mut text_anchor = excerpt
2469 .range
2470 .context
2471 .start
2472 .bias(anchor.text_anchor.bias, &excerpt.buffer);
2473 if text_anchor
2474 .cmp(&excerpt.range.context.end, &excerpt.buffer)
2475 .is_gt()
2476 {
2477 text_anchor = excerpt.range.context.end;
2478 }
2479 Anchor {
2480 buffer_id: Some(excerpt.buffer_id),
2481 excerpt_id: excerpt.id.clone(),
2482 text_anchor,
2483 }
2484 } else if let Some(excerpt) = prev_excerpt {
2485 let mut text_anchor = excerpt
2486 .range
2487 .context
2488 .end
2489 .bias(anchor.text_anchor.bias, &excerpt.buffer);
2490 if text_anchor
2491 .cmp(&excerpt.range.context.start, &excerpt.buffer)
2492 .is_lt()
2493 {
2494 text_anchor = excerpt.range.context.start;
2495 }
2496 Anchor {
2497 buffer_id: Some(excerpt.buffer_id),
2498 excerpt_id: excerpt.id.clone(),
2499 text_anchor,
2500 }
2501 } else if anchor.text_anchor.bias == Bias::Left {
2502 Anchor::min()
2503 } else {
2504 Anchor::max()
2505 };
2506 }
2507
2508 result.push((anchor_ix, anchor, kept_position));
2509 }
2510 }
2511 result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
2512 result
2513 }
2514
2515 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
2516 self.anchor_at(position, Bias::Left)
2517 }
2518
2519 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
2520 self.anchor_at(position, Bias::Right)
2521 }
2522
2523 pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
2524 let offset = position.to_offset(self);
2525 if let Some((excerpt_id, buffer_id, buffer)) = self.as_singleton() {
2526 return Anchor {
2527 buffer_id: Some(buffer_id),
2528 excerpt_id: excerpt_id.clone(),
2529 text_anchor: buffer.anchor_at(offset, bias),
2530 };
2531 }
2532
2533 let mut cursor = self.excerpts.cursor::<(usize, Option<ExcerptId>)>();
2534 cursor.seek(&offset, Bias::Right, &());
2535 if cursor.item().is_none() && offset == cursor.start().0 && bias == Bias::Left {
2536 cursor.prev(&());
2537 }
2538 if let Some(excerpt) = cursor.item() {
2539 let mut overshoot = offset.saturating_sub(cursor.start().0);
2540 if excerpt.has_trailing_newline && offset == cursor.end(&()).0 {
2541 overshoot -= 1;
2542 bias = Bias::Right;
2543 }
2544
2545 let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2546 let text_anchor =
2547 excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
2548 Anchor {
2549 buffer_id: Some(excerpt.buffer_id),
2550 excerpt_id: excerpt.id.clone(),
2551 text_anchor,
2552 }
2553 } else if offset == 0 && bias == Bias::Left {
2554 Anchor::min()
2555 } else {
2556 Anchor::max()
2557 }
2558 }
2559
2560 pub fn anchor_in_excerpt(&self, excerpt_id: ExcerptId, text_anchor: text::Anchor) -> Anchor {
2561 let locator = self.excerpt_locator_for_id(excerpt_id);
2562 let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
2563 cursor.seek(locator, Bias::Left, &());
2564 if let Some(excerpt) = cursor.item() {
2565 if excerpt.id == excerpt_id {
2566 let text_anchor = excerpt.clip_anchor(text_anchor);
2567 drop(cursor);
2568 return Anchor {
2569 buffer_id: Some(excerpt.buffer_id),
2570 excerpt_id,
2571 text_anchor,
2572 };
2573 }
2574 }
2575 panic!("excerpt not found");
2576 }
2577
2578 pub fn can_resolve(&self, anchor: &Anchor) -> bool {
2579 if anchor.excerpt_id == ExcerptId::min() || anchor.excerpt_id == ExcerptId::max() {
2580 true
2581 } else if let Some(excerpt) = self.excerpt(anchor.excerpt_id) {
2582 excerpt.buffer.can_resolve(&anchor.text_anchor)
2583 } else {
2584 false
2585 }
2586 }
2587
2588 pub fn excerpts(
2589 &self,
2590 ) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
2591 self.excerpts
2592 .iter()
2593 .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
2594 }
2595
2596 pub fn excerpt_boundaries_in_range<R, T>(
2597 &self,
2598 range: R,
2599 ) -> impl Iterator<Item = ExcerptBoundary> + '_
2600 where
2601 R: RangeBounds<T>,
2602 T: ToOffset,
2603 {
2604 let start_offset;
2605 let start = match range.start_bound() {
2606 Bound::Included(start) => {
2607 start_offset = start.to_offset(self);
2608 Bound::Included(start_offset)
2609 }
2610 Bound::Excluded(start) => {
2611 start_offset = start.to_offset(self);
2612 Bound::Excluded(start_offset)
2613 }
2614 Bound::Unbounded => {
2615 start_offset = 0;
2616 Bound::Unbounded
2617 }
2618 };
2619 let end = match range.end_bound() {
2620 Bound::Included(end) => Bound::Included(end.to_offset(self)),
2621 Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
2622 Bound::Unbounded => Bound::Unbounded,
2623 };
2624 let bounds = (start, end);
2625
2626 let mut cursor = self.excerpts.cursor::<(usize, Point)>();
2627 cursor.seek(&start_offset, Bias::Right, &());
2628 if cursor.item().is_none() {
2629 cursor.prev(&());
2630 }
2631 if !bounds.contains(&cursor.start().0) {
2632 cursor.next(&());
2633 }
2634
2635 let mut prev_buffer_id = cursor.prev_item().map(|excerpt| excerpt.buffer_id);
2636 std::iter::from_fn(move || {
2637 if self.singleton {
2638 None
2639 } else if bounds.contains(&cursor.start().0) {
2640 let excerpt = cursor.item()?;
2641 let starts_new_buffer = Some(excerpt.buffer_id) != prev_buffer_id;
2642 let boundary = ExcerptBoundary {
2643 id: excerpt.id.clone(),
2644 row: cursor.start().1.row,
2645 buffer: excerpt.buffer.clone(),
2646 range: excerpt.range.clone(),
2647 starts_new_buffer,
2648 };
2649
2650 prev_buffer_id = Some(excerpt.buffer_id);
2651 cursor.next(&());
2652 Some(boundary)
2653 } else {
2654 None
2655 }
2656 })
2657 }
2658
2659 pub fn edit_count(&self) -> usize {
2660 self.edit_count
2661 }
2662
2663 pub fn parse_count(&self) -> usize {
2664 self.parse_count
2665 }
2666
2667 /// Returns the smallest enclosing bracket ranges containing the given range or
2668 /// None if no brackets contain range or the range is not contained in a single
2669 /// excerpt
2670 pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
2671 &self,
2672 range: Range<T>,
2673 ) -> Option<(Range<usize>, Range<usize>)> {
2674 let range = range.start.to_offset(self)..range.end.to_offset(self);
2675
2676 // Get the ranges of the innermost pair of brackets.
2677 let mut result: Option<(Range<usize>, Range<usize>)> = None;
2678
2679 let Some(enclosing_bracket_ranges) = self.enclosing_bracket_ranges(range.clone()) else { return None; };
2680
2681 for (open, close) in enclosing_bracket_ranges {
2682 let len = close.end - open.start;
2683
2684 if let Some((existing_open, existing_close)) = &result {
2685 let existing_len = existing_close.end - existing_open.start;
2686 if len > existing_len {
2687 continue;
2688 }
2689 }
2690
2691 result = Some((open, close));
2692 }
2693
2694 result
2695 }
2696
2697 /// Returns enclosing bracket ranges containing the given range or returns None if the range is
2698 /// not contained in a single excerpt
2699 pub fn enclosing_bracket_ranges<'a, T: ToOffset>(
2700 &'a self,
2701 range: Range<T>,
2702 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + 'a> {
2703 let range = range.start.to_offset(self)..range.end.to_offset(self);
2704
2705 self.bracket_ranges(range.clone()).map(|range_pairs| {
2706 range_pairs
2707 .filter(move |(open, close)| open.start <= range.start && close.end >= range.end)
2708 })
2709 }
2710
2711 /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is
2712 /// not contained in a single excerpt
2713 pub fn bracket_ranges<'a, T: ToOffset>(
2714 &'a self,
2715 range: Range<T>,
2716 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + 'a> {
2717 let range = range.start.to_offset(self)..range.end.to_offset(self);
2718 let excerpt = self.excerpt_containing(range.clone());
2719 excerpt.map(|(excerpt, excerpt_offset)| {
2720 let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2721 let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
2722
2723 let start_in_buffer = excerpt_buffer_start + range.start.saturating_sub(excerpt_offset);
2724 let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset);
2725
2726 excerpt
2727 .buffer
2728 .bracket_ranges(start_in_buffer..end_in_buffer)
2729 .filter_map(move |(start_bracket_range, end_bracket_range)| {
2730 if start_bracket_range.start < excerpt_buffer_start
2731 || end_bracket_range.end > excerpt_buffer_end
2732 {
2733 return None;
2734 }
2735
2736 let mut start_bracket_range = start_bracket_range.clone();
2737 start_bracket_range.start =
2738 excerpt_offset + (start_bracket_range.start - excerpt_buffer_start);
2739 start_bracket_range.end =
2740 excerpt_offset + (start_bracket_range.end - excerpt_buffer_start);
2741
2742 let mut end_bracket_range = end_bracket_range.clone();
2743 end_bracket_range.start =
2744 excerpt_offset + (end_bracket_range.start - excerpt_buffer_start);
2745 end_bracket_range.end =
2746 excerpt_offset + (end_bracket_range.end - excerpt_buffer_start);
2747 Some((start_bracket_range, end_bracket_range))
2748 })
2749 })
2750 }
2751
2752 pub fn diagnostics_update_count(&self) -> usize {
2753 self.diagnostics_update_count
2754 }
2755
2756 pub fn git_diff_update_count(&self) -> usize {
2757 self.git_diff_update_count
2758 }
2759
2760 pub fn trailing_excerpt_update_count(&self) -> usize {
2761 self.trailing_excerpt_update_count
2762 }
2763
2764 pub fn file_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<dyn File>> {
2765 self.point_to_buffer_offset(point)
2766 .and_then(|(buffer, _)| buffer.file())
2767 }
2768
2769 pub fn language_at<'a, T: ToOffset>(&'a self, point: T) -> Option<&'a Arc<Language>> {
2770 self.point_to_buffer_offset(point)
2771 .and_then(|(buffer, offset)| buffer.language_at(offset))
2772 }
2773
2774 pub fn settings_at<'a, T: ToOffset>(
2775 &'a self,
2776 point: T,
2777 cx: &'a AppContext,
2778 ) -> &'a LanguageSettings {
2779 self.point_to_buffer_offset(point)
2780 .map(|(buffer, offset)| buffer.settings_at(offset, cx))
2781 .unwrap_or_else(|| language_settings(None, cx))
2782 }
2783
2784 pub fn language_scope_at<'a, T: ToOffset>(&'a self, point: T) -> Option<LanguageScope> {
2785 self.point_to_buffer_offset(point)
2786 .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
2787 }
2788
2789 pub fn language_indent_size_at<T: ToOffset>(
2790 &self,
2791 position: T,
2792 cx: &AppContext,
2793 ) -> Option<IndentSize> {
2794 let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
2795 Some(buffer_snapshot.language_indent_size_at(offset, cx))
2796 }
2797
2798 pub fn is_dirty(&self) -> bool {
2799 self.is_dirty
2800 }
2801
2802 pub fn has_conflict(&self) -> bool {
2803 self.has_conflict
2804 }
2805
2806 pub fn diagnostic_group<'a, O>(
2807 &'a self,
2808 group_id: usize,
2809 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
2810 where
2811 O: text::FromAnchor + 'a,
2812 {
2813 self.as_singleton()
2814 .into_iter()
2815 .flat_map(move |(_, _, buffer)| buffer.diagnostic_group(group_id))
2816 }
2817
2818 pub fn diagnostics_in_range<'a, T, O>(
2819 &'a self,
2820 range: Range<T>,
2821 reversed: bool,
2822 ) -> impl Iterator<Item = DiagnosticEntry<O>> + 'a
2823 where
2824 T: 'a + ToOffset,
2825 O: 'a + text::FromAnchor + Ord,
2826 {
2827 self.as_singleton()
2828 .into_iter()
2829 .flat_map(move |(_, _, buffer)| {
2830 buffer.diagnostics_in_range(
2831 range.start.to_offset(self)..range.end.to_offset(self),
2832 reversed,
2833 )
2834 })
2835 }
2836
2837 pub fn has_git_diffs(&self) -> bool {
2838 for excerpt in self.excerpts.iter() {
2839 if !excerpt.buffer.git_diff.is_empty() {
2840 return true;
2841 }
2842 }
2843 false
2844 }
2845
2846 pub fn git_diff_hunks_in_range_rev<'a>(
2847 &'a self,
2848 row_range: Range<u32>,
2849 ) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
2850 let mut cursor = self.excerpts.cursor::<Point>();
2851
2852 cursor.seek(&Point::new(row_range.end, 0), Bias::Left, &());
2853 if cursor.item().is_none() {
2854 cursor.prev(&());
2855 }
2856
2857 std::iter::from_fn(move || {
2858 let excerpt = cursor.item()?;
2859 let multibuffer_start = *cursor.start();
2860 let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
2861 if multibuffer_start.row >= row_range.end {
2862 return None;
2863 }
2864
2865 let mut buffer_start = excerpt.range.context.start;
2866 let mut buffer_end = excerpt.range.context.end;
2867 let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
2868 let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
2869
2870 if row_range.start > multibuffer_start.row {
2871 let buffer_start_point =
2872 excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
2873 buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
2874 }
2875
2876 if row_range.end < multibuffer_end.row {
2877 let buffer_end_point =
2878 excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
2879 buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
2880 }
2881
2882 let buffer_hunks = excerpt
2883 .buffer
2884 .git_diff_hunks_intersecting_range_rev(buffer_start..buffer_end)
2885 .filter_map(move |hunk| {
2886 let start = multibuffer_start.row
2887 + hunk
2888 .buffer_range
2889 .start
2890 .saturating_sub(excerpt_start_point.row);
2891 let end = multibuffer_start.row
2892 + hunk
2893 .buffer_range
2894 .end
2895 .min(excerpt_end_point.row + 1)
2896 .saturating_sub(excerpt_start_point.row);
2897
2898 Some(DiffHunk {
2899 buffer_range: start..end,
2900 diff_base_byte_range: hunk.diff_base_byte_range.clone(),
2901 })
2902 });
2903
2904 cursor.prev(&());
2905
2906 Some(buffer_hunks)
2907 })
2908 .flatten()
2909 }
2910
2911 pub fn git_diff_hunks_in_range<'a>(
2912 &'a self,
2913 row_range: Range<u32>,
2914 ) -> impl 'a + Iterator<Item = DiffHunk<u32>> {
2915 let mut cursor = self.excerpts.cursor::<Point>();
2916
2917 cursor.seek(&Point::new(row_range.start, 0), Bias::Right, &());
2918
2919 std::iter::from_fn(move || {
2920 let excerpt = cursor.item()?;
2921 let multibuffer_start = *cursor.start();
2922 let multibuffer_end = multibuffer_start + excerpt.text_summary.lines;
2923 if multibuffer_start.row >= row_range.end {
2924 return None;
2925 }
2926
2927 let mut buffer_start = excerpt.range.context.start;
2928 let mut buffer_end = excerpt.range.context.end;
2929 let excerpt_start_point = buffer_start.to_point(&excerpt.buffer);
2930 let excerpt_end_point = excerpt_start_point + excerpt.text_summary.lines;
2931
2932 if row_range.start > multibuffer_start.row {
2933 let buffer_start_point =
2934 excerpt_start_point + Point::new(row_range.start - multibuffer_start.row, 0);
2935 buffer_start = excerpt.buffer.anchor_before(buffer_start_point);
2936 }
2937
2938 if row_range.end < multibuffer_end.row {
2939 let buffer_end_point =
2940 excerpt_start_point + Point::new(row_range.end - multibuffer_start.row, 0);
2941 buffer_end = excerpt.buffer.anchor_before(buffer_end_point);
2942 }
2943
2944 let buffer_hunks = excerpt
2945 .buffer
2946 .git_diff_hunks_intersecting_range(buffer_start..buffer_end)
2947 .filter_map(move |hunk| {
2948 let start = multibuffer_start.row
2949 + hunk
2950 .buffer_range
2951 .start
2952 .saturating_sub(excerpt_start_point.row);
2953 let end = multibuffer_start.row
2954 + hunk
2955 .buffer_range
2956 .end
2957 .min(excerpt_end_point.row + 1)
2958 .saturating_sub(excerpt_start_point.row);
2959
2960 Some(DiffHunk {
2961 buffer_range: start..end,
2962 diff_base_byte_range: hunk.diff_base_byte_range.clone(),
2963 })
2964 });
2965
2966 cursor.next(&());
2967
2968 Some(buffer_hunks)
2969 })
2970 .flatten()
2971 }
2972
2973 pub fn range_for_syntax_ancestor<T: ToOffset>(&self, range: Range<T>) -> Option<Range<usize>> {
2974 let range = range.start.to_offset(self)..range.end.to_offset(self);
2975
2976 self.excerpt_containing(range.clone())
2977 .and_then(|(excerpt, excerpt_offset)| {
2978 let excerpt_buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
2979 let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
2980
2981 let start_in_buffer =
2982 excerpt_buffer_start + range.start.saturating_sub(excerpt_offset);
2983 let end_in_buffer = excerpt_buffer_start + range.end.saturating_sub(excerpt_offset);
2984 let mut ancestor_buffer_range = excerpt
2985 .buffer
2986 .range_for_syntax_ancestor(start_in_buffer..end_in_buffer)?;
2987 ancestor_buffer_range.start =
2988 cmp::max(ancestor_buffer_range.start, excerpt_buffer_start);
2989 ancestor_buffer_range.end = cmp::min(ancestor_buffer_range.end, excerpt_buffer_end);
2990
2991 let start = excerpt_offset + (ancestor_buffer_range.start - excerpt_buffer_start);
2992 let end = excerpt_offset + (ancestor_buffer_range.end - excerpt_buffer_start);
2993 Some(start..end)
2994 })
2995 }
2996
2997 pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
2998 let (excerpt_id, _, buffer) = self.as_singleton()?;
2999 let outline = buffer.outline(theme)?;
3000 Some(Outline::new(
3001 outline
3002 .items
3003 .into_iter()
3004 .map(|item| OutlineItem {
3005 depth: item.depth,
3006 range: self.anchor_in_excerpt(excerpt_id.clone(), item.range.start)
3007 ..self.anchor_in_excerpt(excerpt_id.clone(), item.range.end),
3008 text: item.text,
3009 highlight_ranges: item.highlight_ranges,
3010 name_ranges: item.name_ranges,
3011 })
3012 .collect(),
3013 ))
3014 }
3015
3016 pub fn symbols_containing<T: ToOffset>(
3017 &self,
3018 offset: T,
3019 theme: Option<&SyntaxTheme>,
3020 ) -> Option<(u64, Vec<OutlineItem<Anchor>>)> {
3021 let anchor = self.anchor_before(offset);
3022 let excerpt_id = anchor.excerpt_id();
3023 let excerpt = self.excerpt(excerpt_id)?;
3024 Some((
3025 excerpt.buffer_id,
3026 excerpt
3027 .buffer
3028 .symbols_containing(anchor.text_anchor, theme)
3029 .into_iter()
3030 .flatten()
3031 .map(|item| OutlineItem {
3032 depth: item.depth,
3033 range: self.anchor_in_excerpt(excerpt_id, item.range.start)
3034 ..self.anchor_in_excerpt(excerpt_id, item.range.end),
3035 text: item.text,
3036 highlight_ranges: item.highlight_ranges,
3037 name_ranges: item.name_ranges,
3038 })
3039 .collect(),
3040 ))
3041 }
3042
3043 fn excerpt_locator_for_id<'a>(&'a self, id: ExcerptId) -> &'a Locator {
3044 if id == ExcerptId::min() {
3045 Locator::min_ref()
3046 } else if id == ExcerptId::max() {
3047 Locator::max_ref()
3048 } else {
3049 let mut cursor = self.excerpt_ids.cursor::<ExcerptId>();
3050 cursor.seek(&id, Bias::Left, &());
3051 if let Some(entry) = cursor.item() {
3052 if entry.id == id {
3053 return &entry.locator;
3054 }
3055 }
3056 panic!("invalid excerpt id {:?}", id)
3057 }
3058 }
3059
3060 pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<u64> {
3061 Some(self.excerpt(excerpt_id)?.buffer_id)
3062 }
3063
3064 pub fn buffer_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&BufferSnapshot> {
3065 Some(&self.excerpt(excerpt_id)?.buffer)
3066 }
3067
3068 fn excerpt<'a>(&'a self, excerpt_id: ExcerptId) -> Option<&'a Excerpt> {
3069 let mut cursor = self.excerpts.cursor::<Option<&Locator>>();
3070 let locator = self.excerpt_locator_for_id(excerpt_id);
3071 cursor.seek(&Some(locator), Bias::Left, &());
3072 if let Some(excerpt) = cursor.item() {
3073 if excerpt.id == excerpt_id {
3074 return Some(excerpt);
3075 }
3076 }
3077 None
3078 }
3079
3080 /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
3081 fn excerpt_containing<'a, T: ToOffset>(
3082 &'a self,
3083 range: Range<T>,
3084 ) -> Option<(&'a Excerpt, usize)> {
3085 let range = range.start.to_offset(self)..range.end.to_offset(self);
3086
3087 let mut cursor = self.excerpts.cursor::<usize>();
3088 cursor.seek(&range.start, Bias::Right, &());
3089 let start_excerpt = cursor.item();
3090
3091 if range.start == range.end {
3092 return start_excerpt.map(|excerpt| (excerpt, *cursor.start()));
3093 }
3094
3095 cursor.seek(&range.end, Bias::Right, &());
3096 let end_excerpt = cursor.item();
3097
3098 start_excerpt
3099 .zip(end_excerpt)
3100 .and_then(|(start_excerpt, end_excerpt)| {
3101 if start_excerpt.id != end_excerpt.id {
3102 return None;
3103 }
3104
3105 Some((start_excerpt, *cursor.start()))
3106 })
3107 }
3108
3109 pub fn remote_selections_in_range<'a>(
3110 &'a self,
3111 range: &'a Range<Anchor>,
3112 ) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
3113 let mut cursor = self.excerpts.cursor::<ExcerptSummary>();
3114 let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id);
3115 let end_locator = self.excerpt_locator_for_id(range.end.excerpt_id);
3116 cursor.seek(start_locator, Bias::Left, &());
3117 cursor
3118 .take_while(move |excerpt| excerpt.locator <= *end_locator)
3119 .flat_map(move |excerpt| {
3120 let mut query_range = excerpt.range.context.start..excerpt.range.context.end;
3121 if excerpt.id == range.start.excerpt_id {
3122 query_range.start = range.start.text_anchor;
3123 }
3124 if excerpt.id == range.end.excerpt_id {
3125 query_range.end = range.end.text_anchor;
3126 }
3127
3128 excerpt
3129 .buffer
3130 .remote_selections_in_range(query_range)
3131 .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
3132 selections.map(move |selection| {
3133 let mut start = Anchor {
3134 buffer_id: Some(excerpt.buffer_id),
3135 excerpt_id: excerpt.id.clone(),
3136 text_anchor: selection.start,
3137 };
3138 let mut end = Anchor {
3139 buffer_id: Some(excerpt.buffer_id),
3140 excerpt_id: excerpt.id.clone(),
3141 text_anchor: selection.end,
3142 };
3143 if range.start.cmp(&start, self).is_gt() {
3144 start = range.start.clone();
3145 }
3146 if range.end.cmp(&end, self).is_lt() {
3147 end = range.end.clone();
3148 }
3149
3150 (
3151 replica_id,
3152 line_mode,
3153 cursor_shape,
3154 Selection {
3155 id: selection.id,
3156 start,
3157 end,
3158 reversed: selection.reversed,
3159 goal: selection.goal,
3160 },
3161 )
3162 })
3163 })
3164 })
3165 }
3166}
3167
3168#[cfg(any(test, feature = "test-support"))]
3169impl MultiBufferSnapshot {
3170 pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
3171 let end = self.clip_offset(rng.gen_range(start_offset..=self.len()), Bias::Right);
3172 let start = self.clip_offset(rng.gen_range(start_offset..=end), Bias::Right);
3173 start..end
3174 }
3175}
3176
3177impl History {
3178 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
3179 self.transaction_depth += 1;
3180 if self.transaction_depth == 1 {
3181 let id = self.next_transaction_id.tick();
3182 self.undo_stack.push(Transaction {
3183 id,
3184 buffer_transactions: Default::default(),
3185 first_edit_at: now,
3186 last_edit_at: now,
3187 suppress_grouping: false,
3188 });
3189 Some(id)
3190 } else {
3191 None
3192 }
3193 }
3194
3195 fn end_transaction(
3196 &mut self,
3197 now: Instant,
3198 buffer_transactions: HashMap<u64, TransactionId>,
3199 ) -> bool {
3200 assert_ne!(self.transaction_depth, 0);
3201 self.transaction_depth -= 1;
3202 if self.transaction_depth == 0 {
3203 if buffer_transactions.is_empty() {
3204 self.undo_stack.pop();
3205 false
3206 } else {
3207 self.redo_stack.clear();
3208 let transaction = self.undo_stack.last_mut().unwrap();
3209 transaction.last_edit_at = now;
3210 for (buffer_id, transaction_id) in buffer_transactions {
3211 transaction
3212 .buffer_transactions
3213 .entry(buffer_id)
3214 .or_insert(transaction_id);
3215 }
3216 true
3217 }
3218 } else {
3219 false
3220 }
3221 }
3222
3223 fn push_transaction<'a, T>(
3224 &mut self,
3225 buffer_transactions: T,
3226 now: Instant,
3227 cx: &mut ModelContext<MultiBuffer>,
3228 ) where
3229 T: IntoIterator<Item = (&'a ModelHandle<Buffer>, &'a language::Transaction)>,
3230 {
3231 assert_eq!(self.transaction_depth, 0);
3232 let transaction = Transaction {
3233 id: self.next_transaction_id.tick(),
3234 buffer_transactions: buffer_transactions
3235 .into_iter()
3236 .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id))
3237 .collect(),
3238 first_edit_at: now,
3239 last_edit_at: now,
3240 suppress_grouping: false,
3241 };
3242 if !transaction.buffer_transactions.is_empty() {
3243 self.undo_stack.push(transaction);
3244 self.redo_stack.clear();
3245 }
3246 }
3247
3248 fn finalize_last_transaction(&mut self) {
3249 if let Some(transaction) = self.undo_stack.last_mut() {
3250 transaction.suppress_grouping = true;
3251 }
3252 }
3253
3254 fn pop_undo(&mut self) -> Option<&mut Transaction> {
3255 assert_eq!(self.transaction_depth, 0);
3256 if let Some(transaction) = self.undo_stack.pop() {
3257 self.redo_stack.push(transaction);
3258 self.redo_stack.last_mut()
3259 } else {
3260 None
3261 }
3262 }
3263
3264 fn pop_redo(&mut self) -> Option<&mut Transaction> {
3265 assert_eq!(self.transaction_depth, 0);
3266 if let Some(transaction) = self.redo_stack.pop() {
3267 self.undo_stack.push(transaction);
3268 self.undo_stack.last_mut()
3269 } else {
3270 None
3271 }
3272 }
3273
3274 fn group(&mut self) -> Option<TransactionId> {
3275 let mut count = 0;
3276 let mut transactions = self.undo_stack.iter();
3277 if let Some(mut transaction) = transactions.next_back() {
3278 while let Some(prev_transaction) = transactions.next_back() {
3279 if !prev_transaction.suppress_grouping
3280 && transaction.first_edit_at - prev_transaction.last_edit_at
3281 <= self.group_interval
3282 {
3283 transaction = prev_transaction;
3284 count += 1;
3285 } else {
3286 break;
3287 }
3288 }
3289 }
3290 self.group_trailing(count)
3291 }
3292
3293 fn group_until(&mut self, transaction_id: TransactionId) {
3294 let mut count = 0;
3295 for transaction in self.undo_stack.iter().rev() {
3296 if transaction.id == transaction_id {
3297 self.group_trailing(count);
3298 break;
3299 } else if transaction.suppress_grouping {
3300 break;
3301 } else {
3302 count += 1;
3303 }
3304 }
3305 }
3306
3307 fn group_trailing(&mut self, n: usize) -> Option<TransactionId> {
3308 let new_len = self.undo_stack.len() - n;
3309 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
3310 if let Some(last_transaction) = transactions_to_keep.last_mut() {
3311 if let Some(transaction) = transactions_to_merge.last() {
3312 last_transaction.last_edit_at = transaction.last_edit_at;
3313 }
3314 for to_merge in transactions_to_merge {
3315 for (buffer_id, transaction_id) in &to_merge.buffer_transactions {
3316 last_transaction
3317 .buffer_transactions
3318 .entry(*buffer_id)
3319 .or_insert(*transaction_id);
3320 }
3321 }
3322 }
3323
3324 self.undo_stack.truncate(new_len);
3325 self.undo_stack.last().map(|t| t.id)
3326 }
3327}
3328
3329impl Excerpt {
3330 fn new(
3331 id: ExcerptId,
3332 locator: Locator,
3333 buffer_id: u64,
3334 buffer: BufferSnapshot,
3335 range: ExcerptRange<text::Anchor>,
3336 has_trailing_newline: bool,
3337 ) -> Self {
3338 Excerpt {
3339 id,
3340 locator,
3341 max_buffer_row: range.context.end.to_point(&buffer).row,
3342 text_summary: buffer
3343 .text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
3344 buffer_id,
3345 buffer,
3346 range,
3347 has_trailing_newline,
3348 }
3349 }
3350
3351 fn chunks_in_range(&self, range: Range<usize>, language_aware: bool) -> ExcerptChunks {
3352 let content_start = self.range.context.start.to_offset(&self.buffer);
3353 let chunks_start = content_start + range.start;
3354 let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
3355
3356 let footer_height = if self.has_trailing_newline
3357 && range.start <= self.text_summary.len
3358 && range.end > self.text_summary.len
3359 {
3360 1
3361 } else {
3362 0
3363 };
3364
3365 let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware);
3366
3367 ExcerptChunks {
3368 content_chunks,
3369 footer_height,
3370 }
3371 }
3372
3373 fn bytes_in_range(&self, range: Range<usize>) -> ExcerptBytes {
3374 let content_start = self.range.context.start.to_offset(&self.buffer);
3375 let bytes_start = content_start + range.start;
3376 let bytes_end = content_start + cmp::min(range.end, self.text_summary.len);
3377 let footer_height = if self.has_trailing_newline
3378 && range.start <= self.text_summary.len
3379 && range.end > self.text_summary.len
3380 {
3381 1
3382 } else {
3383 0
3384 };
3385 let content_bytes = self.buffer.bytes_in_range(bytes_start..bytes_end);
3386
3387 ExcerptBytes {
3388 content_bytes,
3389 footer_height,
3390 }
3391 }
3392
3393 fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
3394 if text_anchor
3395 .cmp(&self.range.context.start, &self.buffer)
3396 .is_lt()
3397 {
3398 self.range.context.start
3399 } else if text_anchor
3400 .cmp(&self.range.context.end, &self.buffer)
3401 .is_gt()
3402 {
3403 self.range.context.end
3404 } else {
3405 text_anchor
3406 }
3407 }
3408
3409 fn contains(&self, anchor: &Anchor) -> bool {
3410 Some(self.buffer_id) == anchor.buffer_id
3411 && self
3412 .range
3413 .context
3414 .start
3415 .cmp(&anchor.text_anchor, &self.buffer)
3416 .is_le()
3417 && self
3418 .range
3419 .context
3420 .end
3421 .cmp(&anchor.text_anchor, &self.buffer)
3422 .is_ge()
3423 }
3424}
3425
3426impl ExcerptId {
3427 pub fn min() -> Self {
3428 Self(0)
3429 }
3430
3431 pub fn max() -> Self {
3432 Self(usize::MAX)
3433 }
3434
3435 pub fn to_proto(&self) -> u64 {
3436 self.0 as _
3437 }
3438
3439 pub fn from_proto(proto: u64) -> Self {
3440 Self(proto as _)
3441 }
3442
3443 pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
3444 let a = snapshot.excerpt_locator_for_id(*self);
3445 let b = snapshot.excerpt_locator_for_id(*other);
3446 a.cmp(&b).then_with(|| self.0.cmp(&other.0))
3447 }
3448}
3449
3450impl Into<usize> for ExcerptId {
3451 fn into(self) -> usize {
3452 self.0
3453 }
3454}
3455
3456impl fmt::Debug for Excerpt {
3457 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3458 f.debug_struct("Excerpt")
3459 .field("id", &self.id)
3460 .field("locator", &self.locator)
3461 .field("buffer_id", &self.buffer_id)
3462 .field("range", &self.range)
3463 .field("text_summary", &self.text_summary)
3464 .field("has_trailing_newline", &self.has_trailing_newline)
3465 .finish()
3466 }
3467}
3468
3469impl sum_tree::Item for Excerpt {
3470 type Summary = ExcerptSummary;
3471
3472 fn summary(&self) -> Self::Summary {
3473 let mut text = self.text_summary.clone();
3474 if self.has_trailing_newline {
3475 text += TextSummary::from("\n");
3476 }
3477 ExcerptSummary {
3478 excerpt_id: self.id,
3479 excerpt_locator: self.locator.clone(),
3480 max_buffer_row: self.max_buffer_row,
3481 text,
3482 }
3483 }
3484}
3485
3486impl sum_tree::Item for ExcerptIdMapping {
3487 type Summary = ExcerptId;
3488
3489 fn summary(&self) -> Self::Summary {
3490 self.id
3491 }
3492}
3493
3494impl sum_tree::KeyedItem for ExcerptIdMapping {
3495 type Key = ExcerptId;
3496
3497 fn key(&self) -> Self::Key {
3498 self.id
3499 }
3500}
3501
3502impl sum_tree::Summary for ExcerptId {
3503 type Context = ();
3504
3505 fn add_summary(&mut self, other: &Self, _: &()) {
3506 *self = *other;
3507 }
3508}
3509
3510impl sum_tree::Summary for ExcerptSummary {
3511 type Context = ();
3512
3513 fn add_summary(&mut self, summary: &Self, _: &()) {
3514 debug_assert!(summary.excerpt_locator > self.excerpt_locator);
3515 self.excerpt_locator = summary.excerpt_locator.clone();
3516 self.text.add_summary(&summary.text, &());
3517 self.max_buffer_row = cmp::max(self.max_buffer_row, summary.max_buffer_row);
3518 }
3519}
3520
3521impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for TextSummary {
3522 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3523 *self += &summary.text;
3524 }
3525}
3526
3527impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for usize {
3528 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3529 *self += summary.text.len;
3530 }
3531}
3532
3533impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for usize {
3534 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
3535 Ord::cmp(self, &cursor_location.text.len)
3536 }
3537}
3538
3539impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
3540 fn cmp(&self, cursor_location: &Option<&'a Locator>, _: &()) -> cmp::Ordering {
3541 Ord::cmp(&Some(self), cursor_location)
3542 }
3543}
3544
3545impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, ExcerptSummary> for Locator {
3546 fn cmp(&self, cursor_location: &ExcerptSummary, _: &()) -> cmp::Ordering {
3547 Ord::cmp(self, &cursor_location.excerpt_locator)
3548 }
3549}
3550
3551impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for OffsetUtf16 {
3552 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3553 *self += summary.text.len_utf16;
3554 }
3555}
3556
3557impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Point {
3558 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3559 *self += summary.text.lines;
3560 }
3561}
3562
3563impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for PointUtf16 {
3564 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3565 *self += summary.text.lines_utf16()
3566 }
3567}
3568
3569impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> {
3570 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3571 *self = Some(&summary.excerpt_locator);
3572 }
3573}
3574
3575impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptId> {
3576 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: &()) {
3577 *self = Some(summary.excerpt_id);
3578 }
3579}
3580
3581impl<'a> MultiBufferRows<'a> {
3582 pub fn seek(&mut self, row: u32) {
3583 self.buffer_row_range = 0..0;
3584
3585 self.excerpts
3586 .seek_forward(&Point::new(row, 0), Bias::Right, &());
3587 if self.excerpts.item().is_none() {
3588 self.excerpts.prev(&());
3589
3590 if self.excerpts.item().is_none() && row == 0 {
3591 self.buffer_row_range = 0..1;
3592 return;
3593 }
3594 }
3595
3596 if let Some(excerpt) = self.excerpts.item() {
3597 let overshoot = row - self.excerpts.start().row;
3598 let excerpt_start = excerpt.range.context.start.to_point(&excerpt.buffer).row;
3599 self.buffer_row_range.start = excerpt_start + overshoot;
3600 self.buffer_row_range.end = excerpt_start + excerpt.text_summary.lines.row + 1;
3601 }
3602 }
3603}
3604
3605impl<'a> Iterator for MultiBufferRows<'a> {
3606 type Item = Option<u32>;
3607
3608 fn next(&mut self) -> Option<Self::Item> {
3609 loop {
3610 if !self.buffer_row_range.is_empty() {
3611 let row = Some(self.buffer_row_range.start);
3612 self.buffer_row_range.start += 1;
3613 return Some(row);
3614 }
3615 self.excerpts.item()?;
3616 self.excerpts.next(&());
3617 let excerpt = self.excerpts.item()?;
3618 self.buffer_row_range.start = excerpt.range.context.start.to_point(&excerpt.buffer).row;
3619 self.buffer_row_range.end =
3620 self.buffer_row_range.start + excerpt.text_summary.lines.row + 1;
3621 }
3622 }
3623}
3624
3625impl<'a> MultiBufferChunks<'a> {
3626 pub fn offset(&self) -> usize {
3627 self.range.start
3628 }
3629
3630 pub fn seek(&mut self, offset: usize) {
3631 self.range.start = offset;
3632 self.excerpts.seek(&offset, Bias::Right, &());
3633 if let Some(excerpt) = self.excerpts.item() {
3634 self.excerpt_chunks = Some(excerpt.chunks_in_range(
3635 self.range.start - self.excerpts.start()..self.range.end - self.excerpts.start(),
3636 self.language_aware,
3637 ));
3638 } else {
3639 self.excerpt_chunks = None;
3640 }
3641 }
3642}
3643
3644impl<'a> Iterator for MultiBufferChunks<'a> {
3645 type Item = Chunk<'a>;
3646
3647 fn next(&mut self) -> Option<Self::Item> {
3648 if self.range.is_empty() {
3649 None
3650 } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
3651 self.range.start += chunk.text.len();
3652 Some(chunk)
3653 } else {
3654 self.excerpts.next(&());
3655 let excerpt = self.excerpts.item()?;
3656 self.excerpt_chunks = Some(excerpt.chunks_in_range(
3657 0..self.range.end - self.excerpts.start(),
3658 self.language_aware,
3659 ));
3660 self.next()
3661 }
3662 }
3663}
3664
3665impl<'a> MultiBufferBytes<'a> {
3666 fn consume(&mut self, len: usize) {
3667 self.range.start += len;
3668 self.chunk = &self.chunk[len..];
3669
3670 if !self.range.is_empty() && self.chunk.is_empty() {
3671 if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
3672 self.chunk = chunk;
3673 } else {
3674 self.excerpts.next(&());
3675 if let Some(excerpt) = self.excerpts.item() {
3676 let mut excerpt_bytes =
3677 excerpt.bytes_in_range(0..self.range.end - self.excerpts.start());
3678 self.chunk = excerpt_bytes.next().unwrap();
3679 self.excerpt_bytes = Some(excerpt_bytes);
3680 }
3681 }
3682 }
3683 }
3684}
3685
3686impl<'a> Iterator for MultiBufferBytes<'a> {
3687 type Item = &'a [u8];
3688
3689 fn next(&mut self) -> Option<Self::Item> {
3690 let chunk = self.chunk;
3691 if chunk.is_empty() {
3692 None
3693 } else {
3694 self.consume(chunk.len());
3695 Some(chunk)
3696 }
3697 }
3698}
3699
3700impl<'a> io::Read for MultiBufferBytes<'a> {
3701 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
3702 let len = cmp::min(buf.len(), self.chunk.len());
3703 buf[..len].copy_from_slice(&self.chunk[..len]);
3704 if len > 0 {
3705 self.consume(len);
3706 }
3707 Ok(len)
3708 }
3709}
3710
3711impl<'a> Iterator for ExcerptBytes<'a> {
3712 type Item = &'a [u8];
3713
3714 fn next(&mut self) -> Option<Self::Item> {
3715 if let Some(chunk) = self.content_bytes.next() {
3716 if !chunk.is_empty() {
3717 return Some(chunk);
3718 }
3719 }
3720
3721 if self.footer_height > 0 {
3722 let result = &NEWLINES[..self.footer_height];
3723 self.footer_height = 0;
3724 return Some(result);
3725 }
3726
3727 None
3728 }
3729}
3730
3731impl<'a> Iterator for ExcerptChunks<'a> {
3732 type Item = Chunk<'a>;
3733
3734 fn next(&mut self) -> Option<Self::Item> {
3735 if let Some(chunk) = self.content_chunks.next() {
3736 return Some(chunk);
3737 }
3738
3739 if self.footer_height > 0 {
3740 let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) };
3741 self.footer_height = 0;
3742 return Some(Chunk {
3743 text,
3744 ..Default::default()
3745 });
3746 }
3747
3748 None
3749 }
3750}
3751
3752impl ToOffset for Point {
3753 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
3754 snapshot.point_to_offset(*self)
3755 }
3756}
3757
3758impl ToOffset for usize {
3759 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
3760 assert!(*self <= snapshot.len(), "offset is out of range");
3761 *self
3762 }
3763}
3764
3765impl ToOffset for OffsetUtf16 {
3766 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
3767 snapshot.offset_utf16_to_offset(*self)
3768 }
3769}
3770
3771impl ToOffset for PointUtf16 {
3772 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
3773 snapshot.point_utf16_to_offset(*self)
3774 }
3775}
3776
3777impl ToOffsetUtf16 for OffsetUtf16 {
3778 fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
3779 *self
3780 }
3781}
3782
3783impl ToOffsetUtf16 for usize {
3784 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
3785 snapshot.offset_to_offset_utf16(*self)
3786 }
3787}
3788
3789impl ToPoint for usize {
3790 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
3791 snapshot.offset_to_point(*self)
3792 }
3793}
3794
3795impl ToPoint for Point {
3796 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
3797 *self
3798 }
3799}
3800
3801impl ToPointUtf16 for usize {
3802 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
3803 snapshot.offset_to_point_utf16(*self)
3804 }
3805}
3806
3807impl ToPointUtf16 for Point {
3808 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
3809 snapshot.point_to_point_utf16(*self)
3810 }
3811}
3812
3813impl ToPointUtf16 for PointUtf16 {
3814 fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 {
3815 *self
3816 }
3817}
3818
3819fn build_excerpt_ranges<T>(
3820 buffer: &BufferSnapshot,
3821 ranges: &[Range<T>],
3822 context_line_count: u32,
3823) -> (Vec<ExcerptRange<Point>>, Vec<usize>)
3824where
3825 T: text::ToPoint,
3826{
3827 let max_point = buffer.max_point();
3828 let mut range_counts = Vec::new();
3829 let mut excerpt_ranges = Vec::new();
3830 let mut range_iter = ranges
3831 .iter()
3832 .map(|range| range.start.to_point(buffer)..range.end.to_point(buffer))
3833 .peekable();
3834 while let Some(range) = range_iter.next() {
3835 let excerpt_start = Point::new(range.start.row.saturating_sub(context_line_count), 0);
3836 let mut excerpt_end = Point::new(range.end.row + 1 + context_line_count, 0).min(max_point);
3837 let mut ranges_in_excerpt = 1;
3838
3839 while let Some(next_range) = range_iter.peek() {
3840 if next_range.start.row <= excerpt_end.row + context_line_count {
3841 excerpt_end =
3842 Point::new(next_range.end.row + 1 + context_line_count, 0).min(max_point);
3843 ranges_in_excerpt += 1;
3844 range_iter.next();
3845 } else {
3846 break;
3847 }
3848 }
3849
3850 excerpt_ranges.push(ExcerptRange {
3851 context: excerpt_start..excerpt_end,
3852 primary: Some(range),
3853 });
3854 range_counts.push(ranges_in_excerpt);
3855 }
3856
3857 (excerpt_ranges, range_counts)
3858}
3859
3860#[cfg(test)]
3861mod tests {
3862 use super::*;
3863 use futures::StreamExt;
3864 use gpui::{AppContext, TestAppContext};
3865 use language::{Buffer, Rope};
3866 use rand::prelude::*;
3867 use settings::SettingsStore;
3868 use std::{env, rc::Rc};
3869 use unindent::Unindent;
3870 use util::test::sample_text;
3871
3872 #[gpui::test]
3873 fn test_singleton(cx: &mut AppContext) {
3874 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
3875 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
3876
3877 let snapshot = multibuffer.read(cx).snapshot(cx);
3878 assert_eq!(snapshot.text(), buffer.read(cx).text());
3879
3880 assert_eq!(
3881 snapshot.buffer_rows(0).collect::<Vec<_>>(),
3882 (0..buffer.read(cx).row_count())
3883 .map(Some)
3884 .collect::<Vec<_>>()
3885 );
3886
3887 buffer.update(cx, |buffer, cx| buffer.edit([(1..3, "XXX\n")], None, cx));
3888 let snapshot = multibuffer.read(cx).snapshot(cx);
3889
3890 assert_eq!(snapshot.text(), buffer.read(cx).text());
3891 assert_eq!(
3892 snapshot.buffer_rows(0).collect::<Vec<_>>(),
3893 (0..buffer.read(cx).row_count())
3894 .map(Some)
3895 .collect::<Vec<_>>()
3896 );
3897 }
3898
3899 #[gpui::test]
3900 fn test_remote(cx: &mut AppContext) {
3901 let host_buffer = cx.add_model(|cx| Buffer::new(0, "a", cx));
3902 let guest_buffer = cx.add_model(|cx| {
3903 let state = host_buffer.read(cx).to_proto();
3904 let ops = cx
3905 .background()
3906 .block(host_buffer.read(cx).serialize_ops(None, cx));
3907 let mut buffer = Buffer::from_proto(1, state, None).unwrap();
3908 buffer
3909 .apply_ops(
3910 ops.into_iter()
3911 .map(|op| language::proto::deserialize_operation(op).unwrap()),
3912 cx,
3913 )
3914 .unwrap();
3915 buffer
3916 });
3917 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(guest_buffer.clone(), cx));
3918 let snapshot = multibuffer.read(cx).snapshot(cx);
3919 assert_eq!(snapshot.text(), "a");
3920
3921 guest_buffer.update(cx, |buffer, cx| buffer.edit([(1..1, "b")], None, cx));
3922 let snapshot = multibuffer.read(cx).snapshot(cx);
3923 assert_eq!(snapshot.text(), "ab");
3924
3925 guest_buffer.update(cx, |buffer, cx| buffer.edit([(2..2, "c")], None, cx));
3926 let snapshot = multibuffer.read(cx).snapshot(cx);
3927 assert_eq!(snapshot.text(), "abc");
3928 }
3929
3930 #[gpui::test]
3931 fn test_excerpt_boundaries_and_clipping(cx: &mut AppContext) {
3932 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'a'), cx));
3933 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(6, 6, 'g'), cx));
3934 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
3935
3936 let events = Rc::new(RefCell::new(Vec::<Event>::new()));
3937 multibuffer.update(cx, |_, cx| {
3938 let events = events.clone();
3939 cx.subscribe(&multibuffer, move |_, _, event, _| {
3940 if let Event::Edited = event {
3941 events.borrow_mut().push(event.clone())
3942 }
3943 })
3944 .detach();
3945 });
3946
3947 let subscription = multibuffer.update(cx, |multibuffer, cx| {
3948 let subscription = multibuffer.subscribe();
3949 multibuffer.push_excerpts(
3950 buffer_1.clone(),
3951 [ExcerptRange {
3952 context: Point::new(1, 2)..Point::new(2, 5),
3953 primary: None,
3954 }],
3955 cx,
3956 );
3957 assert_eq!(
3958 subscription.consume().into_inner(),
3959 [Edit {
3960 old: 0..0,
3961 new: 0..10
3962 }]
3963 );
3964
3965 multibuffer.push_excerpts(
3966 buffer_1.clone(),
3967 [ExcerptRange {
3968 context: Point::new(3, 3)..Point::new(4, 4),
3969 primary: None,
3970 }],
3971 cx,
3972 );
3973 multibuffer.push_excerpts(
3974 buffer_2.clone(),
3975 [ExcerptRange {
3976 context: Point::new(3, 1)..Point::new(3, 3),
3977 primary: None,
3978 }],
3979 cx,
3980 );
3981 assert_eq!(
3982 subscription.consume().into_inner(),
3983 [Edit {
3984 old: 10..10,
3985 new: 10..22
3986 }]
3987 );
3988
3989 subscription
3990 });
3991
3992 // Adding excerpts emits an edited event.
3993 assert_eq!(
3994 events.borrow().as_slice(),
3995 &[Event::Edited, Event::Edited, Event::Edited]
3996 );
3997
3998 let snapshot = multibuffer.read(cx).snapshot(cx);
3999 assert_eq!(
4000 snapshot.text(),
4001 concat!(
4002 "bbbb\n", // Preserve newlines
4003 "ccccc\n", //
4004 "ddd\n", //
4005 "eeee\n", //
4006 "jj" //
4007 )
4008 );
4009 assert_eq!(
4010 snapshot.buffer_rows(0).collect::<Vec<_>>(),
4011 [Some(1), Some(2), Some(3), Some(4), Some(3)]
4012 );
4013 assert_eq!(
4014 snapshot.buffer_rows(2).collect::<Vec<_>>(),
4015 [Some(3), Some(4), Some(3)]
4016 );
4017 assert_eq!(snapshot.buffer_rows(4).collect::<Vec<_>>(), [Some(3)]);
4018 assert_eq!(snapshot.buffer_rows(5).collect::<Vec<_>>(), []);
4019
4020 assert_eq!(
4021 boundaries_in_range(Point::new(0, 0)..Point::new(4, 2), &snapshot),
4022 &[
4023 (0, "bbbb\nccccc".to_string(), true),
4024 (2, "ddd\neeee".to_string(), false),
4025 (4, "jj".to_string(), true),
4026 ]
4027 );
4028 assert_eq!(
4029 boundaries_in_range(Point::new(0, 0)..Point::new(2, 0), &snapshot),
4030 &[(0, "bbbb\nccccc".to_string(), true)]
4031 );
4032 assert_eq!(
4033 boundaries_in_range(Point::new(1, 0)..Point::new(1, 5), &snapshot),
4034 &[]
4035 );
4036 assert_eq!(
4037 boundaries_in_range(Point::new(1, 0)..Point::new(2, 0), &snapshot),
4038 &[]
4039 );
4040 assert_eq!(
4041 boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
4042 &[(2, "ddd\neeee".to_string(), false)]
4043 );
4044 assert_eq!(
4045 boundaries_in_range(Point::new(1, 0)..Point::new(4, 0), &snapshot),
4046 &[(2, "ddd\neeee".to_string(), false)]
4047 );
4048 assert_eq!(
4049 boundaries_in_range(Point::new(2, 0)..Point::new(3, 0), &snapshot),
4050 &[(2, "ddd\neeee".to_string(), false)]
4051 );
4052 assert_eq!(
4053 boundaries_in_range(Point::new(4, 0)..Point::new(4, 2), &snapshot),
4054 &[(4, "jj".to_string(), true)]
4055 );
4056 assert_eq!(
4057 boundaries_in_range(Point::new(4, 2)..Point::new(4, 2), &snapshot),
4058 &[]
4059 );
4060
4061 buffer_1.update(cx, |buffer, cx| {
4062 let text = "\n";
4063 buffer.edit(
4064 [
4065 (Point::new(0, 0)..Point::new(0, 0), text),
4066 (Point::new(2, 1)..Point::new(2, 3), text),
4067 ],
4068 None,
4069 cx,
4070 );
4071 });
4072
4073 let snapshot = multibuffer.read(cx).snapshot(cx);
4074 assert_eq!(
4075 snapshot.text(),
4076 concat!(
4077 "bbbb\n", // Preserve newlines
4078 "c\n", //
4079 "cc\n", //
4080 "ddd\n", //
4081 "eeee\n", //
4082 "jj" //
4083 )
4084 );
4085
4086 assert_eq!(
4087 subscription.consume().into_inner(),
4088 [Edit {
4089 old: 6..8,
4090 new: 6..7
4091 }]
4092 );
4093
4094 let snapshot = multibuffer.read(cx).snapshot(cx);
4095 assert_eq!(
4096 snapshot.clip_point(Point::new(0, 5), Bias::Left),
4097 Point::new(0, 4)
4098 );
4099 assert_eq!(
4100 snapshot.clip_point(Point::new(0, 5), Bias::Right),
4101 Point::new(0, 4)
4102 );
4103 assert_eq!(
4104 snapshot.clip_point(Point::new(5, 1), Bias::Right),
4105 Point::new(5, 1)
4106 );
4107 assert_eq!(
4108 snapshot.clip_point(Point::new(5, 2), Bias::Right),
4109 Point::new(5, 2)
4110 );
4111 assert_eq!(
4112 snapshot.clip_point(Point::new(5, 3), Bias::Right),
4113 Point::new(5, 2)
4114 );
4115
4116 let snapshot = multibuffer.update(cx, |multibuffer, cx| {
4117 let (buffer_2_excerpt_id, _) =
4118 multibuffer.excerpts_for_buffer(&buffer_2, cx)[0].clone();
4119 multibuffer.remove_excerpts([buffer_2_excerpt_id], cx);
4120 multibuffer.snapshot(cx)
4121 });
4122
4123 assert_eq!(
4124 snapshot.text(),
4125 concat!(
4126 "bbbb\n", // Preserve newlines
4127 "c\n", //
4128 "cc\n", //
4129 "ddd\n", //
4130 "eeee", //
4131 )
4132 );
4133
4134 fn boundaries_in_range(
4135 range: Range<Point>,
4136 snapshot: &MultiBufferSnapshot,
4137 ) -> Vec<(u32, String, bool)> {
4138 snapshot
4139 .excerpt_boundaries_in_range(range)
4140 .map(|boundary| {
4141 (
4142 boundary.row,
4143 boundary
4144 .buffer
4145 .text_for_range(boundary.range.context)
4146 .collect::<String>(),
4147 boundary.starts_new_buffer,
4148 )
4149 })
4150 .collect::<Vec<_>>()
4151 }
4152 }
4153
4154 #[gpui::test]
4155 fn test_excerpt_events(cx: &mut AppContext) {
4156 let buffer_1 = cx.add_model(|cx| Buffer::new(0, sample_text(10, 3, 'a'), cx));
4157 let buffer_2 = cx.add_model(|cx| Buffer::new(0, sample_text(10, 3, 'm'), cx));
4158
4159 let leader_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4160 let follower_multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4161 let follower_edit_event_count = Rc::new(RefCell::new(0));
4162
4163 follower_multibuffer.update(cx, |_, cx| {
4164 let follower_edit_event_count = follower_edit_event_count.clone();
4165 cx.subscribe(
4166 &leader_multibuffer,
4167 move |follower, _, event, cx| match event.clone() {
4168 Event::ExcerptsAdded {
4169 buffer,
4170 predecessor,
4171 excerpts,
4172 } => follower.insert_excerpts_with_ids_after(predecessor, buffer, excerpts, cx),
4173 Event::ExcerptsRemoved { ids } => follower.remove_excerpts(ids, cx),
4174 Event::Edited => {
4175 *follower_edit_event_count.borrow_mut() += 1;
4176 }
4177 _ => {}
4178 },
4179 )
4180 .detach();
4181 });
4182
4183 leader_multibuffer.update(cx, |leader, cx| {
4184 leader.push_excerpts(
4185 buffer_1.clone(),
4186 [
4187 ExcerptRange {
4188 context: 0..8,
4189 primary: None,
4190 },
4191 ExcerptRange {
4192 context: 12..16,
4193 primary: None,
4194 },
4195 ],
4196 cx,
4197 );
4198 leader.insert_excerpts_after(
4199 leader.excerpt_ids()[0],
4200 buffer_2.clone(),
4201 [
4202 ExcerptRange {
4203 context: 0..5,
4204 primary: None,
4205 },
4206 ExcerptRange {
4207 context: 10..15,
4208 primary: None,
4209 },
4210 ],
4211 cx,
4212 )
4213 });
4214 assert_eq!(
4215 leader_multibuffer.read(cx).snapshot(cx).text(),
4216 follower_multibuffer.read(cx).snapshot(cx).text(),
4217 );
4218 assert_eq!(*follower_edit_event_count.borrow(), 2);
4219
4220 leader_multibuffer.update(cx, |leader, cx| {
4221 let excerpt_ids = leader.excerpt_ids();
4222 leader.remove_excerpts([excerpt_ids[1], excerpt_ids[3]], cx);
4223 });
4224 assert_eq!(
4225 leader_multibuffer.read(cx).snapshot(cx).text(),
4226 follower_multibuffer.read(cx).snapshot(cx).text(),
4227 );
4228 assert_eq!(*follower_edit_event_count.borrow(), 3);
4229
4230 // Removing an empty set of excerpts is a noop.
4231 leader_multibuffer.update(cx, |leader, cx| {
4232 leader.remove_excerpts([], cx);
4233 });
4234 assert_eq!(
4235 leader_multibuffer.read(cx).snapshot(cx).text(),
4236 follower_multibuffer.read(cx).snapshot(cx).text(),
4237 );
4238 assert_eq!(*follower_edit_event_count.borrow(), 3);
4239
4240 // Adding an empty set of excerpts is a noop.
4241 leader_multibuffer.update(cx, |leader, cx| {
4242 leader.push_excerpts::<usize>(buffer_2.clone(), [], cx);
4243 });
4244 assert_eq!(
4245 leader_multibuffer.read(cx).snapshot(cx).text(),
4246 follower_multibuffer.read(cx).snapshot(cx).text(),
4247 );
4248 assert_eq!(*follower_edit_event_count.borrow(), 3);
4249
4250 leader_multibuffer.update(cx, |leader, cx| {
4251 leader.clear(cx);
4252 });
4253 assert_eq!(
4254 leader_multibuffer.read(cx).snapshot(cx).text(),
4255 follower_multibuffer.read(cx).snapshot(cx).text(),
4256 );
4257 assert_eq!(*follower_edit_event_count.borrow(), 4);
4258 }
4259
4260 #[gpui::test]
4261 fn test_push_excerpts_with_context_lines(cx: &mut AppContext) {
4262 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(20, 3, 'a'), cx));
4263 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4264 let anchor_ranges = multibuffer.update(cx, |multibuffer, cx| {
4265 multibuffer.push_excerpts_with_context_lines(
4266 buffer.clone(),
4267 vec![
4268 Point::new(3, 2)..Point::new(4, 2),
4269 Point::new(7, 1)..Point::new(7, 3),
4270 Point::new(15, 0)..Point::new(15, 0),
4271 ],
4272 2,
4273 cx,
4274 )
4275 });
4276
4277 let snapshot = multibuffer.read(cx).snapshot(cx);
4278 assert_eq!(
4279 snapshot.text(),
4280 "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n"
4281 );
4282
4283 assert_eq!(
4284 anchor_ranges
4285 .iter()
4286 .map(|range| range.to_point(&snapshot))
4287 .collect::<Vec<_>>(),
4288 vec![
4289 Point::new(2, 2)..Point::new(3, 2),
4290 Point::new(6, 1)..Point::new(6, 3),
4291 Point::new(12, 0)..Point::new(12, 0)
4292 ]
4293 );
4294 }
4295
4296 #[gpui::test]
4297 async fn test_stream_excerpts_with_context_lines(cx: &mut TestAppContext) {
4298 let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(20, 3, 'a'), cx));
4299 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4300 let (task, anchor_ranges) = multibuffer.update(cx, |multibuffer, cx| {
4301 let snapshot = buffer.read(cx);
4302 let ranges = vec![
4303 snapshot.anchor_before(Point::new(3, 2))..snapshot.anchor_before(Point::new(4, 2)),
4304 snapshot.anchor_before(Point::new(7, 1))..snapshot.anchor_before(Point::new(7, 3)),
4305 snapshot.anchor_before(Point::new(15, 0))
4306 ..snapshot.anchor_before(Point::new(15, 0)),
4307 ];
4308 multibuffer.stream_excerpts_with_context_lines(vec![(buffer.clone(), ranges)], 2, cx)
4309 });
4310
4311 let anchor_ranges = anchor_ranges.collect::<Vec<_>>().await;
4312 // Ensure task is finished when stream completes.
4313 task.await;
4314
4315 let snapshot = multibuffer.read_with(cx, |multibuffer, cx| multibuffer.snapshot(cx));
4316 assert_eq!(
4317 snapshot.text(),
4318 "bbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\n\nnnn\nooo\nppp\nqqq\nrrr\n"
4319 );
4320
4321 assert_eq!(
4322 anchor_ranges
4323 .iter()
4324 .map(|range| range.to_point(&snapshot))
4325 .collect::<Vec<_>>(),
4326 vec![
4327 Point::new(2, 2)..Point::new(3, 2),
4328 Point::new(6, 1)..Point::new(6, 3),
4329 Point::new(12, 0)..Point::new(12, 0)
4330 ]
4331 );
4332 }
4333
4334 #[gpui::test]
4335 fn test_empty_multibuffer(cx: &mut AppContext) {
4336 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4337
4338 let snapshot = multibuffer.read(cx).snapshot(cx);
4339 assert_eq!(snapshot.text(), "");
4340 assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), &[Some(0)]);
4341 assert_eq!(snapshot.buffer_rows(1).collect::<Vec<_>>(), &[]);
4342 }
4343
4344 #[gpui::test]
4345 fn test_singleton_multibuffer_anchors(cx: &mut AppContext) {
4346 let buffer = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
4347 let multibuffer = cx.add_model(|cx| MultiBuffer::singleton(buffer.clone(), cx));
4348 let old_snapshot = multibuffer.read(cx).snapshot(cx);
4349 buffer.update(cx, |buffer, cx| {
4350 buffer.edit([(0..0, "X")], None, cx);
4351 buffer.edit([(5..5, "Y")], None, cx);
4352 });
4353 let new_snapshot = multibuffer.read(cx).snapshot(cx);
4354
4355 assert_eq!(old_snapshot.text(), "abcd");
4356 assert_eq!(new_snapshot.text(), "XabcdY");
4357
4358 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
4359 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
4360 assert_eq!(old_snapshot.anchor_before(4).to_offset(&new_snapshot), 5);
4361 assert_eq!(old_snapshot.anchor_after(4).to_offset(&new_snapshot), 6);
4362 }
4363
4364 #[gpui::test]
4365 fn test_multibuffer_anchors(cx: &mut AppContext) {
4366 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
4367 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "efghi", cx));
4368 let multibuffer = cx.add_model(|cx| {
4369 let mut multibuffer = MultiBuffer::new(0);
4370 multibuffer.push_excerpts(
4371 buffer_1.clone(),
4372 [ExcerptRange {
4373 context: 0..4,
4374 primary: None,
4375 }],
4376 cx,
4377 );
4378 multibuffer.push_excerpts(
4379 buffer_2.clone(),
4380 [ExcerptRange {
4381 context: 0..5,
4382 primary: None,
4383 }],
4384 cx,
4385 );
4386 multibuffer
4387 });
4388 let old_snapshot = multibuffer.read(cx).snapshot(cx);
4389
4390 assert_eq!(old_snapshot.anchor_before(0).to_offset(&old_snapshot), 0);
4391 assert_eq!(old_snapshot.anchor_after(0).to_offset(&old_snapshot), 0);
4392 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
4393 assert_eq!(Anchor::min().to_offset(&old_snapshot), 0);
4394 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
4395 assert_eq!(Anchor::max().to_offset(&old_snapshot), 10);
4396
4397 buffer_1.update(cx, |buffer, cx| {
4398 buffer.edit([(0..0, "W")], None, cx);
4399 buffer.edit([(5..5, "X")], None, cx);
4400 });
4401 buffer_2.update(cx, |buffer, cx| {
4402 buffer.edit([(0..0, "Y")], None, cx);
4403 buffer.edit([(6..6, "Z")], None, cx);
4404 });
4405 let new_snapshot = multibuffer.read(cx).snapshot(cx);
4406
4407 assert_eq!(old_snapshot.text(), "abcd\nefghi");
4408 assert_eq!(new_snapshot.text(), "WabcdX\nYefghiZ");
4409
4410 assert_eq!(old_snapshot.anchor_before(0).to_offset(&new_snapshot), 0);
4411 assert_eq!(old_snapshot.anchor_after(0).to_offset(&new_snapshot), 1);
4412 assert_eq!(old_snapshot.anchor_before(1).to_offset(&new_snapshot), 2);
4413 assert_eq!(old_snapshot.anchor_after(1).to_offset(&new_snapshot), 2);
4414 assert_eq!(old_snapshot.anchor_before(2).to_offset(&new_snapshot), 3);
4415 assert_eq!(old_snapshot.anchor_after(2).to_offset(&new_snapshot), 3);
4416 assert_eq!(old_snapshot.anchor_before(5).to_offset(&new_snapshot), 7);
4417 assert_eq!(old_snapshot.anchor_after(5).to_offset(&new_snapshot), 8);
4418 assert_eq!(old_snapshot.anchor_before(10).to_offset(&new_snapshot), 13);
4419 assert_eq!(old_snapshot.anchor_after(10).to_offset(&new_snapshot), 14);
4420 }
4421
4422 #[gpui::test]
4423 fn test_resolving_anchors_after_replacing_their_excerpts(cx: &mut AppContext) {
4424 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "abcd", cx));
4425 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "ABCDEFGHIJKLMNOP", cx));
4426 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4427
4428 // Create an insertion id in buffer 1 that doesn't exist in buffer 2.
4429 // Add an excerpt from buffer 1 that spans this new insertion.
4430 buffer_1.update(cx, |buffer, cx| buffer.edit([(4..4, "123")], None, cx));
4431 let excerpt_id_1 = multibuffer.update(cx, |multibuffer, cx| {
4432 multibuffer
4433 .push_excerpts(
4434 buffer_1.clone(),
4435 [ExcerptRange {
4436 context: 0..7,
4437 primary: None,
4438 }],
4439 cx,
4440 )
4441 .pop()
4442 .unwrap()
4443 });
4444
4445 let snapshot_1 = multibuffer.read(cx).snapshot(cx);
4446 assert_eq!(snapshot_1.text(), "abcd123");
4447
4448 // Replace the buffer 1 excerpt with new excerpts from buffer 2.
4449 let (excerpt_id_2, excerpt_id_3) = multibuffer.update(cx, |multibuffer, cx| {
4450 multibuffer.remove_excerpts([excerpt_id_1], cx);
4451 let mut ids = multibuffer
4452 .push_excerpts(
4453 buffer_2.clone(),
4454 [
4455 ExcerptRange {
4456 context: 0..4,
4457 primary: None,
4458 },
4459 ExcerptRange {
4460 context: 6..10,
4461 primary: None,
4462 },
4463 ExcerptRange {
4464 context: 12..16,
4465 primary: None,
4466 },
4467 ],
4468 cx,
4469 )
4470 .into_iter();
4471 (ids.next().unwrap(), ids.next().unwrap())
4472 });
4473 let snapshot_2 = multibuffer.read(cx).snapshot(cx);
4474 assert_eq!(snapshot_2.text(), "ABCD\nGHIJ\nMNOP");
4475
4476 // The old excerpt id doesn't get reused.
4477 assert_ne!(excerpt_id_2, excerpt_id_1);
4478
4479 // Resolve some anchors from the previous snapshot in the new snapshot.
4480 // The current excerpts are from a different buffer, so we don't attempt to
4481 // resolve the old text anchor in the new buffer.
4482 assert_eq!(
4483 snapshot_2.summary_for_anchor::<usize>(&snapshot_1.anchor_before(2)),
4484 0
4485 );
4486 assert_eq!(
4487 snapshot_2.summaries_for_anchors::<usize, _>(&[
4488 snapshot_1.anchor_before(2),
4489 snapshot_1.anchor_after(3)
4490 ]),
4491 vec![0, 0]
4492 );
4493
4494 // Refresh anchors from the old snapshot. The return value indicates that both
4495 // anchors lost their original excerpt.
4496 let refresh =
4497 snapshot_2.refresh_anchors(&[snapshot_1.anchor_before(2), snapshot_1.anchor_after(3)]);
4498 assert_eq!(
4499 refresh,
4500 &[
4501 (0, snapshot_2.anchor_before(0), false),
4502 (1, snapshot_2.anchor_after(0), false),
4503 ]
4504 );
4505
4506 // Replace the middle excerpt with a smaller excerpt in buffer 2,
4507 // that intersects the old excerpt.
4508 let excerpt_id_5 = multibuffer.update(cx, |multibuffer, cx| {
4509 multibuffer.remove_excerpts([excerpt_id_3], cx);
4510 multibuffer
4511 .insert_excerpts_after(
4512 excerpt_id_2,
4513 buffer_2.clone(),
4514 [ExcerptRange {
4515 context: 5..8,
4516 primary: None,
4517 }],
4518 cx,
4519 )
4520 .pop()
4521 .unwrap()
4522 });
4523
4524 let snapshot_3 = multibuffer.read(cx).snapshot(cx);
4525 assert_eq!(snapshot_3.text(), "ABCD\nFGH\nMNOP");
4526 assert_ne!(excerpt_id_5, excerpt_id_3);
4527
4528 // Resolve some anchors from the previous snapshot in the new snapshot.
4529 // The third anchor can't be resolved, since its excerpt has been removed,
4530 // so it resolves to the same position as its predecessor.
4531 let anchors = [
4532 snapshot_2.anchor_before(0),
4533 snapshot_2.anchor_after(2),
4534 snapshot_2.anchor_after(6),
4535 snapshot_2.anchor_after(14),
4536 ];
4537 assert_eq!(
4538 snapshot_3.summaries_for_anchors::<usize, _>(&anchors),
4539 &[0, 2, 9, 13]
4540 );
4541
4542 let new_anchors = snapshot_3.refresh_anchors(&anchors);
4543 assert_eq!(
4544 new_anchors.iter().map(|a| (a.0, a.2)).collect::<Vec<_>>(),
4545 &[(0, true), (1, true), (2, true), (3, true)]
4546 );
4547 assert_eq!(
4548 snapshot_3.summaries_for_anchors::<usize, _>(new_anchors.iter().map(|a| &a.1)),
4549 &[0, 2, 7, 13]
4550 );
4551 }
4552
4553 #[gpui::test]
4554 async fn test_diff_hunks_in_range(cx: &mut TestAppContext) {
4555 use git::diff::DiffHunkStatus;
4556
4557 // buffer has two modified hunks with two rows each
4558 let buffer_1 = cx.add_model(|cx| {
4559 let mut buffer = Buffer::new(
4560 0,
4561 "
4562 1.zero
4563 1.ONE
4564 1.TWO
4565 1.three
4566 1.FOUR
4567 1.FIVE
4568 1.six
4569 "
4570 .unindent(),
4571 cx,
4572 );
4573 buffer.set_diff_base(
4574 Some(
4575 "
4576 1.zero
4577 1.one
4578 1.two
4579 1.three
4580 1.four
4581 1.five
4582 1.six
4583 "
4584 .unindent(),
4585 ),
4586 cx,
4587 );
4588 buffer
4589 });
4590
4591 // buffer has a deletion hunk and an insertion hunk
4592 let buffer_2 = cx.add_model(|cx| {
4593 let mut buffer = Buffer::new(
4594 0,
4595 "
4596 2.zero
4597 2.one
4598 2.two
4599 2.three
4600 2.four
4601 2.five
4602 2.six
4603 "
4604 .unindent(),
4605 cx,
4606 );
4607 buffer.set_diff_base(
4608 Some(
4609 "
4610 2.zero
4611 2.one
4612 2.one-and-a-half
4613 2.two
4614 2.three
4615 2.four
4616 2.six
4617 "
4618 .unindent(),
4619 ),
4620 cx,
4621 );
4622 buffer
4623 });
4624
4625 cx.foreground().run_until_parked();
4626
4627 let multibuffer = cx.add_model(|cx| {
4628 let mut multibuffer = MultiBuffer::new(0);
4629 multibuffer.push_excerpts(
4630 buffer_1.clone(),
4631 [
4632 // excerpt ends in the middle of a modified hunk
4633 ExcerptRange {
4634 context: Point::new(0, 0)..Point::new(1, 5),
4635 primary: Default::default(),
4636 },
4637 // excerpt begins in the middle of a modified hunk
4638 ExcerptRange {
4639 context: Point::new(5, 0)..Point::new(6, 5),
4640 primary: Default::default(),
4641 },
4642 ],
4643 cx,
4644 );
4645 multibuffer.push_excerpts(
4646 buffer_2.clone(),
4647 [
4648 // excerpt ends at a deletion
4649 ExcerptRange {
4650 context: Point::new(0, 0)..Point::new(1, 5),
4651 primary: Default::default(),
4652 },
4653 // excerpt starts at a deletion
4654 ExcerptRange {
4655 context: Point::new(2, 0)..Point::new(2, 5),
4656 primary: Default::default(),
4657 },
4658 // excerpt fully contains a deletion hunk
4659 ExcerptRange {
4660 context: Point::new(1, 0)..Point::new(2, 5),
4661 primary: Default::default(),
4662 },
4663 // excerpt fully contains an insertion hunk
4664 ExcerptRange {
4665 context: Point::new(4, 0)..Point::new(6, 5),
4666 primary: Default::default(),
4667 },
4668 ],
4669 cx,
4670 );
4671 multibuffer
4672 });
4673
4674 let snapshot = multibuffer.read_with(cx, |b, cx| b.snapshot(cx));
4675
4676 assert_eq!(
4677 snapshot.text(),
4678 "
4679 1.zero
4680 1.ONE
4681 1.FIVE
4682 1.six
4683 2.zero
4684 2.one
4685 2.two
4686 2.one
4687 2.two
4688 2.four
4689 2.five
4690 2.six"
4691 .unindent()
4692 );
4693
4694 let expected = [
4695 (DiffHunkStatus::Modified, 1..2),
4696 (DiffHunkStatus::Modified, 2..3),
4697 //TODO: Define better when and where removed hunks show up at range extremities
4698 (DiffHunkStatus::Removed, 6..6),
4699 (DiffHunkStatus::Removed, 8..8),
4700 (DiffHunkStatus::Added, 10..11),
4701 ];
4702
4703 assert_eq!(
4704 snapshot
4705 .git_diff_hunks_in_range(0..12)
4706 .map(|hunk| (hunk.status(), hunk.buffer_range))
4707 .collect::<Vec<_>>(),
4708 &expected,
4709 );
4710
4711 assert_eq!(
4712 snapshot
4713 .git_diff_hunks_in_range_rev(0..12)
4714 .map(|hunk| (hunk.status(), hunk.buffer_range))
4715 .collect::<Vec<_>>(),
4716 expected
4717 .iter()
4718 .rev()
4719 .cloned()
4720 .collect::<Vec<_>>()
4721 .as_slice(),
4722 );
4723 }
4724
4725 #[gpui::test(iterations = 100)]
4726 fn test_random_multibuffer(cx: &mut AppContext, mut rng: StdRng) {
4727 let operations = env::var("OPERATIONS")
4728 .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
4729 .unwrap_or(10);
4730
4731 let mut buffers: Vec<ModelHandle<Buffer>> = Vec::new();
4732 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
4733 let mut excerpt_ids = Vec::<ExcerptId>::new();
4734 let mut expected_excerpts = Vec::<(ModelHandle<Buffer>, Range<text::Anchor>)>::new();
4735 let mut anchors = Vec::new();
4736 let mut old_versions = Vec::new();
4737
4738 for _ in 0..operations {
4739 match rng.gen_range(0..100) {
4740 0..=19 if !buffers.is_empty() => {
4741 let buffer = buffers.choose(&mut rng).unwrap();
4742 buffer.update(cx, |buf, cx| buf.randomly_edit(&mut rng, 5, cx));
4743 }
4744 20..=29 if !expected_excerpts.is_empty() => {
4745 let mut ids_to_remove = vec![];
4746 for _ in 0..rng.gen_range(1..=3) {
4747 if expected_excerpts.is_empty() {
4748 break;
4749 }
4750
4751 let ix = rng.gen_range(0..expected_excerpts.len());
4752 ids_to_remove.push(excerpt_ids.remove(ix));
4753 let (buffer, range) = expected_excerpts.remove(ix);
4754 let buffer = buffer.read(cx);
4755 log::info!(
4756 "Removing excerpt {}: {:?}",
4757 ix,
4758 buffer
4759 .text_for_range(range.to_offset(buffer))
4760 .collect::<String>(),
4761 );
4762 }
4763 let snapshot = multibuffer.read(cx).read(cx);
4764 ids_to_remove.sort_unstable_by(|a, b| a.cmp(&b, &snapshot));
4765 drop(snapshot);
4766 multibuffer.update(cx, |multibuffer, cx| {
4767 multibuffer.remove_excerpts(ids_to_remove, cx)
4768 });
4769 }
4770 30..=39 if !expected_excerpts.is_empty() => {
4771 let multibuffer = multibuffer.read(cx).read(cx);
4772 let offset =
4773 multibuffer.clip_offset(rng.gen_range(0..=multibuffer.len()), Bias::Left);
4774 let bias = if rng.gen() { Bias::Left } else { Bias::Right };
4775 log::info!("Creating anchor at {} with bias {:?}", offset, bias);
4776 anchors.push(multibuffer.anchor_at(offset, bias));
4777 anchors.sort_by(|a, b| a.cmp(b, &multibuffer));
4778 }
4779 40..=44 if !anchors.is_empty() => {
4780 let multibuffer = multibuffer.read(cx).read(cx);
4781 let prev_len = anchors.len();
4782 anchors = multibuffer
4783 .refresh_anchors(&anchors)
4784 .into_iter()
4785 .map(|a| a.1)
4786 .collect();
4787
4788 // Ensure the newly-refreshed anchors point to a valid excerpt and don't
4789 // overshoot its boundaries.
4790 assert_eq!(anchors.len(), prev_len);
4791 for anchor in &anchors {
4792 if anchor.excerpt_id == ExcerptId::min()
4793 || anchor.excerpt_id == ExcerptId::max()
4794 {
4795 continue;
4796 }
4797
4798 let excerpt = multibuffer.excerpt(anchor.excerpt_id).unwrap();
4799 assert_eq!(excerpt.id, anchor.excerpt_id);
4800 assert!(excerpt.contains(anchor));
4801 }
4802 }
4803 _ => {
4804 let buffer_handle = if buffers.is_empty() || rng.gen_bool(0.4) {
4805 let base_text = util::RandomCharIter::new(&mut rng)
4806 .take(10)
4807 .collect::<String>();
4808 buffers.push(cx.add_model(|cx| Buffer::new(0, base_text, cx)));
4809 buffers.last().unwrap()
4810 } else {
4811 buffers.choose(&mut rng).unwrap()
4812 };
4813
4814 let buffer = buffer_handle.read(cx);
4815 let end_ix = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Right);
4816 let start_ix = buffer.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
4817 let anchor_range = buffer.anchor_before(start_ix)..buffer.anchor_after(end_ix);
4818 let prev_excerpt_ix = rng.gen_range(0..=expected_excerpts.len());
4819 let prev_excerpt_id = excerpt_ids
4820 .get(prev_excerpt_ix)
4821 .cloned()
4822 .unwrap_or_else(ExcerptId::max);
4823 let excerpt_ix = (prev_excerpt_ix + 1).min(expected_excerpts.len());
4824
4825 log::info!(
4826 "Inserting excerpt at {} of {} for buffer {}: {:?}[{:?}] = {:?}",
4827 excerpt_ix,
4828 expected_excerpts.len(),
4829 buffer_handle.read(cx).remote_id(),
4830 buffer.text(),
4831 start_ix..end_ix,
4832 &buffer.text()[start_ix..end_ix]
4833 );
4834
4835 let excerpt_id = multibuffer.update(cx, |multibuffer, cx| {
4836 multibuffer
4837 .insert_excerpts_after(
4838 prev_excerpt_id,
4839 buffer_handle.clone(),
4840 [ExcerptRange {
4841 context: start_ix..end_ix,
4842 primary: None,
4843 }],
4844 cx,
4845 )
4846 .pop()
4847 .unwrap()
4848 });
4849
4850 excerpt_ids.insert(excerpt_ix, excerpt_id);
4851 expected_excerpts.insert(excerpt_ix, (buffer_handle.clone(), anchor_range));
4852 }
4853 }
4854
4855 if rng.gen_bool(0.3) {
4856 multibuffer.update(cx, |multibuffer, cx| {
4857 old_versions.push((multibuffer.snapshot(cx), multibuffer.subscribe()));
4858 })
4859 }
4860
4861 let snapshot = multibuffer.read(cx).snapshot(cx);
4862
4863 let mut excerpt_starts = Vec::new();
4864 let mut expected_text = String::new();
4865 let mut expected_buffer_rows = Vec::new();
4866 for (buffer, range) in &expected_excerpts {
4867 let buffer = buffer.read(cx);
4868 let buffer_range = range.to_offset(buffer);
4869
4870 excerpt_starts.push(TextSummary::from(expected_text.as_str()));
4871 expected_text.extend(buffer.text_for_range(buffer_range.clone()));
4872 expected_text.push('\n');
4873
4874 let buffer_row_range = buffer.offset_to_point(buffer_range.start).row
4875 ..=buffer.offset_to_point(buffer_range.end).row;
4876 for row in buffer_row_range {
4877 expected_buffer_rows.push(Some(row));
4878 }
4879 }
4880 // Remove final trailing newline.
4881 if !expected_excerpts.is_empty() {
4882 expected_text.pop();
4883 }
4884
4885 // Always report one buffer row
4886 if expected_buffer_rows.is_empty() {
4887 expected_buffer_rows.push(Some(0));
4888 }
4889
4890 assert_eq!(snapshot.text(), expected_text);
4891 log::info!("MultiBuffer text: {:?}", expected_text);
4892
4893 assert_eq!(
4894 snapshot.buffer_rows(0).collect::<Vec<_>>(),
4895 expected_buffer_rows,
4896 );
4897
4898 for _ in 0..5 {
4899 let start_row = rng.gen_range(0..=expected_buffer_rows.len());
4900 assert_eq!(
4901 snapshot.buffer_rows(start_row as u32).collect::<Vec<_>>(),
4902 &expected_buffer_rows[start_row..],
4903 "buffer_rows({})",
4904 start_row
4905 );
4906 }
4907
4908 assert_eq!(
4909 snapshot.max_buffer_row(),
4910 expected_buffer_rows.into_iter().flatten().max().unwrap()
4911 );
4912
4913 let mut excerpt_starts = excerpt_starts.into_iter();
4914 for (buffer, range) in &expected_excerpts {
4915 let buffer = buffer.read(cx);
4916 let buffer_id = buffer.remote_id();
4917 let buffer_range = range.to_offset(buffer);
4918 let buffer_start_point = buffer.offset_to_point(buffer_range.start);
4919 let buffer_start_point_utf16 =
4920 buffer.text_summary_for_range::<PointUtf16, _>(0..buffer_range.start);
4921
4922 let excerpt_start = excerpt_starts.next().unwrap();
4923 let mut offset = excerpt_start.len;
4924 let mut buffer_offset = buffer_range.start;
4925 let mut point = excerpt_start.lines;
4926 let mut buffer_point = buffer_start_point;
4927 let mut point_utf16 = excerpt_start.lines_utf16();
4928 let mut buffer_point_utf16 = buffer_start_point_utf16;
4929 for ch in buffer
4930 .snapshot()
4931 .chunks(buffer_range.clone(), false)
4932 .flat_map(|c| c.text.chars())
4933 {
4934 for _ in 0..ch.len_utf8() {
4935 let left_offset = snapshot.clip_offset(offset, Bias::Left);
4936 let right_offset = snapshot.clip_offset(offset, Bias::Right);
4937 let buffer_left_offset = buffer.clip_offset(buffer_offset, Bias::Left);
4938 let buffer_right_offset = buffer.clip_offset(buffer_offset, Bias::Right);
4939 assert_eq!(
4940 left_offset,
4941 excerpt_start.len + (buffer_left_offset - buffer_range.start),
4942 "clip_offset({:?}, Left). buffer: {:?}, buffer offset: {:?}",
4943 offset,
4944 buffer_id,
4945 buffer_offset,
4946 );
4947 assert_eq!(
4948 right_offset,
4949 excerpt_start.len + (buffer_right_offset - buffer_range.start),
4950 "clip_offset({:?}, Right). buffer: {:?}, buffer offset: {:?}",
4951 offset,
4952 buffer_id,
4953 buffer_offset,
4954 );
4955
4956 let left_point = snapshot.clip_point(point, Bias::Left);
4957 let right_point = snapshot.clip_point(point, Bias::Right);
4958 let buffer_left_point = buffer.clip_point(buffer_point, Bias::Left);
4959 let buffer_right_point = buffer.clip_point(buffer_point, Bias::Right);
4960 assert_eq!(
4961 left_point,
4962 excerpt_start.lines + (buffer_left_point - buffer_start_point),
4963 "clip_point({:?}, Left). buffer: {:?}, buffer point: {:?}",
4964 point,
4965 buffer_id,
4966 buffer_point,
4967 );
4968 assert_eq!(
4969 right_point,
4970 excerpt_start.lines + (buffer_right_point - buffer_start_point),
4971 "clip_point({:?}, Right). buffer: {:?}, buffer point: {:?}",
4972 point,
4973 buffer_id,
4974 buffer_point,
4975 );
4976
4977 assert_eq!(
4978 snapshot.point_to_offset(left_point),
4979 left_offset,
4980 "point_to_offset({:?})",
4981 left_point,
4982 );
4983 assert_eq!(
4984 snapshot.offset_to_point(left_offset),
4985 left_point,
4986 "offset_to_point({:?})",
4987 left_offset,
4988 );
4989
4990 offset += 1;
4991 buffer_offset += 1;
4992 if ch == '\n' {
4993 point += Point::new(1, 0);
4994 buffer_point += Point::new(1, 0);
4995 } else {
4996 point += Point::new(0, 1);
4997 buffer_point += Point::new(0, 1);
4998 }
4999 }
5000
5001 for _ in 0..ch.len_utf16() {
5002 let left_point_utf16 =
5003 snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Left);
5004 let right_point_utf16 =
5005 snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Right);
5006 let buffer_left_point_utf16 =
5007 buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Left);
5008 let buffer_right_point_utf16 =
5009 buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Right);
5010 assert_eq!(
5011 left_point_utf16,
5012 excerpt_start.lines_utf16()
5013 + (buffer_left_point_utf16 - buffer_start_point_utf16),
5014 "clip_point_utf16({:?}, Left). buffer: {:?}, buffer point_utf16: {:?}",
5015 point_utf16,
5016 buffer_id,
5017 buffer_point_utf16,
5018 );
5019 assert_eq!(
5020 right_point_utf16,
5021 excerpt_start.lines_utf16()
5022 + (buffer_right_point_utf16 - buffer_start_point_utf16),
5023 "clip_point_utf16({:?}, Right). buffer: {:?}, buffer point_utf16: {:?}",
5024 point_utf16,
5025 buffer_id,
5026 buffer_point_utf16,
5027 );
5028
5029 if ch == '\n' {
5030 point_utf16 += PointUtf16::new(1, 0);
5031 buffer_point_utf16 += PointUtf16::new(1, 0);
5032 } else {
5033 point_utf16 += PointUtf16::new(0, 1);
5034 buffer_point_utf16 += PointUtf16::new(0, 1);
5035 }
5036 }
5037 }
5038 }
5039
5040 for (row, line) in expected_text.split('\n').enumerate() {
5041 assert_eq!(
5042 snapshot.line_len(row as u32),
5043 line.len() as u32,
5044 "line_len({}).",
5045 row
5046 );
5047 }
5048
5049 let text_rope = Rope::from(expected_text.as_str());
5050 for _ in 0..10 {
5051 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
5052 let start_ix = text_rope.clip_offset(rng.gen_range(0..=end_ix), Bias::Left);
5053
5054 let text_for_range = snapshot
5055 .text_for_range(start_ix..end_ix)
5056 .collect::<String>();
5057 assert_eq!(
5058 text_for_range,
5059 &expected_text[start_ix..end_ix],
5060 "incorrect text for range {:?}",
5061 start_ix..end_ix
5062 );
5063
5064 let excerpted_buffer_ranges = multibuffer
5065 .read(cx)
5066 .range_to_buffer_ranges(start_ix..end_ix, cx);
5067 let excerpted_buffers_text = excerpted_buffer_ranges
5068 .iter()
5069 .map(|(buffer, buffer_range)| {
5070 buffer
5071 .read(cx)
5072 .text_for_range(buffer_range.clone())
5073 .collect::<String>()
5074 })
5075 .collect::<Vec<_>>()
5076 .join("\n");
5077 assert_eq!(excerpted_buffers_text, text_for_range);
5078 if !expected_excerpts.is_empty() {
5079 assert!(!excerpted_buffer_ranges.is_empty());
5080 }
5081
5082 let expected_summary = TextSummary::from(&expected_text[start_ix..end_ix]);
5083 assert_eq!(
5084 snapshot.text_summary_for_range::<TextSummary, _>(start_ix..end_ix),
5085 expected_summary,
5086 "incorrect summary for range {:?}",
5087 start_ix..end_ix
5088 );
5089 }
5090
5091 // Anchor resolution
5092 let summaries = snapshot.summaries_for_anchors::<usize, _>(&anchors);
5093 assert_eq!(anchors.len(), summaries.len());
5094 for (anchor, resolved_offset) in anchors.iter().zip(summaries) {
5095 assert!(resolved_offset <= snapshot.len());
5096 assert_eq!(
5097 snapshot.summary_for_anchor::<usize>(anchor),
5098 resolved_offset
5099 );
5100 }
5101
5102 for _ in 0..10 {
5103 let end_ix = text_rope.clip_offset(rng.gen_range(0..=text_rope.len()), Bias::Right);
5104 assert_eq!(
5105 snapshot.reversed_chars_at(end_ix).collect::<String>(),
5106 expected_text[..end_ix].chars().rev().collect::<String>(),
5107 );
5108 }
5109
5110 for _ in 0..10 {
5111 let end_ix = rng.gen_range(0..=text_rope.len());
5112 let start_ix = rng.gen_range(0..=end_ix);
5113 assert_eq!(
5114 snapshot
5115 .bytes_in_range(start_ix..end_ix)
5116 .flatten()
5117 .copied()
5118 .collect::<Vec<_>>(),
5119 expected_text.as_bytes()[start_ix..end_ix].to_vec(),
5120 "bytes_in_range({:?})",
5121 start_ix..end_ix,
5122 );
5123 }
5124 }
5125
5126 let snapshot = multibuffer.read(cx).snapshot(cx);
5127 for (old_snapshot, subscription) in old_versions {
5128 let edits = subscription.consume().into_inner();
5129
5130 log::info!(
5131 "applying subscription edits to old text: {:?}: {:?}",
5132 old_snapshot.text(),
5133 edits,
5134 );
5135
5136 let mut text = old_snapshot.text();
5137 for edit in edits {
5138 let new_text: String = snapshot.text_for_range(edit.new.clone()).collect();
5139 text.replace_range(edit.new.start..edit.new.start + edit.old.len(), &new_text);
5140 }
5141 assert_eq!(text.to_string(), snapshot.text());
5142 }
5143 }
5144
5145 #[gpui::test]
5146 fn test_history(cx: &mut AppContext) {
5147 cx.set_global(SettingsStore::test(cx));
5148
5149 let buffer_1 = cx.add_model(|cx| Buffer::new(0, "1234", cx));
5150 let buffer_2 = cx.add_model(|cx| Buffer::new(0, "5678", cx));
5151 let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
5152 let group_interval = multibuffer.read(cx).history.group_interval;
5153 multibuffer.update(cx, |multibuffer, cx| {
5154 multibuffer.push_excerpts(
5155 buffer_1.clone(),
5156 [ExcerptRange {
5157 context: 0..buffer_1.read(cx).len(),
5158 primary: None,
5159 }],
5160 cx,
5161 );
5162 multibuffer.push_excerpts(
5163 buffer_2.clone(),
5164 [ExcerptRange {
5165 context: 0..buffer_2.read(cx).len(),
5166 primary: None,
5167 }],
5168 cx,
5169 );
5170 });
5171
5172 let mut now = Instant::now();
5173
5174 multibuffer.update(cx, |multibuffer, cx| {
5175 let transaction_1 = multibuffer.start_transaction_at(now, cx).unwrap();
5176 multibuffer.edit(
5177 [
5178 (Point::new(0, 0)..Point::new(0, 0), "A"),
5179 (Point::new(1, 0)..Point::new(1, 0), "A"),
5180 ],
5181 None,
5182 cx,
5183 );
5184 multibuffer.edit(
5185 [
5186 (Point::new(0, 1)..Point::new(0, 1), "B"),
5187 (Point::new(1, 1)..Point::new(1, 1), "B"),
5188 ],
5189 None,
5190 cx,
5191 );
5192 multibuffer.end_transaction_at(now, cx);
5193 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
5194
5195 // Edit buffer 1 through the multibuffer
5196 now += 2 * group_interval;
5197 multibuffer.start_transaction_at(now, cx);
5198 multibuffer.edit([(2..2, "C")], None, cx);
5199 multibuffer.end_transaction_at(now, cx);
5200 assert_eq!(multibuffer.read(cx).text(), "ABC1234\nAB5678");
5201
5202 // Edit buffer 1 independently
5203 buffer_1.update(cx, |buffer_1, cx| {
5204 buffer_1.start_transaction_at(now);
5205 buffer_1.edit([(3..3, "D")], None, cx);
5206 buffer_1.end_transaction_at(now, cx);
5207
5208 now += 2 * group_interval;
5209 buffer_1.start_transaction_at(now);
5210 buffer_1.edit([(4..4, "E")], None, cx);
5211 buffer_1.end_transaction_at(now, cx);
5212 });
5213 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
5214
5215 // An undo in the multibuffer undoes the multibuffer transaction
5216 // and also any individual buffer edits that have occured since
5217 // that transaction.
5218 multibuffer.undo(cx);
5219 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
5220
5221 multibuffer.undo(cx);
5222 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
5223
5224 multibuffer.redo(cx);
5225 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
5226
5227 multibuffer.redo(cx);
5228 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\nAB5678");
5229
5230 // Undo buffer 2 independently.
5231 buffer_2.update(cx, |buffer_2, cx| buffer_2.undo(cx));
5232 assert_eq!(multibuffer.read(cx).text(), "ABCDE1234\n5678");
5233
5234 // An undo in the multibuffer undoes the components of the
5235 // the last multibuffer transaction that are not already undone.
5236 multibuffer.undo(cx);
5237 assert_eq!(multibuffer.read(cx).text(), "AB1234\n5678");
5238
5239 multibuffer.undo(cx);
5240 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
5241
5242 multibuffer.redo(cx);
5243 assert_eq!(multibuffer.read(cx).text(), "AB1234\nAB5678");
5244
5245 buffer_1.update(cx, |buffer_1, cx| buffer_1.redo(cx));
5246 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
5247
5248 // Redo stack gets cleared after an edit.
5249 now += 2 * group_interval;
5250 multibuffer.start_transaction_at(now, cx);
5251 multibuffer.edit([(0..0, "X")], None, cx);
5252 multibuffer.end_transaction_at(now, cx);
5253 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
5254 multibuffer.redo(cx);
5255 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
5256 multibuffer.undo(cx);
5257 assert_eq!(multibuffer.read(cx).text(), "ABCD1234\nAB5678");
5258 multibuffer.undo(cx);
5259 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
5260
5261 // Transactions can be grouped manually.
5262 multibuffer.redo(cx);
5263 multibuffer.redo(cx);
5264 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
5265 multibuffer.group_until_transaction(transaction_1, cx);
5266 multibuffer.undo(cx);
5267 assert_eq!(multibuffer.read(cx).text(), "1234\n5678");
5268 multibuffer.redo(cx);
5269 assert_eq!(multibuffer.read(cx).text(), "XABCD1234\nAB5678");
5270 });
5271 }
5272}