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