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