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