1mod anchor;
2#[cfg(test)]
3mod multi_buffer_tests;
4mod position;
5
6pub use anchor::{Anchor, AnchorRangeExt, Offset};
7pub use position::{TypedOffset, TypedPoint, TypedRow};
8
9use anyhow::{Result, anyhow};
10use buffer_diff::{
11 BufferDiff, BufferDiffEvent, BufferDiffSnapshot, DiffHunkSecondaryStatus, DiffHunkStatus,
12 DiffHunkStatusKind,
13};
14use clock::ReplicaId;
15use collections::{BTreeMap, Bound, HashMap, HashSet};
16use gpui::{App, AppContext as _, Context, Entity, EntityId, EventEmitter, Task};
17use itertools::Itertools;
18use language::{
19 AutoindentMode, Buffer, BufferChunks, BufferRow, BufferSnapshot, Capability, CharClassifier,
20 CharKind, CharScopeContext, Chunk, CursorShape, DiagnosticEntry, DiskState, File,
21 IndentGuideSettings, IndentSize, Language, LanguageScope, OffsetRangeExt, OffsetUtf16, Outline,
22 OutlineItem, Point, PointUtf16, Selection, TextDimension, TextObject, ToOffset as _,
23 ToPoint as _, TransactionId, TreeSitterOptions, Unclipped,
24 language_settings::{LanguageSettings, language_settings},
25};
26
27use rope::DimensionPair;
28use smallvec::SmallVec;
29use smol::future::yield_now;
30use std::{
31 any::type_name,
32 borrow::Cow,
33 cell::{Cell, Ref, RefCell},
34 cmp, fmt,
35 future::Future,
36 io,
37 iter::{self, FromIterator},
38 mem,
39 ops::{Range, RangeBounds, Sub},
40 rc::Rc,
41 str,
42 sync::Arc,
43 time::{Duration, Instant},
44};
45use sum_tree::{Bias, Cursor, Dimension, Dimensions, SumTree, Summary, TreeMap};
46use text::{
47 BufferId, Edit, LineIndent, TextSummary,
48 locator::Locator,
49 subscription::{Subscription, Topic},
50};
51use theme::SyntaxTheme;
52use util::post_inc;
53
54const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
55
56#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
57pub struct ExcerptId(u32);
58
59/// One or more [`Buffers`](Buffer) being edited in a single view.
60///
61/// See <https://zed.dev/features#multi-buffers>
62pub struct MultiBuffer {
63 /// A snapshot of the [`Excerpt`]s in the MultiBuffer.
64 /// Use [`MultiBuffer::snapshot`] to get a up-to-date snapshot.
65 snapshot: RefCell<MultiBufferSnapshot>,
66 /// Contains the state of the buffers being edited
67 buffers: RefCell<HashMap<BufferId, BufferState>>,
68 // only used by consumers using `set_excerpts_for_buffer`
69 excerpts_by_path: BTreeMap<PathKey, Vec<ExcerptId>>,
70 paths_by_excerpt: HashMap<ExcerptId, PathKey>,
71 diffs: HashMap<BufferId, DiffState>,
72 // all_diff_hunks_expanded: bool,
73 subscriptions: Topic,
74 /// If true, the multi-buffer only contains a single [`Buffer`] and a single [`Excerpt`]
75 singleton: bool,
76 history: History,
77 title: Option<String>,
78 capability: Capability,
79 buffer_changed_since_sync: Rc<Cell<bool>>,
80}
81
82#[derive(Clone, Debug, PartialEq, Eq)]
83pub enum MultiOrSingleBufferOffsetRange {
84 Single(Range<usize>),
85 Multi(Range<usize>),
86}
87
88#[derive(Clone, Debug, PartialEq, Eq)]
89pub enum Event {
90 ExcerptsAdded {
91 buffer: Entity<Buffer>,
92 predecessor: ExcerptId,
93 excerpts: Vec<(ExcerptId, ExcerptRange<language::Anchor>)>,
94 },
95 ExcerptsRemoved {
96 ids: Vec<ExcerptId>,
97 removed_buffer_ids: Vec<BufferId>,
98 },
99 ExcerptsExpanded {
100 ids: Vec<ExcerptId>,
101 },
102 ExcerptsEdited {
103 excerpt_ids: Vec<ExcerptId>,
104 buffer_ids: Vec<BufferId>,
105 },
106 DiffHunksToggled,
107 Edited {
108 singleton_buffer_edited: bool,
109 edited_buffer: Option<Entity<Buffer>>,
110 },
111 TransactionUndone {
112 transaction_id: TransactionId,
113 },
114 Reloaded,
115 LanguageChanged(BufferId),
116 Reparsed(BufferId),
117 Saved,
118 FileHandleChanged,
119 DirtyChanged,
120 DiagnosticsUpdated,
121 BufferDiffChanged,
122}
123
124/// A diff hunk, representing a range of consequent lines in a multibuffer.
125#[derive(Debug, Clone, PartialEq, Eq)]
126pub struct MultiBufferDiffHunk {
127 /// The row range in the multibuffer where this diff hunk appears.
128 pub row_range: Range<MultiBufferRow>,
129 /// The buffer ID that this hunk belongs to.
130 pub buffer_id: BufferId,
131 /// The range of the underlying buffer that this hunk corresponds to.
132 pub buffer_range: Range<text::Anchor>,
133 /// The excerpt that contains the diff hunk.
134 pub excerpt_id: ExcerptId,
135 /// The range within the buffer's diff base that this hunk corresponds to.
136 pub diff_base_byte_range: Range<usize>,
137 /// Whether or not this hunk also appears in the 'secondary diff'.
138 pub secondary_status: DiffHunkSecondaryStatus,
139}
140
141impl MultiBufferDiffHunk {
142 pub fn status(&self) -> DiffHunkStatus {
143 let kind = if self.buffer_range.start == self.buffer_range.end {
144 DiffHunkStatusKind::Deleted
145 } else if self.diff_base_byte_range.is_empty() {
146 DiffHunkStatusKind::Added
147 } else {
148 DiffHunkStatusKind::Modified
149 };
150 DiffHunkStatus {
151 kind,
152 secondary: self.secondary_status,
153 }
154 }
155
156 pub fn is_created_file(&self) -> bool {
157 self.diff_base_byte_range == (0..0)
158 && self.buffer_range == (text::Anchor::MIN..text::Anchor::MAX)
159 }
160
161 pub fn multi_buffer_range(&self) -> Range<Anchor> {
162 let start = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.start);
163 let end = Anchor::in_buffer(self.excerpt_id, self.buffer_id, self.buffer_range.end);
164 start..end
165 }
166}
167
168#[derive(PartialEq, Eq, Ord, PartialOrd, Clone, Hash, Debug)]
169pub struct PathKey {
170 namespace: u32,
171 path: Arc<str>,
172}
173
174impl PathKey {
175 pub fn namespaced(namespace: u32, path: Arc<str>) -> Self {
176 Self { namespace, path }
177 }
178
179 pub fn for_buffer(buffer: &Entity<Buffer>, cx: &App) -> Self {
180 if let Some(file) = buffer.read(cx).file() {
181 Self::namespaced(1, file.full_path(cx).to_string_lossy().into_owned().into())
182 } else {
183 Self::namespaced(0, buffer.entity_id().to_string().into())
184 }
185 }
186
187 pub fn path(&self) -> &Arc<str> {
188 &self.path
189 }
190}
191
192pub type MultiBufferPoint = Point;
193type ExcerptOffset = TypedOffset<Excerpt>;
194type ExcerptPoint = TypedPoint<Excerpt>;
195
196#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq, Hash, serde::Deserialize)]
197#[serde(transparent)]
198pub struct MultiBufferRow(pub u32);
199
200impl MultiBufferRow {
201 pub const MIN: Self = Self(0);
202 pub const MAX: Self = Self(u32::MAX);
203}
204
205impl std::ops::Add<usize> for MultiBufferRow {
206 type Output = Self;
207
208 fn add(self, rhs: usize) -> Self::Output {
209 MultiBufferRow(self.0 + rhs as u32)
210 }
211}
212
213#[derive(Clone)]
214struct History {
215 next_transaction_id: TransactionId,
216 undo_stack: Vec<Transaction>,
217 redo_stack: Vec<Transaction>,
218 transaction_depth: usize,
219 group_interval: Duration,
220}
221
222#[derive(Clone)]
223struct Transaction {
224 id: TransactionId,
225 buffer_transactions: HashMap<BufferId, text::TransactionId>,
226 first_edit_at: Instant,
227 last_edit_at: Instant,
228 suppress_grouping: bool,
229}
230
231pub trait ToOffset: 'static + fmt::Debug {
232 fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize;
233}
234
235pub trait ToOffsetUtf16: 'static + fmt::Debug {
236 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16;
237}
238
239pub trait ToPoint: 'static + fmt::Debug {
240 fn to_point(&self, snapshot: &MultiBufferSnapshot) -> Point;
241}
242
243pub trait ToPointUtf16: 'static + fmt::Debug {
244 fn to_point_utf16(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16;
245}
246
247struct BufferState {
248 buffer: Entity<Buffer>,
249 last_version: clock::Global,
250 last_non_text_state_update_count: usize,
251 excerpts: Vec<Locator>,
252 _subscriptions: [gpui::Subscription; 2],
253}
254
255struct DiffState {
256 diff: Entity<BufferDiff>,
257 _subscription: gpui::Subscription,
258}
259
260impl DiffState {
261 fn new(diff: Entity<BufferDiff>, cx: &mut Context<MultiBuffer>) -> Self {
262 DiffState {
263 _subscription: cx.subscribe(&diff, |this, diff, event, cx| match event {
264 BufferDiffEvent::DiffChanged { changed_range } => {
265 if let Some(changed_range) = changed_range.clone() {
266 this.buffer_diff_changed(diff, changed_range, cx)
267 }
268 cx.emit(Event::BufferDiffChanged);
269 }
270 BufferDiffEvent::LanguageChanged => this.buffer_diff_language_changed(diff, cx),
271 _ => {}
272 }),
273 diff,
274 }
275 }
276}
277
278/// The contents of a [`MultiBuffer`] at a single point in time.
279#[derive(Clone, Default)]
280pub struct MultiBufferSnapshot {
281 singleton: bool,
282 excerpts: SumTree<Excerpt>,
283 excerpt_ids: SumTree<ExcerptIdMapping>,
284 diffs: TreeMap<BufferId, BufferDiffSnapshot>,
285 diff_transforms: SumTree<DiffTransform>,
286 replaced_excerpts: TreeMap<ExcerptId, ExcerptId>,
287 trailing_excerpt_update_count: usize,
288 all_diff_hunks_expanded: bool,
289 non_text_state_update_count: usize,
290 edit_count: usize,
291 is_dirty: bool,
292 has_deleted_file: bool,
293 has_conflict: bool,
294 show_headers: bool,
295}
296
297#[derive(Debug, Clone)]
298enum DiffTransform {
299 BufferContent {
300 summary: TextSummary,
301 inserted_hunk_info: Option<DiffTransformHunkInfo>,
302 },
303 DeletedHunk {
304 summary: TextSummary,
305 buffer_id: BufferId,
306 hunk_info: DiffTransformHunkInfo,
307 base_text_byte_range: Range<usize>,
308 has_trailing_newline: bool,
309 },
310}
311
312#[derive(Clone, Copy, Debug)]
313struct DiffTransformHunkInfo {
314 excerpt_id: ExcerptId,
315 hunk_start_anchor: text::Anchor,
316 hunk_secondary_status: DiffHunkSecondaryStatus,
317}
318
319impl Eq for DiffTransformHunkInfo {}
320
321impl PartialEq for DiffTransformHunkInfo {
322 fn eq(&self, other: &DiffTransformHunkInfo) -> bool {
323 self.excerpt_id == other.excerpt_id && self.hunk_start_anchor == other.hunk_start_anchor
324 }
325}
326
327impl std::hash::Hash for DiffTransformHunkInfo {
328 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
329 self.excerpt_id.hash(state);
330 self.hunk_start_anchor.hash(state);
331 }
332}
333
334#[derive(Clone)]
335pub struct ExcerptInfo {
336 pub id: ExcerptId,
337 pub buffer: BufferSnapshot,
338 pub buffer_id: BufferId,
339 pub range: ExcerptRange<text::Anchor>,
340 pub end_row: MultiBufferRow,
341}
342
343impl std::fmt::Debug for ExcerptInfo {
344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
345 f.debug_struct(type_name::<Self>())
346 .field("id", &self.id)
347 .field("buffer_id", &self.buffer_id)
348 .field("path", &self.buffer.file().map(|f| f.path()))
349 .field("range", &self.range)
350 .finish()
351 }
352}
353
354/// A boundary between `Excerpt`s in a [`MultiBuffer`]
355#[derive(Debug)]
356pub struct ExcerptBoundary {
357 pub prev: Option<ExcerptInfo>,
358 pub next: ExcerptInfo,
359 /// The row in the `MultiBuffer` where the boundary is located
360 pub row: MultiBufferRow,
361}
362
363impl ExcerptBoundary {
364 pub fn starts_new_buffer(&self) -> bool {
365 match (self.prev.as_ref(), &self.next) {
366 (None, _) => true,
367 (Some(prev), next) => prev.buffer_id != next.buffer_id,
368 }
369 }
370}
371
372#[derive(Copy, Clone, Debug, PartialEq, Eq)]
373pub struct ExpandInfo {
374 pub direction: ExpandExcerptDirection,
375 pub excerpt_id: ExcerptId,
376}
377
378#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
379pub struct RowInfo {
380 pub buffer_id: Option<BufferId>,
381 pub buffer_row: Option<u32>,
382 pub multibuffer_row: Option<MultiBufferRow>,
383 pub diff_status: Option<buffer_diff::DiffHunkStatus>,
384 pub expand_info: Option<ExpandInfo>,
385}
386
387/// A slice into a [`Buffer`] that is being edited in a [`MultiBuffer`].
388#[derive(Clone)]
389struct Excerpt {
390 /// The unique identifier for this excerpt
391 id: ExcerptId,
392 /// The location of the excerpt in the [`MultiBuffer`]
393 locator: Locator,
394 /// The buffer being excerpted
395 buffer_id: BufferId,
396 /// A snapshot of the buffer being excerpted
397 buffer: BufferSnapshot,
398 /// The range of the buffer to be shown in the excerpt
399 range: ExcerptRange<text::Anchor>,
400 /// The last row in the excerpted slice of the buffer
401 max_buffer_row: BufferRow,
402 /// A summary of the text in the excerpt
403 text_summary: TextSummary,
404 has_trailing_newline: bool,
405}
406
407/// A public view into an `Excerpt` in a [`MultiBuffer`].
408///
409/// Contains methods for getting the [`Buffer`] of the excerpt,
410/// as well as mapping offsets to/from buffer and multibuffer coordinates.
411#[derive(Clone)]
412pub struct MultiBufferExcerpt<'a> {
413 excerpt: &'a Excerpt,
414 diff_transforms: sum_tree::Cursor<'a, 'static, DiffTransform, DiffTransforms<usize>>,
415 offset: usize,
416 excerpt_offset: ExcerptDimension<usize>,
417 buffer_offset: usize,
418}
419
420#[derive(Clone, Debug)]
421struct ExcerptIdMapping {
422 id: ExcerptId,
423 locator: Locator,
424}
425
426/// A range of text from a single [`Buffer`], to be shown as an `Excerpt`.
427/// These ranges are relative to the buffer itself
428#[derive(Clone, Debug, Eq, PartialEq, Hash)]
429pub struct ExcerptRange<T> {
430 /// The full range of text to be shown in the excerpt.
431 pub context: Range<T>,
432 /// The primary range of text to be highlighted in the excerpt.
433 /// In a multi-buffer search, this would be the text that matched the search
434 pub primary: Range<T>,
435}
436
437impl<T: Clone> ExcerptRange<T> {
438 pub fn new(context: Range<T>) -> Self {
439 Self {
440 context: context.clone(),
441 primary: context,
442 }
443 }
444}
445
446#[derive(Clone, Debug, Default)]
447pub struct ExcerptSummary {
448 excerpt_id: ExcerptId,
449 /// The location of the last [`Excerpt`] being summarized
450 excerpt_locator: Locator,
451 widest_line_number: u32,
452 text: TextSummary,
453}
454
455#[derive(Debug, Clone)]
456pub struct DiffTransformSummary {
457 input: TextSummary,
458 output: TextSummary,
459}
460
461#[derive(Clone)]
462pub struct MultiBufferRows<'a> {
463 point: Point,
464 is_empty: bool,
465 is_singleton: bool,
466 cursor: MultiBufferCursor<'a, Point>,
467}
468
469pub struct MultiBufferChunks<'a> {
470 excerpts: Cursor<'a, 'static, Excerpt, ExcerptOffset>,
471 diff_transforms: Cursor<'a, 'static, DiffTransform, Dimensions<usize, ExcerptOffset>>,
472 diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
473 diff_base_chunks: Option<(BufferId, BufferChunks<'a>)>,
474 buffer_chunk: Option<Chunk<'a>>,
475 range: Range<usize>,
476 excerpt_offset_range: Range<ExcerptOffset>,
477 excerpt_chunks: Option<ExcerptChunks<'a>>,
478 language_aware: bool,
479}
480
481pub struct ReversedMultiBufferChunks<'a> {
482 cursor: MultiBufferCursor<'a, usize>,
483 current_chunks: Option<rope::Chunks<'a>>,
484 start: usize,
485 offset: usize,
486}
487
488pub struct MultiBufferBytes<'a> {
489 range: Range<usize>,
490 cursor: MultiBufferCursor<'a, usize>,
491 excerpt_bytes: Option<text::Bytes<'a>>,
492 has_trailing_newline: bool,
493 chunk: &'a [u8],
494}
495
496pub struct ReversedMultiBufferBytes<'a> {
497 range: Range<usize>,
498 chunks: ReversedMultiBufferChunks<'a>,
499 chunk: &'a [u8],
500}
501
502#[derive(Clone)]
503struct DiffTransforms<D> {
504 output_dimension: OutputDimension<D>,
505 excerpt_dimension: ExcerptDimension<D>,
506}
507
508impl<'a, D: TextDimension> Dimension<'a, DiffTransformSummary> for DiffTransforms<D> {
509 fn zero(cx: <DiffTransformSummary as sum_tree::Summary>::Context<'_>) -> Self {
510 Self {
511 output_dimension: OutputDimension::zero(cx),
512 excerpt_dimension: <ExcerptDimension<D> as Dimension<'a, DiffTransformSummary>>::zero(
513 cx,
514 ),
515 }
516 }
517
518 fn add_summary(
519 &mut self,
520 summary: &'a DiffTransformSummary,
521 cx: <DiffTransformSummary as sum_tree::Summary>::Context<'_>,
522 ) {
523 self.output_dimension.add_summary(summary, cx);
524 self.excerpt_dimension.add_summary(summary, cx);
525 }
526}
527
528#[derive(Clone)]
529struct MultiBufferCursor<'a, D: TextDimension> {
530 excerpts: Cursor<'a, 'static, Excerpt, ExcerptDimension<D>>,
531 diff_transforms: Cursor<'a, 'static, DiffTransform, DiffTransforms<D>>,
532 diffs: &'a TreeMap<BufferId, BufferDiffSnapshot>,
533 cached_region: Option<MultiBufferRegion<'a, D>>,
534}
535
536#[derive(Clone)]
537struct MultiBufferRegion<'a, D: TextDimension> {
538 buffer: &'a BufferSnapshot,
539 is_main_buffer: bool,
540 diff_hunk_status: Option<DiffHunkStatus>,
541 excerpt: &'a Excerpt,
542 buffer_range: Range<D>,
543 range: Range<D>,
544 has_trailing_newline: bool,
545}
546
547struct ExcerptChunks<'a> {
548 excerpt_id: ExcerptId,
549 content_chunks: BufferChunks<'a>,
550 footer_height: usize,
551}
552
553#[derive(Debug)]
554struct BufferEdit {
555 range: Range<usize>,
556 new_text: Arc<str>,
557 is_insertion: bool,
558 original_indent_column: Option<u32>,
559 excerpt_id: ExcerptId,
560}
561
562#[derive(Clone, Copy, Debug, PartialEq)]
563enum DiffChangeKind {
564 BufferEdited,
565 DiffUpdated { base_changed: bool },
566 ExpandOrCollapseHunks { expand: bool },
567}
568
569#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
570pub enum ExpandExcerptDirection {
571 Up,
572 Down,
573 UpAndDown,
574}
575
576impl ExpandExcerptDirection {
577 pub fn should_expand_up(&self) -> bool {
578 match self {
579 ExpandExcerptDirection::Up => true,
580 ExpandExcerptDirection::Down => false,
581 ExpandExcerptDirection::UpAndDown => true,
582 }
583 }
584
585 pub fn should_expand_down(&self) -> bool {
586 match self {
587 ExpandExcerptDirection::Up => false,
588 ExpandExcerptDirection::Down => true,
589 ExpandExcerptDirection::UpAndDown => true,
590 }
591 }
592}
593
594#[derive(Clone, Debug, PartialEq)]
595pub struct IndentGuide {
596 pub buffer_id: BufferId,
597 pub start_row: MultiBufferRow,
598 pub end_row: MultiBufferRow,
599 pub depth: u32,
600 pub tab_size: u32,
601 pub settings: IndentGuideSettings,
602}
603
604impl IndentGuide {
605 pub fn indent_level(&self) -> u32 {
606 self.depth * self.tab_size
607 }
608}
609
610impl MultiBuffer {
611 pub fn new(capability: Capability) -> Self {
612 Self {
613 snapshot: RefCell::new(MultiBufferSnapshot {
614 show_headers: true,
615 ..MultiBufferSnapshot::default()
616 }),
617 buffers: RefCell::default(),
618 diffs: HashMap::default(),
619 subscriptions: Topic::default(),
620 singleton: false,
621 capability,
622 title: None,
623 excerpts_by_path: Default::default(),
624 paths_by_excerpt: Default::default(),
625 buffer_changed_since_sync: Default::default(),
626 history: History {
627 next_transaction_id: clock::Lamport::default(),
628 undo_stack: Vec::new(),
629 redo_stack: Vec::new(),
630 transaction_depth: 0,
631 group_interval: Duration::from_millis(300),
632 },
633 }
634 }
635
636 pub fn without_headers(capability: Capability) -> Self {
637 Self {
638 snapshot: Default::default(),
639 buffers: Default::default(),
640 excerpts_by_path: Default::default(),
641 paths_by_excerpt: Default::default(),
642 diffs: HashMap::default(),
643 subscriptions: Default::default(),
644 singleton: false,
645 capability,
646 buffer_changed_since_sync: Default::default(),
647 history: History {
648 next_transaction_id: Default::default(),
649 undo_stack: Default::default(),
650 redo_stack: Default::default(),
651 transaction_depth: 0,
652 group_interval: Duration::from_millis(300),
653 },
654 title: Default::default(),
655 }
656 }
657
658 pub fn clone(&self, new_cx: &mut Context<Self>) -> Self {
659 let mut buffers = HashMap::default();
660 let buffer_changed_since_sync = Rc::new(Cell::new(false));
661 for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
662 buffer_state.buffer.update(new_cx, |buffer, _| {
663 buffer.record_changes(Rc::downgrade(&buffer_changed_since_sync));
664 });
665 buffers.insert(
666 *buffer_id,
667 BufferState {
668 buffer: buffer_state.buffer.clone(),
669 last_version: buffer_state.last_version.clone(),
670 last_non_text_state_update_count: buffer_state.last_non_text_state_update_count,
671 excerpts: buffer_state.excerpts.clone(),
672 _subscriptions: [
673 new_cx.observe(&buffer_state.buffer, |_, _, cx| cx.notify()),
674 new_cx.subscribe(&buffer_state.buffer, Self::on_buffer_event),
675 ],
676 },
677 );
678 }
679 let mut diff_bases = HashMap::default();
680 for (buffer_id, diff) in self.diffs.iter() {
681 diff_bases.insert(*buffer_id, DiffState::new(diff.diff.clone(), new_cx));
682 }
683 Self {
684 snapshot: RefCell::new(self.snapshot.borrow().clone()),
685 buffers: RefCell::new(buffers),
686 excerpts_by_path: Default::default(),
687 paths_by_excerpt: Default::default(),
688 diffs: diff_bases,
689 subscriptions: Default::default(),
690 singleton: self.singleton,
691 capability: self.capability,
692 history: self.history.clone(),
693 title: self.title.clone(),
694 buffer_changed_since_sync,
695 }
696 }
697
698 pub fn with_title(mut self, title: String) -> Self {
699 self.title = Some(title);
700 self
701 }
702
703 pub fn read_only(&self) -> bool {
704 self.capability == Capability::ReadOnly
705 }
706
707 pub fn singleton(buffer: Entity<Buffer>, cx: &mut Context<Self>) -> Self {
708 let mut this = Self::new(buffer.read(cx).capability());
709 this.singleton = true;
710 this.push_excerpts(
711 buffer,
712 [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
713 cx,
714 );
715 this.snapshot.borrow_mut().singleton = true;
716 this
717 }
718
719 /// Returns an up-to-date snapshot of the MultiBuffer.
720 pub fn snapshot(&self, cx: &App) -> MultiBufferSnapshot {
721 self.sync(cx);
722 self.snapshot.borrow().clone()
723 }
724
725 pub fn read(&self, cx: &App) -> Ref<'_, MultiBufferSnapshot> {
726 self.sync(cx);
727 self.snapshot.borrow()
728 }
729
730 pub fn as_singleton(&self) -> Option<Entity<Buffer>> {
731 if self.singleton {
732 Some(
733 self.buffers
734 .borrow()
735 .values()
736 .next()
737 .unwrap()
738 .buffer
739 .clone(),
740 )
741 } else {
742 None
743 }
744 }
745
746 pub fn is_singleton(&self) -> bool {
747 self.singleton
748 }
749
750 pub fn subscribe(&mut self) -> Subscription {
751 self.subscriptions.subscribe()
752 }
753
754 pub fn is_dirty(&self, cx: &App) -> bool {
755 self.read(cx).is_dirty()
756 }
757
758 pub fn has_deleted_file(&self, cx: &App) -> bool {
759 self.read(cx).has_deleted_file()
760 }
761
762 pub fn has_conflict(&self, cx: &App) -> bool {
763 self.read(cx).has_conflict()
764 }
765
766 // The `is_empty` signature doesn't match what clippy expects
767 #[allow(clippy::len_without_is_empty)]
768 pub fn len(&self, cx: &App) -> usize {
769 self.read(cx).len()
770 }
771
772 pub fn is_empty(&self) -> bool {
773 self.buffers.borrow().is_empty()
774 }
775
776 pub fn symbols_containing<T: ToOffset>(
777 &self,
778 offset: T,
779 theme: Option<&SyntaxTheme>,
780 cx: &App,
781 ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {
782 self.read(cx).symbols_containing(offset, theme)
783 }
784
785 pub fn edit<I, S, T>(
786 &self,
787 edits: I,
788 autoindent_mode: Option<AutoindentMode>,
789 cx: &mut Context<Self>,
790 ) where
791 I: IntoIterator<Item = (Range<S>, T)>,
792 S: ToOffset,
793 T: Into<Arc<str>>,
794 {
795 let snapshot = self.read(cx);
796 let edits = edits
797 .into_iter()
798 .map(|(range, new_text)| {
799 let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);
800 if range.start > range.end {
801 mem::swap(&mut range.start, &mut range.end);
802 }
803 (range, new_text.into())
804 })
805 .collect::<Vec<_>>();
806
807 return edit_internal(self, snapshot, edits, autoindent_mode, cx);
808
809 // Non-generic part of edit, hoisted out to avoid blowing up LLVM IR.
810 fn edit_internal(
811 this: &MultiBuffer,
812 snapshot: Ref<MultiBufferSnapshot>,
813 edits: Vec<(Range<usize>, Arc<str>)>,
814 mut autoindent_mode: Option<AutoindentMode>,
815 cx: &mut Context<MultiBuffer>,
816 ) {
817 if this.read_only() || this.buffers.borrow().is_empty() {
818 return;
819 }
820
821 let original_indent_columns = match &mut autoindent_mode {
822 Some(AutoindentMode::Block {
823 original_indent_columns,
824 }) => mem::take(original_indent_columns),
825 _ => Default::default(),
826 };
827
828 let (buffer_edits, edited_excerpt_ids) =
829 this.convert_edits_to_buffer_edits(edits, &snapshot, &original_indent_columns);
830 drop(snapshot);
831
832 let mut buffer_ids = Vec::with_capacity(buffer_edits.len());
833 for (buffer_id, mut edits) in buffer_edits {
834 buffer_ids.push(buffer_id);
835 edits.sort_by_key(|edit| edit.range.start);
836 this.buffers.borrow()[&buffer_id]
837 .buffer
838 .update(cx, |buffer, cx| {
839 let mut edits = edits.into_iter().peekable();
840 let mut insertions = Vec::new();
841 let mut original_indent_columns = Vec::new();
842 let mut deletions = Vec::new();
843 let empty_str: Arc<str> = Arc::default();
844 while let Some(BufferEdit {
845 mut range,
846 mut new_text,
847 mut is_insertion,
848 original_indent_column,
849 excerpt_id,
850 }) = edits.next()
851 {
852 while let Some(BufferEdit {
853 range: next_range,
854 is_insertion: next_is_insertion,
855 new_text: next_new_text,
856 excerpt_id: next_excerpt_id,
857 ..
858 }) = edits.peek()
859 {
860 if range.end >= next_range.start {
861 range.end = cmp::max(next_range.end, range.end);
862 is_insertion |= *next_is_insertion;
863 if excerpt_id == *next_excerpt_id {
864 new_text = format!("{new_text}{next_new_text}").into();
865 }
866 edits.next();
867 } else {
868 break;
869 }
870 }
871
872 if is_insertion {
873 original_indent_columns.push(original_indent_column);
874 insertions.push((
875 buffer.anchor_before(range.start)
876 ..buffer.anchor_before(range.end),
877 new_text.clone(),
878 ));
879 } else if !range.is_empty() {
880 deletions.push((
881 buffer.anchor_before(range.start)
882 ..buffer.anchor_before(range.end),
883 empty_str.clone(),
884 ));
885 }
886 }
887
888 let deletion_autoindent_mode =
889 if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
890 Some(AutoindentMode::Block {
891 original_indent_columns: Default::default(),
892 })
893 } else {
894 autoindent_mode.clone()
895 };
896 let insertion_autoindent_mode =
897 if let Some(AutoindentMode::Block { .. }) = autoindent_mode {
898 Some(AutoindentMode::Block {
899 original_indent_columns,
900 })
901 } else {
902 autoindent_mode.clone()
903 };
904
905 buffer.edit(deletions, deletion_autoindent_mode, cx);
906 buffer.edit(insertions, insertion_autoindent_mode, cx);
907 })
908 }
909
910 cx.emit(Event::ExcerptsEdited {
911 excerpt_ids: edited_excerpt_ids,
912 buffer_ids,
913 });
914 }
915 }
916
917 fn convert_edits_to_buffer_edits(
918 &self,
919 edits: Vec<(Range<usize>, Arc<str>)>,
920 snapshot: &MultiBufferSnapshot,
921 original_indent_columns: &[Option<u32>],
922 ) -> (HashMap<BufferId, Vec<BufferEdit>>, Vec<ExcerptId>) {
923 let mut buffer_edits: HashMap<BufferId, Vec<BufferEdit>> = Default::default();
924 let mut edited_excerpt_ids = Vec::new();
925 let mut cursor = snapshot.cursor::<usize>();
926 for (ix, (range, new_text)) in edits.into_iter().enumerate() {
927 let original_indent_column = original_indent_columns.get(ix).copied().flatten();
928
929 cursor.seek(&range.start);
930 let mut start_region = cursor.region().expect("start offset out of bounds");
931 if !start_region.is_main_buffer {
932 cursor.next();
933 if let Some(region) = cursor.region() {
934 start_region = region;
935 } else {
936 continue;
937 }
938 }
939
940 if range.end < start_region.range.start {
941 continue;
942 }
943
944 if range.end > start_region.range.end {
945 cursor.seek_forward(&range.end);
946 }
947 let mut end_region = cursor.region().expect("end offset out of bounds");
948 if !end_region.is_main_buffer {
949 cursor.prev();
950 if let Some(region) = cursor.region() {
951 end_region = region;
952 } else {
953 continue;
954 }
955 }
956
957 if range.start > end_region.range.end {
958 continue;
959 }
960
961 let start_overshoot = range.start.saturating_sub(start_region.range.start);
962 let end_overshoot = range.end.saturating_sub(end_region.range.start);
963 let buffer_start = (start_region.buffer_range.start + start_overshoot)
964 .min(start_region.buffer_range.end);
965 let buffer_end =
966 (end_region.buffer_range.start + end_overshoot).min(end_region.buffer_range.end);
967
968 if start_region.excerpt.id == end_region.excerpt.id {
969 if start_region.is_main_buffer {
970 edited_excerpt_ids.push(start_region.excerpt.id);
971 buffer_edits
972 .entry(start_region.buffer.remote_id())
973 .or_default()
974 .push(BufferEdit {
975 range: buffer_start..buffer_end,
976 new_text,
977 is_insertion: true,
978 original_indent_column,
979 excerpt_id: start_region.excerpt.id,
980 });
981 }
982 } else {
983 let start_excerpt_range = buffer_start..start_region.buffer_range.end;
984 let end_excerpt_range = end_region.buffer_range.start..buffer_end;
985 if start_region.is_main_buffer {
986 edited_excerpt_ids.push(start_region.excerpt.id);
987 buffer_edits
988 .entry(start_region.buffer.remote_id())
989 .or_default()
990 .push(BufferEdit {
991 range: start_excerpt_range,
992 new_text: new_text.clone(),
993 is_insertion: true,
994 original_indent_column,
995 excerpt_id: start_region.excerpt.id,
996 });
997 }
998 if end_region.is_main_buffer {
999 edited_excerpt_ids.push(end_region.excerpt.id);
1000 buffer_edits
1001 .entry(end_region.buffer.remote_id())
1002 .or_default()
1003 .push(BufferEdit {
1004 range: end_excerpt_range,
1005 new_text: new_text.clone(),
1006 is_insertion: false,
1007 original_indent_column,
1008 excerpt_id: end_region.excerpt.id,
1009 });
1010 }
1011
1012 cursor.seek(&range.start);
1013 cursor.next_excerpt();
1014 while let Some(region) = cursor.region() {
1015 if region.excerpt.id == end_region.excerpt.id {
1016 break;
1017 }
1018 if region.is_main_buffer {
1019 edited_excerpt_ids.push(region.excerpt.id);
1020 buffer_edits
1021 .entry(region.buffer.remote_id())
1022 .or_default()
1023 .push(BufferEdit {
1024 range: region.buffer_range,
1025 new_text: new_text.clone(),
1026 is_insertion: false,
1027 original_indent_column,
1028 excerpt_id: region.excerpt.id,
1029 });
1030 }
1031 cursor.next_excerpt();
1032 }
1033 }
1034 }
1035 (buffer_edits, edited_excerpt_ids)
1036 }
1037
1038 pub fn autoindent_ranges<I, S>(&self, ranges: I, cx: &mut Context<Self>)
1039 where
1040 I: IntoIterator<Item = Range<S>>,
1041 S: ToOffset,
1042 {
1043 let snapshot = self.read(cx);
1044 let empty = Arc::<str>::from("");
1045 let edits = ranges
1046 .into_iter()
1047 .map(|range| {
1048 let mut range = range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot);
1049 if range.start > range.end {
1050 mem::swap(&mut range.start, &mut range.end);
1051 }
1052 (range, empty.clone())
1053 })
1054 .collect::<Vec<_>>();
1055
1056 return autoindent_ranges_internal(self, snapshot, edits, cx);
1057
1058 fn autoindent_ranges_internal(
1059 this: &MultiBuffer,
1060 snapshot: Ref<MultiBufferSnapshot>,
1061 edits: Vec<(Range<usize>, Arc<str>)>,
1062 cx: &mut Context<MultiBuffer>,
1063 ) {
1064 if this.read_only() || this.buffers.borrow().is_empty() {
1065 return;
1066 }
1067
1068 let (buffer_edits, edited_excerpt_ids) =
1069 this.convert_edits_to_buffer_edits(edits, &snapshot, &[]);
1070 drop(snapshot);
1071
1072 let mut buffer_ids = Vec::new();
1073 for (buffer_id, mut edits) in buffer_edits {
1074 buffer_ids.push(buffer_id);
1075 edits.sort_unstable_by_key(|edit| edit.range.start);
1076
1077 let mut ranges: Vec<Range<usize>> = Vec::new();
1078 for edit in edits {
1079 if let Some(last_range) = ranges.last_mut()
1080 && edit.range.start <= last_range.end
1081 {
1082 last_range.end = last_range.end.max(edit.range.end);
1083 continue;
1084 }
1085 ranges.push(edit.range);
1086 }
1087
1088 this.buffers.borrow()[&buffer_id]
1089 .buffer
1090 .update(cx, |buffer, cx| {
1091 buffer.autoindent_ranges(ranges, cx);
1092 })
1093 }
1094
1095 cx.emit(Event::ExcerptsEdited {
1096 excerpt_ids: edited_excerpt_ids,
1097 buffer_ids,
1098 });
1099 }
1100 }
1101
1102 // Inserts newlines at the given position to create an empty line, returning the start of the new line.
1103 // You can also request the insertion of empty lines above and below the line starting at the returned point.
1104 // Panics if the given position is invalid.
1105 pub fn insert_empty_line(
1106 &mut self,
1107 position: impl ToPoint,
1108 space_above: bool,
1109 space_below: bool,
1110 cx: &mut Context<Self>,
1111 ) -> Point {
1112 let multibuffer_point = position.to_point(&self.read(cx));
1113 let (buffer, buffer_point, _) = self.point_to_buffer_point(multibuffer_point, cx).unwrap();
1114 self.start_transaction(cx);
1115 let empty_line_start = buffer.update(cx, |buffer, cx| {
1116 buffer.insert_empty_line(buffer_point, space_above, space_below, cx)
1117 });
1118 self.end_transaction(cx);
1119 multibuffer_point + (empty_line_start - buffer_point)
1120 }
1121
1122 pub fn start_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
1123 self.start_transaction_at(Instant::now(), cx)
1124 }
1125
1126 pub fn start_transaction_at(
1127 &mut self,
1128 now: Instant,
1129 cx: &mut Context<Self>,
1130 ) -> Option<TransactionId> {
1131 if let Some(buffer) = self.as_singleton() {
1132 return buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
1133 }
1134
1135 for BufferState { buffer, .. } in self.buffers.borrow().values() {
1136 buffer.update(cx, |buffer, _| buffer.start_transaction_at(now));
1137 }
1138 self.history.start_transaction(now)
1139 }
1140
1141 pub fn last_transaction_id(&self, cx: &App) -> Option<TransactionId> {
1142 if let Some(buffer) = self.as_singleton() {
1143 buffer
1144 .read(cx)
1145 .peek_undo_stack()
1146 .map(|history_entry| history_entry.transaction_id())
1147 } else {
1148 let last_transaction = self.history.undo_stack.last()?;
1149 Some(last_transaction.id)
1150 }
1151 }
1152
1153 pub fn end_transaction(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
1154 self.end_transaction_at(Instant::now(), cx)
1155 }
1156
1157 pub fn end_transaction_at(
1158 &mut self,
1159 now: Instant,
1160 cx: &mut Context<Self>,
1161 ) -> Option<TransactionId> {
1162 if let Some(buffer) = self.as_singleton() {
1163 return buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx));
1164 }
1165
1166 let mut buffer_transactions = HashMap::default();
1167 for BufferState { buffer, .. } in self.buffers.borrow().values() {
1168 if let Some(transaction_id) =
1169 buffer.update(cx, |buffer, cx| buffer.end_transaction_at(now, cx))
1170 {
1171 buffer_transactions.insert(buffer.read(cx).remote_id(), transaction_id);
1172 }
1173 }
1174
1175 if self.history.end_transaction(now, buffer_transactions) {
1176 let transaction_id = self.history.group().unwrap();
1177 Some(transaction_id)
1178 } else {
1179 None
1180 }
1181 }
1182
1183 pub fn edited_ranges_for_transaction<D>(
1184 &self,
1185 transaction_id: TransactionId,
1186 cx: &App,
1187 ) -> Vec<Range<D>>
1188 where
1189 D: TextDimension + Ord + Sub<D, Output = D>,
1190 {
1191 let Some(transaction) = self.history.transaction(transaction_id) else {
1192 return Vec::new();
1193 };
1194
1195 let mut ranges = Vec::new();
1196 let snapshot = self.read(cx);
1197 let buffers = self.buffers.borrow();
1198 let mut cursor = snapshot.excerpts.cursor::<ExcerptSummary>(());
1199
1200 for (buffer_id, buffer_transaction) in &transaction.buffer_transactions {
1201 let Some(buffer_state) = buffers.get(buffer_id) else {
1202 continue;
1203 };
1204
1205 let buffer = buffer_state.buffer.read(cx);
1206 for range in buffer.edited_ranges_for_transaction_id::<D>(*buffer_transaction) {
1207 for excerpt_id in &buffer_state.excerpts {
1208 cursor.seek(excerpt_id, Bias::Left);
1209 if let Some(excerpt) = cursor.item()
1210 && excerpt.locator == *excerpt_id
1211 {
1212 let excerpt_buffer_start = excerpt.range.context.start.summary::<D>(buffer);
1213 let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(buffer);
1214 let excerpt_range = excerpt_buffer_start..excerpt_buffer_end;
1215 if excerpt_range.contains(&range.start)
1216 && excerpt_range.contains(&range.end)
1217 {
1218 let excerpt_start = D::from_text_summary(&cursor.start().text);
1219
1220 let mut start = excerpt_start;
1221 start.add_assign(&(range.start - excerpt_buffer_start));
1222 let mut end = excerpt_start;
1223 end.add_assign(&(range.end - excerpt_buffer_start));
1224
1225 ranges.push(start..end);
1226 break;
1227 }
1228 }
1229 }
1230 }
1231 }
1232
1233 ranges.sort_by_key(|range| range.start);
1234 ranges
1235 }
1236
1237 pub fn merge_transactions(
1238 &mut self,
1239 transaction: TransactionId,
1240 destination: TransactionId,
1241 cx: &mut Context<Self>,
1242 ) {
1243 if let Some(buffer) = self.as_singleton() {
1244 buffer.update(cx, |buffer, _| {
1245 buffer.merge_transactions(transaction, destination)
1246 });
1247 } else if let Some(transaction) = self.history.forget(transaction)
1248 && let Some(destination) = self.history.transaction_mut(destination)
1249 {
1250 for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
1251 if let Some(destination_buffer_transaction_id) =
1252 destination.buffer_transactions.get(&buffer_id)
1253 {
1254 if let Some(state) = self.buffers.borrow().get(&buffer_id) {
1255 state.buffer.update(cx, |buffer, _| {
1256 buffer.merge_transactions(
1257 buffer_transaction_id,
1258 *destination_buffer_transaction_id,
1259 )
1260 });
1261 }
1262 } else {
1263 destination
1264 .buffer_transactions
1265 .insert(buffer_id, buffer_transaction_id);
1266 }
1267 }
1268 }
1269 }
1270
1271 pub fn finalize_last_transaction(&mut self, cx: &mut Context<Self>) {
1272 self.history.finalize_last_transaction();
1273 for BufferState { buffer, .. } in self.buffers.borrow().values() {
1274 buffer.update(cx, |buffer, _| {
1275 buffer.finalize_last_transaction();
1276 });
1277 }
1278 }
1279
1280 pub fn push_transaction<'a, T>(&mut self, buffer_transactions: T, cx: &Context<Self>)
1281 where
1282 T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
1283 {
1284 self.history
1285 .push_transaction(buffer_transactions, Instant::now(), cx);
1286 self.history.finalize_last_transaction();
1287 }
1288
1289 pub fn group_until_transaction(
1290 &mut self,
1291 transaction_id: TransactionId,
1292 cx: &mut Context<Self>,
1293 ) {
1294 if let Some(buffer) = self.as_singleton() {
1295 buffer.update(cx, |buffer, _| {
1296 buffer.group_until_transaction(transaction_id)
1297 });
1298 } else {
1299 self.history.group_until(transaction_id);
1300 }
1301 }
1302
1303 pub fn set_active_selections(
1304 &self,
1305 selections: &[Selection<Anchor>],
1306 line_mode: bool,
1307 cursor_shape: CursorShape,
1308 cx: &mut Context<Self>,
1309 ) {
1310 let mut selections_by_buffer: HashMap<BufferId, Vec<Selection<text::Anchor>>> =
1311 Default::default();
1312 let snapshot = self.read(cx);
1313 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
1314 for selection in selections {
1315 let start_locator = snapshot.excerpt_locator_for_id(selection.start.excerpt_id);
1316 let end_locator = snapshot.excerpt_locator_for_id(selection.end.excerpt_id);
1317
1318 cursor.seek(&Some(start_locator), Bias::Left);
1319 while let Some(excerpt) = cursor.item() {
1320 if excerpt.locator > *end_locator {
1321 break;
1322 }
1323
1324 let mut start = excerpt.range.context.start;
1325 let mut end = excerpt.range.context.end;
1326 if excerpt.id == selection.start.excerpt_id {
1327 start = selection.start.text_anchor;
1328 }
1329 if excerpt.id == selection.end.excerpt_id {
1330 end = selection.end.text_anchor;
1331 }
1332 selections_by_buffer
1333 .entry(excerpt.buffer_id)
1334 .or_default()
1335 .push(Selection {
1336 id: selection.id,
1337 start,
1338 end,
1339 reversed: selection.reversed,
1340 goal: selection.goal,
1341 });
1342
1343 cursor.next();
1344 }
1345 }
1346
1347 for (buffer_id, buffer_state) in self.buffers.borrow().iter() {
1348 if !selections_by_buffer.contains_key(buffer_id) {
1349 buffer_state
1350 .buffer
1351 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
1352 }
1353 }
1354
1355 for (buffer_id, mut selections) in selections_by_buffer {
1356 self.buffers.borrow()[&buffer_id]
1357 .buffer
1358 .update(cx, |buffer, cx| {
1359 selections.sort_unstable_by(|a, b| a.start.cmp(&b.start, buffer));
1360 let mut selections = selections.into_iter().peekable();
1361 let merged_selections = Arc::from_iter(iter::from_fn(|| {
1362 let mut selection = selections.next()?;
1363 while let Some(next_selection) = selections.peek() {
1364 if selection.end.cmp(&next_selection.start, buffer).is_ge() {
1365 let next_selection = selections.next().unwrap();
1366 if next_selection.end.cmp(&selection.end, buffer).is_ge() {
1367 selection.end = next_selection.end;
1368 }
1369 } else {
1370 break;
1371 }
1372 }
1373 Some(selection)
1374 }));
1375 buffer.set_active_selections(merged_selections, line_mode, cursor_shape, cx);
1376 });
1377 }
1378 }
1379
1380 pub fn remove_active_selections(&self, cx: &mut Context<Self>) {
1381 for buffer in self.buffers.borrow().values() {
1382 buffer
1383 .buffer
1384 .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
1385 }
1386 }
1387
1388 pub fn undo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
1389 let mut transaction_id = None;
1390 if let Some(buffer) = self.as_singleton() {
1391 transaction_id = buffer.update(cx, |buffer, cx| buffer.undo(cx));
1392 } else {
1393 while let Some(transaction) = self.history.pop_undo() {
1394 let mut undone = false;
1395 for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
1396 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
1397 undone |= buffer.update(cx, |buffer, cx| {
1398 let undo_to = *buffer_transaction_id;
1399 if let Some(entry) = buffer.peek_undo_stack() {
1400 *buffer_transaction_id = entry.transaction_id();
1401 }
1402 buffer.undo_to_transaction(undo_to, cx)
1403 });
1404 }
1405 }
1406
1407 if undone {
1408 transaction_id = Some(transaction.id);
1409 break;
1410 }
1411 }
1412 }
1413
1414 if let Some(transaction_id) = transaction_id {
1415 cx.emit(Event::TransactionUndone { transaction_id });
1416 }
1417
1418 transaction_id
1419 }
1420
1421 pub fn redo(&mut self, cx: &mut Context<Self>) -> Option<TransactionId> {
1422 if let Some(buffer) = self.as_singleton() {
1423 return buffer.update(cx, |buffer, cx| buffer.redo(cx));
1424 }
1425
1426 while let Some(transaction) = self.history.pop_redo() {
1427 let mut redone = false;
1428 for (buffer_id, buffer_transaction_id) in &mut transaction.buffer_transactions {
1429 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
1430 redone |= buffer.update(cx, |buffer, cx| {
1431 let redo_to = *buffer_transaction_id;
1432 if let Some(entry) = buffer.peek_redo_stack() {
1433 *buffer_transaction_id = entry.transaction_id();
1434 }
1435 buffer.redo_to_transaction(redo_to, cx)
1436 });
1437 }
1438 }
1439
1440 if redone {
1441 return Some(transaction.id);
1442 }
1443 }
1444
1445 None
1446 }
1447
1448 pub fn undo_transaction(&mut self, transaction_id: TransactionId, cx: &mut Context<Self>) {
1449 if let Some(buffer) = self.as_singleton() {
1450 buffer.update(cx, |buffer, cx| buffer.undo_transaction(transaction_id, cx));
1451 } else if let Some(transaction) = self.history.remove_from_undo(transaction_id) {
1452 for (buffer_id, transaction_id) in &transaction.buffer_transactions {
1453 if let Some(BufferState { buffer, .. }) = self.buffers.borrow().get(buffer_id) {
1454 buffer.update(cx, |buffer, cx| {
1455 buffer.undo_transaction(*transaction_id, cx)
1456 });
1457 }
1458 }
1459 }
1460 }
1461
1462 pub fn forget_transaction(&mut self, transaction_id: TransactionId, cx: &mut Context<Self>) {
1463 if let Some(buffer) = self.as_singleton() {
1464 buffer.update(cx, |buffer, _| {
1465 buffer.forget_transaction(transaction_id);
1466 });
1467 } else if let Some(transaction) = self.history.forget(transaction_id) {
1468 for (buffer_id, buffer_transaction_id) in transaction.buffer_transactions {
1469 if let Some(state) = self.buffers.borrow_mut().get_mut(&buffer_id) {
1470 state.buffer.update(cx, |buffer, _| {
1471 buffer.forget_transaction(buffer_transaction_id);
1472 });
1473 }
1474 }
1475 }
1476 }
1477
1478 pub fn push_excerpts<O>(
1479 &mut self,
1480 buffer: Entity<Buffer>,
1481 ranges: impl IntoIterator<Item = ExcerptRange<O>>,
1482 cx: &mut Context<Self>,
1483 ) -> Vec<ExcerptId>
1484 where
1485 O: text::ToOffset,
1486 {
1487 self.insert_excerpts_after(ExcerptId::max(), buffer, ranges, cx)
1488 }
1489
1490 pub fn location_for_path(&self, path: &PathKey, cx: &App) -> Option<Anchor> {
1491 let excerpt_id = self.excerpts_by_path.get(path)?.first()?;
1492 let snapshot = self.snapshot(cx);
1493 let excerpt = snapshot.excerpt(*excerpt_id)?;
1494 Some(Anchor::in_buffer(
1495 *excerpt_id,
1496 excerpt.buffer_id,
1497 excerpt.range.context.start,
1498 ))
1499 }
1500
1501 pub fn excerpt_paths(&self) -> impl Iterator<Item = &PathKey> {
1502 self.excerpts_by_path.keys()
1503 }
1504
1505 fn expand_excerpts_with_paths(
1506 &mut self,
1507 ids: impl IntoIterator<Item = ExcerptId>,
1508 line_count: u32,
1509 direction: ExpandExcerptDirection,
1510 cx: &mut Context<Self>,
1511 ) {
1512 let grouped = ids
1513 .into_iter()
1514 .chunk_by(|id| self.paths_by_excerpt.get(id).cloned())
1515 .into_iter()
1516 .flat_map(|(k, v)| Some((k?, v.into_iter().collect::<Vec<_>>())))
1517 .collect::<Vec<_>>();
1518 let snapshot = self.snapshot(cx);
1519
1520 for (path, ids) in grouped.into_iter() {
1521 let Some(excerpt_ids) = self.excerpts_by_path.get(&path) else {
1522 continue;
1523 };
1524
1525 let ids_to_expand = HashSet::from_iter(ids);
1526 let expanded_ranges = excerpt_ids.iter().filter_map(|excerpt_id| {
1527 let excerpt = snapshot.excerpt(*excerpt_id)?;
1528
1529 let mut context = excerpt.range.context.to_point(&excerpt.buffer);
1530 if ids_to_expand.contains(excerpt_id) {
1531 match direction {
1532 ExpandExcerptDirection::Up => {
1533 context.start.row = context.start.row.saturating_sub(line_count);
1534 context.start.column = 0;
1535 }
1536 ExpandExcerptDirection::Down => {
1537 context.end.row =
1538 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
1539 context.end.column = excerpt.buffer.line_len(context.end.row);
1540 }
1541 ExpandExcerptDirection::UpAndDown => {
1542 context.start.row = context.start.row.saturating_sub(line_count);
1543 context.start.column = 0;
1544 context.end.row =
1545 (context.end.row + line_count).min(excerpt.buffer.max_point().row);
1546 context.end.column = excerpt.buffer.line_len(context.end.row);
1547 }
1548 }
1549 }
1550
1551 Some(ExcerptRange {
1552 context,
1553 primary: excerpt.range.primary.to_point(&excerpt.buffer),
1554 })
1555 });
1556 let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
1557 for range in expanded_ranges {
1558 if let Some(last_range) = merged_ranges.last_mut()
1559 && last_range.context.end >= range.context.start
1560 {
1561 last_range.context.end = range.context.end;
1562 continue;
1563 }
1564 merged_ranges.push(range)
1565 }
1566 let Some(excerpt_id) = excerpt_ids.first() else {
1567 continue;
1568 };
1569 let Some(buffer_id) = &snapshot.buffer_id_for_excerpt(*excerpt_id) else {
1570 continue;
1571 };
1572
1573 let Some(buffer) = self
1574 .buffers
1575 .borrow()
1576 .get(buffer_id)
1577 .map(|b| b.buffer.clone())
1578 else {
1579 continue;
1580 };
1581
1582 let buffer_snapshot = buffer.read(cx).snapshot();
1583 self.update_path_excerpts(path.clone(), buffer, &buffer_snapshot, merged_ranges, cx);
1584 }
1585 }
1586
1587 /// Sets excerpts, returns `true` if at least one new excerpt was added.
1588 pub fn set_excerpts_for_path(
1589 &mut self,
1590 path: PathKey,
1591 buffer: Entity<Buffer>,
1592 ranges: impl IntoIterator<Item = Range<Point>>,
1593 context_line_count: u32,
1594 cx: &mut Context<Self>,
1595 ) -> (Vec<Range<Anchor>>, bool) {
1596 let buffer_snapshot = buffer.read(cx).snapshot();
1597 let excerpt_ranges = build_excerpt_ranges(ranges, context_line_count, &buffer_snapshot);
1598
1599 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
1600 self.set_merged_excerpt_ranges_for_path(
1601 path,
1602 buffer,
1603 excerpt_ranges,
1604 &buffer_snapshot,
1605 new,
1606 counts,
1607 cx,
1608 )
1609 }
1610
1611 pub fn set_excerpt_ranges_for_path(
1612 &mut self,
1613 path: PathKey,
1614 buffer: Entity<Buffer>,
1615 buffer_snapshot: &BufferSnapshot,
1616 excerpt_ranges: Vec<ExcerptRange<Point>>,
1617 cx: &mut Context<Self>,
1618 ) -> (Vec<Range<Anchor>>, bool) {
1619 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
1620 self.set_merged_excerpt_ranges_for_path(
1621 path,
1622 buffer,
1623 excerpt_ranges,
1624 buffer_snapshot,
1625 new,
1626 counts,
1627 cx,
1628 )
1629 }
1630
1631 pub fn set_anchored_excerpts_for_path(
1632 &self,
1633 buffer: Entity<Buffer>,
1634 ranges: Vec<Range<text::Anchor>>,
1635 context_line_count: u32,
1636 cx: &mut Context<Self>,
1637 ) -> Task<Vec<Range<Anchor>>> {
1638 let buffer_snapshot = buffer.read(cx).snapshot();
1639 let path_key = PathKey::for_buffer(&buffer, cx);
1640 cx.spawn(async move |multi_buffer, cx| {
1641 let snapshot = buffer_snapshot.clone();
1642 let (excerpt_ranges, new, counts) = cx
1643 .background_spawn(async move {
1644 let ranges = ranges.into_iter().map(|range| range.to_point(&snapshot));
1645 let excerpt_ranges =
1646 build_excerpt_ranges(ranges, context_line_count, &snapshot);
1647 let (new, counts) = Self::merge_excerpt_ranges(&excerpt_ranges);
1648 (excerpt_ranges, new, counts)
1649 })
1650 .await;
1651
1652 multi_buffer
1653 .update(cx, move |multi_buffer, cx| {
1654 let (ranges, _) = multi_buffer.set_merged_excerpt_ranges_for_path(
1655 path_key,
1656 buffer,
1657 excerpt_ranges,
1658 &buffer_snapshot,
1659 new,
1660 counts,
1661 cx,
1662 );
1663 ranges
1664 })
1665 .ok()
1666 .unwrap_or_default()
1667 })
1668 }
1669
1670 /// Sets excerpts, returns `true` if at least one new excerpt was added.
1671 fn set_merged_excerpt_ranges_for_path(
1672 &mut self,
1673 path: PathKey,
1674 buffer: Entity<Buffer>,
1675 ranges: Vec<ExcerptRange<Point>>,
1676 buffer_snapshot: &BufferSnapshot,
1677 new: Vec<ExcerptRange<Point>>,
1678 counts: Vec<usize>,
1679 cx: &mut Context<Self>,
1680 ) -> (Vec<Range<Anchor>>, bool) {
1681 let (excerpt_ids, added_a_new_excerpt) =
1682 self.update_path_excerpts(path, buffer, buffer_snapshot, new, cx);
1683
1684 let mut result = Vec::new();
1685 let mut ranges = ranges.into_iter();
1686 for (excerpt_id, range_count) in excerpt_ids.into_iter().zip(counts.into_iter()) {
1687 for range in ranges.by_ref().take(range_count) {
1688 let range = Anchor::range_in_buffer(
1689 excerpt_id,
1690 buffer_snapshot.remote_id(),
1691 buffer_snapshot.anchor_before(&range.primary.start)
1692 ..buffer_snapshot.anchor_after(&range.primary.end),
1693 );
1694 result.push(range)
1695 }
1696 }
1697 (result, added_a_new_excerpt)
1698 }
1699
1700 fn merge_excerpt_ranges<'a>(
1701 expanded_ranges: impl IntoIterator<Item = &'a ExcerptRange<Point>> + 'a,
1702 ) -> (Vec<ExcerptRange<Point>>, Vec<usize>) {
1703 let mut merged_ranges: Vec<ExcerptRange<Point>> = Vec::new();
1704 let mut counts: Vec<usize> = Vec::new();
1705 for range in expanded_ranges {
1706 if let Some(last_range) = merged_ranges.last_mut() {
1707 debug_assert!(
1708 last_range.context.start <= range.context.start,
1709 "Last range: {last_range:?} Range: {range:?}"
1710 );
1711 if last_range.context.end >= range.context.start
1712 || last_range.context.end.row + 1 == range.context.start.row
1713 {
1714 last_range.context.end = range.context.end.max(last_range.context.end);
1715 *counts.last_mut().unwrap() += 1;
1716 continue;
1717 }
1718 }
1719 merged_ranges.push(range.clone());
1720 counts.push(1);
1721 }
1722 (merged_ranges, counts)
1723 }
1724
1725 fn update_path_excerpts(
1726 &mut self,
1727 path: PathKey,
1728 buffer: Entity<Buffer>,
1729 buffer_snapshot: &BufferSnapshot,
1730 new: Vec<ExcerptRange<Point>>,
1731 cx: &mut Context<Self>,
1732 ) -> (Vec<ExcerptId>, bool) {
1733 let mut insert_after = self
1734 .excerpts_by_path
1735 .range(..path.clone())
1736 .next_back()
1737 .map(|(_, value)| *value.last().unwrap())
1738 .unwrap_or(ExcerptId::min());
1739
1740 let existing = self
1741 .excerpts_by_path
1742 .get(&path)
1743 .cloned()
1744 .unwrap_or_default();
1745
1746 let mut new_iter = new.into_iter().peekable();
1747 let mut existing_iter = existing.into_iter().peekable();
1748
1749 let mut excerpt_ids = Vec::new();
1750 let mut to_remove = Vec::new();
1751 let mut to_insert: Vec<(ExcerptId, ExcerptRange<Point>)> = Vec::new();
1752 let mut added_a_new_excerpt = false;
1753 let snapshot = self.snapshot(cx);
1754
1755 let mut next_excerpt_id =
1756 if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
1757 last_entry.id.0 + 1
1758 } else {
1759 1
1760 };
1761
1762 let mut next_excerpt_id = move || ExcerptId(post_inc(&mut next_excerpt_id));
1763
1764 let mut excerpts_cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
1765 excerpts_cursor.next();
1766
1767 loop {
1768 let new = new_iter.peek();
1769 let existing = if let Some(existing_id) = existing_iter.peek() {
1770 let locator = snapshot.excerpt_locator_for_id(*existing_id);
1771 excerpts_cursor.seek_forward(&Some(locator), Bias::Left);
1772 if let Some(excerpt) = excerpts_cursor.item() {
1773 if excerpt.buffer_id != buffer_snapshot.remote_id() {
1774 to_remove.push(*existing_id);
1775 existing_iter.next();
1776 continue;
1777 }
1778 Some((
1779 *existing_id,
1780 excerpt.range.context.to_point(buffer_snapshot),
1781 ))
1782 } else {
1783 None
1784 }
1785 } else {
1786 None
1787 };
1788
1789 if let Some((last_id, last)) = to_insert.last_mut() {
1790 if let Some(new) = new
1791 && last.context.end >= new.context.start
1792 {
1793 last.context.end = last.context.end.max(new.context.end);
1794 excerpt_ids.push(*last_id);
1795 new_iter.next();
1796 continue;
1797 }
1798 if let Some((existing_id, existing_range)) = &existing
1799 && last.context.end >= existing_range.start
1800 {
1801 last.context.end = last.context.end.max(existing_range.end);
1802 to_remove.push(*existing_id);
1803 self.snapshot
1804 .borrow_mut()
1805 .replaced_excerpts
1806 .insert(*existing_id, *last_id);
1807 existing_iter.next();
1808 continue;
1809 }
1810 }
1811
1812 match (new, existing) {
1813 (None, None) => break,
1814 (None, Some((existing_id, _))) => {
1815 existing_iter.next();
1816 to_remove.push(existing_id);
1817 continue;
1818 }
1819 (Some(_), None) => {
1820 added_a_new_excerpt = true;
1821 let new_id = next_excerpt_id();
1822 excerpt_ids.push(new_id);
1823 to_insert.push((new_id, new_iter.next().unwrap()));
1824 continue;
1825 }
1826 (Some(new), Some((_, existing_range))) => {
1827 if existing_range.end < new.context.start {
1828 let existing_id = existing_iter.next().unwrap();
1829 to_remove.push(existing_id);
1830 continue;
1831 } else if existing_range.start > new.context.end {
1832 let new_id = next_excerpt_id();
1833 excerpt_ids.push(new_id);
1834 to_insert.push((new_id, new_iter.next().unwrap()));
1835 continue;
1836 }
1837
1838 if existing_range.start == new.context.start
1839 && existing_range.end == new.context.end
1840 {
1841 self.insert_excerpts_with_ids_after(
1842 insert_after,
1843 buffer.clone(),
1844 mem::take(&mut to_insert),
1845 cx,
1846 );
1847 insert_after = existing_iter.next().unwrap();
1848 excerpt_ids.push(insert_after);
1849 new_iter.next();
1850 } else {
1851 let existing_id = existing_iter.next().unwrap();
1852 let new_id = next_excerpt_id();
1853 self.snapshot
1854 .borrow_mut()
1855 .replaced_excerpts
1856 .insert(existing_id, new_id);
1857 to_remove.push(existing_id);
1858 let mut range = new_iter.next().unwrap();
1859 range.context.start = range.context.start.min(existing_range.start);
1860 range.context.end = range.context.end.max(existing_range.end);
1861 excerpt_ids.push(new_id);
1862 to_insert.push((new_id, range));
1863 }
1864 }
1865 };
1866 }
1867
1868 self.insert_excerpts_with_ids_after(insert_after, buffer, to_insert, cx);
1869 self.remove_excerpts(to_remove, cx);
1870 if excerpt_ids.is_empty() {
1871 self.excerpts_by_path.remove(&path);
1872 } else {
1873 for excerpt_id in &excerpt_ids {
1874 self.paths_by_excerpt.insert(*excerpt_id, path.clone());
1875 }
1876 self.excerpts_by_path
1877 .insert(path, excerpt_ids.iter().dedup().cloned().collect());
1878 }
1879
1880 (excerpt_ids, added_a_new_excerpt)
1881 }
1882
1883 pub fn paths(&self) -> impl Iterator<Item = PathKey> + '_ {
1884 self.excerpts_by_path.keys().cloned()
1885 }
1886
1887 pub fn remove_excerpts_for_path(&mut self, path: PathKey, cx: &mut Context<Self>) {
1888 if let Some(to_remove) = self.excerpts_by_path.remove(&path) {
1889 self.remove_excerpts(to_remove, cx)
1890 }
1891 }
1892
1893 pub fn insert_excerpts_after<O>(
1894 &mut self,
1895 prev_excerpt_id: ExcerptId,
1896 buffer: Entity<Buffer>,
1897 ranges: impl IntoIterator<Item = ExcerptRange<O>>,
1898 cx: &mut Context<Self>,
1899 ) -> Vec<ExcerptId>
1900 where
1901 O: text::ToOffset,
1902 {
1903 let mut ids = Vec::new();
1904 let mut next_excerpt_id =
1905 if let Some(last_entry) = self.snapshot.borrow().excerpt_ids.last() {
1906 last_entry.id.0 + 1
1907 } else {
1908 1
1909 };
1910 self.insert_excerpts_with_ids_after(
1911 prev_excerpt_id,
1912 buffer,
1913 ranges.into_iter().map(|range| {
1914 let id = ExcerptId(post_inc(&mut next_excerpt_id));
1915 ids.push(id);
1916 (id, range)
1917 }),
1918 cx,
1919 );
1920 ids
1921 }
1922
1923 pub fn insert_excerpts_with_ids_after<O>(
1924 &mut self,
1925 prev_excerpt_id: ExcerptId,
1926 buffer: Entity<Buffer>,
1927 ranges: impl IntoIterator<Item = (ExcerptId, ExcerptRange<O>)>,
1928 cx: &mut Context<Self>,
1929 ) where
1930 O: text::ToOffset,
1931 {
1932 assert_eq!(self.history.transaction_depth, 0);
1933 let mut ranges = ranges.into_iter().peekable();
1934 if ranges.peek().is_none() {
1935 return Default::default();
1936 }
1937
1938 self.sync(cx);
1939
1940 let buffer_snapshot = buffer.read(cx).snapshot();
1941 let buffer_id = buffer_snapshot.remote_id();
1942
1943 let mut buffers = self.buffers.borrow_mut();
1944 let buffer_state = buffers.entry(buffer_id).or_insert_with(|| {
1945 self.buffer_changed_since_sync.replace(true);
1946 buffer.update(cx, |buffer, _| {
1947 buffer.record_changes(Rc::downgrade(&self.buffer_changed_since_sync));
1948 });
1949 BufferState {
1950 last_version: buffer_snapshot.version().clone(),
1951 last_non_text_state_update_count: buffer_snapshot.non_text_state_update_count(),
1952 excerpts: Default::default(),
1953 _subscriptions: [
1954 cx.observe(&buffer, |_, _, cx| cx.notify()),
1955 cx.subscribe(&buffer, Self::on_buffer_event),
1956 ],
1957 buffer: buffer.clone(),
1958 }
1959 });
1960
1961 let mut snapshot = self.snapshot.borrow_mut();
1962
1963 let mut prev_locator = snapshot.excerpt_locator_for_id(prev_excerpt_id).clone();
1964 let mut new_excerpt_ids = mem::take(&mut snapshot.excerpt_ids);
1965 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
1966 let mut new_excerpts = cursor.slice(&prev_locator, Bias::Right);
1967 prev_locator = cursor.start().unwrap_or(Locator::min_ref()).clone();
1968
1969 let edit_start = ExcerptOffset::new(new_excerpts.summary().text.len);
1970 new_excerpts.update_last(
1971 |excerpt| {
1972 excerpt.has_trailing_newline = true;
1973 },
1974 (),
1975 );
1976
1977 let next_locator = if let Some(excerpt) = cursor.item() {
1978 excerpt.locator.clone()
1979 } else {
1980 Locator::max()
1981 };
1982
1983 let mut excerpts = Vec::new();
1984 while let Some((id, range)) = ranges.next() {
1985 let locator = Locator::between(&prev_locator, &next_locator);
1986 if let Err(ix) = buffer_state.excerpts.binary_search(&locator) {
1987 buffer_state.excerpts.insert(ix, locator.clone());
1988 }
1989 let range = ExcerptRange {
1990 context: buffer_snapshot.anchor_before(&range.context.start)
1991 ..buffer_snapshot.anchor_after(&range.context.end),
1992 primary: buffer_snapshot.anchor_before(&range.primary.start)
1993 ..buffer_snapshot.anchor_after(&range.primary.end),
1994 };
1995 excerpts.push((id, range.clone()));
1996 let excerpt = Excerpt::new(
1997 id,
1998 locator.clone(),
1999 buffer_id,
2000 buffer_snapshot.clone(),
2001 range,
2002 ranges.peek().is_some() || cursor.item().is_some(),
2003 );
2004 new_excerpts.push(excerpt, ());
2005 prev_locator = locator.clone();
2006
2007 if let Some(last_mapping_entry) = new_excerpt_ids.last() {
2008 assert!(id > last_mapping_entry.id, "excerpt ids must be increasing");
2009 }
2010 new_excerpt_ids.push(ExcerptIdMapping { id, locator }, ());
2011 }
2012
2013 let edit_end = ExcerptOffset::new(new_excerpts.summary().text.len);
2014
2015 let suffix = cursor.suffix();
2016 let changed_trailing_excerpt = suffix.is_empty();
2017 new_excerpts.append(suffix, ());
2018 drop(cursor);
2019 snapshot.excerpts = new_excerpts;
2020 snapshot.excerpt_ids = new_excerpt_ids;
2021 if changed_trailing_excerpt {
2022 snapshot.trailing_excerpt_update_count += 1;
2023 }
2024
2025 self.sync_diff_transforms(
2026 &mut snapshot,
2027 vec![Edit {
2028 old: edit_start..edit_start,
2029 new: edit_start..edit_end,
2030 }],
2031 DiffChangeKind::BufferEdited,
2032 );
2033 cx.emit(Event::Edited {
2034 singleton_buffer_edited: false,
2035 edited_buffer: None,
2036 });
2037 cx.emit(Event::ExcerptsAdded {
2038 buffer,
2039 predecessor: prev_excerpt_id,
2040 excerpts,
2041 });
2042 cx.notify();
2043 }
2044
2045 pub fn clear(&mut self, cx: &mut Context<Self>) {
2046 self.sync(cx);
2047 let ids = self.excerpt_ids();
2048 let removed_buffer_ids = self
2049 .buffers
2050 .borrow_mut()
2051 .drain()
2052 .map(|(id, _)| id)
2053 .collect();
2054 self.excerpts_by_path.clear();
2055 self.paths_by_excerpt.clear();
2056 let mut snapshot = self.snapshot.borrow_mut();
2057 let start = ExcerptOffset::new(0);
2058 let prev_len = ExcerptOffset::new(snapshot.excerpts.summary().text.len);
2059 snapshot.excerpts = Default::default();
2060 snapshot.trailing_excerpt_update_count += 1;
2061 snapshot.is_dirty = false;
2062 snapshot.has_deleted_file = false;
2063 snapshot.has_conflict = false;
2064 snapshot.replaced_excerpts.clear();
2065
2066 self.sync_diff_transforms(
2067 &mut snapshot,
2068 vec![Edit {
2069 old: start..prev_len,
2070 new: start..start,
2071 }],
2072 DiffChangeKind::BufferEdited,
2073 );
2074 cx.emit(Event::Edited {
2075 singleton_buffer_edited: false,
2076 edited_buffer: None,
2077 });
2078 cx.emit(Event::ExcerptsRemoved {
2079 ids,
2080 removed_buffer_ids,
2081 });
2082 cx.notify();
2083 }
2084
2085 pub fn excerpts_for_buffer(
2086 &self,
2087 buffer_id: BufferId,
2088 cx: &App,
2089 ) -> Vec<(ExcerptId, ExcerptRange<text::Anchor>)> {
2090 let mut excerpts = Vec::new();
2091 let snapshot = self.read(cx);
2092 let buffers = self.buffers.borrow();
2093 let mut cursor = snapshot.excerpts.cursor::<Option<&Locator>>(());
2094 if let Some(locators) = buffers.get(&buffer_id).map(|state| &state.excerpts) {
2095 for locator in locators {
2096 cursor.seek_forward(&Some(locator), Bias::Left);
2097 if let Some(excerpt) = cursor.item()
2098 && excerpt.locator == *locator
2099 {
2100 excerpts.push((excerpt.id, excerpt.range.clone()));
2101 }
2102 }
2103 }
2104
2105 excerpts
2106 }
2107
2108 pub fn excerpt_ranges_for_buffer(&self, buffer_id: BufferId, cx: &App) -> Vec<Range<Point>> {
2109 let snapshot = self.read(cx);
2110 let buffers = self.buffers.borrow();
2111 let mut excerpts = snapshot
2112 .excerpts
2113 .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(());
2114 let mut diff_transforms = snapshot
2115 .diff_transforms
2116 .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(());
2117 diff_transforms.next();
2118 let locators = buffers
2119 .get(&buffer_id)
2120 .into_iter()
2121 .flat_map(|state| &state.excerpts);
2122 let mut result = Vec::new();
2123 for locator in locators {
2124 excerpts.seek_forward(&Some(locator), Bias::Left);
2125 if let Some(excerpt) = excerpts.item()
2126 && excerpt.locator == *locator
2127 {
2128 let excerpt_start = excerpts.start().1.clone();
2129 let excerpt_end = ExcerptDimension(excerpt_start.0 + excerpt.text_summary.lines);
2130
2131 diff_transforms.seek_forward(&excerpt_start, Bias::Left);
2132 let overshoot = excerpt_start.0 - diff_transforms.start().0.0;
2133 let start = diff_transforms.start().1.0 + overshoot;
2134
2135 diff_transforms.seek_forward(&excerpt_end, Bias::Right);
2136 let overshoot = excerpt_end.0 - diff_transforms.start().0.0;
2137 let end = diff_transforms.start().1.0 + overshoot;
2138
2139 result.push(start..end)
2140 }
2141 }
2142 result
2143 }
2144
2145 pub fn excerpt_buffer_ids(&self) -> Vec<BufferId> {
2146 self.snapshot
2147 .borrow()
2148 .excerpts
2149 .iter()
2150 .map(|entry| entry.buffer_id)
2151 .collect()
2152 }
2153
2154 pub fn excerpt_ids(&self) -> Vec<ExcerptId> {
2155 self.snapshot
2156 .borrow()
2157 .excerpts
2158 .iter()
2159 .map(|entry| entry.id)
2160 .collect()
2161 }
2162
2163 pub fn excerpt_containing(
2164 &self,
2165 position: impl ToOffset,
2166 cx: &App,
2167 ) -> Option<(ExcerptId, Entity<Buffer>, Range<text::Anchor>)> {
2168 let snapshot = self.read(cx);
2169 let offset = position.to_offset(&snapshot);
2170
2171 let mut cursor = snapshot.cursor::<usize>();
2172 cursor.seek(&offset);
2173 cursor
2174 .excerpt()
2175 .or_else(|| snapshot.excerpts.last())
2176 .map(|excerpt| {
2177 (
2178 excerpt.id,
2179 self.buffers
2180 .borrow()
2181 .get(&excerpt.buffer_id)
2182 .unwrap()
2183 .buffer
2184 .clone(),
2185 excerpt.range.context.clone(),
2186 )
2187 })
2188 }
2189
2190 pub fn buffer_for_anchor(&self, anchor: Anchor, cx: &App) -> Option<Entity<Buffer>> {
2191 if let Some(buffer_id) = anchor.buffer_id {
2192 self.buffer(buffer_id)
2193 } else {
2194 let (_, buffer, _) = self.excerpt_containing(anchor, cx)?;
2195 Some(buffer)
2196 }
2197 }
2198
2199 // If point is at the end of the buffer, the last excerpt is returned
2200 pub fn point_to_buffer_offset<T: ToOffset>(
2201 &self,
2202 point: T,
2203 cx: &App,
2204 ) -> Option<(Entity<Buffer>, usize)> {
2205 let snapshot = self.read(cx);
2206 let (buffer, offset) = snapshot.point_to_buffer_offset(point)?;
2207 Some((
2208 self.buffers
2209 .borrow()
2210 .get(&buffer.remote_id())?
2211 .buffer
2212 .clone(),
2213 offset,
2214 ))
2215 }
2216
2217 // If point is at the end of the buffer, the last excerpt is returned
2218 pub fn point_to_buffer_point<T: ToPoint>(
2219 &self,
2220 point: T,
2221 cx: &App,
2222 ) -> Option<(Entity<Buffer>, Point, ExcerptId)> {
2223 let snapshot = self.read(cx);
2224 let (buffer, point, is_main_buffer) =
2225 snapshot.point_to_buffer_point(point.to_point(&snapshot))?;
2226 Some((
2227 self.buffers
2228 .borrow()
2229 .get(&buffer.remote_id())?
2230 .buffer
2231 .clone(),
2232 point,
2233 is_main_buffer,
2234 ))
2235 }
2236
2237 pub fn buffer_point_to_anchor(
2238 &self,
2239 buffer: &Entity<Buffer>,
2240 point: Point,
2241 cx: &App,
2242 ) -> Option<Anchor> {
2243 let mut found = None;
2244 let snapshot = buffer.read(cx).snapshot();
2245 for (excerpt_id, range) in self.excerpts_for_buffer(snapshot.remote_id(), cx) {
2246 let start = range.context.start.to_point(&snapshot);
2247 let end = range.context.end.to_point(&snapshot);
2248 if start <= point && point < end {
2249 found = Some((snapshot.clip_point(point, Bias::Left), excerpt_id));
2250 break;
2251 }
2252 if point < start {
2253 found = Some((start, excerpt_id));
2254 }
2255 if point > end {
2256 found = Some((end, excerpt_id));
2257 }
2258 }
2259
2260 found.map(|(point, excerpt_id)| {
2261 let text_anchor = snapshot.anchor_after(point);
2262 Anchor::in_buffer(excerpt_id, snapshot.remote_id(), text_anchor)
2263 })
2264 }
2265
2266 pub fn remove_excerpts(
2267 &mut self,
2268 excerpt_ids: impl IntoIterator<Item = ExcerptId>,
2269 cx: &mut Context<Self>,
2270 ) {
2271 self.sync(cx);
2272 let ids = excerpt_ids.into_iter().collect::<Vec<_>>();
2273 if ids.is_empty() {
2274 return;
2275 }
2276
2277 let mut buffers = self.buffers.borrow_mut();
2278 let mut snapshot = self.snapshot.borrow_mut();
2279 let mut new_excerpts = SumTree::default();
2280 let mut cursor = snapshot
2281 .excerpts
2282 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2283 let mut edits = Vec::new();
2284 let mut excerpt_ids = ids.iter().copied().peekable();
2285 let mut removed_buffer_ids = Vec::new();
2286
2287 while let Some(excerpt_id) = excerpt_ids.next() {
2288 self.paths_by_excerpt.remove(&excerpt_id);
2289 // Seek to the next excerpt to remove, preserving any preceding excerpts.
2290 let locator = snapshot.excerpt_locator_for_id(excerpt_id);
2291 new_excerpts.append(cursor.slice(&Some(locator), Bias::Left), ());
2292
2293 if let Some(mut excerpt) = cursor.item() {
2294 if excerpt.id != excerpt_id {
2295 continue;
2296 }
2297 let mut old_start = cursor.start().1;
2298
2299 // Skip over the removed excerpt.
2300 'remove_excerpts: loop {
2301 if let Some(buffer_state) = buffers.get_mut(&excerpt.buffer_id) {
2302 buffer_state.excerpts.retain(|l| l != &excerpt.locator);
2303 if buffer_state.excerpts.is_empty() {
2304 log::debug!(
2305 "removing buffer and diff for buffer {}",
2306 excerpt.buffer_id
2307 );
2308 buffers.remove(&excerpt.buffer_id);
2309 removed_buffer_ids.push(excerpt.buffer_id);
2310 }
2311 }
2312 cursor.next();
2313
2314 // Skip over any subsequent excerpts that are also removed.
2315 if let Some(&next_excerpt_id) = excerpt_ids.peek() {
2316 let next_locator = snapshot.excerpt_locator_for_id(next_excerpt_id);
2317 if let Some(next_excerpt) = cursor.item()
2318 && next_excerpt.locator == *next_locator
2319 {
2320 excerpt_ids.next();
2321 excerpt = next_excerpt;
2322 continue 'remove_excerpts;
2323 }
2324 }
2325
2326 break;
2327 }
2328
2329 // When removing the last excerpt, remove the trailing newline from
2330 // the previous excerpt.
2331 if cursor.item().is_none() && old_start.value > 0 {
2332 old_start.value -= 1;
2333 new_excerpts.update_last(|e| e.has_trailing_newline = false, ());
2334 }
2335
2336 // Push an edit for the removal of this run of excerpts.
2337 let old_end = cursor.start().1;
2338 let new_start = ExcerptOffset::new(new_excerpts.summary().text.len);
2339 edits.push(Edit {
2340 old: old_start..old_end,
2341 new: new_start..new_start,
2342 });
2343 }
2344 }
2345 let suffix = cursor.suffix();
2346 let changed_trailing_excerpt = suffix.is_empty();
2347 new_excerpts.append(suffix, ());
2348 drop(cursor);
2349 snapshot.excerpts = new_excerpts;
2350 for buffer_id in &removed_buffer_ids {
2351 self.diffs.remove(buffer_id);
2352 snapshot.diffs.remove(buffer_id);
2353 }
2354
2355 if changed_trailing_excerpt {
2356 snapshot.trailing_excerpt_update_count += 1;
2357 }
2358
2359 self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
2360 self.buffer_changed_since_sync.replace(true);
2361 cx.emit(Event::Edited {
2362 singleton_buffer_edited: false,
2363 edited_buffer: None,
2364 });
2365 cx.emit(Event::ExcerptsRemoved {
2366 ids,
2367 removed_buffer_ids,
2368 });
2369 cx.notify();
2370 }
2371
2372 pub fn wait_for_anchors<'a, Anchors: 'a + Iterator<Item = Anchor>>(
2373 &self,
2374 anchors: Anchors,
2375 cx: &mut Context<Self>,
2376 ) -> impl 'static + Future<Output = Result<()>> + use<Anchors> {
2377 let borrow = self.buffers.borrow();
2378 let mut error = None;
2379 let mut futures = Vec::new();
2380 for anchor in anchors {
2381 if let Some(buffer_id) = anchor.buffer_id {
2382 if let Some(buffer) = borrow.get(&buffer_id) {
2383 buffer.buffer.update(cx, |buffer, _| {
2384 futures.push(buffer.wait_for_anchors([anchor.text_anchor]))
2385 });
2386 } else {
2387 error = Some(anyhow!(
2388 "buffer {buffer_id} is not part of this multi-buffer"
2389 ));
2390 break;
2391 }
2392 }
2393 }
2394 async move {
2395 if let Some(error) = error {
2396 Err(error)?;
2397 }
2398 for future in futures {
2399 future.await?;
2400 }
2401 Ok(())
2402 }
2403 }
2404
2405 pub fn text_anchor_for_position<T: ToOffset>(
2406 &self,
2407 position: T,
2408 cx: &App,
2409 ) -> Option<(Entity<Buffer>, language::Anchor)> {
2410 let snapshot = self.read(cx);
2411 let anchor = snapshot.anchor_before(position);
2412 let buffer = self
2413 .buffers
2414 .borrow()
2415 .get(&anchor.buffer_id?)?
2416 .buffer
2417 .clone();
2418 Some((buffer, anchor.text_anchor))
2419 }
2420
2421 fn on_buffer_event(
2422 &mut self,
2423 buffer: Entity<Buffer>,
2424 event: &language::BufferEvent,
2425 cx: &mut Context<Self>,
2426 ) {
2427 use language::BufferEvent;
2428 cx.emit(match event {
2429 BufferEvent::Edited => Event::Edited {
2430 singleton_buffer_edited: true,
2431 edited_buffer: Some(buffer),
2432 },
2433 BufferEvent::DirtyChanged => Event::DirtyChanged,
2434 BufferEvent::Saved => Event::Saved,
2435 BufferEvent::FileHandleChanged => Event::FileHandleChanged,
2436 BufferEvent::Reloaded => Event::Reloaded,
2437 BufferEvent::LanguageChanged => Event::LanguageChanged(buffer.read(cx).remote_id()),
2438 BufferEvent::Reparsed => Event::Reparsed(buffer.read(cx).remote_id()),
2439 BufferEvent::DiagnosticsUpdated => Event::DiagnosticsUpdated,
2440 BufferEvent::CapabilityChanged => {
2441 self.capability = buffer.read(cx).capability();
2442 return;
2443 }
2444 BufferEvent::Operation { .. } | BufferEvent::ReloadNeeded => return,
2445 });
2446 }
2447
2448 fn buffer_diff_language_changed(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
2449 self.sync(cx);
2450 let mut snapshot = self.snapshot.borrow_mut();
2451 let diff = diff.read(cx);
2452 let buffer_id = diff.buffer_id;
2453 let diff = diff.snapshot(cx);
2454 snapshot.diffs.insert(buffer_id, diff);
2455 }
2456
2457 fn buffer_diff_changed(
2458 &mut self,
2459 diff: Entity<BufferDiff>,
2460 range: Range<text::Anchor>,
2461 cx: &mut Context<Self>,
2462 ) {
2463 self.sync(cx);
2464 self.buffer_changed_since_sync.replace(true);
2465
2466 let diff = diff.read(cx);
2467 let buffer_id = diff.buffer_id;
2468 let buffers = self.buffers.borrow();
2469 let Some(buffer_state) = buffers.get(&buffer_id) else {
2470 return;
2471 };
2472
2473 let buffer = buffer_state.buffer.read(cx);
2474 let diff_change_range = range.to_offset(buffer);
2475
2476 let new_diff = diff.snapshot(cx);
2477 let mut snapshot = self.snapshot.borrow_mut();
2478 let base_text_changed = snapshot
2479 .diffs
2480 .get(&buffer_id)
2481 .is_none_or(|old_diff| !new_diff.base_texts_eq(old_diff));
2482
2483 snapshot.diffs.insert(buffer_id, new_diff);
2484
2485 let mut excerpt_edits = Vec::new();
2486 for locator in &buffer_state.excerpts {
2487 let mut cursor = snapshot
2488 .excerpts
2489 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2490 cursor.seek_forward(&Some(locator), Bias::Left);
2491 if let Some(excerpt) = cursor.item()
2492 && excerpt.locator == *locator
2493 {
2494 let excerpt_buffer_range = excerpt.range.context.to_offset(&excerpt.buffer);
2495 if diff_change_range.end < excerpt_buffer_range.start
2496 || diff_change_range.start > excerpt_buffer_range.end
2497 {
2498 continue;
2499 }
2500 let excerpt_start = cursor.start().1;
2501 let excerpt_len = ExcerptOffset::new(excerpt.text_summary.len);
2502 let diff_change_start_in_excerpt = ExcerptOffset::new(
2503 diff_change_range
2504 .start
2505 .saturating_sub(excerpt_buffer_range.start),
2506 );
2507 let diff_change_end_in_excerpt = ExcerptOffset::new(
2508 diff_change_range
2509 .end
2510 .saturating_sub(excerpt_buffer_range.start),
2511 );
2512 let edit_start = excerpt_start + diff_change_start_in_excerpt.min(excerpt_len);
2513 let edit_end = excerpt_start + diff_change_end_in_excerpt.min(excerpt_len);
2514 excerpt_edits.push(Edit {
2515 old: edit_start..edit_end,
2516 new: edit_start..edit_end,
2517 });
2518 }
2519 }
2520
2521 self.sync_diff_transforms(
2522 &mut snapshot,
2523 excerpt_edits,
2524 DiffChangeKind::DiffUpdated {
2525 base_changed: base_text_changed,
2526 },
2527 );
2528 cx.emit(Event::Edited {
2529 singleton_buffer_edited: false,
2530 edited_buffer: None,
2531 });
2532 }
2533
2534 pub fn all_buffers(&self) -> HashSet<Entity<Buffer>> {
2535 self.buffers
2536 .borrow()
2537 .values()
2538 .map(|state| state.buffer.clone())
2539 .collect()
2540 }
2541
2542 pub fn all_buffer_ids(&self) -> Vec<BufferId> {
2543 self.buffers.borrow().keys().copied().collect()
2544 }
2545
2546 pub fn buffer(&self, buffer_id: BufferId) -> Option<Entity<Buffer>> {
2547 self.buffers
2548 .borrow()
2549 .get(&buffer_id)
2550 .map(|state| state.buffer.clone())
2551 }
2552
2553 pub fn language_at<T: ToOffset>(&self, point: T, cx: &App) -> Option<Arc<Language>> {
2554 self.point_to_buffer_offset(point, cx)
2555 .and_then(|(buffer, offset)| buffer.read(cx).language_at(offset))
2556 }
2557
2558 pub fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
2559 let buffer_id = self
2560 .snapshot
2561 .borrow()
2562 .excerpts
2563 .first()
2564 .map(|excerpt| excerpt.buffer.remote_id());
2565 buffer_id
2566 .and_then(|buffer_id| self.buffer(buffer_id))
2567 .map(|buffer| {
2568 let buffer = buffer.read(cx);
2569 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx)
2570 })
2571 .unwrap_or_else(move || self.language_settings_at(0, cx))
2572 }
2573
2574 pub fn language_settings_at<'a, T: ToOffset>(
2575 &'a self,
2576 point: T,
2577 cx: &'a App,
2578 ) -> Cow<'a, LanguageSettings> {
2579 let mut language = None;
2580 let mut file = None;
2581 if let Some((buffer, offset)) = self.point_to_buffer_offset(point, cx) {
2582 let buffer = buffer.read(cx);
2583 language = buffer.language_at(offset);
2584 file = buffer.file();
2585 }
2586 language_settings(language.map(|l| l.name()), file, cx)
2587 }
2588
2589 pub fn for_each_buffer(&self, mut f: impl FnMut(&Entity<Buffer>)) {
2590 self.buffers
2591 .borrow()
2592 .values()
2593 .for_each(|state| f(&state.buffer))
2594 }
2595
2596 pub fn title<'a>(&'a self, cx: &'a App) -> Cow<'a, str> {
2597 if let Some(title) = self.title.as_ref() {
2598 return title.into();
2599 }
2600
2601 if let Some(buffer) = self.as_singleton() {
2602 let buffer = buffer.read(cx);
2603
2604 if let Some(file) = buffer.file() {
2605 return file.file_name(cx).into();
2606 }
2607
2608 if let Some(title) = self.buffer_content_title(buffer) {
2609 return title;
2610 }
2611 };
2612
2613 "untitled".into()
2614 }
2615
2616 fn buffer_content_title(&self, buffer: &Buffer) -> Option<Cow<'_, str>> {
2617 let mut is_leading_whitespace = true;
2618 let mut count = 0;
2619 let mut prev_was_space = false;
2620 let mut title = String::new();
2621
2622 for ch in buffer.snapshot().chars() {
2623 if is_leading_whitespace && ch.is_whitespace() {
2624 continue;
2625 }
2626
2627 is_leading_whitespace = false;
2628
2629 if ch == '\n' || count >= 40 {
2630 break;
2631 }
2632
2633 if ch.is_whitespace() {
2634 if !prev_was_space {
2635 title.push(' ');
2636 count += 1;
2637 prev_was_space = true;
2638 }
2639 } else {
2640 title.push(ch);
2641 count += 1;
2642 prev_was_space = false;
2643 }
2644 }
2645
2646 let title = title.trim_end().to_string();
2647
2648 if title.is_empty() {
2649 return None;
2650 }
2651
2652 Some(title.into())
2653 }
2654
2655 pub fn set_title(&mut self, title: String, cx: &mut Context<Self>) {
2656 self.title = Some(title);
2657 cx.notify();
2658 }
2659
2660 /// Preserve preview tabs containing this multibuffer until additional edits occur.
2661 pub fn refresh_preview(&self, cx: &mut Context<Self>) {
2662 for buffer_state in self.buffers.borrow().values() {
2663 buffer_state
2664 .buffer
2665 .update(cx, |buffer, _cx| buffer.refresh_preview());
2666 }
2667 }
2668
2669 /// Whether we should preserve the preview status of a tab containing this multi-buffer.
2670 pub fn preserve_preview(&self, cx: &App) -> bool {
2671 self.buffers
2672 .borrow()
2673 .values()
2674 .all(|state| state.buffer.read(cx).preserve_preview())
2675 }
2676
2677 #[cfg(any(test, feature = "test-support"))]
2678 pub fn is_parsing(&self, cx: &App) -> bool {
2679 self.as_singleton().unwrap().read(cx).is_parsing()
2680 }
2681
2682 pub fn add_diff(&mut self, diff: Entity<BufferDiff>, cx: &mut Context<Self>) {
2683 let buffer_id = diff.read(cx).buffer_id;
2684 self.buffer_diff_changed(diff.clone(), text::Anchor::MIN..text::Anchor::MAX, cx);
2685 self.diffs.insert(buffer_id, DiffState::new(diff, cx));
2686 }
2687
2688 pub fn diff_for(&self, buffer_id: BufferId) -> Option<Entity<BufferDiff>> {
2689 self.diffs.get(&buffer_id).map(|state| state.diff.clone())
2690 }
2691
2692 pub fn expand_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
2693 self.expand_or_collapse_diff_hunks(ranges, true, cx);
2694 }
2695
2696 pub fn collapse_diff_hunks(&mut self, ranges: Vec<Range<Anchor>>, cx: &mut Context<Self>) {
2697 self.expand_or_collapse_diff_hunks(ranges, false, cx);
2698 }
2699
2700 pub fn set_all_diff_hunks_expanded(&mut self, cx: &mut Context<Self>) {
2701 self.snapshot.borrow_mut().all_diff_hunks_expanded = true;
2702 self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], true, cx);
2703 }
2704
2705 pub fn all_diff_hunks_expanded(&self) -> bool {
2706 self.snapshot.borrow().all_diff_hunks_expanded
2707 }
2708
2709 pub fn set_all_diff_hunks_collapsed(&mut self, cx: &mut Context<Self>) {
2710 self.snapshot.borrow_mut().all_diff_hunks_expanded = false;
2711 self.expand_or_collapse_diff_hunks(vec![Anchor::min()..Anchor::max()], false, cx);
2712 }
2713
2714 pub fn has_multiple_hunks(&self, cx: &App) -> bool {
2715 self.read(cx)
2716 .diff_hunks_in_range(Anchor::min()..Anchor::max())
2717 .nth(1)
2718 .is_some()
2719 }
2720
2721 pub fn single_hunk_is_expanded(&self, range: Range<Anchor>, cx: &App) -> bool {
2722 let snapshot = self.read(cx);
2723 let mut cursor = snapshot.diff_transforms.cursor::<usize>(());
2724 let offset_range = range.to_offset(&snapshot);
2725 cursor.seek(&offset_range.start, Bias::Left);
2726 while let Some(item) = cursor.item() {
2727 if *cursor.start() >= offset_range.end && *cursor.start() > offset_range.start {
2728 break;
2729 }
2730 if item.hunk_info().is_some() {
2731 return true;
2732 }
2733 cursor.next();
2734 }
2735 false
2736 }
2737
2738 pub fn has_expanded_diff_hunks_in_ranges(&self, ranges: &[Range<Anchor>], cx: &App) -> bool {
2739 let snapshot = self.read(cx);
2740 let mut cursor = snapshot.diff_transforms.cursor::<usize>(());
2741 for range in ranges {
2742 let range = range.to_point(&snapshot);
2743 let start = snapshot.point_to_offset(Point::new(range.start.row, 0));
2744 let end = snapshot.point_to_offset(Point::new(range.end.row + 1, 0));
2745 let start = start.saturating_sub(1);
2746 let end = snapshot.len().min(end + 1);
2747 cursor.seek(&start, Bias::Right);
2748 while let Some(item) = cursor.item() {
2749 if *cursor.start() >= end {
2750 break;
2751 }
2752 if item.hunk_info().is_some() {
2753 return true;
2754 }
2755 cursor.next();
2756 }
2757 }
2758 false
2759 }
2760
2761 pub fn expand_or_collapse_diff_hunks_inner(
2762 &mut self,
2763 ranges: impl IntoIterator<Item = (Range<Point>, ExcerptId)>,
2764 expand: bool,
2765 cx: &mut Context<Self>,
2766 ) {
2767 if self.snapshot.borrow().all_diff_hunks_expanded && !expand {
2768 return;
2769 }
2770 self.sync(cx);
2771 let mut snapshot = self.snapshot.borrow_mut();
2772 let mut excerpt_edits = Vec::new();
2773 let mut last_hunk_row = None;
2774 for (range, end_excerpt_id) in ranges {
2775 for diff_hunk in snapshot.diff_hunks_in_range(range) {
2776 if diff_hunk.excerpt_id.cmp(&end_excerpt_id, &snapshot).is_gt() {
2777 continue;
2778 }
2779 if last_hunk_row.is_some_and(|row| row >= diff_hunk.row_range.start) {
2780 continue;
2781 }
2782 let start = Anchor::in_buffer(
2783 diff_hunk.excerpt_id,
2784 diff_hunk.buffer_id,
2785 diff_hunk.buffer_range.start,
2786 );
2787 let end = Anchor::in_buffer(
2788 diff_hunk.excerpt_id,
2789 diff_hunk.buffer_id,
2790 diff_hunk.buffer_range.end,
2791 );
2792 let start = snapshot.excerpt_offset_for_anchor(&start);
2793 let end = snapshot.excerpt_offset_for_anchor(&end);
2794 last_hunk_row = Some(diff_hunk.row_range.start);
2795 excerpt_edits.push(text::Edit {
2796 old: start..end,
2797 new: start..end,
2798 });
2799 }
2800 }
2801
2802 self.sync_diff_transforms(
2803 &mut snapshot,
2804 excerpt_edits,
2805 DiffChangeKind::ExpandOrCollapseHunks { expand },
2806 );
2807 cx.emit(Event::DiffHunksToggled);
2808 cx.emit(Event::Edited {
2809 singleton_buffer_edited: false,
2810 edited_buffer: None,
2811 });
2812 }
2813
2814 pub fn expand_or_collapse_diff_hunks(
2815 &mut self,
2816 ranges: Vec<Range<Anchor>>,
2817 expand: bool,
2818 cx: &mut Context<Self>,
2819 ) {
2820 let snapshot = self.snapshot.borrow().clone();
2821 let ranges = ranges.iter().map(move |range| {
2822 let end_excerpt_id = range.end.excerpt_id;
2823 let range = range.to_point(&snapshot);
2824 let mut peek_end = range.end;
2825 if range.end.row < snapshot.max_row().0 {
2826 peek_end = Point::new(range.end.row + 1, 0);
2827 };
2828 (range.start..peek_end, end_excerpt_id)
2829 });
2830 self.expand_or_collapse_diff_hunks_inner(ranges, expand, cx);
2831 }
2832
2833 pub fn resize_excerpt(
2834 &mut self,
2835 id: ExcerptId,
2836 range: Range<text::Anchor>,
2837 cx: &mut Context<Self>,
2838 ) {
2839 self.sync(cx);
2840
2841 let mut snapshot = self.snapshot.borrow_mut();
2842 let locator = snapshot.excerpt_locator_for_id(id);
2843 let mut new_excerpts = SumTree::default();
2844 let mut cursor = snapshot
2845 .excerpts
2846 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2847 let mut edits = Vec::<Edit<ExcerptOffset>>::new();
2848
2849 let prefix = cursor.slice(&Some(locator), Bias::Left);
2850 new_excerpts.append(prefix, ());
2851
2852 let mut excerpt = cursor.item().unwrap().clone();
2853 let old_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2854
2855 excerpt.range.context.start = range.start;
2856 excerpt.range.context.end = range.end;
2857 excerpt.max_buffer_row = range.end.to_point(&excerpt.buffer).row;
2858
2859 excerpt.text_summary = excerpt
2860 .buffer
2861 .text_summary_for_range(excerpt.range.context.clone());
2862
2863 let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len);
2864 let old_start_offset = cursor.start().1;
2865 let new_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2866 let edit = Edit {
2867 old: old_start_offset..old_start_offset + old_text_len,
2868 new: new_start_offset..new_start_offset + new_text_len,
2869 };
2870
2871 if let Some(last_edit) = edits.last_mut() {
2872 if last_edit.old.end == edit.old.start {
2873 last_edit.old.end = edit.old.end;
2874 last_edit.new.end = edit.new.end;
2875 } else {
2876 edits.push(edit);
2877 }
2878 } else {
2879 edits.push(edit);
2880 }
2881
2882 new_excerpts.push(excerpt, ());
2883
2884 cursor.next();
2885
2886 new_excerpts.append(cursor.suffix(), ());
2887
2888 drop(cursor);
2889 snapshot.excerpts = new_excerpts;
2890
2891 self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
2892 cx.emit(Event::Edited {
2893 singleton_buffer_edited: false,
2894 edited_buffer: None,
2895 });
2896 cx.emit(Event::ExcerptsExpanded { ids: vec![id] });
2897 cx.notify();
2898 }
2899
2900 pub fn expand_excerpts(
2901 &mut self,
2902 ids: impl IntoIterator<Item = ExcerptId>,
2903 line_count: u32,
2904 direction: ExpandExcerptDirection,
2905 cx: &mut Context<Self>,
2906 ) {
2907 if line_count == 0 {
2908 return;
2909 }
2910 self.sync(cx);
2911 if !self.excerpts_by_path.is_empty() {
2912 self.expand_excerpts_with_paths(ids, line_count, direction, cx);
2913 return;
2914 }
2915 let mut snapshot = self.snapshot.borrow_mut();
2916
2917 let ids = ids.into_iter().collect::<Vec<_>>();
2918 let locators = snapshot.excerpt_locators_for_ids(ids.iter().copied());
2919 let mut new_excerpts = SumTree::default();
2920 let mut cursor = snapshot
2921 .excerpts
2922 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
2923 let mut edits = Vec::<Edit<ExcerptOffset>>::new();
2924
2925 for locator in &locators {
2926 let prefix = cursor.slice(&Some(locator), Bias::Left);
2927 new_excerpts.append(prefix, ());
2928
2929 let mut excerpt = cursor.item().unwrap().clone();
2930 let old_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2931
2932 let up_line_count = if direction.should_expand_up() {
2933 line_count
2934 } else {
2935 0
2936 };
2937
2938 let start_row = excerpt
2939 .range
2940 .context
2941 .start
2942 .to_point(&excerpt.buffer)
2943 .row
2944 .saturating_sub(up_line_count);
2945 let start_point = Point::new(start_row, 0);
2946 excerpt.range.context.start = excerpt.buffer.anchor_before(start_point);
2947
2948 let down_line_count = if direction.should_expand_down() {
2949 line_count
2950 } else {
2951 0
2952 };
2953
2954 let mut end_point = excerpt.buffer.clip_point(
2955 excerpt.range.context.end.to_point(&excerpt.buffer)
2956 + Point::new(down_line_count, 0),
2957 Bias::Left,
2958 );
2959 end_point.column = excerpt.buffer.line_len(end_point.row);
2960 excerpt.range.context.end = excerpt.buffer.anchor_after(end_point);
2961 excerpt.max_buffer_row = end_point.row;
2962
2963 excerpt.text_summary = excerpt
2964 .buffer
2965 .text_summary_for_range(excerpt.range.context.clone());
2966
2967 let new_start_offset = ExcerptOffset::new(new_excerpts.summary().text.len);
2968 let old_start_offset = cursor.start().1;
2969 let new_text_len = ExcerptOffset::new(excerpt.text_summary.len);
2970 let edit = Edit {
2971 old: old_start_offset..old_start_offset + old_text_len,
2972 new: new_start_offset..new_start_offset + new_text_len,
2973 };
2974
2975 if let Some(last_edit) = edits.last_mut() {
2976 if last_edit.old.end == edit.old.start {
2977 last_edit.old.end = edit.old.end;
2978 last_edit.new.end = edit.new.end;
2979 } else {
2980 edits.push(edit);
2981 }
2982 } else {
2983 edits.push(edit);
2984 }
2985
2986 new_excerpts.push(excerpt, ());
2987
2988 cursor.next();
2989 }
2990
2991 new_excerpts.append(cursor.suffix(), ());
2992
2993 drop(cursor);
2994 snapshot.excerpts = new_excerpts;
2995
2996 self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
2997 cx.emit(Event::Edited {
2998 singleton_buffer_edited: false,
2999 edited_buffer: None,
3000 });
3001 cx.emit(Event::ExcerptsExpanded { ids });
3002 cx.notify();
3003 }
3004
3005 fn sync(&self, cx: &App) {
3006 let changed = self.buffer_changed_since_sync.replace(false);
3007 if !changed {
3008 return;
3009 }
3010
3011 let mut snapshot = self.snapshot.borrow_mut();
3012 let mut excerpts_to_edit = Vec::new();
3013 let mut non_text_state_updated = false;
3014 let mut is_dirty = false;
3015 let mut has_deleted_file = false;
3016 let mut has_conflict = false;
3017 let mut edited = false;
3018 let mut buffers = self.buffers.borrow_mut();
3019 for buffer_state in buffers.values_mut() {
3020 let buffer = buffer_state.buffer.read(cx);
3021 let version = buffer.version();
3022 let non_text_state_update_count = buffer.non_text_state_update_count();
3023
3024 let buffer_edited = version.changed_since(&buffer_state.last_version);
3025 let buffer_non_text_state_updated =
3026 non_text_state_update_count > buffer_state.last_non_text_state_update_count;
3027 if buffer_edited || buffer_non_text_state_updated {
3028 buffer_state.last_version = version;
3029 buffer_state.last_non_text_state_update_count = non_text_state_update_count;
3030 excerpts_to_edit.extend(
3031 buffer_state
3032 .excerpts
3033 .iter()
3034 .map(|locator| (locator, buffer_state.buffer.clone(), buffer_edited)),
3035 );
3036 }
3037
3038 edited |= buffer_edited;
3039 non_text_state_updated |= buffer_non_text_state_updated;
3040 is_dirty |= buffer.is_dirty();
3041 has_deleted_file |= buffer
3042 .file()
3043 .is_some_and(|file| file.disk_state() == DiskState::Deleted);
3044 has_conflict |= buffer.has_conflict();
3045 }
3046 if edited {
3047 snapshot.edit_count += 1;
3048 }
3049 if non_text_state_updated {
3050 snapshot.non_text_state_update_count += 1;
3051 }
3052 snapshot.is_dirty = is_dirty;
3053 snapshot.has_deleted_file = has_deleted_file;
3054 snapshot.has_conflict = has_conflict;
3055
3056 for (id, diff) in self.diffs.iter() {
3057 if snapshot.diffs.get(id).is_none() {
3058 snapshot.diffs.insert(*id, diff.diff.read(cx).snapshot(cx));
3059 }
3060 }
3061
3062 excerpts_to_edit.sort_unstable_by_key(|(locator, _, _)| *locator);
3063
3064 let mut edits = Vec::new();
3065 let mut new_excerpts = SumTree::default();
3066 let mut cursor = snapshot
3067 .excerpts
3068 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
3069
3070 for (locator, buffer, buffer_edited) in excerpts_to_edit {
3071 new_excerpts.append(cursor.slice(&Some(locator), Bias::Left), ());
3072 let old_excerpt = cursor.item().unwrap();
3073 let buffer = buffer.read(cx);
3074 let buffer_id = buffer.remote_id();
3075
3076 let mut new_excerpt;
3077 if buffer_edited {
3078 edits.extend(
3079 buffer
3080 .edits_since_in_range::<usize>(
3081 old_excerpt.buffer.version(),
3082 old_excerpt.range.context.clone(),
3083 )
3084 .map(|edit| {
3085 let excerpt_old_start = cursor.start().1;
3086 let excerpt_new_start =
3087 ExcerptOffset::new(new_excerpts.summary().text.len);
3088 let old_start = excerpt_old_start + ExcerptOffset::new(edit.old.start);
3089 let old_end = excerpt_old_start + ExcerptOffset::new(edit.old.end);
3090 let new_start = excerpt_new_start + ExcerptOffset::new(edit.new.start);
3091 let new_end = excerpt_new_start + ExcerptOffset::new(edit.new.end);
3092 Edit {
3093 old: old_start..old_end,
3094 new: new_start..new_end,
3095 }
3096 }),
3097 );
3098
3099 new_excerpt = Excerpt::new(
3100 old_excerpt.id,
3101 locator.clone(),
3102 buffer_id,
3103 buffer.snapshot(),
3104 old_excerpt.range.clone(),
3105 old_excerpt.has_trailing_newline,
3106 );
3107 } else {
3108 new_excerpt = old_excerpt.clone();
3109 new_excerpt.buffer = buffer.snapshot();
3110 }
3111
3112 new_excerpts.push(new_excerpt, ());
3113 cursor.next();
3114 }
3115 new_excerpts.append(cursor.suffix(), ());
3116
3117 drop(cursor);
3118 snapshot.excerpts = new_excerpts;
3119
3120 self.sync_diff_transforms(&mut snapshot, edits, DiffChangeKind::BufferEdited);
3121 }
3122
3123 fn sync_diff_transforms(
3124 &self,
3125 snapshot: &mut MultiBufferSnapshot,
3126 excerpt_edits: Vec<text::Edit<ExcerptOffset>>,
3127 change_kind: DiffChangeKind,
3128 ) {
3129 if excerpt_edits.is_empty() {
3130 return;
3131 }
3132
3133 let mut excerpts = snapshot.excerpts.cursor::<ExcerptOffset>(());
3134 let mut old_diff_transforms = snapshot
3135 .diff_transforms
3136 .cursor::<Dimensions<ExcerptOffset, usize>>(());
3137 let mut new_diff_transforms = SumTree::default();
3138 let mut old_expanded_hunks = HashSet::default();
3139 let mut output_edits = Vec::new();
3140 let mut output_delta = 0_isize;
3141 let mut at_transform_boundary = true;
3142 let mut end_of_current_insert = None;
3143
3144 let mut excerpt_edits = excerpt_edits.into_iter().peekable();
3145 while let Some(edit) = excerpt_edits.next() {
3146 excerpts.seek_forward(&edit.new.start, Bias::Right);
3147 if excerpts.item().is_none() && *excerpts.start() == edit.new.start {
3148 excerpts.prev();
3149 }
3150
3151 // Keep any transforms that are before the edit.
3152 if at_transform_boundary {
3153 at_transform_boundary = false;
3154 let transforms_before_edit = old_diff_transforms.slice(&edit.old.start, Bias::Left);
3155 self.append_diff_transforms(&mut new_diff_transforms, transforms_before_edit);
3156 if let Some(transform) = old_diff_transforms.item()
3157 && old_diff_transforms.end().0 == edit.old.start
3158 && old_diff_transforms.start().0 < edit.old.start
3159 {
3160 self.push_diff_transform(&mut new_diff_transforms, transform.clone());
3161 old_diff_transforms.next();
3162 }
3163 }
3164
3165 // Compute the start of the edit in output coordinates.
3166 let edit_start_overshoot = (edit.old.start - old_diff_transforms.start().0).value;
3167 let edit_old_start = old_diff_transforms.start().1 + edit_start_overshoot;
3168 let edit_new_start = (edit_old_start as isize + output_delta) as usize;
3169
3170 let changed_diff_hunks = self.recompute_diff_transforms_for_edit(
3171 &edit,
3172 &mut excerpts,
3173 &mut old_diff_transforms,
3174 &mut new_diff_transforms,
3175 &mut end_of_current_insert,
3176 &mut old_expanded_hunks,
3177 snapshot,
3178 change_kind,
3179 );
3180
3181 // Compute the end of the edit in output coordinates.
3182 let edit_old_end_overshoot = edit.old.end - old_diff_transforms.start().0;
3183 let edit_new_end_overshoot = edit.new.end - new_diff_transforms.summary().excerpt_len();
3184 let edit_old_end = old_diff_transforms.start().1 + edit_old_end_overshoot.value;
3185 let edit_new_end =
3186 new_diff_transforms.summary().output.len + edit_new_end_overshoot.value;
3187 let output_edit = Edit {
3188 old: edit_old_start..edit_old_end,
3189 new: edit_new_start..edit_new_end,
3190 };
3191
3192 output_delta += (output_edit.new.end - output_edit.new.start) as isize;
3193 output_delta -= (output_edit.old.end - output_edit.old.start) as isize;
3194 if changed_diff_hunks || matches!(change_kind, DiffChangeKind::BufferEdited) {
3195 output_edits.push(output_edit);
3196 }
3197
3198 // If this is the last edit that intersects the current diff transform,
3199 // then recreate the content up to the end of this transform, to prepare
3200 // for reusing additional slices of the old transforms.
3201 if excerpt_edits
3202 .peek()
3203 .is_none_or(|next_edit| next_edit.old.start >= old_diff_transforms.end().0)
3204 {
3205 let keep_next_old_transform = (old_diff_transforms.start().0 >= edit.old.end)
3206 && match old_diff_transforms.item() {
3207 Some(DiffTransform::BufferContent {
3208 inserted_hunk_info: Some(hunk),
3209 ..
3210 }) => excerpts.item().is_some_and(|excerpt| {
3211 hunk.hunk_start_anchor.is_valid(&excerpt.buffer)
3212 }),
3213 _ => true,
3214 };
3215
3216 let mut excerpt_offset = edit.new.end;
3217 if !keep_next_old_transform {
3218 excerpt_offset += old_diff_transforms.end().0 - edit.old.end;
3219 old_diff_transforms.next();
3220 }
3221
3222 old_expanded_hunks.clear();
3223 self.push_buffer_content_transform(
3224 snapshot,
3225 &mut new_diff_transforms,
3226 excerpt_offset,
3227 end_of_current_insert,
3228 );
3229 at_transform_boundary = true;
3230 }
3231 }
3232
3233 // Keep any transforms that are after the last edit.
3234 self.append_diff_transforms(&mut new_diff_transforms, old_diff_transforms.suffix());
3235
3236 // Ensure there's always at least one buffer content transform.
3237 if new_diff_transforms.is_empty() {
3238 new_diff_transforms.push(
3239 DiffTransform::BufferContent {
3240 summary: Default::default(),
3241 inserted_hunk_info: None,
3242 },
3243 (),
3244 );
3245 }
3246
3247 self.subscriptions.publish(output_edits);
3248 drop(old_diff_transforms);
3249 drop(excerpts);
3250 snapshot.diff_transforms = new_diff_transforms;
3251 snapshot.edit_count += 1;
3252
3253 #[cfg(any(test, feature = "test-support"))]
3254 snapshot.check_invariants();
3255 }
3256
3257 fn recompute_diff_transforms_for_edit(
3258 &self,
3259 edit: &Edit<TypedOffset<Excerpt>>,
3260 excerpts: &mut Cursor<Excerpt, TypedOffset<Excerpt>>,
3261 old_diff_transforms: &mut Cursor<DiffTransform, Dimensions<TypedOffset<Excerpt>, usize>>,
3262 new_diff_transforms: &mut SumTree<DiffTransform>,
3263 end_of_current_insert: &mut Option<(TypedOffset<Excerpt>, DiffTransformHunkInfo)>,
3264 old_expanded_hunks: &mut HashSet<DiffTransformHunkInfo>,
3265 snapshot: &MultiBufferSnapshot,
3266 change_kind: DiffChangeKind,
3267 ) -> bool {
3268 log::trace!(
3269 "recomputing diff transform for edit {:?} => {:?}",
3270 edit.old.start.value..edit.old.end.value,
3271 edit.new.start.value..edit.new.end.value
3272 );
3273
3274 // Record which hunks were previously expanded.
3275 while let Some(item) = old_diff_transforms.item() {
3276 if let Some(hunk_info) = item.hunk_info() {
3277 log::trace!(
3278 "previously expanded hunk at {}",
3279 old_diff_transforms.start().0
3280 );
3281 old_expanded_hunks.insert(hunk_info);
3282 }
3283 if old_diff_transforms.end().0 > edit.old.end {
3284 break;
3285 }
3286 old_diff_transforms.next();
3287 }
3288
3289 // Avoid querying diff hunks if there's no possibility of hunks being expanded.
3290 let all_diff_hunks_expanded = snapshot.all_diff_hunks_expanded;
3291 if old_expanded_hunks.is_empty()
3292 && change_kind == DiffChangeKind::BufferEdited
3293 && !all_diff_hunks_expanded
3294 {
3295 return false;
3296 }
3297
3298 // Visit each excerpt that intersects the edit.
3299 let mut did_expand_hunks = false;
3300 while let Some(excerpt) = excerpts.item() {
3301 // Recompute the expanded hunks in the portion of the excerpt that
3302 // intersects the edit.
3303 if let Some(diff) = snapshot.diffs.get(&excerpt.buffer_id) {
3304 let buffer = &excerpt.buffer;
3305 let excerpt_start = *excerpts.start();
3306 let excerpt_end = excerpt_start + ExcerptOffset::new(excerpt.text_summary.len);
3307 let excerpt_buffer_start = excerpt.range.context.start.to_offset(buffer);
3308 let excerpt_buffer_end = excerpt_buffer_start + excerpt.text_summary.len;
3309 let edit_buffer_start =
3310 excerpt_buffer_start + edit.new.start.value.saturating_sub(excerpt_start.value);
3311 let edit_buffer_end =
3312 excerpt_buffer_start + edit.new.end.value.saturating_sub(excerpt_start.value);
3313 let edit_buffer_end = edit_buffer_end.min(excerpt_buffer_end);
3314 let edit_anchor_range =
3315 buffer.anchor_before(edit_buffer_start)..buffer.anchor_after(edit_buffer_end);
3316
3317 for hunk in diff.hunks_intersecting_range(edit_anchor_range, buffer) {
3318 if hunk.is_created_file() && !all_diff_hunks_expanded {
3319 continue;
3320 }
3321
3322 let hunk_buffer_range = hunk.buffer_range.to_offset(buffer);
3323 if hunk_buffer_range.start < excerpt_buffer_start {
3324 log::trace!("skipping hunk that starts before excerpt");
3325 continue;
3326 }
3327
3328 let hunk_info = DiffTransformHunkInfo {
3329 excerpt_id: excerpt.id,
3330 hunk_start_anchor: hunk.buffer_range.start,
3331 hunk_secondary_status: hunk.secondary_status,
3332 };
3333
3334 let hunk_excerpt_start = excerpt_start
3335 + ExcerptOffset::new(
3336 hunk_buffer_range.start.saturating_sub(excerpt_buffer_start),
3337 );
3338 let hunk_excerpt_end = excerpt_end.min(
3339 excerpt_start
3340 + ExcerptOffset::new(hunk_buffer_range.end - excerpt_buffer_start),
3341 );
3342
3343 self.push_buffer_content_transform(
3344 snapshot,
3345 new_diff_transforms,
3346 hunk_excerpt_start,
3347 *end_of_current_insert,
3348 );
3349
3350 // For every existing hunk, determine if it was previously expanded
3351 // and if it should currently be expanded.
3352 let was_previously_expanded = old_expanded_hunks.contains(&hunk_info);
3353 let should_expand_hunk = match &change_kind {
3354 DiffChangeKind::DiffUpdated { base_changed: true } => {
3355 was_previously_expanded || all_diff_hunks_expanded
3356 }
3357 DiffChangeKind::ExpandOrCollapseHunks { expand } => {
3358 let intersects = hunk_buffer_range.is_empty()
3359 || hunk_buffer_range.end > edit_buffer_start;
3360 if *expand {
3361 intersects || was_previously_expanded || all_diff_hunks_expanded
3362 } else {
3363 !intersects && (was_previously_expanded || all_diff_hunks_expanded)
3364 }
3365 }
3366 _ => was_previously_expanded || all_diff_hunks_expanded,
3367 };
3368
3369 if should_expand_hunk {
3370 did_expand_hunks = true;
3371 log::trace!(
3372 "expanding hunk {:?}, excerpt:{:?}",
3373 hunk_excerpt_start.value..hunk_excerpt_end.value,
3374 excerpt.id
3375 );
3376
3377 if !hunk.diff_base_byte_range.is_empty()
3378 && hunk_buffer_range.start >= edit_buffer_start
3379 && hunk_buffer_range.start <= excerpt_buffer_end
3380 {
3381 let base_text = diff.base_text();
3382 let mut text_cursor =
3383 base_text.as_rope().cursor(hunk.diff_base_byte_range.start);
3384 let mut base_text_summary =
3385 text_cursor.summary::<TextSummary>(hunk.diff_base_byte_range.end);
3386
3387 let mut has_trailing_newline = false;
3388 if base_text_summary.last_line_chars > 0 {
3389 base_text_summary += TextSummary::newline();
3390 has_trailing_newline = true;
3391 }
3392
3393 new_diff_transforms.push(
3394 DiffTransform::DeletedHunk {
3395 base_text_byte_range: hunk.diff_base_byte_range.clone(),
3396 summary: base_text_summary,
3397 buffer_id: excerpt.buffer_id,
3398 hunk_info,
3399 has_trailing_newline,
3400 },
3401 (),
3402 );
3403 }
3404
3405 if !hunk_buffer_range.is_empty() {
3406 *end_of_current_insert =
3407 Some((hunk_excerpt_end.min(excerpt_end), hunk_info));
3408 }
3409 }
3410 }
3411 }
3412
3413 if excerpts.end() <= edit.new.end {
3414 excerpts.next();
3415 } else {
3416 break;
3417 }
3418 }
3419
3420 did_expand_hunks || !old_expanded_hunks.is_empty()
3421 }
3422
3423 fn append_diff_transforms(
3424 &self,
3425 new_transforms: &mut SumTree<DiffTransform>,
3426 subtree: SumTree<DiffTransform>,
3427 ) {
3428 if let Some(DiffTransform::BufferContent {
3429 inserted_hunk_info,
3430 summary,
3431 }) = subtree.first()
3432 && self.extend_last_buffer_content_transform(
3433 new_transforms,
3434 *inserted_hunk_info,
3435 *summary,
3436 )
3437 {
3438 let mut cursor = subtree.cursor::<()>(());
3439 cursor.next();
3440 cursor.next();
3441 new_transforms.append(cursor.suffix(), ());
3442 return;
3443 }
3444 new_transforms.append(subtree, ());
3445 }
3446
3447 fn push_diff_transform(
3448 &self,
3449 new_transforms: &mut SumTree<DiffTransform>,
3450 transform: DiffTransform,
3451 ) {
3452 if let DiffTransform::BufferContent {
3453 inserted_hunk_info: inserted_hunk_anchor,
3454 summary,
3455 } = transform
3456 && self.extend_last_buffer_content_transform(
3457 new_transforms,
3458 inserted_hunk_anchor,
3459 summary,
3460 )
3461 {
3462 return;
3463 }
3464 new_transforms.push(transform, ());
3465 }
3466
3467 fn push_buffer_content_transform(
3468 &self,
3469 old_snapshot: &MultiBufferSnapshot,
3470 new_transforms: &mut SumTree<DiffTransform>,
3471 end_offset: ExcerptOffset,
3472 current_inserted_hunk: Option<(ExcerptOffset, DiffTransformHunkInfo)>,
3473 ) {
3474 let inserted_region = current_inserted_hunk.map(|(insertion_end_offset, hunk_info)| {
3475 (end_offset.min(insertion_end_offset), Some(hunk_info))
3476 });
3477 let unchanged_region = [(end_offset, None)];
3478
3479 for (end_offset, inserted_hunk_info) in inserted_region.into_iter().chain(unchanged_region)
3480 {
3481 let start_offset = new_transforms.summary().excerpt_len();
3482 if end_offset <= start_offset {
3483 continue;
3484 }
3485 let summary_to_add = old_snapshot
3486 .text_summary_for_excerpt_offset_range::<TextSummary>(start_offset..end_offset);
3487
3488 if !self.extend_last_buffer_content_transform(
3489 new_transforms,
3490 inserted_hunk_info,
3491 summary_to_add,
3492 ) {
3493 new_transforms.push(
3494 DiffTransform::BufferContent {
3495 summary: summary_to_add,
3496 inserted_hunk_info,
3497 },
3498 (),
3499 )
3500 }
3501 }
3502 }
3503
3504 fn extend_last_buffer_content_transform(
3505 &self,
3506 new_transforms: &mut SumTree<DiffTransform>,
3507 new_inserted_hunk_info: Option<DiffTransformHunkInfo>,
3508 summary_to_add: TextSummary,
3509 ) -> bool {
3510 let mut did_extend = false;
3511 new_transforms.update_last(
3512 |last_transform| {
3513 if let DiffTransform::BufferContent {
3514 summary,
3515 inserted_hunk_info: inserted_hunk_anchor,
3516 } = last_transform
3517 && *inserted_hunk_anchor == new_inserted_hunk_info
3518 {
3519 *summary += summary_to_add;
3520 did_extend = true;
3521 }
3522 },
3523 (),
3524 );
3525 did_extend
3526 }
3527}
3528
3529fn build_excerpt_ranges(
3530 ranges: impl IntoIterator<Item = Range<Point>>,
3531 context_line_count: u32,
3532 buffer_snapshot: &BufferSnapshot,
3533) -> Vec<ExcerptRange<Point>> {
3534 ranges
3535 .into_iter()
3536 .map(|range| {
3537 let start_row = range.start.row.saturating_sub(context_line_count);
3538 let start = Point::new(start_row, 0);
3539 let end_row = (range.end.row + context_line_count).min(buffer_snapshot.max_point().row);
3540 let end = Point::new(end_row, buffer_snapshot.line_len(end_row));
3541 ExcerptRange {
3542 context: start..end,
3543 primary: range,
3544 }
3545 })
3546 .collect()
3547}
3548
3549#[cfg(any(test, feature = "test-support"))]
3550impl MultiBuffer {
3551 pub fn build_simple(text: &str, cx: &mut gpui::App) -> Entity<Self> {
3552 let buffer = cx.new(|cx| Buffer::local(text, cx));
3553 cx.new(|cx| Self::singleton(buffer, cx))
3554 }
3555
3556 pub fn build_multi<const COUNT: usize>(
3557 excerpts: [(&str, Vec<Range<Point>>); COUNT],
3558 cx: &mut gpui::App,
3559 ) -> Entity<Self> {
3560 let multi = cx.new(|_| Self::new(Capability::ReadWrite));
3561 for (text, ranges) in excerpts {
3562 let buffer = cx.new(|cx| Buffer::local(text, cx));
3563 let excerpt_ranges = ranges.into_iter().map(ExcerptRange::new);
3564 multi.update(cx, |multi, cx| {
3565 multi.push_excerpts(buffer, excerpt_ranges, cx)
3566 });
3567 }
3568
3569 multi
3570 }
3571
3572 pub fn build_from_buffer(buffer: Entity<Buffer>, cx: &mut gpui::App) -> Entity<Self> {
3573 cx.new(|cx| Self::singleton(buffer, cx))
3574 }
3575
3576 pub fn build_random(rng: &mut impl rand::Rng, cx: &mut gpui::App) -> Entity<Self> {
3577 cx.new(|cx| {
3578 let mut multibuffer = MultiBuffer::new(Capability::ReadWrite);
3579 let mutation_count = rng.random_range(1..=5);
3580 multibuffer.randomly_edit_excerpts(rng, mutation_count, cx);
3581 multibuffer
3582 })
3583 }
3584
3585 pub fn randomly_edit(
3586 &mut self,
3587 rng: &mut impl rand::Rng,
3588 edit_count: usize,
3589 cx: &mut Context<Self>,
3590 ) {
3591 use util::RandomCharIter;
3592
3593 let snapshot = self.read(cx);
3594 let mut edits: Vec<(Range<usize>, Arc<str>)> = Vec::new();
3595 let mut last_end = None;
3596 for _ in 0..edit_count {
3597 if last_end.is_some_and(|last_end| last_end >= snapshot.len()) {
3598 break;
3599 }
3600
3601 let new_start = last_end.map_or(0, |last_end| last_end + 1);
3602 let end =
3603 snapshot.clip_offset(rng.random_range(new_start..=snapshot.len()), Bias::Right);
3604 let start = snapshot.clip_offset(rng.random_range(new_start..=end), Bias::Right);
3605 last_end = Some(end);
3606
3607 let mut range = start..end;
3608 if rng.random_bool(0.2) {
3609 mem::swap(&mut range.start, &mut range.end);
3610 }
3611
3612 let new_text_len = rng.random_range(0..10);
3613 let new_text: String = RandomCharIter::new(&mut *rng).take(new_text_len).collect();
3614
3615 edits.push((range, new_text.into()));
3616 }
3617 log::info!("mutating multi-buffer with {:?}", edits);
3618 drop(snapshot);
3619
3620 self.edit(edits, None, cx);
3621 }
3622
3623 pub fn randomly_edit_excerpts(
3624 &mut self,
3625 rng: &mut impl rand::Rng,
3626 mutation_count: usize,
3627 cx: &mut Context<Self>,
3628 ) {
3629 use rand::prelude::*;
3630 use std::env;
3631 use util::RandomCharIter;
3632
3633 let max_excerpts = env::var("MAX_EXCERPTS")
3634 .map(|i| i.parse().expect("invalid `MAX_EXCERPTS` variable"))
3635 .unwrap_or(5);
3636
3637 let mut buffers = Vec::new();
3638 for _ in 0..mutation_count {
3639 if rng.random_bool(0.05) {
3640 log::info!("Clearing multi-buffer");
3641 self.clear(cx);
3642 continue;
3643 } else if rng.random_bool(0.1) && !self.excerpt_ids().is_empty() {
3644 let ids = self.excerpt_ids();
3645 let mut excerpts = HashSet::default();
3646 for _ in 0..rng.random_range(0..ids.len()) {
3647 excerpts.extend(ids.choose(rng).copied());
3648 }
3649
3650 let line_count = rng.random_range(0..5);
3651
3652 log::info!("Expanding excerpts {excerpts:?} by {line_count} lines");
3653
3654 self.expand_excerpts(
3655 excerpts.iter().cloned(),
3656 line_count,
3657 ExpandExcerptDirection::UpAndDown,
3658 cx,
3659 );
3660 continue;
3661 }
3662
3663 let excerpt_ids = self.excerpt_ids();
3664 if excerpt_ids.is_empty() || (rng.random() && excerpt_ids.len() < max_excerpts) {
3665 let buffer_handle = if rng.random() || self.buffers.borrow().is_empty() {
3666 let text = RandomCharIter::new(&mut *rng).take(10).collect::<String>();
3667 buffers.push(cx.new(|cx| Buffer::local(text, cx)));
3668 let buffer = buffers.last().unwrap().read(cx);
3669 log::info!(
3670 "Creating new buffer {} with text: {:?}",
3671 buffer.remote_id(),
3672 buffer.text()
3673 );
3674 buffers.last().unwrap().clone()
3675 } else {
3676 self.buffers
3677 .borrow()
3678 .values()
3679 .choose(rng)
3680 .unwrap()
3681 .buffer
3682 .clone()
3683 };
3684
3685 let buffer = buffer_handle.read(cx);
3686 let buffer_text = buffer.text();
3687 let ranges = (0..rng.random_range(0..5))
3688 .map(|_| {
3689 let end_ix =
3690 buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Right);
3691 let start_ix = buffer.clip_offset(rng.random_range(0..=end_ix), Bias::Left);
3692 ExcerptRange::new(start_ix..end_ix)
3693 })
3694 .collect::<Vec<_>>();
3695 log::info!(
3696 "Inserting excerpts from buffer {} and ranges {:?}: {:?}",
3697 buffer_handle.read(cx).remote_id(),
3698 ranges.iter().map(|r| &r.context).collect::<Vec<_>>(),
3699 ranges
3700 .iter()
3701 .map(|r| &buffer_text[r.context.clone()])
3702 .collect::<Vec<_>>()
3703 );
3704
3705 let excerpt_id = self.push_excerpts(buffer_handle.clone(), ranges, cx);
3706 log::info!("Inserted with ids: {:?}", excerpt_id);
3707 } else {
3708 let remove_count = rng.random_range(1..=excerpt_ids.len());
3709 let mut excerpts_to_remove = excerpt_ids
3710 .choose_multiple(rng, remove_count)
3711 .cloned()
3712 .collect::<Vec<_>>();
3713 let snapshot = self.snapshot.borrow();
3714 excerpts_to_remove.sort_unstable_by(|a, b| a.cmp(b, &snapshot));
3715 drop(snapshot);
3716 log::info!("Removing excerpts {:?}", excerpts_to_remove);
3717 self.remove_excerpts(excerpts_to_remove, cx);
3718 }
3719 }
3720 }
3721
3722 pub fn randomly_mutate(
3723 &mut self,
3724 rng: &mut impl rand::Rng,
3725 mutation_count: usize,
3726 cx: &mut Context<Self>,
3727 ) {
3728 use rand::prelude::*;
3729
3730 if rng.random_bool(0.7) || self.singleton {
3731 let buffer = self
3732 .buffers
3733 .borrow()
3734 .values()
3735 .choose(rng)
3736 .map(|state| state.buffer.clone());
3737
3738 if let Some(buffer) = buffer {
3739 buffer.update(cx, |buffer, cx| {
3740 if rng.random() {
3741 buffer.randomly_edit(rng, mutation_count, cx);
3742 } else {
3743 buffer.randomly_undo_redo(rng, cx);
3744 }
3745 });
3746 } else {
3747 self.randomly_edit(rng, mutation_count, cx);
3748 }
3749 } else {
3750 self.randomly_edit_excerpts(rng, mutation_count, cx);
3751 }
3752
3753 self.check_invariants(cx);
3754 }
3755
3756 fn check_invariants(&self, cx: &App) {
3757 self.read(cx).check_invariants();
3758 }
3759}
3760
3761impl EventEmitter<Event> for MultiBuffer {}
3762
3763impl MultiBufferSnapshot {
3764 pub fn text(&self) -> String {
3765 self.chunks(0..self.len(), false)
3766 .map(|chunk| chunk.text)
3767 .collect()
3768 }
3769
3770 pub fn reversed_chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3771 self.reversed_chunks_in_range(0..position.to_offset(self))
3772 .flat_map(|c| c.chars().rev())
3773 }
3774
3775 fn reversed_chunks_in_range(&self, range: Range<usize>) -> ReversedMultiBufferChunks<'_> {
3776 let mut cursor = self.cursor::<usize>();
3777 cursor.seek(&range.end);
3778 let current_chunks = cursor.region().as_ref().map(|region| {
3779 let start_overshoot = range.start.saturating_sub(region.range.start);
3780 let end_overshoot = range.end - region.range.start;
3781 let end = (region.buffer_range.start + end_overshoot).min(region.buffer_range.end);
3782 let start = region.buffer_range.start + start_overshoot;
3783 region.buffer.reversed_chunks_in_range(start..end)
3784 });
3785 ReversedMultiBufferChunks {
3786 cursor,
3787 current_chunks,
3788 start: range.start,
3789 offset: range.end,
3790 }
3791 }
3792
3793 pub fn chars_at<T: ToOffset>(&self, position: T) -> impl Iterator<Item = char> + '_ {
3794 let offset = position.to_offset(self);
3795 self.text_for_range(offset..self.len())
3796 .flat_map(|chunk| chunk.chars())
3797 }
3798
3799 pub fn text_for_range<T: ToOffset>(&self, range: Range<T>) -> impl Iterator<Item = &str> + '_ {
3800 self.chunks(range, false).map(|chunk| chunk.text)
3801 }
3802
3803 pub fn is_line_blank(&self, row: MultiBufferRow) -> bool {
3804 self.text_for_range(Point::new(row.0, 0)..Point::new(row.0, self.line_len(row)))
3805 .all(|chunk| chunk.matches(|c: char| !c.is_whitespace()).next().is_none())
3806 }
3807
3808 pub fn contains_str_at<T>(&self, position: T, needle: &str) -> bool
3809 where
3810 T: ToOffset,
3811 {
3812 let position = position.to_offset(self);
3813 position == self.clip_offset(position, Bias::Left)
3814 && self
3815 .bytes_in_range(position..self.len())
3816 .flatten()
3817 .copied()
3818 .take(needle.len())
3819 .eq(needle.bytes())
3820 }
3821
3822 pub fn diff_hunks(&self) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3823 self.diff_hunks_in_range(Anchor::min()..Anchor::max())
3824 }
3825
3826 pub fn diff_hunks_in_range<T: ToPoint>(
3827 &self,
3828 range: Range<T>,
3829 ) -> impl Iterator<Item = MultiBufferDiffHunk> + '_ {
3830 let query_range = range.start.to_point(self)..range.end.to_point(self);
3831 self.lift_buffer_metadata(query_range.clone(), move |buffer, buffer_range| {
3832 let diff = self.diffs.get(&buffer.remote_id())?;
3833 let buffer_start = buffer.anchor_before(buffer_range.start);
3834 let buffer_end = buffer.anchor_after(buffer_range.end);
3835 Some(
3836 diff.hunks_intersecting_range(buffer_start..buffer_end, buffer)
3837 .filter_map(|hunk| {
3838 if hunk.is_created_file() && !self.all_diff_hunks_expanded {
3839 return None;
3840 }
3841 Some((hunk.range.clone(), hunk))
3842 }),
3843 )
3844 })
3845 .filter_map(move |(range, hunk, excerpt)| {
3846 if range.start != range.end && range.end == query_range.start && !hunk.range.is_empty()
3847 {
3848 return None;
3849 }
3850 let end_row = if range.end.column == 0 {
3851 range.end.row
3852 } else {
3853 range.end.row + 1
3854 };
3855 Some(MultiBufferDiffHunk {
3856 row_range: MultiBufferRow(range.start.row)..MultiBufferRow(end_row),
3857 buffer_id: excerpt.buffer_id,
3858 excerpt_id: excerpt.id,
3859 buffer_range: hunk.buffer_range.clone(),
3860 diff_base_byte_range: hunk.diff_base_byte_range.clone(),
3861 secondary_status: hunk.secondary_status,
3862 })
3863 })
3864 }
3865
3866 pub fn excerpt_ids_for_range<T: ToOffset>(
3867 &self,
3868 range: Range<T>,
3869 ) -> impl Iterator<Item = ExcerptId> + '_ {
3870 let range = range.start.to_offset(self)..range.end.to_offset(self);
3871 let mut cursor = self.cursor::<usize>();
3872 cursor.seek(&range.start);
3873 std::iter::from_fn(move || {
3874 let region = cursor.region()?;
3875 if region.range.start >= range.end {
3876 return None;
3877 }
3878 cursor.next_excerpt();
3879 Some(region.excerpt.id)
3880 })
3881 }
3882
3883 pub fn buffer_ids_for_range<T: ToOffset>(
3884 &self,
3885 range: Range<T>,
3886 ) -> impl Iterator<Item = BufferId> + '_ {
3887 let range = range.start.to_offset(self)..range.end.to_offset(self);
3888 let mut cursor = self.cursor::<usize>();
3889 cursor.seek(&range.start);
3890 std::iter::from_fn(move || {
3891 let region = cursor.region()?;
3892 if region.range.start > range.end
3893 || region.range.start == range.end && region.range.start > range.start
3894 {
3895 return None;
3896 }
3897 cursor.next_excerpt();
3898 Some(region.excerpt.buffer_id)
3899 })
3900 }
3901
3902 pub fn ranges_to_buffer_ranges<T: ToOffset>(
3903 &self,
3904 ranges: impl Iterator<Item = Range<T>>,
3905 ) -> impl Iterator<Item = (&BufferSnapshot, Range<usize>, ExcerptId)> {
3906 ranges.flat_map(|range| self.range_to_buffer_ranges(range).into_iter())
3907 }
3908
3909 pub fn range_to_buffer_ranges<T: ToOffset>(
3910 &self,
3911 range: Range<T>,
3912 ) -> Vec<(&BufferSnapshot, Range<usize>, ExcerptId)> {
3913 let start = range.start.to_offset(self);
3914 let end = range.end.to_offset(self);
3915
3916 let mut cursor = self.cursor::<usize>();
3917 cursor.seek(&start);
3918
3919 let mut result: Vec<(&BufferSnapshot, Range<usize>, ExcerptId)> = Vec::new();
3920 while let Some(region) = cursor.region() {
3921 if region.range.start > end {
3922 break;
3923 }
3924 if region.is_main_buffer {
3925 let start_overshoot = start.saturating_sub(region.range.start);
3926 let end_overshoot = end.saturating_sub(region.range.start);
3927 let start = region
3928 .buffer_range
3929 .end
3930 .min(region.buffer_range.start + start_overshoot);
3931 let end = region
3932 .buffer_range
3933 .end
3934 .min(region.buffer_range.start + end_overshoot);
3935 if let Some(prev) = result.last_mut().filter(|(_, prev_range, excerpt_id)| {
3936 *excerpt_id == region.excerpt.id && prev_range.end == start
3937 }) {
3938 prev.1.end = end;
3939 } else {
3940 result.push((region.buffer, start..end, region.excerpt.id));
3941 }
3942 }
3943 cursor.next();
3944 }
3945 result
3946 }
3947
3948 pub fn range_to_buffer_ranges_with_deleted_hunks<T: ToOffset>(
3949 &self,
3950 range: Range<T>,
3951 ) -> impl Iterator<Item = (&BufferSnapshot, Range<usize>, ExcerptId, Option<Anchor>)> + '_ {
3952 let start = range.start.to_offset(self);
3953 let end = range.end.to_offset(self);
3954
3955 let mut cursor = self.cursor::<usize>();
3956 cursor.seek(&start);
3957
3958 std::iter::from_fn(move || {
3959 let region = cursor.region()?;
3960 if region.range.start > end {
3961 return None;
3962 }
3963 let start_overshoot = start.saturating_sub(region.range.start);
3964 let end_overshoot = end.saturating_sub(region.range.start);
3965 let start = region
3966 .buffer_range
3967 .end
3968 .min(region.buffer_range.start + start_overshoot);
3969 let end = region
3970 .buffer_range
3971 .end
3972 .min(region.buffer_range.start + end_overshoot);
3973
3974 let region_excerpt_id = region.excerpt.id;
3975 let deleted_hunk_anchor = if region.is_main_buffer {
3976 None
3977 } else {
3978 Some(self.anchor_before(region.range.start))
3979 };
3980 let result = (
3981 region.buffer,
3982 start..end,
3983 region_excerpt_id,
3984 deleted_hunk_anchor,
3985 );
3986 cursor.next();
3987 Some(result)
3988 })
3989 }
3990
3991 /// Retrieves buffer metadata for the given range, and converts it into multi-buffer
3992 /// coordinates.
3993 ///
3994 /// The given callback will be called for every excerpt intersecting the given range. It will
3995 /// be passed the excerpt's buffer and the buffer range that the input range intersects.
3996 /// The callback should return an iterator of metadata items from that buffer, each paired
3997 /// with a buffer range.
3998 ///
3999 /// The returned iterator yields each of these metadata items, paired with its range in
4000 /// multi-buffer coordinates.
4001 fn lift_buffer_metadata<'a, D, M, I>(
4002 &'a self,
4003 query_range: Range<D>,
4004 get_buffer_metadata: impl 'a + Fn(&'a BufferSnapshot, Range<D>) -> Option<I>,
4005 ) -> impl Iterator<Item = (Range<D>, M, &'a Excerpt)> + 'a
4006 where
4007 I: Iterator<Item = (Range<D>, M)> + 'a,
4008 D: TextDimension + Ord + Sub<D, Output = D>,
4009 {
4010 let max_position = D::from_text_summary(&self.text_summary());
4011 let mut current_excerpt_metadata: Option<(ExcerptId, I)> = None;
4012 let mut cursor = self.cursor::<D>();
4013
4014 // Find the excerpt and buffer offset where the given range ends.
4015 cursor.seek(&query_range.end);
4016 let mut range_end = None;
4017 while let Some(region) = cursor.region() {
4018 if region.is_main_buffer {
4019 let mut buffer_end = region.buffer_range.start;
4020 let overshoot = if query_range.end > region.range.start {
4021 query_range.end - region.range.start
4022 } else {
4023 D::default()
4024 };
4025 buffer_end.add_assign(&overshoot);
4026 range_end = Some((region.excerpt.id, buffer_end));
4027 break;
4028 }
4029 cursor.next();
4030 }
4031
4032 cursor.seek(&query_range.start);
4033
4034 if let Some(region) = cursor.region().filter(|region| !region.is_main_buffer)
4035 && region.range.start > D::zero(())
4036 {
4037 cursor.prev()
4038 }
4039
4040 iter::from_fn(move || {
4041 loop {
4042 let excerpt = cursor.excerpt()?;
4043
4044 // If we have already retrieved metadata for this excerpt, continue to use it.
4045 let metadata_iter = if let Some((_, metadata)) = current_excerpt_metadata
4046 .as_mut()
4047 .filter(|(excerpt_id, _)| *excerpt_id == excerpt.id)
4048 {
4049 Some(metadata)
4050 }
4051 // Otherwise, compute the intersection of the input range with the excerpt's range,
4052 // and retrieve the metadata for the resulting range.
4053 else {
4054 let region = cursor.region()?;
4055 let mut buffer_start;
4056 if region.is_main_buffer {
4057 buffer_start = region.buffer_range.start;
4058 if query_range.start > region.range.start {
4059 let overshoot = query_range.start - region.range.start;
4060 buffer_start.add_assign(&overshoot);
4061 }
4062 buffer_start = buffer_start.min(region.buffer_range.end);
4063 } else {
4064 buffer_start = cursor.main_buffer_position()?;
4065 };
4066 let mut buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
4067 if let Some((end_excerpt_id, end_buffer_offset)) = range_end
4068 && excerpt.id == end_excerpt_id
4069 {
4070 buffer_end = buffer_end.min(end_buffer_offset);
4071 }
4072
4073 get_buffer_metadata(&excerpt.buffer, buffer_start..buffer_end).map(|iterator| {
4074 &mut current_excerpt_metadata.insert((excerpt.id, iterator)).1
4075 })
4076 };
4077
4078 // Visit each metadata item.
4079 if let Some((metadata_buffer_range, metadata)) =
4080 metadata_iter.and_then(Iterator::next)
4081 {
4082 // Find the multibuffer regions that contain the start and end of
4083 // the metadata item's range.
4084 if metadata_buffer_range.start > D::default() {
4085 while let Some(region) = cursor.region() {
4086 if region.is_main_buffer
4087 && (region.buffer_range.end >= metadata_buffer_range.start
4088 || cursor.is_at_end_of_excerpt())
4089 {
4090 break;
4091 }
4092 cursor.next();
4093 }
4094 }
4095 let start_region = cursor.region()?;
4096 while let Some(region) = cursor.region() {
4097 if region.is_main_buffer
4098 && (region.buffer_range.end > metadata_buffer_range.end
4099 || cursor.is_at_end_of_excerpt())
4100 {
4101 break;
4102 }
4103 cursor.next();
4104 }
4105 let end_region = cursor.region();
4106
4107 // Convert the metadata item's range into multibuffer coordinates.
4108 let mut start_position = start_region.range.start;
4109 let region_buffer_start = start_region.buffer_range.start;
4110 if start_region.is_main_buffer
4111 && metadata_buffer_range.start > region_buffer_start
4112 {
4113 start_position
4114 .add_assign(&(metadata_buffer_range.start - region_buffer_start));
4115 start_position = start_position.min(start_region.range.end);
4116 }
4117
4118 let mut end_position = max_position;
4119 if let Some(end_region) = &end_region {
4120 end_position = end_region.range.start;
4121 debug_assert!(end_region.is_main_buffer);
4122 let region_buffer_start = end_region.buffer_range.start;
4123 if metadata_buffer_range.end > region_buffer_start {
4124 end_position
4125 .add_assign(&(metadata_buffer_range.end - region_buffer_start));
4126 }
4127 end_position = end_position.min(end_region.range.end);
4128 }
4129
4130 if start_position <= query_range.end && end_position >= query_range.start {
4131 return Some((start_position..end_position, metadata, excerpt));
4132 }
4133 }
4134 // When there are no more metadata items for this excerpt, move to the next excerpt.
4135 else {
4136 current_excerpt_metadata.take();
4137 if let Some((end_excerpt_id, _)) = range_end
4138 && excerpt.id == end_excerpt_id
4139 {
4140 return None;
4141 }
4142 cursor.next_excerpt();
4143 }
4144 }
4145 })
4146 }
4147
4148 pub fn diff_hunk_before<T: ToOffset>(&self, position: T) -> Option<MultiBufferRow> {
4149 let offset = position.to_offset(self);
4150
4151 let mut cursor = self.cursor::<DimensionPair<usize, Point>>();
4152 cursor.seek(&DimensionPair {
4153 key: offset,
4154 value: None,
4155 });
4156 cursor.seek_to_start_of_current_excerpt();
4157 let excerpt = cursor.excerpt()?;
4158
4159 let excerpt_end = excerpt.range.context.end.to_offset(&excerpt.buffer);
4160 let current_position = self
4161 .anchor_before(offset)
4162 .text_anchor
4163 .to_offset(&excerpt.buffer);
4164 let excerpt_end = excerpt
4165 .buffer
4166 .anchor_before(excerpt_end.min(current_position));
4167
4168 if let Some(diff) = self.diffs.get(&excerpt.buffer_id) {
4169 for hunk in diff.hunks_intersecting_range_rev(
4170 excerpt.range.context.start..excerpt_end,
4171 &excerpt.buffer,
4172 ) {
4173 let hunk_end = hunk.buffer_range.end.to_offset(&excerpt.buffer);
4174 if hunk_end >= current_position {
4175 continue;
4176 }
4177 let start =
4178 Anchor::in_buffer(excerpt.id, excerpt.buffer_id, hunk.buffer_range.start)
4179 .to_point(self);
4180 return Some(MultiBufferRow(start.row));
4181 }
4182 }
4183
4184 loop {
4185 cursor.prev_excerpt();
4186 let excerpt = cursor.excerpt()?;
4187
4188 let Some(diff) = self.diffs.get(&excerpt.buffer_id) else {
4189 continue;
4190 };
4191 let mut hunks =
4192 diff.hunks_intersecting_range_rev(excerpt.range.context.clone(), &excerpt.buffer);
4193 let Some(hunk) = hunks.next() else {
4194 continue;
4195 };
4196 let start = Anchor::in_buffer(excerpt.id, excerpt.buffer_id, hunk.buffer_range.start)
4197 .to_point(self);
4198 return Some(MultiBufferRow(start.row));
4199 }
4200 }
4201
4202 pub fn has_diff_hunks(&self) -> bool {
4203 self.diffs.values().any(|diff| !diff.is_empty())
4204 }
4205
4206 pub fn is_inside_word<T: ToOffset>(
4207 &self,
4208 position: T,
4209 scope_context: Option<CharScopeContext>,
4210 ) -> bool {
4211 let position = position.to_offset(self);
4212 let classifier = self
4213 .char_classifier_at(position)
4214 .scope_context(scope_context);
4215 let next_char_kind = self.chars_at(position).next().map(|c| classifier.kind(c));
4216 let prev_char_kind = self
4217 .reversed_chars_at(position)
4218 .next()
4219 .map(|c| classifier.kind(c));
4220 prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
4221 }
4222
4223 pub fn surrounding_word<T: ToOffset>(
4224 &self,
4225 start: T,
4226 scope_context: Option<CharScopeContext>,
4227 ) -> (Range<usize>, Option<CharKind>) {
4228 let mut start = start.to_offset(self);
4229 let mut end = start;
4230 let mut next_chars = self.chars_at(start).peekable();
4231 let mut prev_chars = self.reversed_chars_at(start).peekable();
4232
4233 let classifier = self.char_classifier_at(start).scope_context(scope_context);
4234
4235 let word_kind = cmp::max(
4236 prev_chars.peek().copied().map(|c| classifier.kind(c)),
4237 next_chars.peek().copied().map(|c| classifier.kind(c)),
4238 );
4239
4240 for ch in prev_chars {
4241 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4242 start -= ch.len_utf8();
4243 } else {
4244 break;
4245 }
4246 }
4247
4248 for ch in next_chars {
4249 if Some(classifier.kind(ch)) == word_kind && ch != '\n' {
4250 end += ch.len_utf8();
4251 } else {
4252 break;
4253 }
4254 }
4255
4256 (start..end, word_kind)
4257 }
4258
4259 pub fn char_kind_before<T: ToOffset>(
4260 &self,
4261 start: T,
4262 scope_context: Option<CharScopeContext>,
4263 ) -> Option<CharKind> {
4264 let start = start.to_offset(self);
4265 let classifier = self.char_classifier_at(start).scope_context(scope_context);
4266 self.reversed_chars_at(start)
4267 .next()
4268 .map(|ch| classifier.kind(ch))
4269 }
4270
4271 pub fn is_singleton(&self) -> bool {
4272 self.singleton
4273 }
4274
4275 pub fn as_singleton(&self) -> Option<(&ExcerptId, BufferId, &BufferSnapshot)> {
4276 if self.singleton {
4277 self.excerpts
4278 .iter()
4279 .next()
4280 .map(|e| (&e.id, e.buffer_id, &e.buffer))
4281 } else {
4282 None
4283 }
4284 }
4285
4286 pub fn len(&self) -> usize {
4287 self.diff_transforms.summary().output.len
4288 }
4289
4290 pub fn is_empty(&self) -> bool {
4291 self.excerpts.summary().text.len == 0
4292 }
4293
4294 pub fn widest_line_number(&self) -> u32 {
4295 // widest_line_number is 0-based, so 1 is added to get the displayed line number.
4296 self.excerpts.summary().widest_line_number + 1
4297 }
4298
4299 pub fn bytes_in_range<T: ToOffset>(&self, range: Range<T>) -> MultiBufferBytes<'_> {
4300 let range = range.start.to_offset(self)..range.end.to_offset(self);
4301 let mut excerpts = self.cursor::<usize>();
4302 excerpts.seek(&range.start);
4303
4304 let mut chunk;
4305 let mut has_trailing_newline;
4306 let excerpt_bytes;
4307 if let Some(region) = excerpts.region() {
4308 let mut bytes = region.buffer.bytes_in_range(
4309 region.buffer_range.start + range.start - region.range.start
4310 ..(region.buffer_range.start + range.end - region.range.start)
4311 .min(region.buffer_range.end),
4312 );
4313 chunk = bytes.next().unwrap_or(&[][..]);
4314 excerpt_bytes = Some(bytes);
4315 has_trailing_newline = region.has_trailing_newline && range.end >= region.range.end;
4316 if chunk.is_empty() && has_trailing_newline {
4317 chunk = b"\n";
4318 has_trailing_newline = false;
4319 }
4320 } else {
4321 chunk = &[][..];
4322 excerpt_bytes = None;
4323 has_trailing_newline = false;
4324 };
4325
4326 MultiBufferBytes {
4327 range,
4328 cursor: excerpts,
4329 excerpt_bytes,
4330 has_trailing_newline,
4331 chunk,
4332 }
4333 }
4334
4335 pub fn reversed_bytes_in_range<T: ToOffset>(
4336 &self,
4337 range: Range<T>,
4338 ) -> ReversedMultiBufferBytes<'_> {
4339 let range = range.start.to_offset(self)..range.end.to_offset(self);
4340 let mut chunks = self.reversed_chunks_in_range(range.clone());
4341 let chunk = chunks.next().map_or(&[][..], |c| c.as_bytes());
4342 ReversedMultiBufferBytes {
4343 range,
4344 chunks,
4345 chunk,
4346 }
4347 }
4348
4349 pub fn row_infos(&self, start_row: MultiBufferRow) -> MultiBufferRows<'_> {
4350 let mut cursor = self.cursor::<Point>();
4351 cursor.seek(&Point::new(start_row.0, 0));
4352 let mut result = MultiBufferRows {
4353 point: Point::new(0, 0),
4354 is_empty: self.excerpts.is_empty(),
4355 is_singleton: self.is_singleton(),
4356 cursor,
4357 };
4358 result.seek(start_row);
4359 result
4360 }
4361
4362 pub fn chunks<T: ToOffset>(
4363 &self,
4364 range: Range<T>,
4365 language_aware: bool,
4366 ) -> MultiBufferChunks<'_> {
4367 let mut chunks = MultiBufferChunks {
4368 excerpt_offset_range: ExcerptOffset::new(0)..ExcerptOffset::new(0),
4369 range: 0..0,
4370 excerpts: self.excerpts.cursor(()),
4371 diff_transforms: self.diff_transforms.cursor(()),
4372 diffs: &self.diffs,
4373 diff_base_chunks: None,
4374 excerpt_chunks: None,
4375 buffer_chunk: None,
4376 language_aware,
4377 };
4378 let range = range.start.to_offset(self)..range.end.to_offset(self);
4379 chunks.seek(range);
4380 chunks
4381 }
4382
4383 pub fn clip_offset(&self, offset: usize, bias: Bias) -> usize {
4384 self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset)
4385 }
4386
4387 pub fn clip_point(&self, point: Point, bias: Bias) -> Point {
4388 self.clip_dimension(point, bias, text::BufferSnapshot::clip_point)
4389 }
4390
4391 pub fn clip_offset_utf16(&self, offset: OffsetUtf16, bias: Bias) -> OffsetUtf16 {
4392 self.clip_dimension(offset, bias, text::BufferSnapshot::clip_offset_utf16)
4393 }
4394
4395 pub fn clip_point_utf16(&self, point: Unclipped<PointUtf16>, bias: Bias) -> PointUtf16 {
4396 self.clip_dimension(point.0, bias, |buffer, point, bias| {
4397 buffer.clip_point_utf16(Unclipped(point), bias)
4398 })
4399 }
4400
4401 pub fn offset_to_point(&self, offset: usize) -> Point {
4402 self.convert_dimension(offset, text::BufferSnapshot::offset_to_point)
4403 }
4404
4405 pub fn offset_to_point_utf16(&self, offset: usize) -> PointUtf16 {
4406 self.convert_dimension(offset, text::BufferSnapshot::offset_to_point_utf16)
4407 }
4408
4409 pub fn point_to_point_utf16(&self, point: Point) -> PointUtf16 {
4410 self.convert_dimension(point, text::BufferSnapshot::point_to_point_utf16)
4411 }
4412
4413 pub fn point_to_offset(&self, point: Point) -> usize {
4414 self.convert_dimension(point, text::BufferSnapshot::point_to_offset)
4415 }
4416
4417 pub fn offset_utf16_to_offset(&self, offset: OffsetUtf16) -> usize {
4418 self.convert_dimension(offset, text::BufferSnapshot::offset_utf16_to_offset)
4419 }
4420
4421 pub fn offset_to_offset_utf16(&self, offset: usize) -> OffsetUtf16 {
4422 self.convert_dimension(offset, text::BufferSnapshot::offset_to_offset_utf16)
4423 }
4424
4425 pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize {
4426 self.convert_dimension(point, text::BufferSnapshot::point_utf16_to_offset)
4427 }
4428
4429 fn clip_dimension<D>(
4430 &self,
4431 position: D,
4432 bias: Bias,
4433 clip_buffer_position: fn(&text::BufferSnapshot, D, Bias) -> D,
4434 ) -> D
4435 where
4436 D: TextDimension + Ord + Sub<D, Output = D>,
4437 {
4438 let mut cursor = self.cursor();
4439 cursor.seek(&position);
4440 if let Some(region) = cursor.region() {
4441 if position >= region.range.end {
4442 return region.range.end;
4443 }
4444 let overshoot = position - region.range.start;
4445 let mut buffer_position = region.buffer_range.start;
4446 buffer_position.add_assign(&overshoot);
4447 let clipped_buffer_position =
4448 clip_buffer_position(region.buffer, buffer_position, bias);
4449 let mut position = region.range.start;
4450 position.add_assign(&(clipped_buffer_position - region.buffer_range.start));
4451 position
4452 } else {
4453 D::from_text_summary(&self.text_summary())
4454 }
4455 }
4456
4457 fn convert_dimension<D1, D2>(
4458 &self,
4459 key: D1,
4460 convert_buffer_dimension: fn(&text::BufferSnapshot, D1) -> D2,
4461 ) -> D2
4462 where
4463 D1: TextDimension + Ord + Sub<D1, Output = D1>,
4464 D2: TextDimension + Ord + Sub<D2, Output = D2>,
4465 {
4466 let mut cursor = self.cursor::<DimensionPair<D1, D2>>();
4467 cursor.seek(&DimensionPair { key, value: None });
4468 if let Some(region) = cursor.region() {
4469 if key >= region.range.end.key {
4470 return region.range.end.value.unwrap();
4471 }
4472 let start_key = region.range.start.key;
4473 let start_value = region.range.start.value.unwrap();
4474 let buffer_start_key = region.buffer_range.start.key;
4475 let buffer_start_value = region.buffer_range.start.value.unwrap();
4476 let mut buffer_key = buffer_start_key;
4477 buffer_key.add_assign(&(key - start_key));
4478 let buffer_value = convert_buffer_dimension(region.buffer, buffer_key);
4479 let mut result = start_value;
4480 result.add_assign(&(buffer_value - buffer_start_value));
4481 result
4482 } else {
4483 D2::from_text_summary(&self.text_summary())
4484 }
4485 }
4486
4487 pub fn point_to_buffer_offset<T: ToOffset>(
4488 &self,
4489 point: T,
4490 ) -> Option<(&BufferSnapshot, usize)> {
4491 let offset = point.to_offset(self);
4492 let mut cursor = self.cursor::<usize>();
4493 cursor.seek(&offset);
4494 let region = cursor.region()?;
4495 let overshoot = offset - region.range.start;
4496 let buffer_offset = region.buffer_range.start + overshoot;
4497 if buffer_offset == region.buffer.len() + 1
4498 && region.has_trailing_newline
4499 && !region.is_main_buffer
4500 {
4501 return Some((&cursor.excerpt()?.buffer, cursor.main_buffer_position()?));
4502 } else if buffer_offset > region.buffer.len() {
4503 return None;
4504 }
4505 Some((region.buffer, buffer_offset))
4506 }
4507
4508 pub fn point_to_buffer_point(
4509 &self,
4510 point: Point,
4511 ) -> Option<(&BufferSnapshot, Point, ExcerptId)> {
4512 let mut cursor = self.cursor::<Point>();
4513 cursor.seek(&point);
4514 let region = cursor.region()?;
4515 let overshoot = point - region.range.start;
4516 let buffer_point = region.buffer_range.start + overshoot;
4517 let excerpt = cursor.excerpt()?;
4518 if buffer_point == region.buffer.max_point() + Point::new(1, 0)
4519 && region.has_trailing_newline
4520 && !region.is_main_buffer
4521 {
4522 return Some((&excerpt.buffer, cursor.main_buffer_position()?, excerpt.id));
4523 } else if buffer_point > region.buffer.max_point() {
4524 return None;
4525 }
4526 Some((region.buffer, buffer_point, excerpt.id))
4527 }
4528
4529 pub fn suggested_indents(
4530 &self,
4531 rows: impl IntoIterator<Item = u32>,
4532 cx: &App,
4533 ) -> BTreeMap<MultiBufferRow, IndentSize> {
4534 let mut result = BTreeMap::new();
4535
4536 let mut rows_for_excerpt = Vec::new();
4537 let mut cursor = self.cursor::<Point>();
4538 let mut rows = rows.into_iter().peekable();
4539 let mut prev_row = u32::MAX;
4540 let mut prev_language_indent_size = IndentSize::default();
4541
4542 while let Some(row) = rows.next() {
4543 cursor.seek(&Point::new(row, 0));
4544 let Some(region) = cursor.region() else {
4545 continue;
4546 };
4547
4548 // Retrieve the language and indent size once for each disjoint region being indented.
4549 let single_indent_size = if row.saturating_sub(1) == prev_row {
4550 prev_language_indent_size
4551 } else {
4552 region
4553 .buffer
4554 .language_indent_size_at(Point::new(row, 0), cx)
4555 };
4556 prev_language_indent_size = single_indent_size;
4557 prev_row = row;
4558
4559 let start_buffer_row = region.buffer_range.start.row;
4560 let start_multibuffer_row = region.range.start.row;
4561 let end_multibuffer_row = region.range.end.row;
4562
4563 rows_for_excerpt.push(row);
4564 while let Some(next_row) = rows.peek().copied() {
4565 if end_multibuffer_row > next_row {
4566 rows_for_excerpt.push(next_row);
4567 rows.next();
4568 } else {
4569 break;
4570 }
4571 }
4572
4573 let buffer_rows = rows_for_excerpt
4574 .drain(..)
4575 .map(|row| start_buffer_row + row - start_multibuffer_row);
4576 let buffer_indents = region
4577 .buffer
4578 .suggested_indents(buffer_rows, single_indent_size);
4579 let multibuffer_indents = buffer_indents.into_iter().map(|(row, indent)| {
4580 (
4581 MultiBufferRow(start_multibuffer_row + row - start_buffer_row),
4582 indent,
4583 )
4584 });
4585 result.extend(multibuffer_indents);
4586 }
4587
4588 result
4589 }
4590
4591 pub fn indent_size_for_line(&self, row: MultiBufferRow) -> IndentSize {
4592 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
4593 let mut size = buffer.indent_size_for_line(range.start.row);
4594 size.len = size
4595 .len
4596 .min(range.end.column)
4597 .saturating_sub(range.start.column);
4598 size
4599 } else {
4600 IndentSize::spaces(0)
4601 }
4602 }
4603
4604 pub fn line_indent_for_row(&self, row: MultiBufferRow) -> LineIndent {
4605 if let Some((buffer, range)) = self.buffer_line_for_row(row) {
4606 LineIndent::from_iter(buffer.text_for_range(range).flat_map(|s| s.chars()))
4607 } else {
4608 LineIndent::spaces(0)
4609 }
4610 }
4611
4612 pub fn indent_and_comment_for_line(&self, row: MultiBufferRow, cx: &App) -> String {
4613 let mut indent = self.indent_size_for_line(row).chars().collect::<String>();
4614
4615 if self.language_settings(cx).extend_comment_on_newline
4616 && let Some(language_scope) = self.language_scope_at(Point::new(row.0, 0))
4617 {
4618 let delimiters = language_scope.line_comment_prefixes();
4619 for delimiter in delimiters {
4620 if *self
4621 .chars_at(Point::new(row.0, indent.len() as u32))
4622 .take(delimiter.chars().count())
4623 .collect::<String>()
4624 .as_str()
4625 == **delimiter
4626 {
4627 indent.push_str(delimiter);
4628 break;
4629 }
4630 }
4631 }
4632
4633 indent
4634 }
4635
4636 pub fn is_line_whitespace_upto<T>(&self, position: T) -> bool
4637 where
4638 T: ToOffset,
4639 {
4640 for char in self.reversed_chars_at(position) {
4641 if !char.is_whitespace() {
4642 return false;
4643 }
4644 if char == '\n' {
4645 return true;
4646 }
4647 }
4648 true
4649 }
4650
4651 pub fn prev_non_blank_row(&self, mut row: MultiBufferRow) -> Option<MultiBufferRow> {
4652 while row.0 > 0 {
4653 row.0 -= 1;
4654 if !self.is_line_blank(row) {
4655 return Some(row);
4656 }
4657 }
4658 None
4659 }
4660
4661 pub fn line_len(&self, row: MultiBufferRow) -> u32 {
4662 if let Some((_, range)) = self.buffer_line_for_row(row) {
4663 range.end.column - range.start.column
4664 } else {
4665 0
4666 }
4667 }
4668
4669 pub fn buffer_line_for_row(
4670 &self,
4671 row: MultiBufferRow,
4672 ) -> Option<(&BufferSnapshot, Range<Point>)> {
4673 let mut cursor = self.cursor::<Point>();
4674 let point = Point::new(row.0, 0);
4675 cursor.seek(&point);
4676 let region = cursor.region()?;
4677 let overshoot = point.min(region.range.end) - region.range.start;
4678 let buffer_point = region.buffer_range.start + overshoot;
4679 if buffer_point.row > region.buffer_range.end.row {
4680 return None;
4681 }
4682 let line_start = Point::new(buffer_point.row, 0).max(region.buffer_range.start);
4683 let line_end = Point::new(buffer_point.row, region.buffer.line_len(buffer_point.row))
4684 .min(region.buffer_range.end);
4685 Some((region.buffer, line_start..line_end))
4686 }
4687
4688 pub fn max_point(&self) -> Point {
4689 self.text_summary().lines
4690 }
4691
4692 pub fn max_row(&self) -> MultiBufferRow {
4693 MultiBufferRow(self.text_summary().lines.row)
4694 }
4695
4696 pub fn text_summary(&self) -> TextSummary {
4697 self.diff_transforms.summary().output
4698 }
4699
4700 pub fn text_summary_for_range<D, O>(&self, range: Range<O>) -> D
4701 where
4702 D: TextDimension,
4703 O: ToOffset,
4704 {
4705 let range = range.start.to_offset(self)..range.end.to_offset(self);
4706 let mut cursor = self
4707 .diff_transforms
4708 .cursor::<Dimensions<usize, ExcerptOffset>>(());
4709 cursor.seek(&range.start, Bias::Right);
4710
4711 let Some(first_transform) = cursor.item() else {
4712 return D::from_text_summary(&TextSummary::default());
4713 };
4714
4715 let diff_transform_start = cursor.start().0;
4716 let diff_transform_end = cursor.end().0;
4717 let diff_start = range.start;
4718 let start_overshoot = diff_start - diff_transform_start;
4719 let end_overshoot = std::cmp::min(range.end, diff_transform_end) - diff_transform_start;
4720
4721 let mut result = match first_transform {
4722 DiffTransform::BufferContent { .. } => {
4723 let excerpt_start = cursor.start().1 + ExcerptOffset::new(start_overshoot);
4724 let excerpt_end = cursor.start().1 + ExcerptOffset::new(end_overshoot);
4725 self.text_summary_for_excerpt_offset_range(excerpt_start..excerpt_end)
4726 }
4727 DiffTransform::DeletedHunk {
4728 buffer_id,
4729 base_text_byte_range,
4730 has_trailing_newline,
4731 ..
4732 } => {
4733 let buffer_start = base_text_byte_range.start + start_overshoot;
4734 let mut buffer_end = base_text_byte_range.start + end_overshoot;
4735 let Some(base_text) = self.diffs.get(buffer_id).map(|diff| diff.base_text()) else {
4736 panic!("{:?} is in non-existent deleted hunk", range.start)
4737 };
4738
4739 let include_trailing_newline =
4740 *has_trailing_newline && range.end >= diff_transform_end;
4741 if include_trailing_newline {
4742 buffer_end -= 1;
4743 }
4744
4745 let mut summary =
4746 base_text.text_summary_for_range::<D, _>(buffer_start..buffer_end);
4747
4748 if include_trailing_newline {
4749 summary.add_assign(&D::from_text_summary(&TextSummary::newline()))
4750 }
4751
4752 summary
4753 }
4754 };
4755 if range.end < diff_transform_end {
4756 return result;
4757 }
4758
4759 cursor.next();
4760 result.add_assign(&D::from_text_summary(
4761 &cursor.summary(&range.end, Bias::Right),
4762 ));
4763
4764 let Some(last_transform) = cursor.item() else {
4765 return result;
4766 };
4767
4768 let overshoot = range.end - cursor.start().0;
4769 let suffix = match last_transform {
4770 DiffTransform::BufferContent { .. } => {
4771 let end = cursor.start().1 + ExcerptOffset::new(overshoot);
4772 self.text_summary_for_excerpt_offset_range::<D>(cursor.start().1..end)
4773 }
4774 DiffTransform::DeletedHunk {
4775 base_text_byte_range,
4776 buffer_id,
4777 has_trailing_newline,
4778 ..
4779 } => {
4780 let buffer_end = base_text_byte_range.start + overshoot;
4781 let Some(base_text) = self.diffs.get(buffer_id).map(|diff| diff.base_text()) else {
4782 panic!("{:?} is in non-existent deleted hunk", range.end)
4783 };
4784
4785 let mut suffix = base_text
4786 .text_summary_for_range::<D, _>(base_text_byte_range.start..buffer_end);
4787 if *has_trailing_newline && buffer_end == base_text_byte_range.end + 1 {
4788 suffix.add_assign(&D::from_text_summary(&TextSummary::newline()))
4789 }
4790 suffix
4791 }
4792 };
4793
4794 result.add_assign(&suffix);
4795 result
4796 }
4797
4798 fn text_summary_for_excerpt_offset_range<D>(&self, mut range: Range<ExcerptOffset>) -> D
4799 where
4800 D: TextDimension,
4801 {
4802 // let mut range = range.start..range.end;
4803 let mut summary = D::zero(());
4804 let mut cursor = self.excerpts.cursor::<ExcerptOffset>(());
4805 cursor.seek(&range.start, Bias::Right);
4806 if let Some(excerpt) = cursor.item() {
4807 let mut end_before_newline = cursor.end();
4808 if excerpt.has_trailing_newline {
4809 end_before_newline -= ExcerptOffset::new(1);
4810 }
4811
4812 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
4813 let start_in_excerpt = excerpt_start + (range.start - *cursor.start()).value;
4814 let end_in_excerpt =
4815 excerpt_start + (cmp::min(end_before_newline, range.end) - *cursor.start()).value;
4816 summary.add_assign(
4817 &excerpt
4818 .buffer
4819 .text_summary_for_range(start_in_excerpt..end_in_excerpt),
4820 );
4821
4822 if range.end > end_before_newline {
4823 summary.add_assign(&D::from_text_summary(&TextSummary::from("\n")));
4824 }
4825
4826 cursor.next();
4827 }
4828
4829 if range.end > *cursor.start() {
4830 summary.add_assign(
4831 &cursor
4832 .summary::<_, ExcerptDimension<D>>(&range.end, Bias::Right)
4833 .0,
4834 );
4835 if let Some(excerpt) = cursor.item() {
4836 range.end = cmp::max(*cursor.start(), range.end);
4837
4838 let excerpt_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
4839 let end_in_excerpt = excerpt_start + (range.end - *cursor.start()).value;
4840 summary.add_assign(
4841 &excerpt
4842 .buffer
4843 .text_summary_for_range(excerpt_start..end_in_excerpt),
4844 );
4845 }
4846 }
4847
4848 summary
4849 }
4850
4851 pub fn summary_for_anchor<D>(&self, anchor: &Anchor) -> D
4852 where
4853 D: TextDimension + Ord + Sub<D, Output = D>,
4854 {
4855 self.summaries_for_anchors([anchor])[0]
4856 }
4857
4858 fn resolve_summary_for_anchor<D>(
4859 &self,
4860 anchor: &Anchor,
4861 excerpt_position: D,
4862 diff_transforms: &mut Cursor<
4863 DiffTransform,
4864 Dimensions<ExcerptDimension<D>, OutputDimension<D>>,
4865 >,
4866 ) -> D
4867 where
4868 D: TextDimension + Ord + Sub<D, Output = D>,
4869 {
4870 loop {
4871 let transform_end_position = diff_transforms.end().0.0;
4872 let at_transform_end =
4873 excerpt_position == transform_end_position && diff_transforms.item().is_some();
4874 if at_transform_end && anchor.text_anchor.bias == Bias::Right {
4875 diff_transforms.next();
4876 continue;
4877 }
4878
4879 let mut position = diff_transforms.start().1.0;
4880 match diff_transforms.item() {
4881 Some(DiffTransform::DeletedHunk {
4882 buffer_id,
4883 base_text_byte_range,
4884 ..
4885 }) => {
4886 if let Some(diff_base_anchor) = &anchor.diff_base_anchor
4887 && let Some(base_text) =
4888 self.diffs.get(buffer_id).map(|diff| diff.base_text())
4889 && base_text.can_resolve(diff_base_anchor)
4890 {
4891 let base_text_offset = diff_base_anchor.to_offset(base_text);
4892 if base_text_offset >= base_text_byte_range.start
4893 && base_text_offset <= base_text_byte_range.end
4894 {
4895 let position_in_hunk = base_text.text_summary_for_range::<D, _>(
4896 base_text_byte_range.start..base_text_offset,
4897 );
4898 position.add_assign(&position_in_hunk);
4899 } else if at_transform_end {
4900 diff_transforms.next();
4901 continue;
4902 }
4903 }
4904 }
4905 _ => {
4906 if at_transform_end && anchor.diff_base_anchor.is_some() {
4907 diff_transforms.next();
4908 continue;
4909 }
4910 let overshoot = excerpt_position - diff_transforms.start().0.0;
4911 position.add_assign(&overshoot);
4912 }
4913 }
4914
4915 return position;
4916 }
4917 }
4918
4919 fn excerpt_offset_for_anchor(&self, anchor: &Anchor) -> ExcerptOffset {
4920 let mut cursor = self
4921 .excerpts
4922 .cursor::<Dimensions<Option<&Locator>, ExcerptOffset>>(());
4923 let locator = self.excerpt_locator_for_id(anchor.excerpt_id);
4924
4925 cursor.seek(&Some(locator), Bias::Left);
4926 if cursor.item().is_none() {
4927 cursor.next();
4928 }
4929
4930 let mut position = cursor.start().1;
4931 if let Some(excerpt) = cursor.item()
4932 && excerpt.id == anchor.excerpt_id
4933 {
4934 let excerpt_buffer_start = excerpt
4935 .buffer
4936 .offset_for_anchor(&excerpt.range.context.start);
4937 let excerpt_buffer_end = excerpt.buffer.offset_for_anchor(&excerpt.range.context.end);
4938 let buffer_position = cmp::min(
4939 excerpt_buffer_end,
4940 excerpt.buffer.offset_for_anchor(&anchor.text_anchor),
4941 );
4942 if buffer_position > excerpt_buffer_start {
4943 position.value += buffer_position - excerpt_buffer_start;
4944 }
4945 }
4946 position
4947 }
4948
4949 pub fn latest_excerpt_id(&self, mut excerpt_id: ExcerptId) -> ExcerptId {
4950 while let Some(replacement) = self.replaced_excerpts.get(&excerpt_id) {
4951 excerpt_id = *replacement;
4952 }
4953 excerpt_id
4954 }
4955
4956 pub fn summaries_for_anchors<'a, D, I>(&'a self, anchors: I) -> Vec<D>
4957 where
4958 D: TextDimension + Ord + Sub<D, Output = D>,
4959 I: 'a + IntoIterator<Item = &'a Anchor>,
4960 {
4961 let mut anchors = anchors.into_iter().peekable();
4962 let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
4963 let mut diff_transforms_cursor = self
4964 .diff_transforms
4965 .cursor::<Dimensions<ExcerptDimension<D>, OutputDimension<D>>>(());
4966 diff_transforms_cursor.next();
4967
4968 let mut summaries = Vec::new();
4969 while let Some(anchor) = anchors.peek() {
4970 let excerpt_id = self.latest_excerpt_id(anchor.excerpt_id);
4971 let excerpt_anchors = iter::from_fn(|| {
4972 let anchor = anchors.peek()?;
4973 if self.latest_excerpt_id(anchor.excerpt_id) == excerpt_id {
4974 Some(anchors.next().unwrap())
4975 } else {
4976 None
4977 }
4978 });
4979
4980 let locator = self.excerpt_locator_for_id(excerpt_id);
4981 cursor.seek_forward(locator, Bias::Left);
4982 if cursor.item().is_none() {
4983 cursor.next();
4984 }
4985
4986 let excerpt_start_position = D::from_text_summary(&cursor.start().text);
4987 if let Some(excerpt) = cursor.item() {
4988 if excerpt.id != excerpt_id {
4989 let position = self.resolve_summary_for_anchor(
4990 &Anchor::min(),
4991 excerpt_start_position,
4992 &mut diff_transforms_cursor,
4993 );
4994 summaries.extend(excerpt_anchors.map(|_| position));
4995 continue;
4996 }
4997 let excerpt_buffer_start =
4998 excerpt.range.context.start.summary::<D>(&excerpt.buffer);
4999 let excerpt_buffer_end = excerpt.range.context.end.summary::<D>(&excerpt.buffer);
5000 for (buffer_summary, anchor) in excerpt
5001 .buffer
5002 .summaries_for_anchors_with_payload::<D, _, _>(
5003 excerpt_anchors.map(|a| (&a.text_anchor, a)),
5004 )
5005 {
5006 let summary = cmp::min(excerpt_buffer_end, buffer_summary);
5007 let mut position = excerpt_start_position;
5008 if summary > excerpt_buffer_start {
5009 position.add_assign(&(summary - excerpt_buffer_start));
5010 }
5011
5012 if position > diff_transforms_cursor.start().0.0 {
5013 diff_transforms_cursor
5014 .seek_forward(&ExcerptDimension(position), Bias::Left);
5015 }
5016
5017 summaries.push(self.resolve_summary_for_anchor(
5018 anchor,
5019 position,
5020 &mut diff_transforms_cursor,
5021 ));
5022 }
5023 } else {
5024 diff_transforms_cursor
5025 .seek_forward(&ExcerptDimension(excerpt_start_position), Bias::Left);
5026 let position = self.resolve_summary_for_anchor(
5027 &Anchor::max(),
5028 excerpt_start_position,
5029 &mut diff_transforms_cursor,
5030 );
5031 summaries.extend(excerpt_anchors.map(|_| position));
5032 }
5033 }
5034
5035 summaries
5036 }
5037
5038 pub fn dimensions_from_points<'a, D>(
5039 &'a self,
5040 points: impl 'a + IntoIterator<Item = Point>,
5041 ) -> impl 'a + Iterator<Item = D>
5042 where
5043 D: TextDimension + Sub<D, Output = D>,
5044 {
5045 let mut cursor = self.cursor::<DimensionPair<Point, D>>();
5046 cursor.seek(&DimensionPair {
5047 key: Point::default(),
5048 value: None,
5049 });
5050 let mut points = points.into_iter();
5051 iter::from_fn(move || {
5052 let point = points.next()?;
5053
5054 cursor.seek_forward(&DimensionPair {
5055 key: point,
5056 value: None,
5057 });
5058
5059 if let Some(region) = cursor.region() {
5060 let overshoot = point - region.range.start.key;
5061 let buffer_point = region.buffer_range.start.key + overshoot;
5062 let mut position = region.range.start.value.unwrap();
5063 position.add_assign(
5064 ®ion
5065 .buffer
5066 .text_summary_for_range(region.buffer_range.start.key..buffer_point),
5067 );
5068 if point == region.range.end.key && region.has_trailing_newline {
5069 position.add_assign(&D::from_text_summary(&TextSummary::newline()));
5070 }
5071 Some(position)
5072 } else {
5073 Some(D::from_text_summary(&self.text_summary()))
5074 }
5075 })
5076 }
5077
5078 pub fn refresh_anchors<'a, I>(&'a self, anchors: I) -> Vec<(usize, Anchor, bool)>
5079 where
5080 I: 'a + IntoIterator<Item = &'a Anchor>,
5081 {
5082 let mut anchors = anchors.into_iter().enumerate().peekable();
5083 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
5084 cursor.next();
5085
5086 let mut result = Vec::new();
5087
5088 while let Some((_, anchor)) = anchors.peek() {
5089 let old_excerpt_id = anchor.excerpt_id;
5090
5091 // Find the location where this anchor's excerpt should be.
5092 let old_locator = self.excerpt_locator_for_id(old_excerpt_id);
5093 cursor.seek_forward(&Some(old_locator), Bias::Left);
5094
5095 if cursor.item().is_none() {
5096 cursor.next();
5097 }
5098
5099 let next_excerpt = cursor.item();
5100 let prev_excerpt = cursor.prev_item();
5101
5102 // Process all of the anchors for this excerpt.
5103 while let Some((_, anchor)) = anchors.peek() {
5104 if anchor.excerpt_id != old_excerpt_id {
5105 break;
5106 }
5107 let (anchor_ix, anchor) = anchors.next().unwrap();
5108 let mut anchor = *anchor;
5109
5110 // Leave min and max anchors unchanged if invalid or
5111 // if the old excerpt still exists at this location
5112 let mut kept_position = next_excerpt
5113 .is_some_and(|e| e.id == old_excerpt_id && e.contains(&anchor))
5114 || old_excerpt_id == ExcerptId::max()
5115 || old_excerpt_id == ExcerptId::min();
5116
5117 // If the old excerpt no longer exists at this location, then attempt to
5118 // find an equivalent position for this anchor in an adjacent excerpt.
5119 if !kept_position {
5120 for excerpt in [next_excerpt, prev_excerpt].iter().filter_map(|e| *e) {
5121 if excerpt.contains(&anchor) {
5122 anchor.excerpt_id = excerpt.id;
5123 kept_position = true;
5124 break;
5125 }
5126 }
5127 }
5128
5129 // If there's no adjacent excerpt that contains the anchor's position,
5130 // then report that the anchor has lost its position.
5131 if !kept_position {
5132 anchor = if let Some(excerpt) = next_excerpt {
5133 let mut text_anchor = excerpt
5134 .range
5135 .context
5136 .start
5137 .bias(anchor.text_anchor.bias, &excerpt.buffer);
5138 if text_anchor
5139 .cmp(&excerpt.range.context.end, &excerpt.buffer)
5140 .is_gt()
5141 {
5142 text_anchor = excerpt.range.context.end;
5143 }
5144 Anchor {
5145 buffer_id: Some(excerpt.buffer_id),
5146 excerpt_id: excerpt.id,
5147 text_anchor,
5148 diff_base_anchor: None,
5149 }
5150 } else if let Some(excerpt) = prev_excerpt {
5151 let mut text_anchor = excerpt
5152 .range
5153 .context
5154 .end
5155 .bias(anchor.text_anchor.bias, &excerpt.buffer);
5156 if text_anchor
5157 .cmp(&excerpt.range.context.start, &excerpt.buffer)
5158 .is_lt()
5159 {
5160 text_anchor = excerpt.range.context.start;
5161 }
5162 Anchor {
5163 buffer_id: Some(excerpt.buffer_id),
5164 excerpt_id: excerpt.id,
5165 text_anchor,
5166 diff_base_anchor: None,
5167 }
5168 } else if anchor.text_anchor.bias == Bias::Left {
5169 Anchor::min()
5170 } else {
5171 Anchor::max()
5172 };
5173 }
5174
5175 result.push((anchor_ix, anchor, kept_position));
5176 }
5177 }
5178 result.sort_unstable_by(|a, b| a.1.cmp(&b.1, self));
5179 result
5180 }
5181
5182 pub fn anchor_before<T: ToOffset>(&self, position: T) -> Anchor {
5183 self.anchor_at(position, Bias::Left)
5184 }
5185
5186 pub fn anchor_after<T: ToOffset>(&self, position: T) -> Anchor {
5187 self.anchor_at(position, Bias::Right)
5188 }
5189
5190 pub fn anchor_at<T: ToOffset>(&self, position: T, mut bias: Bias) -> Anchor {
5191 let offset = position.to_offset(self);
5192
5193 // Find the given position in the diff transforms. Determine the corresponding
5194 // offset in the excerpts, and whether the position is within a deleted hunk.
5195 let mut diff_transforms = self
5196 .diff_transforms
5197 .cursor::<Dimensions<usize, ExcerptOffset>>(());
5198 diff_transforms.seek(&offset, Bias::Right);
5199
5200 if offset == diff_transforms.start().0
5201 && bias == Bias::Left
5202 && let Some(prev_item) = diff_transforms.prev_item()
5203 && let DiffTransform::DeletedHunk { .. } = prev_item
5204 {
5205 diff_transforms.prev();
5206 }
5207 let offset_in_transform = offset - diff_transforms.start().0;
5208 let mut excerpt_offset = diff_transforms.start().1;
5209 let mut diff_base_anchor = None;
5210 if let Some(DiffTransform::DeletedHunk {
5211 buffer_id,
5212 base_text_byte_range,
5213 has_trailing_newline,
5214 ..
5215 }) = diff_transforms.item()
5216 {
5217 let diff = self.diffs.get(buffer_id).expect("missing diff");
5218 if offset_in_transform > base_text_byte_range.len() {
5219 debug_assert!(*has_trailing_newline);
5220 bias = Bias::Right;
5221 } else {
5222 diff_base_anchor = Some(
5223 diff.base_text()
5224 .anchor_at(base_text_byte_range.start + offset_in_transform, bias),
5225 );
5226 bias = Bias::Left;
5227 }
5228 } else {
5229 excerpt_offset += ExcerptOffset::new(offset_in_transform);
5230 };
5231
5232 let mut excerpts = self
5233 .excerpts
5234 .cursor::<Dimensions<ExcerptOffset, Option<ExcerptId>>>(());
5235 excerpts.seek(&excerpt_offset, Bias::Right);
5236 if excerpts.item().is_none() && excerpt_offset == excerpts.start().0 && bias == Bias::Left {
5237 excerpts.prev();
5238 }
5239 if let Some(excerpt) = excerpts.item() {
5240 let mut overshoot = excerpt_offset.saturating_sub(excerpts.start().0).value;
5241 if excerpt.has_trailing_newline && excerpt_offset == excerpts.end().0 {
5242 overshoot -= 1;
5243 bias = Bias::Right;
5244 }
5245
5246 let buffer_start = excerpt.range.context.start.to_offset(&excerpt.buffer);
5247 let text_anchor =
5248 excerpt.clip_anchor(excerpt.buffer.anchor_at(buffer_start + overshoot, bias));
5249 Anchor {
5250 buffer_id: Some(excerpt.buffer_id),
5251 excerpt_id: excerpt.id,
5252 text_anchor,
5253 diff_base_anchor,
5254 }
5255 } else {
5256 let mut anchor = if excerpt_offset.is_zero() && bias == Bias::Left {
5257 Anchor::min()
5258 } else {
5259 Anchor::max()
5260 };
5261
5262 // TODO this is a hack, because all APIs should be able to handle ExcerptId::min and max.
5263 if let Some((excerpt_id, _, _)) = self.as_singleton() {
5264 anchor.excerpt_id = *excerpt_id;
5265 }
5266 anchor
5267 }
5268 }
5269
5270 /// Returns an anchor for the given excerpt and text anchor,
5271 /// returns None if the excerpt_id is no longer valid.
5272 pub fn anchor_in_excerpt(
5273 &self,
5274 excerpt_id: ExcerptId,
5275 text_anchor: text::Anchor,
5276 ) -> Option<Anchor> {
5277 let excerpt_id = self.latest_excerpt_id(excerpt_id);
5278 let locator = self.excerpt_locator_for_id(excerpt_id);
5279 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
5280 cursor.seek(locator, Bias::Left);
5281 if let Some(excerpt) = cursor.item()
5282 && excerpt.id == excerpt_id
5283 {
5284 let text_anchor = excerpt.clip_anchor(text_anchor);
5285 drop(cursor);
5286 return Some(Anchor {
5287 buffer_id: Some(excerpt.buffer_id),
5288 excerpt_id,
5289 text_anchor,
5290 diff_base_anchor: None,
5291 });
5292 }
5293 None
5294 }
5295
5296 pub fn context_range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<text::Anchor>> {
5297 Some(self.excerpt(excerpt_id)?.range.context.clone())
5298 }
5299
5300 pub fn can_resolve(&self, anchor: &Anchor) -> bool {
5301 if anchor.excerpt_id == ExcerptId::min() || anchor.excerpt_id == ExcerptId::max() {
5302 true
5303 } else if let Some(excerpt) = self.excerpt(anchor.excerpt_id) {
5304 excerpt.buffer.can_resolve(&anchor.text_anchor)
5305 } else {
5306 false
5307 }
5308 }
5309
5310 pub fn excerpts(
5311 &self,
5312 ) -> impl Iterator<Item = (ExcerptId, &BufferSnapshot, ExcerptRange<text::Anchor>)> {
5313 self.excerpts
5314 .iter()
5315 .map(|excerpt| (excerpt.id, &excerpt.buffer, excerpt.range.clone()))
5316 }
5317
5318 fn cursor<D: TextDimension + Default>(&self) -> MultiBufferCursor<'_, D> {
5319 let excerpts = self.excerpts.cursor(());
5320 let diff_transforms = self.diff_transforms.cursor(());
5321 MultiBufferCursor {
5322 excerpts,
5323 diff_transforms,
5324 diffs: &self.diffs,
5325 cached_region: None,
5326 }
5327 }
5328
5329 pub fn excerpt_before(&self, id: ExcerptId) -> Option<MultiBufferExcerpt<'_>> {
5330 let start_locator = self.excerpt_locator_for_id(id);
5331 let mut excerpts = self
5332 .excerpts
5333 .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<usize>>>(());
5334 excerpts.seek(&Some(start_locator), Bias::Left);
5335 excerpts.prev();
5336
5337 let mut diff_transforms = self.diff_transforms.cursor::<DiffTransforms<usize>>(());
5338 diff_transforms.seek(&excerpts.start().1, Bias::Left);
5339 if diff_transforms.end().excerpt_dimension < excerpts.start().1 {
5340 diff_transforms.next();
5341 }
5342
5343 let excerpt = excerpts.item()?;
5344 Some(MultiBufferExcerpt {
5345 excerpt,
5346 offset: diff_transforms.start().output_dimension.0,
5347 buffer_offset: excerpt.range.context.start.to_offset(&excerpt.buffer),
5348 excerpt_offset: excerpts.start().1.clone(),
5349 diff_transforms,
5350 })
5351 }
5352
5353 pub fn excerpt_boundaries_in_range<R, T>(
5354 &self,
5355 range: R,
5356 ) -> impl Iterator<Item = ExcerptBoundary> + '_
5357 where
5358 R: RangeBounds<T>,
5359 T: ToOffset,
5360 {
5361 let start_offset;
5362 let start = match range.start_bound() {
5363 Bound::Included(start) => {
5364 start_offset = start.to_offset(self);
5365 Bound::Included(start_offset)
5366 }
5367 Bound::Excluded(_) => {
5368 panic!("not supported")
5369 }
5370 Bound::Unbounded => {
5371 start_offset = 0;
5372 Bound::Unbounded
5373 }
5374 };
5375 let end = match range.end_bound() {
5376 Bound::Included(end) => Bound::Included(end.to_offset(self)),
5377 Bound::Excluded(end) => Bound::Excluded(end.to_offset(self)),
5378 Bound::Unbounded => Bound::Unbounded,
5379 };
5380 let bounds = (start, end);
5381
5382 let mut cursor = self.cursor::<DimensionPair<usize, Point>>();
5383 cursor.seek(&DimensionPair {
5384 key: start_offset,
5385 value: None,
5386 });
5387
5388 if cursor
5389 .region()
5390 .is_some_and(|region| bounds.contains(®ion.range.start.key))
5391 {
5392 cursor.prev_excerpt();
5393 } else {
5394 cursor.seek_to_start_of_current_excerpt();
5395 }
5396 let mut prev_region = cursor.region();
5397
5398 cursor.next_excerpt();
5399
5400 iter::from_fn(move || {
5401 loop {
5402 if self.singleton {
5403 return None;
5404 }
5405
5406 let next_region = cursor.region()?;
5407 cursor.next_excerpt();
5408 if !bounds.contains(&next_region.range.start.key) {
5409 prev_region = Some(next_region);
5410 continue;
5411 }
5412
5413 let next_region_start = next_region.range.start.value.unwrap();
5414 let next_region_end = if let Some(region) = cursor.region() {
5415 region.range.start.value.unwrap()
5416 } else {
5417 self.max_point()
5418 };
5419
5420 let prev = prev_region.as_ref().map(|region| ExcerptInfo {
5421 id: region.excerpt.id,
5422 buffer: region.excerpt.buffer.clone(),
5423 buffer_id: region.excerpt.buffer_id,
5424 range: region.excerpt.range.clone(),
5425 end_row: MultiBufferRow(next_region_start.row),
5426 });
5427
5428 let next = ExcerptInfo {
5429 id: next_region.excerpt.id,
5430 buffer: next_region.excerpt.buffer.clone(),
5431 buffer_id: next_region.excerpt.buffer_id,
5432 range: next_region.excerpt.range.clone(),
5433 end_row: if next_region.excerpt.has_trailing_newline {
5434 MultiBufferRow(next_region_end.row - 1)
5435 } else {
5436 MultiBufferRow(next_region_end.row)
5437 },
5438 };
5439
5440 let row = MultiBufferRow(next_region_start.row);
5441
5442 prev_region = Some(next_region);
5443
5444 return Some(ExcerptBoundary { row, prev, next });
5445 }
5446 })
5447 }
5448
5449 pub fn edit_count(&self) -> usize {
5450 self.edit_count
5451 }
5452
5453 pub fn non_text_state_update_count(&self) -> usize {
5454 self.non_text_state_update_count
5455 }
5456
5457 /// Returns the smallest enclosing bracket ranges containing the given range or
5458 /// None if no brackets contain range or the range is not contained in a single
5459 /// excerpt
5460 ///
5461 /// Can optionally pass a range_filter to filter the ranges of brackets to consider
5462 pub fn innermost_enclosing_bracket_ranges<T: ToOffset>(
5463 &self,
5464 range: Range<T>,
5465 range_filter: Option<&dyn Fn(&BufferSnapshot, Range<usize>, Range<usize>) -> bool>,
5466 ) -> Option<(Range<usize>, Range<usize>)> {
5467 let range = range.start.to_offset(self)..range.end.to_offset(self);
5468 let mut excerpt = self.excerpt_containing(range.clone())?;
5469 let buffer = excerpt.buffer();
5470 let excerpt_buffer_range = excerpt.buffer_range();
5471
5472 // Filter to ranges contained in the excerpt
5473 let range_filter = |open: Range<usize>, close: Range<usize>| -> bool {
5474 excerpt_buffer_range.contains(&open.start)
5475 && excerpt_buffer_range.contains(&close.end)
5476 && range_filter.is_none_or(|filter| filter(buffer, open, close))
5477 };
5478
5479 let (open, close) = excerpt.buffer().innermost_enclosing_bracket_ranges(
5480 excerpt.map_range_to_buffer(range),
5481 Some(&range_filter),
5482 )?;
5483
5484 Some((
5485 excerpt.map_range_from_buffer(open),
5486 excerpt.map_range_from_buffer(close),
5487 ))
5488 }
5489
5490 /// Returns enclosing bracket ranges containing the given range or returns None if the range is
5491 /// not contained in a single excerpt
5492 pub fn enclosing_bracket_ranges<T: ToOffset>(
5493 &self,
5494 range: Range<T>,
5495 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + '_> {
5496 let range = range.start.to_offset(self)..range.end.to_offset(self);
5497 let mut excerpt = self.excerpt_containing(range.clone())?;
5498
5499 Some(
5500 excerpt
5501 .buffer()
5502 .enclosing_bracket_ranges(excerpt.map_range_to_buffer(range))
5503 .filter_map(move |pair| {
5504 if excerpt.contains_buffer_range(pair.open_range.start..pair.close_range.end) {
5505 Some((
5506 excerpt.map_range_from_buffer(pair.open_range),
5507 excerpt.map_range_from_buffer(pair.close_range),
5508 ))
5509 } else {
5510 None
5511 }
5512 }),
5513 )
5514 }
5515
5516 /// Returns enclosing bracket ranges containing the given range or returns None if the range is
5517 /// not contained in a single excerpt
5518 pub fn text_object_ranges<T: ToOffset>(
5519 &self,
5520 range: Range<T>,
5521 options: TreeSitterOptions,
5522 ) -> impl Iterator<Item = (Range<usize>, TextObject)> + '_ {
5523 let range = range.start.to_offset(self)..range.end.to_offset(self);
5524 self.excerpt_containing(range.clone())
5525 .map(|mut excerpt| {
5526 excerpt
5527 .buffer()
5528 .text_object_ranges(excerpt.map_range_to_buffer(range), options)
5529 .filter_map(move |(range, text_object)| {
5530 if excerpt.contains_buffer_range(range.clone()) {
5531 Some((excerpt.map_range_from_buffer(range), text_object))
5532 } else {
5533 None
5534 }
5535 })
5536 })
5537 .into_iter()
5538 .flatten()
5539 }
5540
5541 /// Returns bracket range pairs overlapping the given `range` or returns None if the `range` is
5542 /// not contained in a single excerpt
5543 pub fn bracket_ranges<T: ToOffset>(
5544 &self,
5545 range: Range<T>,
5546 ) -> Option<impl Iterator<Item = (Range<usize>, Range<usize>)> + '_> {
5547 let range = range.start.to_offset(self)..range.end.to_offset(self);
5548 let mut excerpt = self.excerpt_containing(range.clone())?;
5549
5550 Some(
5551 excerpt
5552 .buffer()
5553 .bracket_ranges(excerpt.map_range_to_buffer(range))
5554 .filter_map(move |pair| {
5555 let buffer_range = pair.open_range.start..pair.close_range.end;
5556 if excerpt.contains_buffer_range(buffer_range) {
5557 Some((
5558 excerpt.map_range_from_buffer(pair.open_range),
5559 excerpt.map_range_from_buffer(pair.close_range),
5560 ))
5561 } else {
5562 None
5563 }
5564 }),
5565 )
5566 }
5567
5568 pub fn redacted_ranges<'a, T: ToOffset>(
5569 &'a self,
5570 range: Range<T>,
5571 redaction_enabled: impl Fn(Option<&Arc<dyn File>>) -> bool + 'a,
5572 ) -> impl Iterator<Item = Range<usize>> + 'a {
5573 let range = range.start.to_offset(self)..range.end.to_offset(self);
5574 self.lift_buffer_metadata(range, move |buffer, range| {
5575 if redaction_enabled(buffer.file()) {
5576 Some(buffer.redacted_ranges(range).map(|range| (range, ())))
5577 } else {
5578 None
5579 }
5580 })
5581 .map(|(range, _, _)| range)
5582 }
5583
5584 pub fn runnable_ranges(
5585 &self,
5586 range: Range<Anchor>,
5587 ) -> impl Iterator<Item = language::RunnableRange> + '_ {
5588 let range = range.start.to_offset(self)..range.end.to_offset(self);
5589 self.lift_buffer_metadata(range, move |buffer, range| {
5590 Some(
5591 buffer
5592 .runnable_ranges(range.clone())
5593 .filter(move |runnable| {
5594 runnable.run_range.start >= range.start
5595 && runnable.run_range.end < range.end
5596 })
5597 .map(|runnable| (runnable.run_range.clone(), runnable)),
5598 )
5599 })
5600 .map(|(run_range, runnable, _)| language::RunnableRange {
5601 run_range,
5602 ..runnable
5603 })
5604 }
5605
5606 pub fn line_indents(
5607 &self,
5608 start_row: MultiBufferRow,
5609 buffer_filter: impl Fn(&BufferSnapshot) -> bool,
5610 ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
5611 let max_point = self.max_point();
5612 let mut cursor = self.cursor::<Point>();
5613 cursor.seek(&Point::new(start_row.0, 0));
5614 iter::from_fn(move || {
5615 let mut region = cursor.region()?;
5616 while !buffer_filter(®ion.excerpt.buffer) {
5617 cursor.next();
5618 region = cursor.region()?;
5619 }
5620 let overshoot = start_row.0.saturating_sub(region.range.start.row);
5621 let buffer_start_row =
5622 (region.buffer_range.start.row + overshoot).min(region.buffer_range.end.row);
5623
5624 let buffer_end_row = if region.is_main_buffer
5625 && (region.has_trailing_newline || region.range.end == max_point)
5626 {
5627 region.buffer_range.end.row
5628 } else {
5629 region.buffer_range.end.row.saturating_sub(1)
5630 };
5631
5632 let line_indents = region
5633 .buffer
5634 .line_indents_in_row_range(buffer_start_row..buffer_end_row);
5635 cursor.next();
5636 Some(line_indents.map(move |(buffer_row, indent)| {
5637 let row = region.range.start.row + (buffer_row - region.buffer_range.start.row);
5638 (MultiBufferRow(row), indent, ®ion.excerpt.buffer)
5639 }))
5640 })
5641 .flatten()
5642 }
5643
5644 pub fn reversed_line_indents(
5645 &self,
5646 end_row: MultiBufferRow,
5647 buffer_filter: impl Fn(&BufferSnapshot) -> bool,
5648 ) -> impl Iterator<Item = (MultiBufferRow, LineIndent, &BufferSnapshot)> {
5649 let max_point = self.max_point();
5650 let mut cursor = self.cursor::<Point>();
5651 cursor.seek(&Point::new(end_row.0, 0));
5652 iter::from_fn(move || {
5653 let mut region = cursor.region()?;
5654 while !buffer_filter(®ion.excerpt.buffer) {
5655 cursor.prev();
5656 region = cursor.region()?;
5657 }
5658
5659 let buffer_start_row = region.buffer_range.start.row;
5660 let buffer_end_row = if region.is_main_buffer
5661 && (region.has_trailing_newline || region.range.end == max_point)
5662 {
5663 region.buffer_range.end.row + 1
5664 } else {
5665 region.buffer_range.end.row
5666 };
5667
5668 let overshoot = end_row.0 - region.range.start.row;
5669 let buffer_end_row =
5670 (region.buffer_range.start.row + overshoot + 1).min(buffer_end_row);
5671
5672 let line_indents = region
5673 .buffer
5674 .reversed_line_indents_in_row_range(buffer_start_row..buffer_end_row);
5675 cursor.prev();
5676 Some(line_indents.map(move |(buffer_row, indent)| {
5677 let row = region.range.start.row + (buffer_row - region.buffer_range.start.row);
5678 (MultiBufferRow(row), indent, ®ion.excerpt.buffer)
5679 }))
5680 })
5681 .flatten()
5682 }
5683
5684 pub async fn enclosing_indent(
5685 &self,
5686 mut target_row: MultiBufferRow,
5687 ) -> Option<(Range<MultiBufferRow>, LineIndent)> {
5688 let max_row = MultiBufferRow(self.max_point().row);
5689 if target_row >= max_row {
5690 return None;
5691 }
5692
5693 let mut target_indent = self.line_indent_for_row(target_row);
5694
5695 // If the current row is at the start of an indented block, we want to return this
5696 // block as the enclosing indent.
5697 if !target_indent.is_line_empty() && target_row < max_row {
5698 let next_line_indent = self.line_indent_for_row(MultiBufferRow(target_row.0 + 1));
5699 if !next_line_indent.is_line_empty()
5700 && target_indent.raw_len() < next_line_indent.raw_len()
5701 {
5702 target_indent = next_line_indent;
5703 target_row.0 += 1;
5704 }
5705 }
5706
5707 const SEARCH_ROW_LIMIT: u32 = 25000;
5708 const SEARCH_WHITESPACE_ROW_LIMIT: u32 = 2500;
5709 const YIELD_INTERVAL: u32 = 100;
5710
5711 let mut accessed_row_counter = 0;
5712
5713 // If there is a blank line at the current row, search for the next non indented lines
5714 if target_indent.is_line_empty() {
5715 let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_WHITESPACE_ROW_LIMIT));
5716 let end =
5717 MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_WHITESPACE_ROW_LIMIT));
5718
5719 let mut non_empty_line_above = None;
5720 for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5721 if row < start {
5722 break;
5723 }
5724 accessed_row_counter += 1;
5725 if accessed_row_counter == YIELD_INTERVAL {
5726 accessed_row_counter = 0;
5727 yield_now().await;
5728 }
5729 if !indent.is_line_empty() {
5730 non_empty_line_above = Some((row, indent));
5731 break;
5732 }
5733 }
5734
5735 let mut non_empty_line_below = None;
5736 for (row, indent, _) in self.line_indents(target_row, |_| true) {
5737 if row > end {
5738 break;
5739 }
5740 accessed_row_counter += 1;
5741 if accessed_row_counter == YIELD_INTERVAL {
5742 accessed_row_counter = 0;
5743 yield_now().await;
5744 }
5745 if !indent.is_line_empty() {
5746 non_empty_line_below = Some((row, indent));
5747 break;
5748 }
5749 }
5750
5751 let (row, indent) = match (non_empty_line_above, non_empty_line_below) {
5752 (Some((above_row, above_indent)), Some((below_row, below_indent))) => {
5753 if above_indent.raw_len() >= below_indent.raw_len() {
5754 (above_row, above_indent)
5755 } else {
5756 (below_row, below_indent)
5757 }
5758 }
5759 (Some(above), None) => above,
5760 (None, Some(below)) => below,
5761 _ => return None,
5762 };
5763
5764 target_indent = indent;
5765 target_row = row;
5766 }
5767
5768 let start = MultiBufferRow(target_row.0.saturating_sub(SEARCH_ROW_LIMIT));
5769 let end = MultiBufferRow((max_row.0 + 1).min(target_row.0 + SEARCH_ROW_LIMIT));
5770
5771 let mut start_indent = None;
5772 for (row, indent, _) in self.reversed_line_indents(target_row, |_| true) {
5773 if row < start {
5774 break;
5775 }
5776 accessed_row_counter += 1;
5777 if accessed_row_counter == YIELD_INTERVAL {
5778 accessed_row_counter = 0;
5779 yield_now().await;
5780 }
5781 if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5782 start_indent = Some((row, indent));
5783 break;
5784 }
5785 }
5786 let (start_row, start_indent_size) = start_indent?;
5787
5788 let mut end_indent = (end, None);
5789 for (row, indent, _) in self.line_indents(target_row, |_| true) {
5790 if row > end {
5791 break;
5792 }
5793 accessed_row_counter += 1;
5794 if accessed_row_counter == YIELD_INTERVAL {
5795 accessed_row_counter = 0;
5796 yield_now().await;
5797 }
5798 if !indent.is_line_empty() && indent.raw_len() < target_indent.raw_len() {
5799 end_indent = (MultiBufferRow(row.0.saturating_sub(1)), Some(indent));
5800 break;
5801 }
5802 }
5803 let (end_row, end_indent_size) = end_indent;
5804
5805 let indent = if let Some(end_indent_size) = end_indent_size {
5806 if start_indent_size.raw_len() > end_indent_size.raw_len() {
5807 start_indent_size
5808 } else {
5809 end_indent_size
5810 }
5811 } else {
5812 start_indent_size
5813 };
5814
5815 Some((start_row..end_row, indent))
5816 }
5817
5818 pub fn indent_guides_in_range<T: ToPoint>(
5819 &self,
5820 range: Range<T>,
5821 ignore_disabled_for_language: bool,
5822 cx: &App,
5823 ) -> impl Iterator<Item = IndentGuide> {
5824 let range = range.start.to_point(self)..range.end.to_point(self);
5825 let start_row = MultiBufferRow(range.start.row);
5826 let end_row = MultiBufferRow(range.end.row);
5827
5828 let mut row_indents = self.line_indents(start_row, |buffer| {
5829 let settings =
5830 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx);
5831 settings.indent_guides.enabled || ignore_disabled_for_language
5832 });
5833
5834 let mut result = Vec::new();
5835 let mut indent_stack = SmallVec::<[IndentGuide; 8]>::new();
5836
5837 let mut prev_settings = None;
5838 while let Some((first_row, mut line_indent, buffer)) = row_indents.next() {
5839 if first_row > end_row {
5840 break;
5841 }
5842 let current_depth = indent_stack.len() as u32;
5843
5844 // Avoid retrieving the language settings repeatedly for every buffer row.
5845 if let Some((prev_buffer_id, _)) = &prev_settings
5846 && prev_buffer_id != &buffer.remote_id()
5847 {
5848 prev_settings.take();
5849 }
5850 let settings = &prev_settings
5851 .get_or_insert_with(|| {
5852 (
5853 buffer.remote_id(),
5854 language_settings(buffer.language().map(|l| l.name()), buffer.file(), cx),
5855 )
5856 })
5857 .1;
5858 let tab_size = settings.tab_size.get();
5859
5860 // When encountering empty, continue until found useful line indent
5861 // then add to the indent stack with the depth found
5862 let mut found_indent = false;
5863 let mut last_row = first_row;
5864 if line_indent.is_line_blank() {
5865 while !found_indent {
5866 let Some((target_row, new_line_indent, _)) = row_indents.next() else {
5867 break;
5868 };
5869 const TRAILING_ROW_SEARCH_LIMIT: u32 = 25;
5870 if target_row > MultiBufferRow(end_row.0 + TRAILING_ROW_SEARCH_LIMIT) {
5871 break;
5872 }
5873
5874 if new_line_indent.is_line_blank() {
5875 continue;
5876 }
5877 last_row = target_row.min(end_row);
5878 line_indent = new_line_indent;
5879 found_indent = true;
5880 break;
5881 }
5882 } else {
5883 found_indent = true
5884 }
5885
5886 let depth = if found_indent {
5887 line_indent.len(tab_size) / tab_size
5888 } else {
5889 0
5890 };
5891
5892 match depth.cmp(¤t_depth) {
5893 cmp::Ordering::Less => {
5894 for _ in 0..(current_depth - depth) {
5895 let mut indent = indent_stack.pop().unwrap();
5896 if last_row != first_row {
5897 // In this case, we landed on an empty row, had to seek forward,
5898 // and discovered that the indent we where on is ending.
5899 // This means that the last display row must
5900 // be on line that ends this indent range, so we
5901 // should display the range up to the first non-empty line
5902 indent.end_row = MultiBufferRow(first_row.0.saturating_sub(1));
5903 }
5904
5905 result.push(indent)
5906 }
5907 }
5908 cmp::Ordering::Greater => {
5909 for next_depth in current_depth..depth {
5910 indent_stack.push(IndentGuide {
5911 buffer_id: buffer.remote_id(),
5912 start_row: first_row,
5913 end_row: last_row,
5914 depth: next_depth,
5915 tab_size,
5916 settings: settings.indent_guides.clone(),
5917 });
5918 }
5919 }
5920 _ => {}
5921 }
5922
5923 for indent in indent_stack.iter_mut() {
5924 indent.end_row = last_row;
5925 }
5926 }
5927
5928 result.extend(indent_stack);
5929 result.into_iter()
5930 }
5931
5932 pub fn trailing_excerpt_update_count(&self) -> usize {
5933 self.trailing_excerpt_update_count
5934 }
5935
5936 pub fn file_at<T: ToOffset>(&self, point: T) -> Option<&Arc<dyn File>> {
5937 self.point_to_buffer_offset(point)
5938 .and_then(|(buffer, _)| buffer.file())
5939 }
5940
5941 pub fn language_at<T: ToOffset>(&self, point: T) -> Option<&Arc<Language>> {
5942 self.point_to_buffer_offset(point)
5943 .and_then(|(buffer, offset)| buffer.language_at(offset))
5944 }
5945
5946 fn language_settings<'a>(&'a self, cx: &'a App) -> Cow<'a, LanguageSettings> {
5947 self.excerpts
5948 .first()
5949 .map(|excerpt| &excerpt.buffer)
5950 .map(|buffer| {
5951 language_settings(
5952 buffer.language().map(|language| language.name()),
5953 buffer.file(),
5954 cx,
5955 )
5956 })
5957 .unwrap_or_else(move || self.language_settings_at(0, cx))
5958 }
5959
5960 pub fn language_settings_at<'a, T: ToOffset>(
5961 &'a self,
5962 point: T,
5963 cx: &'a App,
5964 ) -> Cow<'a, LanguageSettings> {
5965 let mut language = None;
5966 let mut file = None;
5967 if let Some((buffer, offset)) = self.point_to_buffer_offset(point) {
5968 language = buffer.language_at(offset);
5969 file = buffer.file();
5970 }
5971 language_settings(language.map(|l| l.name()), file, cx)
5972 }
5973
5974 pub fn language_scope_at<T: ToOffset>(&self, point: T) -> Option<LanguageScope> {
5975 self.point_to_buffer_offset(point)
5976 .and_then(|(buffer, offset)| buffer.language_scope_at(offset))
5977 }
5978
5979 pub fn char_classifier_at<T: ToOffset>(&self, point: T) -> CharClassifier {
5980 self.point_to_buffer_offset(point)
5981 .map(|(buffer, offset)| buffer.char_classifier_at(offset))
5982 .unwrap_or_default()
5983 }
5984
5985 pub fn language_indent_size_at<T: ToOffset>(
5986 &self,
5987 position: T,
5988 cx: &App,
5989 ) -> Option<IndentSize> {
5990 let (buffer_snapshot, offset) = self.point_to_buffer_offset(position)?;
5991 Some(buffer_snapshot.language_indent_size_at(offset, cx))
5992 }
5993
5994 pub fn is_dirty(&self) -> bool {
5995 self.is_dirty
5996 }
5997
5998 pub fn has_deleted_file(&self) -> bool {
5999 self.has_deleted_file
6000 }
6001
6002 pub fn has_conflict(&self) -> bool {
6003 self.has_conflict
6004 }
6005
6006 pub fn has_diagnostics(&self) -> bool {
6007 self.excerpts
6008 .iter()
6009 .any(|excerpt| excerpt.buffer.has_diagnostics())
6010 }
6011
6012 pub fn diagnostic_group(
6013 &self,
6014 buffer_id: BufferId,
6015 group_id: usize,
6016 ) -> impl Iterator<Item = DiagnosticEntry<Point>> + '_ {
6017 self.lift_buffer_metadata(Point::zero()..self.max_point(), move |buffer, range| {
6018 if buffer.remote_id() != buffer_id {
6019 return None;
6020 };
6021 Some(
6022 buffer
6023 .diagnostics_in_range(range, false)
6024 .filter(move |diagnostic| diagnostic.diagnostic.group_id == group_id)
6025 .map(move |DiagnosticEntry { diagnostic, range }| (range, diagnostic)),
6026 )
6027 })
6028 .map(|(range, diagnostic, _)| DiagnosticEntry { diagnostic, range })
6029 }
6030
6031 pub fn diagnostics_in_range<'a, T>(
6032 &'a self,
6033 range: Range<T>,
6034 ) -> impl Iterator<Item = DiagnosticEntry<T>> + 'a
6035 where
6036 T: 'a
6037 + text::ToOffset
6038 + text::FromAnchor
6039 + TextDimension
6040 + Ord
6041 + Sub<T, Output = T>
6042 + fmt::Debug,
6043 {
6044 self.lift_buffer_metadata(range, move |buffer, buffer_range| {
6045 Some(
6046 buffer
6047 .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
6048 .map(|entry| (entry.range, entry.diagnostic)),
6049 )
6050 })
6051 .map(|(range, diagnostic, _)| DiagnosticEntry { diagnostic, range })
6052 }
6053
6054 pub fn diagnostics_with_buffer_ids_in_range<'a, T>(
6055 &'a self,
6056 range: Range<T>,
6057 ) -> impl Iterator<Item = (BufferId, DiagnosticEntry<T>)> + 'a
6058 where
6059 T: 'a
6060 + text::ToOffset
6061 + text::FromAnchor
6062 + TextDimension
6063 + Ord
6064 + Sub<T, Output = T>
6065 + fmt::Debug,
6066 {
6067 self.lift_buffer_metadata(range, move |buffer, buffer_range| {
6068 Some(
6069 buffer
6070 .diagnostics_in_range(buffer_range.start..buffer_range.end, false)
6071 .map(|entry| (entry.range, entry.diagnostic)),
6072 )
6073 })
6074 .map(|(range, diagnostic, b)| (b.buffer_id, DiagnosticEntry { diagnostic, range }))
6075 }
6076
6077 pub fn syntax_ancestor<T: ToOffset>(
6078 &self,
6079 range: Range<T>,
6080 ) -> Option<(tree_sitter::Node<'_>, MultiOrSingleBufferOffsetRange)> {
6081 let range = range.start.to_offset(self)..range.end.to_offset(self);
6082 let mut excerpt = self.excerpt_containing(range.clone())?;
6083 let node = excerpt
6084 .buffer()
6085 .syntax_ancestor(excerpt.map_range_to_buffer(range))?;
6086 let node_range = node.byte_range();
6087 let range = if excerpt.contains_buffer_range(node_range.clone()) {
6088 MultiOrSingleBufferOffsetRange::Multi(excerpt.map_range_from_buffer(node_range))
6089 } else {
6090 MultiOrSingleBufferOffsetRange::Single(node_range)
6091 };
6092 Some((node, range))
6093 }
6094
6095 pub fn syntax_next_sibling<T: ToOffset>(
6096 &self,
6097 range: Range<T>,
6098 ) -> Option<tree_sitter::Node<'_>> {
6099 let range = range.start.to_offset(self)..range.end.to_offset(self);
6100 let mut excerpt = self.excerpt_containing(range.clone())?;
6101 excerpt
6102 .buffer()
6103 .syntax_next_sibling(excerpt.map_range_to_buffer(range))
6104 }
6105
6106 pub fn syntax_prev_sibling<T: ToOffset>(
6107 &self,
6108 range: Range<T>,
6109 ) -> Option<tree_sitter::Node<'_>> {
6110 let range = range.start.to_offset(self)..range.end.to_offset(self);
6111 let mut excerpt = self.excerpt_containing(range.clone())?;
6112 excerpt
6113 .buffer()
6114 .syntax_prev_sibling(excerpt.map_range_to_buffer(range))
6115 }
6116
6117 pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
6118 let (excerpt_id, _, buffer) = self.as_singleton()?;
6119 let outline = buffer.outline(theme);
6120 Some(Outline::new(
6121 outline
6122 .items
6123 .into_iter()
6124 .flat_map(|item| {
6125 Some(OutlineItem {
6126 depth: item.depth,
6127 range: self.anchor_in_excerpt(*excerpt_id, item.range.start)?
6128 ..self.anchor_in_excerpt(*excerpt_id, item.range.end)?,
6129 text: item.text,
6130 highlight_ranges: item.highlight_ranges,
6131 name_ranges: item.name_ranges,
6132 body_range: item.body_range.and_then(|body_range| {
6133 Some(
6134 self.anchor_in_excerpt(*excerpt_id, body_range.start)?
6135 ..self.anchor_in_excerpt(*excerpt_id, body_range.end)?,
6136 )
6137 }),
6138 annotation_range: item.annotation_range.and_then(|annotation_range| {
6139 Some(
6140 self.anchor_in_excerpt(*excerpt_id, annotation_range.start)?
6141 ..self.anchor_in_excerpt(*excerpt_id, annotation_range.end)?,
6142 )
6143 }),
6144 })
6145 })
6146 .collect(),
6147 ))
6148 }
6149
6150 pub fn symbols_containing<T: ToOffset>(
6151 &self,
6152 offset: T,
6153 theme: Option<&SyntaxTheme>,
6154 ) -> Option<(BufferId, Vec<OutlineItem<Anchor>>)> {
6155 let anchor = self.anchor_before(offset);
6156 let excerpt_id = anchor.excerpt_id;
6157 let excerpt = self.excerpt(excerpt_id)?;
6158 let buffer_id = excerpt.buffer_id;
6159 Some((
6160 buffer_id,
6161 excerpt
6162 .buffer
6163 .symbols_containing(anchor.text_anchor, theme)
6164 .into_iter()
6165 .flat_map(|item| {
6166 Some(OutlineItem {
6167 depth: item.depth,
6168 range: Anchor::range_in_buffer(excerpt_id, buffer_id, item.range),
6169 text: item.text,
6170 highlight_ranges: item.highlight_ranges,
6171 name_ranges: item.name_ranges,
6172 body_range: item.body_range.map(|body_range| {
6173 Anchor::range_in_buffer(excerpt_id, buffer_id, body_range)
6174 }),
6175 annotation_range: item.annotation_range.map(|body_range| {
6176 Anchor::range_in_buffer(excerpt_id, buffer_id, body_range)
6177 }),
6178 })
6179 })
6180 .collect(),
6181 ))
6182 }
6183
6184 fn excerpt_locator_for_id(&self, id: ExcerptId) -> &Locator {
6185 if id == ExcerptId::min() {
6186 Locator::min_ref()
6187 } else if id == ExcerptId::max() {
6188 Locator::max_ref()
6189 } else {
6190 let mut cursor = self.excerpt_ids.cursor::<ExcerptId>(());
6191 cursor.seek(&id, Bias::Left);
6192 if let Some(entry) = cursor.item()
6193 && entry.id == id
6194 {
6195 return &entry.locator;
6196 }
6197 panic!("invalid excerpt id {id:?}")
6198 }
6199 }
6200
6201 /// Returns the locators referenced by the given excerpt IDs, sorted by locator.
6202 fn excerpt_locators_for_ids(
6203 &self,
6204 ids: impl IntoIterator<Item = ExcerptId>,
6205 ) -> SmallVec<[Locator; 1]> {
6206 let mut sorted_ids = ids.into_iter().collect::<SmallVec<[_; 1]>>();
6207 sorted_ids.sort_unstable();
6208 let mut locators = SmallVec::new();
6209
6210 while sorted_ids.last() == Some(&ExcerptId::max()) {
6211 sorted_ids.pop();
6212 if let Some(mapping) = self.excerpt_ids.last() {
6213 locators.push(mapping.locator.clone());
6214 }
6215 }
6216
6217 let mut sorted_ids = sorted_ids.into_iter().dedup().peekable();
6218 if sorted_ids.peek() == Some(&ExcerptId::min()) {
6219 sorted_ids.next();
6220 if let Some(mapping) = self.excerpt_ids.first() {
6221 locators.push(mapping.locator.clone());
6222 }
6223 }
6224
6225 let mut cursor = self.excerpt_ids.cursor::<ExcerptId>(());
6226 for id in sorted_ids {
6227 if cursor.seek_forward(&id, Bias::Left) {
6228 locators.push(cursor.item().unwrap().locator.clone());
6229 } else {
6230 panic!("invalid excerpt id {:?}", id);
6231 }
6232 }
6233
6234 locators.sort_unstable();
6235 locators
6236 }
6237
6238 pub fn buffer_id_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<BufferId> {
6239 Some(self.excerpt(excerpt_id)?.buffer_id)
6240 }
6241
6242 pub fn buffer_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<&BufferSnapshot> {
6243 Some(&self.excerpt(excerpt_id)?.buffer)
6244 }
6245
6246 pub fn range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<Point>> {
6247 let mut cursor = self
6248 .excerpts
6249 .cursor::<Dimensions<Option<&Locator>, ExcerptDimension<Point>>>(());
6250 let locator = self.excerpt_locator_for_id(excerpt_id);
6251 if cursor.seek(&Some(locator), Bias::Left) {
6252 let start = cursor.start().1.clone();
6253 let end = cursor.end().1;
6254 let mut diff_transforms = self
6255 .diff_transforms
6256 .cursor::<Dimensions<ExcerptDimension<Point>, OutputDimension<Point>>>(());
6257 diff_transforms.seek(&start, Bias::Left);
6258 let overshoot = start.0 - diff_transforms.start().0.0;
6259 let start = diff_transforms.start().1.0 + overshoot;
6260 diff_transforms.seek(&end, Bias::Right);
6261 let overshoot = end.0 - diff_transforms.start().0.0;
6262 let end = diff_transforms.start().1.0 + overshoot;
6263 Some(start..end)
6264 } else {
6265 None
6266 }
6267 }
6268
6269 pub fn buffer_range_for_excerpt(&self, excerpt_id: ExcerptId) -> Option<Range<text::Anchor>> {
6270 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
6271 let locator = self.excerpt_locator_for_id(excerpt_id);
6272 if cursor.seek(&Some(locator), Bias::Left)
6273 && let Some(excerpt) = cursor.item()
6274 {
6275 return Some(excerpt.range.context.clone());
6276 }
6277 None
6278 }
6279
6280 fn excerpt(&self, excerpt_id: ExcerptId) -> Option<&Excerpt> {
6281 let mut cursor = self.excerpts.cursor::<Option<&Locator>>(());
6282 let locator = self.excerpt_locator_for_id(excerpt_id);
6283 cursor.seek(&Some(locator), Bias::Left);
6284 if let Some(excerpt) = cursor.item()
6285 && excerpt.id == excerpt_id
6286 {
6287 return Some(excerpt);
6288 }
6289 None
6290 }
6291
6292 /// Returns the excerpt containing range and its offset start within the multibuffer or none if `range` spans multiple excerpts
6293 pub fn excerpt_containing<T: ToOffset>(
6294 &self,
6295 range: Range<T>,
6296 ) -> Option<MultiBufferExcerpt<'_>> {
6297 let range = range.start.to_offset(self)..range.end.to_offset(self);
6298 let mut cursor = self.cursor::<usize>();
6299 cursor.seek(&range.start);
6300
6301 let start_excerpt = cursor.excerpt()?;
6302 if range.end != range.start {
6303 cursor.seek_forward(&range.end);
6304 if cursor.excerpt()?.id != start_excerpt.id {
6305 return None;
6306 }
6307 }
6308
6309 cursor.seek_to_start_of_current_excerpt();
6310 let region = cursor.region()?;
6311 let offset = region.range.start;
6312 let buffer_offset = start_excerpt.buffer_start_offset();
6313 let excerpt_offset = cursor.excerpts.start().clone();
6314 Some(MultiBufferExcerpt {
6315 diff_transforms: cursor.diff_transforms,
6316 excerpt: start_excerpt,
6317 offset,
6318 buffer_offset,
6319 excerpt_offset,
6320 })
6321 }
6322
6323 pub fn buffer_id_for_anchor(&self, anchor: Anchor) -> Option<BufferId> {
6324 if let Some(id) = anchor.buffer_id {
6325 return Some(id);
6326 }
6327 let excerpt = self.excerpt_containing(anchor..anchor)?;
6328 Some(excerpt.buffer_id())
6329 }
6330
6331 pub fn selections_in_range<'a>(
6332 &'a self,
6333 range: &'a Range<Anchor>,
6334 include_local: bool,
6335 ) -> impl 'a + Iterator<Item = (ReplicaId, bool, CursorShape, Selection<Anchor>)> {
6336 let mut cursor = self.excerpts.cursor::<ExcerptSummary>(());
6337 let start_locator = self.excerpt_locator_for_id(range.start.excerpt_id);
6338 let end_locator = self.excerpt_locator_for_id(range.end.excerpt_id);
6339 cursor.seek(start_locator, Bias::Left);
6340 cursor
6341 .take_while(move |excerpt| excerpt.locator <= *end_locator)
6342 .flat_map(move |excerpt| {
6343 let mut query_range = excerpt.range.context.start..excerpt.range.context.end;
6344 if excerpt.id == range.start.excerpt_id {
6345 query_range.start = range.start.text_anchor;
6346 }
6347 if excerpt.id == range.end.excerpt_id {
6348 query_range.end = range.end.text_anchor;
6349 }
6350
6351 excerpt
6352 .buffer
6353 .selections_in_range(query_range, include_local)
6354 .flat_map(move |(replica_id, line_mode, cursor_shape, selections)| {
6355 selections.map(move |selection| {
6356 let mut start = Anchor {
6357 buffer_id: Some(excerpt.buffer_id),
6358 excerpt_id: excerpt.id,
6359 text_anchor: selection.start,
6360 diff_base_anchor: None,
6361 };
6362 let mut end = Anchor {
6363 buffer_id: Some(excerpt.buffer_id),
6364 excerpt_id: excerpt.id,
6365 text_anchor: selection.end,
6366 diff_base_anchor: None,
6367 };
6368 if range.start.cmp(&start, self).is_gt() {
6369 start = range.start;
6370 }
6371 if range.end.cmp(&end, self).is_lt() {
6372 end = range.end;
6373 }
6374
6375 (
6376 replica_id,
6377 line_mode,
6378 cursor_shape,
6379 Selection {
6380 id: selection.id,
6381 start,
6382 end,
6383 reversed: selection.reversed,
6384 goal: selection.goal,
6385 },
6386 )
6387 })
6388 })
6389 })
6390 }
6391
6392 pub fn show_headers(&self) -> bool {
6393 self.show_headers
6394 }
6395
6396 pub fn diff_for_buffer_id(&self, buffer_id: BufferId) -> Option<&BufferDiffSnapshot> {
6397 self.diffs.get(&buffer_id)
6398 }
6399
6400 /// Visually annotates a position or range with the `Debug` representation of a value. The
6401 /// callsite of this function is used as a key - previous annotations will be removed.
6402 #[cfg(debug_assertions)]
6403 #[track_caller]
6404 pub fn debug<V, R>(&self, ranges: &R, value: V)
6405 where
6406 R: debug::ToMultiBufferDebugRanges,
6407 V: std::fmt::Debug,
6408 {
6409 self.debug_with_key(std::panic::Location::caller(), ranges, value);
6410 }
6411
6412 /// Visually annotates a position or range with the `Debug` representation of a value. Previous
6413 /// debug annotations with the same key will be removed. The key is also used to determine the
6414 /// annotation's color.
6415 #[cfg(debug_assertions)]
6416 #[track_caller]
6417 pub fn debug_with_key<K, R, V>(&self, key: &K, ranges: &R, value: V)
6418 where
6419 K: std::hash::Hash + 'static,
6420 R: debug::ToMultiBufferDebugRanges,
6421 V: std::fmt::Debug,
6422 {
6423 let text_ranges = ranges
6424 .to_multi_buffer_debug_ranges(self)
6425 .into_iter()
6426 .flat_map(|range| {
6427 self.range_to_buffer_ranges(range).into_iter().map(
6428 |(buffer, range, _excerpt_id)| {
6429 buffer.anchor_after(range.start)..buffer.anchor_before(range.end)
6430 },
6431 )
6432 })
6433 .collect();
6434 text::debug::GlobalDebugRanges::with_locked(|debug_ranges| {
6435 debug_ranges.insert(key, text_ranges, format!("{value:?}").into())
6436 });
6437 }
6438}
6439
6440#[cfg(any(test, feature = "test-support"))]
6441impl MultiBufferSnapshot {
6442 pub fn random_byte_range(&self, start_offset: usize, rng: &mut impl rand::Rng) -> Range<usize> {
6443 let end = self.clip_offset(rng.random_range(start_offset..=self.len()), Bias::Right);
6444 let start = self.clip_offset(rng.random_range(start_offset..=end), Bias::Right);
6445 start..end
6446 }
6447
6448 #[cfg(any(test, feature = "test-support"))]
6449 fn check_invariants(&self) {
6450 let excerpts = self.excerpts.items(());
6451 let excerpt_ids = self.excerpt_ids.items(());
6452
6453 for (ix, excerpt) in excerpts.iter().enumerate() {
6454 if ix == 0 {
6455 if excerpt.locator <= Locator::min() {
6456 panic!("invalid first excerpt locator {:?}", excerpt.locator);
6457 }
6458 } else if excerpt.locator <= excerpts[ix - 1].locator {
6459 panic!("excerpts are out-of-order: {:?}", excerpts);
6460 }
6461 }
6462
6463 for (ix, entry) in excerpt_ids.iter().enumerate() {
6464 if ix == 0 {
6465 if entry.id.cmp(&ExcerptId::min(), self).is_le() {
6466 panic!("invalid first excerpt id {:?}", entry.id);
6467 }
6468 } else if entry.id <= excerpt_ids[ix - 1].id {
6469 panic!("excerpt ids are out-of-order: {:?}", excerpt_ids);
6470 }
6471 }
6472
6473 if self.diff_transforms.summary().input != self.excerpts.summary().text {
6474 panic!(
6475 "incorrect input summary. expected {:?}, got {:?}. transforms: {:+?}",
6476 self.excerpts.summary().text.len,
6477 self.diff_transforms.summary().input,
6478 self.diff_transforms.items(()),
6479 );
6480 }
6481
6482 let mut prev_transform: Option<&DiffTransform> = None;
6483 for item in self.diff_transforms.iter() {
6484 if let DiffTransform::BufferContent {
6485 summary,
6486 inserted_hunk_info,
6487 } = item
6488 {
6489 if let Some(DiffTransform::BufferContent {
6490 inserted_hunk_info: prev_inserted_hunk_info,
6491 ..
6492 }) = prev_transform
6493 && *inserted_hunk_info == *prev_inserted_hunk_info
6494 {
6495 panic!(
6496 "multiple adjacent buffer content transforms with is_inserted_hunk = {inserted_hunk_info:?}. transforms: {:+?}",
6497 self.diff_transforms.items(())
6498 );
6499 }
6500 if summary.len == 0 && !self.is_empty() {
6501 panic!("empty buffer content transform");
6502 }
6503 }
6504 prev_transform = Some(item);
6505 }
6506 }
6507}
6508
6509impl<'a, D> MultiBufferCursor<'a, D>
6510where
6511 D: TextDimension + Ord + Sub<D, Output = D>,
6512{
6513 fn seek(&mut self, position: &D) {
6514 self.cached_region.take();
6515 self.diff_transforms
6516 .seek(&OutputDimension(*position), Bias::Right);
6517 if self.diff_transforms.item().is_none()
6518 && *position == self.diff_transforms.start().output_dimension.0
6519 {
6520 self.diff_transforms.prev();
6521 }
6522
6523 let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0;
6524 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6525 let overshoot = *position - self.diff_transforms.start().output_dimension.0;
6526 excerpt_position.add_assign(&overshoot);
6527 }
6528
6529 self.excerpts
6530 .seek(&ExcerptDimension(excerpt_position), Bias::Right);
6531 if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 {
6532 self.excerpts.prev();
6533 }
6534 }
6535
6536 fn seek_forward(&mut self, position: &D) {
6537 self.cached_region.take();
6538 self.diff_transforms
6539 .seek_forward(&OutputDimension(*position), Bias::Right);
6540 if self.diff_transforms.item().is_none()
6541 && *position == self.diff_transforms.start().output_dimension.0
6542 {
6543 self.diff_transforms.prev();
6544 }
6545
6546 let overshoot = *position - self.diff_transforms.start().output_dimension.0;
6547 let mut excerpt_position = self.diff_transforms.start().excerpt_dimension.0;
6548 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
6549 excerpt_position.add_assign(&overshoot);
6550 }
6551
6552 self.excerpts
6553 .seek_forward(&ExcerptDimension(excerpt_position), Bias::Right);
6554 if self.excerpts.item().is_none() && excerpt_position == self.excerpts.start().0 {
6555 self.excerpts.prev();
6556 }
6557 }
6558
6559 fn next_excerpt(&mut self) {
6560 self.excerpts.next();
6561 self.seek_to_start_of_current_excerpt();
6562 }
6563
6564 fn prev_excerpt(&mut self) {
6565 self.excerpts.prev();
6566 self.seek_to_start_of_current_excerpt();
6567 }
6568
6569 fn seek_to_start_of_current_excerpt(&mut self) {
6570 self.cached_region.take();
6571 self.diff_transforms.seek(self.excerpts.start(), Bias::Left);
6572 if self.diff_transforms.end().excerpt_dimension == *self.excerpts.start()
6573 && self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
6574 && self.diff_transforms.next_item().is_some()
6575 {
6576 self.diff_transforms.next();
6577 }
6578 }
6579
6580 fn next(&mut self) {
6581 self.cached_region.take();
6582 match self
6583 .diff_transforms
6584 .end()
6585 .excerpt_dimension
6586 .cmp(&self.excerpts.end())
6587 {
6588 cmp::Ordering::Less => self.diff_transforms.next(),
6589 cmp::Ordering::Greater => self.excerpts.next(),
6590 cmp::Ordering::Equal => {
6591 self.diff_transforms.next();
6592 if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
6593 || self.diff_transforms.item().is_none()
6594 {
6595 self.excerpts.next();
6596 } else if let Some(DiffTransform::DeletedHunk { hunk_info, .. }) =
6597 self.diff_transforms.item()
6598 && self
6599 .excerpts
6600 .item()
6601 .is_some_and(|excerpt| excerpt.id != hunk_info.excerpt_id)
6602 {
6603 self.excerpts.next();
6604 }
6605 }
6606 }
6607 }
6608
6609 fn prev(&mut self) {
6610 self.cached_region.take();
6611 match self
6612 .diff_transforms
6613 .start()
6614 .excerpt_dimension
6615 .cmp(self.excerpts.start())
6616 {
6617 cmp::Ordering::Less => self.excerpts.prev(),
6618 cmp::Ordering::Greater => self.diff_transforms.prev(),
6619 cmp::Ordering::Equal => {
6620 self.diff_transforms.prev();
6621 if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start()
6622 || self.diff_transforms.item().is_none()
6623 {
6624 self.excerpts.prev();
6625 }
6626 }
6627 }
6628 }
6629
6630 fn region(&mut self) -> Option<MultiBufferRegion<'a, D>> {
6631 if self.cached_region.is_none() {
6632 self.cached_region = self.build_region();
6633 }
6634 self.cached_region.clone()
6635 }
6636
6637 fn is_at_start_of_excerpt(&mut self) -> bool {
6638 if self.diff_transforms.start().excerpt_dimension > *self.excerpts.start() {
6639 return false;
6640 } else if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
6641 return true;
6642 }
6643
6644 self.diff_transforms.prev();
6645 let prev_transform = self.diff_transforms.item();
6646 self.diff_transforms.next();
6647
6648 prev_transform.is_none_or(|next_transform| {
6649 matches!(next_transform, DiffTransform::BufferContent { .. })
6650 })
6651 }
6652
6653 fn is_at_end_of_excerpt(&mut self) -> bool {
6654 if self.diff_transforms.end().excerpt_dimension < self.excerpts.end() {
6655 return false;
6656 } else if self.diff_transforms.end().excerpt_dimension > self.excerpts.end()
6657 || self.diff_transforms.item().is_none()
6658 {
6659 return true;
6660 }
6661
6662 let next_transform = self.diff_transforms.next_item();
6663 next_transform.is_none_or(|next_transform| match next_transform {
6664 DiffTransform::BufferContent { .. } => true,
6665 DiffTransform::DeletedHunk { hunk_info, .. } => self
6666 .excerpts
6667 .item()
6668 .is_some_and(|excerpt| excerpt.id != hunk_info.excerpt_id),
6669 })
6670 }
6671
6672 fn main_buffer_position(&self) -> Option<D> {
6673 let excerpt = self.excerpts.item()?;
6674 let buffer = &excerpt.buffer;
6675 let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
6676 let mut buffer_start = buffer_context_start;
6677 let overshoot = self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0;
6678 buffer_start.add_assign(&overshoot);
6679 Some(buffer_start)
6680 }
6681
6682 fn build_region(&self) -> Option<MultiBufferRegion<'a, D>> {
6683 let excerpt = self.excerpts.item()?;
6684 match self.diff_transforms.item()? {
6685 DiffTransform::DeletedHunk {
6686 buffer_id,
6687 base_text_byte_range,
6688 has_trailing_newline,
6689 hunk_info,
6690 ..
6691 } => {
6692 let diff = self.diffs.get(buffer_id)?;
6693 let buffer = diff.base_text();
6694 let mut rope_cursor = buffer.as_rope().cursor(0);
6695 let buffer_start = rope_cursor.summary::<D>(base_text_byte_range.start);
6696 let buffer_range_len = rope_cursor.summary::<D>(base_text_byte_range.end);
6697 let mut buffer_end = buffer_start;
6698 buffer_end.add_assign(&buffer_range_len);
6699 let start = self.diff_transforms.start().output_dimension.0;
6700 let end = self.diff_transforms.end().output_dimension.0;
6701 Some(MultiBufferRegion {
6702 buffer,
6703 excerpt,
6704 has_trailing_newline: *has_trailing_newline,
6705 is_main_buffer: false,
6706 diff_hunk_status: Some(DiffHunkStatus::deleted(
6707 hunk_info.hunk_secondary_status,
6708 )),
6709 buffer_range: buffer_start..buffer_end,
6710 range: start..end,
6711 })
6712 }
6713 DiffTransform::BufferContent {
6714 inserted_hunk_info, ..
6715 } => {
6716 let buffer = &excerpt.buffer;
6717 let buffer_context_start = excerpt.range.context.start.summary::<D>(buffer);
6718
6719 let mut start = self.diff_transforms.start().output_dimension.0;
6720 let mut buffer_start = buffer_context_start;
6721 if self.diff_transforms.start().excerpt_dimension < *self.excerpts.start() {
6722 let overshoot =
6723 self.excerpts.start().0 - self.diff_transforms.start().excerpt_dimension.0;
6724 start.add_assign(&overshoot);
6725 } else {
6726 let overshoot =
6727 self.diff_transforms.start().excerpt_dimension.0 - self.excerpts.start().0;
6728 buffer_start.add_assign(&overshoot);
6729 }
6730
6731 let mut end;
6732 let mut buffer_end;
6733 let has_trailing_newline;
6734 if self.diff_transforms.end().excerpt_dimension.0 < self.excerpts.end().0 {
6735 let overshoot =
6736 self.diff_transforms.end().excerpt_dimension.0 - self.excerpts.start().0;
6737 end = self.diff_transforms.end().output_dimension.0;
6738 buffer_end = buffer_context_start;
6739 buffer_end.add_assign(&overshoot);
6740 has_trailing_newline = false;
6741 } else {
6742 let overshoot =
6743 self.excerpts.end().0 - self.diff_transforms.start().excerpt_dimension.0;
6744 end = self.diff_transforms.start().output_dimension.0;
6745 end.add_assign(&overshoot);
6746 buffer_end = excerpt.range.context.end.summary::<D>(buffer);
6747 has_trailing_newline = excerpt.has_trailing_newline;
6748 };
6749
6750 Some(MultiBufferRegion {
6751 buffer,
6752 excerpt,
6753 has_trailing_newline,
6754 is_main_buffer: true,
6755 diff_hunk_status: inserted_hunk_info
6756 .map(|info| DiffHunkStatus::added(info.hunk_secondary_status)),
6757 buffer_range: buffer_start..buffer_end,
6758 range: start..end,
6759 })
6760 }
6761 }
6762 }
6763
6764 fn excerpt(&self) -> Option<&'a Excerpt> {
6765 self.excerpts.item()
6766 }
6767}
6768
6769impl History {
6770 fn start_transaction(&mut self, now: Instant) -> Option<TransactionId> {
6771 self.transaction_depth += 1;
6772 if self.transaction_depth == 1 {
6773 let id = self.next_transaction_id.tick();
6774 self.undo_stack.push(Transaction {
6775 id,
6776 buffer_transactions: Default::default(),
6777 first_edit_at: now,
6778 last_edit_at: now,
6779 suppress_grouping: false,
6780 });
6781 Some(id)
6782 } else {
6783 None
6784 }
6785 }
6786
6787 fn end_transaction(
6788 &mut self,
6789 now: Instant,
6790 buffer_transactions: HashMap<BufferId, TransactionId>,
6791 ) -> bool {
6792 assert_ne!(self.transaction_depth, 0);
6793 self.transaction_depth -= 1;
6794 if self.transaction_depth == 0 {
6795 if buffer_transactions.is_empty() {
6796 self.undo_stack.pop();
6797 false
6798 } else {
6799 self.redo_stack.clear();
6800 let transaction = self.undo_stack.last_mut().unwrap();
6801 transaction.last_edit_at = now;
6802 for (buffer_id, transaction_id) in buffer_transactions {
6803 transaction
6804 .buffer_transactions
6805 .entry(buffer_id)
6806 .or_insert(transaction_id);
6807 }
6808 true
6809 }
6810 } else {
6811 false
6812 }
6813 }
6814
6815 fn push_transaction<'a, T>(
6816 &mut self,
6817 buffer_transactions: T,
6818 now: Instant,
6819 cx: &Context<MultiBuffer>,
6820 ) where
6821 T: IntoIterator<Item = (&'a Entity<Buffer>, &'a language::Transaction)>,
6822 {
6823 assert_eq!(self.transaction_depth, 0);
6824 let transaction = Transaction {
6825 id: self.next_transaction_id.tick(),
6826 buffer_transactions: buffer_transactions
6827 .into_iter()
6828 .map(|(buffer, transaction)| (buffer.read(cx).remote_id(), transaction.id))
6829 .collect(),
6830 first_edit_at: now,
6831 last_edit_at: now,
6832 suppress_grouping: false,
6833 };
6834 if !transaction.buffer_transactions.is_empty() {
6835 self.undo_stack.push(transaction);
6836 self.redo_stack.clear();
6837 }
6838 }
6839
6840 fn finalize_last_transaction(&mut self) {
6841 if let Some(transaction) = self.undo_stack.last_mut() {
6842 transaction.suppress_grouping = true;
6843 }
6844 }
6845
6846 fn forget(&mut self, transaction_id: TransactionId) -> Option<Transaction> {
6847 if let Some(ix) = self
6848 .undo_stack
6849 .iter()
6850 .rposition(|transaction| transaction.id == transaction_id)
6851 {
6852 Some(self.undo_stack.remove(ix))
6853 } else if let Some(ix) = self
6854 .redo_stack
6855 .iter()
6856 .rposition(|transaction| transaction.id == transaction_id)
6857 {
6858 Some(self.redo_stack.remove(ix))
6859 } else {
6860 None
6861 }
6862 }
6863
6864 fn transaction(&self, transaction_id: TransactionId) -> Option<&Transaction> {
6865 self.undo_stack
6866 .iter()
6867 .find(|transaction| transaction.id == transaction_id)
6868 .or_else(|| {
6869 self.redo_stack
6870 .iter()
6871 .find(|transaction| transaction.id == transaction_id)
6872 })
6873 }
6874
6875 fn transaction_mut(&mut self, transaction_id: TransactionId) -> Option<&mut Transaction> {
6876 self.undo_stack
6877 .iter_mut()
6878 .find(|transaction| transaction.id == transaction_id)
6879 .or_else(|| {
6880 self.redo_stack
6881 .iter_mut()
6882 .find(|transaction| transaction.id == transaction_id)
6883 })
6884 }
6885
6886 fn pop_undo(&mut self) -> Option<&mut Transaction> {
6887 assert_eq!(self.transaction_depth, 0);
6888 if let Some(transaction) = self.undo_stack.pop() {
6889 self.redo_stack.push(transaction);
6890 self.redo_stack.last_mut()
6891 } else {
6892 None
6893 }
6894 }
6895
6896 fn pop_redo(&mut self) -> Option<&mut Transaction> {
6897 assert_eq!(self.transaction_depth, 0);
6898 if let Some(transaction) = self.redo_stack.pop() {
6899 self.undo_stack.push(transaction);
6900 self.undo_stack.last_mut()
6901 } else {
6902 None
6903 }
6904 }
6905
6906 fn remove_from_undo(&mut self, transaction_id: TransactionId) -> Option<&Transaction> {
6907 let ix = self
6908 .undo_stack
6909 .iter()
6910 .rposition(|transaction| transaction.id == transaction_id)?;
6911 let transaction = self.undo_stack.remove(ix);
6912 self.redo_stack.push(transaction);
6913 self.redo_stack.last()
6914 }
6915
6916 fn group(&mut self) -> Option<TransactionId> {
6917 let mut count = 0;
6918 let mut transactions = self.undo_stack.iter();
6919 if let Some(mut transaction) = transactions.next_back() {
6920 while let Some(prev_transaction) = transactions.next_back() {
6921 if !prev_transaction.suppress_grouping
6922 && transaction.first_edit_at - prev_transaction.last_edit_at
6923 <= self.group_interval
6924 {
6925 transaction = prev_transaction;
6926 count += 1;
6927 } else {
6928 break;
6929 }
6930 }
6931 }
6932 self.group_trailing(count)
6933 }
6934
6935 fn group_until(&mut self, transaction_id: TransactionId) {
6936 let mut count = 0;
6937 for transaction in self.undo_stack.iter().rev() {
6938 if transaction.id == transaction_id {
6939 self.group_trailing(count);
6940 break;
6941 } else if transaction.suppress_grouping {
6942 break;
6943 } else {
6944 count += 1;
6945 }
6946 }
6947 }
6948
6949 fn group_trailing(&mut self, n: usize) -> Option<TransactionId> {
6950 let new_len = self.undo_stack.len() - n;
6951 let (transactions_to_keep, transactions_to_merge) = self.undo_stack.split_at_mut(new_len);
6952 if let Some(last_transaction) = transactions_to_keep.last_mut() {
6953 if let Some(transaction) = transactions_to_merge.last() {
6954 last_transaction.last_edit_at = transaction.last_edit_at;
6955 }
6956 for to_merge in transactions_to_merge {
6957 for (buffer_id, transaction_id) in &to_merge.buffer_transactions {
6958 last_transaction
6959 .buffer_transactions
6960 .entry(*buffer_id)
6961 .or_insert(*transaction_id);
6962 }
6963 }
6964 }
6965
6966 self.undo_stack.truncate(new_len);
6967 self.undo_stack.last().map(|t| t.id)
6968 }
6969}
6970
6971impl Excerpt {
6972 fn new(
6973 id: ExcerptId,
6974 locator: Locator,
6975 buffer_id: BufferId,
6976 buffer: BufferSnapshot,
6977 range: ExcerptRange<text::Anchor>,
6978 has_trailing_newline: bool,
6979 ) -> Self {
6980 Excerpt {
6981 id,
6982 locator,
6983 max_buffer_row: range.context.end.to_point(&buffer).row,
6984 text_summary: buffer
6985 .text_summary_for_range::<TextSummary, _>(range.context.to_offset(&buffer)),
6986 buffer_id,
6987 buffer,
6988 range,
6989 has_trailing_newline,
6990 }
6991 }
6992
6993 fn chunks_in_range(&self, range: Range<usize>, language_aware: bool) -> ExcerptChunks<'_> {
6994 let content_start = self.range.context.start.to_offset(&self.buffer);
6995 let chunks_start = content_start + range.start;
6996 let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
6997
6998 let footer_height = if self.has_trailing_newline
6999 && range.start <= self.text_summary.len
7000 && range.end > self.text_summary.len
7001 {
7002 1
7003 } else {
7004 0
7005 };
7006
7007 let content_chunks = self.buffer.chunks(chunks_start..chunks_end, language_aware);
7008
7009 ExcerptChunks {
7010 excerpt_id: self.id,
7011 content_chunks,
7012 footer_height,
7013 }
7014 }
7015
7016 fn seek_chunks(&self, excerpt_chunks: &mut ExcerptChunks, range: Range<usize>) {
7017 let content_start = self.range.context.start.to_offset(&self.buffer);
7018 let chunks_start = content_start + range.start;
7019 let chunks_end = content_start + cmp::min(range.end, self.text_summary.len);
7020 excerpt_chunks.content_chunks.seek(chunks_start..chunks_end);
7021 excerpt_chunks.footer_height = if self.has_trailing_newline
7022 && range.start <= self.text_summary.len
7023 && range.end > self.text_summary.len
7024 {
7025 1
7026 } else {
7027 0
7028 };
7029 }
7030
7031 fn clip_anchor(&self, text_anchor: text::Anchor) -> text::Anchor {
7032 if text_anchor
7033 .cmp(&self.range.context.start, &self.buffer)
7034 .is_lt()
7035 {
7036 self.range.context.start
7037 } else if text_anchor
7038 .cmp(&self.range.context.end, &self.buffer)
7039 .is_gt()
7040 {
7041 self.range.context.end
7042 } else {
7043 text_anchor
7044 }
7045 }
7046
7047 fn contains(&self, anchor: &Anchor) -> bool {
7048 (anchor.buffer_id == None || anchor.buffer_id == Some(self.buffer_id))
7049 && self
7050 .range
7051 .context
7052 .start
7053 .cmp(&anchor.text_anchor, &self.buffer)
7054 .is_le()
7055 && self
7056 .range
7057 .context
7058 .end
7059 .cmp(&anchor.text_anchor, &self.buffer)
7060 .is_ge()
7061 }
7062
7063 /// The [`Excerpt`]'s start offset in its [`Buffer`]
7064 fn buffer_start_offset(&self) -> usize {
7065 self.range.context.start.to_offset(&self.buffer)
7066 }
7067
7068 /// The [`Excerpt`]'s end offset in its [`Buffer`]
7069 fn buffer_end_offset(&self) -> usize {
7070 self.buffer_start_offset() + self.text_summary.len
7071 }
7072}
7073
7074impl<'a> MultiBufferExcerpt<'a> {
7075 pub fn id(&self) -> ExcerptId {
7076 self.excerpt.id
7077 }
7078
7079 pub fn buffer_id(&self) -> BufferId {
7080 self.excerpt.buffer_id
7081 }
7082
7083 pub fn start_anchor(&self) -> Anchor {
7084 Anchor {
7085 buffer_id: Some(self.excerpt.buffer_id),
7086 excerpt_id: self.excerpt.id,
7087 text_anchor: self.excerpt.range.context.start,
7088 diff_base_anchor: None,
7089 }
7090 }
7091
7092 pub fn end_anchor(&self) -> Anchor {
7093 Anchor {
7094 buffer_id: Some(self.excerpt.buffer_id),
7095 excerpt_id: self.excerpt.id,
7096 text_anchor: self.excerpt.range.context.end,
7097 diff_base_anchor: None,
7098 }
7099 }
7100
7101 pub fn buffer(&self) -> &'a BufferSnapshot {
7102 &self.excerpt.buffer
7103 }
7104
7105 pub fn buffer_range(&self) -> Range<usize> {
7106 self.buffer_offset
7107 ..self
7108 .excerpt
7109 .range
7110 .context
7111 .end
7112 .to_offset(&self.excerpt.buffer.text)
7113 }
7114
7115 pub fn start_offset(&self) -> usize {
7116 self.offset
7117 }
7118
7119 /// Maps an offset within the [`MultiBuffer`] to an offset within the [`Buffer`]
7120 pub fn map_offset_to_buffer(&mut self, offset: usize) -> usize {
7121 self.map_range_to_buffer(offset..offset).start
7122 }
7123
7124 /// Maps a range within the [`MultiBuffer`] to a range within the [`Buffer`]
7125 pub fn map_range_to_buffer(&mut self, range: Range<usize>) -> Range<usize> {
7126 self.diff_transforms
7127 .seek(&OutputDimension(range.start), Bias::Right);
7128 let start = self.map_offset_to_buffer_internal(range.start);
7129 let end = if range.end > range.start {
7130 self.diff_transforms
7131 .seek_forward(&OutputDimension(range.end), Bias::Right);
7132 self.map_offset_to_buffer_internal(range.end)
7133 } else {
7134 start
7135 };
7136 start..end
7137 }
7138
7139 fn map_offset_to_buffer_internal(&self, offset: usize) -> usize {
7140 let mut excerpt_offset = self.diff_transforms.start().excerpt_dimension.clone();
7141 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7142 excerpt_offset.0 += offset - self.diff_transforms.start().output_dimension.0;
7143 };
7144 let offset_in_excerpt = excerpt_offset.0.saturating_sub(self.excerpt_offset.0);
7145 self.buffer_offset + offset_in_excerpt
7146 }
7147
7148 /// Map an offset within the [`Buffer`] to an offset within the [`MultiBuffer`]
7149 pub fn map_offset_from_buffer(&mut self, buffer_offset: usize) -> usize {
7150 self.map_range_from_buffer(buffer_offset..buffer_offset)
7151 .start
7152 }
7153
7154 /// Map a range within the [`Buffer`] to a range within the [`MultiBuffer`]
7155 pub fn map_range_from_buffer(&mut self, buffer_range: Range<usize>) -> Range<usize> {
7156 if buffer_range.start < self.buffer_offset {
7157 log::warn!(
7158 "Attempting to map a range from a buffer offset that starts before the current buffer offset"
7159 );
7160 return buffer_range;
7161 }
7162 let overshoot = buffer_range.start - self.buffer_offset;
7163 let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot);
7164 self.diff_transforms.seek(&excerpt_offset, Bias::Right);
7165 if excerpt_offset.0 < self.diff_transforms.start().excerpt_dimension.0 {
7166 log::warn!(
7167 "Attempting to map a range from a buffer offset that starts before the current buffer offset"
7168 );
7169 return buffer_range;
7170 }
7171 let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0;
7172 let start = self.diff_transforms.start().output_dimension.0 + overshoot;
7173
7174 let end = if buffer_range.end > buffer_range.start {
7175 let overshoot = buffer_range.end - self.buffer_offset;
7176 let excerpt_offset = ExcerptDimension(self.excerpt_offset.0 + overshoot);
7177 self.diff_transforms
7178 .seek_forward(&excerpt_offset, Bias::Right);
7179 let overshoot = excerpt_offset.0 - self.diff_transforms.start().excerpt_dimension.0;
7180 self.diff_transforms.start().output_dimension.0 + overshoot
7181 } else {
7182 start
7183 };
7184
7185 start..end
7186 }
7187
7188 /// Returns true if the entirety of the given range is in the buffer's excerpt
7189 pub fn contains_buffer_range(&self, range: Range<usize>) -> bool {
7190 range.start >= self.excerpt.buffer_start_offset()
7191 && range.end <= self.excerpt.buffer_end_offset()
7192 }
7193
7194 pub fn max_buffer_row(&self) -> u32 {
7195 self.excerpt.max_buffer_row
7196 }
7197}
7198
7199impl ExcerptId {
7200 pub fn min() -> Self {
7201 Self(0)
7202 }
7203
7204 pub fn max() -> Self {
7205 Self(u32::MAX)
7206 }
7207
7208 pub fn to_proto(self) -> u64 {
7209 self.0 as _
7210 }
7211
7212 pub fn from_proto(proto: u64) -> Self {
7213 Self(proto as _)
7214 }
7215
7216 pub fn cmp(&self, other: &Self, snapshot: &MultiBufferSnapshot) -> cmp::Ordering {
7217 let a = snapshot.excerpt_locator_for_id(*self);
7218 let b = snapshot.excerpt_locator_for_id(*other);
7219 a.cmp(b).then_with(|| self.0.cmp(&other.0))
7220 }
7221}
7222
7223impl From<ExcerptId> for usize {
7224 fn from(val: ExcerptId) -> Self {
7225 val.0 as usize
7226 }
7227}
7228
7229impl fmt::Debug for Excerpt {
7230 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
7231 f.debug_struct("Excerpt")
7232 .field("id", &self.id)
7233 .field("locator", &self.locator)
7234 .field("buffer_id", &self.buffer_id)
7235 .field("range", &self.range)
7236 .field("text_summary", &self.text_summary)
7237 .field("has_trailing_newline", &self.has_trailing_newline)
7238 .finish()
7239 }
7240}
7241
7242impl sum_tree::Item for Excerpt {
7243 type Summary = ExcerptSummary;
7244
7245 fn summary(&self, _cx: ()) -> Self::Summary {
7246 let mut text = self.text_summary;
7247 if self.has_trailing_newline {
7248 text += TextSummary::from("\n");
7249 }
7250 ExcerptSummary {
7251 excerpt_id: self.id,
7252 excerpt_locator: self.locator.clone(),
7253 widest_line_number: self.max_buffer_row,
7254 text,
7255 }
7256 }
7257}
7258
7259impl sum_tree::Item for ExcerptIdMapping {
7260 type Summary = ExcerptId;
7261
7262 fn summary(&self, _cx: ()) -> Self::Summary {
7263 self.id
7264 }
7265}
7266
7267impl sum_tree::KeyedItem for ExcerptIdMapping {
7268 type Key = ExcerptId;
7269
7270 fn key(&self) -> Self::Key {
7271 self.id
7272 }
7273}
7274
7275impl DiffTransform {
7276 fn hunk_info(&self) -> Option<DiffTransformHunkInfo> {
7277 match self {
7278 DiffTransform::DeletedHunk { hunk_info, .. } => Some(*hunk_info),
7279 DiffTransform::BufferContent {
7280 inserted_hunk_info, ..
7281 } => *inserted_hunk_info,
7282 }
7283 }
7284}
7285
7286impl sum_tree::Item for DiffTransform {
7287 type Summary = DiffTransformSummary;
7288
7289 fn summary(&self, _: <Self::Summary as sum_tree::Summary>::Context<'_>) -> Self::Summary {
7290 match self {
7291 DiffTransform::BufferContent { summary, .. } => DiffTransformSummary {
7292 input: *summary,
7293 output: *summary,
7294 },
7295 DiffTransform::DeletedHunk { summary, .. } => DiffTransformSummary {
7296 input: TextSummary::default(),
7297 output: *summary,
7298 },
7299 }
7300 }
7301}
7302
7303impl DiffTransformSummary {
7304 fn excerpt_len(&self) -> ExcerptOffset {
7305 ExcerptOffset::new(self.input.len)
7306 }
7307}
7308
7309impl sum_tree::ContextLessSummary for DiffTransformSummary {
7310 fn zero() -> Self {
7311 DiffTransformSummary {
7312 input: TextSummary::default(),
7313 output: TextSummary::default(),
7314 }
7315 }
7316
7317 fn add_summary(&mut self, other: &Self) {
7318 self.input += other.input;
7319 self.output += other.output;
7320 }
7321}
7322
7323impl sum_tree::ContextLessSummary for ExcerptId {
7324 fn zero() -> Self {
7325 Self(0)
7326 }
7327
7328 fn add_summary(&mut self, summary: &Self) {
7329 *self = cmp::max(*self, *summary);
7330 }
7331}
7332
7333impl sum_tree::ContextLessSummary for ExcerptSummary {
7334 fn zero() -> Self {
7335 Self::default()
7336 }
7337
7338 fn add_summary(&mut self, summary: &Self) {
7339 debug_assert!(summary.excerpt_locator > self.excerpt_locator);
7340 self.excerpt_locator = summary.excerpt_locator.clone();
7341 Summary::add_summary(&mut self.text, &summary.text, ());
7342 self.widest_line_number = cmp::max(self.widest_line_number, summary.widest_line_number);
7343 }
7344}
7345
7346impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptOffset {
7347 fn zero(_cx: ()) -> Self {
7348 Default::default()
7349 }
7350
7351 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7352 self.value += summary.text.len;
7353 }
7354}
7355
7356impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for ExcerptOffset {
7357 fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering {
7358 Ord::cmp(&self.value, &cursor_location.text.len)
7359 }
7360}
7361
7362impl<'a> sum_tree::SeekTarget<'a, ExcerptSummary, Option<&'a Locator>> for Locator {
7363 fn cmp(&self, cursor_location: &Option<&'a Locator>, _: ()) -> cmp::Ordering {
7364 Ord::cmp(&Some(self), cursor_location)
7365 }
7366}
7367
7368impl sum_tree::SeekTarget<'_, ExcerptSummary, ExcerptSummary> for Locator {
7369 fn cmp(&self, cursor_location: &ExcerptSummary, _: ()) -> cmp::Ordering {
7370 Ord::cmp(self, &cursor_location.excerpt_locator)
7371 }
7372}
7373
7374impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for ExcerptPoint {
7375 fn zero(_cx: ()) -> Self {
7376 Default::default()
7377 }
7378
7379 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7380 self.value += summary.text.lines;
7381 }
7382}
7383
7384impl<'a, D: TextDimension + Default> sum_tree::Dimension<'a, ExcerptSummary>
7385 for ExcerptDimension<D>
7386{
7387 fn zero(_: ()) -> Self {
7388 ExcerptDimension(D::default())
7389 }
7390
7391 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7392 self.0.add_assign(&D::from_text_summary(&summary.text))
7393 }
7394}
7395
7396impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<&'a Locator> {
7397 fn zero(_cx: ()) -> Self {
7398 Default::default()
7399 }
7400
7401 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7402 *self = Some(&summary.excerpt_locator);
7403 }
7404}
7405
7406impl<'a> sum_tree::Dimension<'a, ExcerptSummary> for Option<ExcerptId> {
7407 fn zero(_cx: ()) -> Self {
7408 Default::default()
7409 }
7410
7411 fn add_summary(&mut self, summary: &'a ExcerptSummary, _: ()) {
7412 *self = Some(summary.excerpt_id);
7413 }
7414}
7415
7416#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
7417struct ExcerptDimension<T>(T);
7418
7419#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
7420struct OutputDimension<T>(T);
7421
7422impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptOffset {
7423 fn zero(_: ()) -> Self {
7424 ExcerptOffset::new(0)
7425 }
7426
7427 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7428 self.value += summary.input.len;
7429 }
7430}
7431
7432impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptPoint {
7433 fn zero(_: ()) -> Self {
7434 ExcerptPoint::new(0, 0)
7435 }
7436
7437 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7438 self.value += summary.input.lines;
7439 }
7440}
7441
7442impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransformSummary>
7443 for ExcerptDimension<D>
7444{
7445 fn cmp(&self, cursor_location: &DiffTransformSummary, _: ()) -> cmp::Ordering {
7446 Ord::cmp(&self.0, &D::from_text_summary(&cursor_location.input))
7447 }
7448}
7449
7450impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms<D>>
7451 for ExcerptDimension<D>
7452{
7453 fn cmp(&self, cursor_location: &DiffTransforms<D>, _: ()) -> cmp::Ordering {
7454 Ord::cmp(&self.0, &cursor_location.excerpt_dimension.0)
7455 }
7456}
7457
7458impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for ExcerptDimension<D> {
7459 fn zero(_: ()) -> Self {
7460 ExcerptDimension(D::default())
7461 }
7462
7463 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7464 self.0.add_assign(&D::from_text_summary(&summary.input))
7465 }
7466}
7467
7468impl<D: TextDimension + Ord> sum_tree::SeekTarget<'_, DiffTransformSummary, DiffTransforms<D>>
7469 for OutputDimension<D>
7470{
7471 fn cmp(&self, cursor_location: &DiffTransforms<D>, _: ()) -> cmp::Ordering {
7472 Ord::cmp(&self.0, &cursor_location.output_dimension.0)
7473 }
7474}
7475
7476impl<'a, D: TextDimension> sum_tree::Dimension<'a, DiffTransformSummary> for OutputDimension<D> {
7477 fn zero(_: ()) -> Self {
7478 OutputDimension(D::default())
7479 }
7480
7481 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7482 self.0.add_assign(&D::from_text_summary(&summary.output))
7483 }
7484}
7485
7486impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for TextSummary {
7487 fn zero(_: ()) -> Self {
7488 TextSummary::default()
7489 }
7490
7491 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7492 *self += summary.output
7493 }
7494}
7495
7496impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for usize {
7497 fn zero(_: ()) -> Self {
7498 0
7499 }
7500
7501 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7502 *self += summary.output.len
7503 }
7504}
7505
7506impl<'a> sum_tree::Dimension<'a, DiffTransformSummary> for Point {
7507 fn zero(_: ()) -> Self {
7508 Point::new(0, 0)
7509 }
7510
7511 fn add_summary(&mut self, summary: &'a DiffTransformSummary, _: ()) {
7512 *self += summary.output.lines
7513 }
7514}
7515
7516impl MultiBufferRows<'_> {
7517 pub fn seek(&mut self, MultiBufferRow(row): MultiBufferRow) {
7518 self.point = Point::new(row, 0);
7519 self.cursor.seek(&self.point);
7520 }
7521}
7522
7523impl Iterator for MultiBufferRows<'_> {
7524 type Item = RowInfo;
7525
7526 fn next(&mut self) -> Option<Self::Item> {
7527 if self.is_empty && self.point.row == 0 {
7528 self.point += Point::new(1, 0);
7529 return Some(RowInfo {
7530 buffer_id: None,
7531 buffer_row: Some(0),
7532 multibuffer_row: Some(MultiBufferRow(0)),
7533 diff_status: None,
7534 expand_info: None,
7535 });
7536 }
7537
7538 let mut region = self.cursor.region()?;
7539 while self.point >= region.range.end {
7540 self.cursor.next();
7541 if let Some(next_region) = self.cursor.region() {
7542 region = next_region;
7543 } else if self.point == self.cursor.diff_transforms.end().output_dimension.0 {
7544 let multibuffer_row = MultiBufferRow(self.point.row);
7545 let last_excerpt = self
7546 .cursor
7547 .excerpts
7548 .item()
7549 .or(self.cursor.excerpts.prev_item())?;
7550 let last_row = last_excerpt
7551 .range
7552 .context
7553 .end
7554 .to_point(&last_excerpt.buffer)
7555 .row;
7556
7557 let first_row = last_excerpt
7558 .range
7559 .context
7560 .start
7561 .to_point(&last_excerpt.buffer)
7562 .row;
7563
7564 let expand_info = if self.is_singleton {
7565 None
7566 } else {
7567 let needs_expand_up = first_row == last_row
7568 && last_row > 0
7569 && !region.diff_hunk_status.is_some_and(|d| d.is_deleted());
7570 let needs_expand_down = last_row < last_excerpt.buffer.max_point().row;
7571
7572 if needs_expand_up && needs_expand_down {
7573 Some(ExpandExcerptDirection::UpAndDown)
7574 } else if needs_expand_up {
7575 Some(ExpandExcerptDirection::Up)
7576 } else if needs_expand_down {
7577 Some(ExpandExcerptDirection::Down)
7578 } else {
7579 None
7580 }
7581 .map(|direction| ExpandInfo {
7582 direction,
7583 excerpt_id: last_excerpt.id,
7584 })
7585 };
7586 self.point += Point::new(1, 0);
7587 return Some(RowInfo {
7588 buffer_id: Some(last_excerpt.buffer_id),
7589 buffer_row: Some(last_row),
7590 multibuffer_row: Some(multibuffer_row),
7591 diff_status: None,
7592 expand_info,
7593 });
7594 } else {
7595 return None;
7596 };
7597 }
7598
7599 let overshoot = self.point - region.range.start;
7600 let buffer_point = region.buffer_range.start + overshoot;
7601 let expand_info = if self.is_singleton {
7602 None
7603 } else {
7604 let needs_expand_up = self.point.row == region.range.start.row
7605 && self.cursor.is_at_start_of_excerpt()
7606 && buffer_point.row > 0;
7607 let needs_expand_down = (region.excerpt.has_trailing_newline
7608 && self.point.row + 1 == region.range.end.row
7609 || !region.excerpt.has_trailing_newline && self.point.row == region.range.end.row)
7610 && self.cursor.is_at_end_of_excerpt()
7611 && buffer_point.row < region.buffer.max_point().row;
7612
7613 if needs_expand_up && needs_expand_down {
7614 Some(ExpandExcerptDirection::UpAndDown)
7615 } else if needs_expand_up {
7616 Some(ExpandExcerptDirection::Up)
7617 } else if needs_expand_down {
7618 Some(ExpandExcerptDirection::Down)
7619 } else {
7620 None
7621 }
7622 .map(|direction| ExpandInfo {
7623 direction,
7624 excerpt_id: region.excerpt.id,
7625 })
7626 };
7627
7628 let result = Some(RowInfo {
7629 buffer_id: Some(region.buffer.remote_id()),
7630 buffer_row: Some(buffer_point.row),
7631 multibuffer_row: Some(MultiBufferRow(self.point.row)),
7632 diff_status: region
7633 .diff_hunk_status
7634 .filter(|_| self.point < region.range.end),
7635 expand_info,
7636 });
7637 self.point += Point::new(1, 0);
7638 result
7639 }
7640}
7641
7642impl<'a> MultiBufferChunks<'a> {
7643 pub fn offset(&self) -> usize {
7644 self.range.start
7645 }
7646
7647 pub fn seek(&mut self, range: Range<usize>) {
7648 self.diff_transforms.seek(&range.end, Bias::Right);
7649 let mut excerpt_end = self.diff_transforms.start().1;
7650 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7651 let overshoot = range.end - self.diff_transforms.start().0;
7652 excerpt_end.value += overshoot;
7653 }
7654
7655 self.diff_transforms.seek(&range.start, Bias::Right);
7656 let mut excerpt_start = self.diff_transforms.start().1;
7657 if let Some(DiffTransform::BufferContent { .. }) = self.diff_transforms.item() {
7658 let overshoot = range.start - self.diff_transforms.start().0;
7659 excerpt_start.value += overshoot;
7660 }
7661
7662 self.seek_to_excerpt_offset_range(excerpt_start..excerpt_end);
7663 self.buffer_chunk.take();
7664 self.range = range;
7665 }
7666
7667 fn seek_to_excerpt_offset_range(&mut self, new_range: Range<ExcerptOffset>) {
7668 self.excerpt_offset_range = new_range.clone();
7669 self.excerpts.seek(&new_range.start, Bias::Right);
7670 if let Some(excerpt) = self.excerpts.item() {
7671 let excerpt_start = *self.excerpts.start();
7672 if let Some(excerpt_chunks) = self
7673 .excerpt_chunks
7674 .as_mut()
7675 .filter(|chunks| excerpt.id == chunks.excerpt_id)
7676 {
7677 excerpt.seek_chunks(
7678 excerpt_chunks,
7679 (self.excerpt_offset_range.start - excerpt_start).value
7680 ..(self.excerpt_offset_range.end - excerpt_start).value,
7681 );
7682 } else {
7683 self.excerpt_chunks = Some(excerpt.chunks_in_range(
7684 (self.excerpt_offset_range.start - excerpt_start).value
7685 ..(self.excerpt_offset_range.end - excerpt_start).value,
7686 self.language_aware,
7687 ));
7688 }
7689 } else {
7690 self.excerpt_chunks = None;
7691 }
7692 }
7693
7694 fn next_excerpt_chunk(&mut self) -> Option<Chunk<'a>> {
7695 loop {
7696 if self.excerpt_offset_range.is_empty() {
7697 return None;
7698 } else if let Some(chunk) = self.excerpt_chunks.as_mut()?.next() {
7699 self.excerpt_offset_range.start.value += chunk.text.len();
7700 return Some(chunk);
7701 } else {
7702 self.excerpts.next();
7703 let excerpt = self.excerpts.item()?;
7704 self.excerpt_chunks = Some(excerpt.chunks_in_range(
7705 0..(self.excerpt_offset_range.end - *self.excerpts.start()).value,
7706 self.language_aware,
7707 ));
7708 }
7709 }
7710 }
7711}
7712
7713impl<'a> Iterator for ReversedMultiBufferChunks<'a> {
7714 type Item = &'a str;
7715
7716 fn next(&mut self) -> Option<Self::Item> {
7717 let mut region = self.cursor.region()?;
7718 if self.offset == region.range.start {
7719 self.cursor.prev();
7720 region = self.cursor.region()?;
7721 let start_overshoot = self.start.saturating_sub(region.range.start);
7722 self.current_chunks = Some(region.buffer.reversed_chunks_in_range(
7723 region.buffer_range.start + start_overshoot..region.buffer_range.end,
7724 ));
7725 }
7726
7727 if self.offset == region.range.end && region.has_trailing_newline {
7728 self.offset -= 1;
7729 Some("\n")
7730 } else {
7731 let chunk = self.current_chunks.as_mut().unwrap().next()?;
7732 self.offset -= chunk.len();
7733 Some(chunk)
7734 }
7735 }
7736}
7737
7738impl<'a> Iterator for MultiBufferChunks<'a> {
7739 type Item = Chunk<'a>;
7740
7741 fn next(&mut self) -> Option<Chunk<'a>> {
7742 if self.range.start >= self.range.end {
7743 return None;
7744 }
7745 if self.range.start == self.diff_transforms.end().0 {
7746 self.diff_transforms.next();
7747 }
7748
7749 let diff_transform_start = self.diff_transforms.start().0;
7750 let diff_transform_end = self.diff_transforms.end().0;
7751 debug_assert!(self.range.start < diff_transform_end);
7752
7753 let diff_transform = self.diff_transforms.item()?;
7754 match diff_transform {
7755 DiffTransform::BufferContent { .. } => {
7756 let chunk = if let Some(chunk) = &mut self.buffer_chunk {
7757 chunk
7758 } else {
7759 let chunk = self.next_excerpt_chunk().unwrap();
7760 self.buffer_chunk.insert(chunk)
7761 };
7762
7763 let chunk_end = self.range.start + chunk.text.len();
7764 let diff_transform_end = diff_transform_end.min(self.range.end);
7765
7766 if diff_transform_end < chunk_end {
7767 let split_idx = diff_transform_end - self.range.start;
7768 let (before, after) = chunk.text.split_at(split_idx);
7769 self.range.start = diff_transform_end;
7770 let mask = (1 << split_idx) - 1;
7771 let chars = chunk.chars & mask;
7772 let tabs = chunk.tabs & mask;
7773
7774 chunk.text = after;
7775 chunk.chars = chunk.chars >> split_idx;
7776 chunk.tabs = chunk.tabs >> split_idx;
7777
7778 Some(Chunk {
7779 text: before,
7780 chars,
7781 tabs,
7782 ..chunk.clone()
7783 })
7784 } else {
7785 self.range.start = chunk_end;
7786 self.buffer_chunk.take()
7787 }
7788 }
7789 DiffTransform::DeletedHunk {
7790 buffer_id,
7791 base_text_byte_range,
7792 has_trailing_newline,
7793 ..
7794 } => {
7795 let base_text_start =
7796 base_text_byte_range.start + self.range.start - diff_transform_start;
7797 let base_text_end =
7798 base_text_byte_range.start + self.range.end - diff_transform_start;
7799 let base_text_end = base_text_end.min(base_text_byte_range.end);
7800
7801 let mut chunks = if let Some((_, mut chunks)) = self
7802 .diff_base_chunks
7803 .take()
7804 .filter(|(id, _)| id == buffer_id)
7805 {
7806 if chunks.range().start != base_text_start || chunks.range().end < base_text_end
7807 {
7808 chunks.seek(base_text_start..base_text_end);
7809 }
7810 chunks
7811 } else {
7812 let base_buffer = &self.diffs.get(buffer_id)?.base_text();
7813 base_buffer.chunks(base_text_start..base_text_end, self.language_aware)
7814 };
7815
7816 let chunk = if let Some(chunk) = chunks.next() {
7817 self.range.start += chunk.text.len();
7818 self.diff_base_chunks = Some((*buffer_id, chunks));
7819 chunk
7820 } else {
7821 debug_assert!(has_trailing_newline);
7822 self.range.start += "\n".len();
7823 Chunk {
7824 text: "\n",
7825 chars: 1u128,
7826 ..Default::default()
7827 }
7828 };
7829 Some(chunk)
7830 }
7831 }
7832 }
7833}
7834
7835impl MultiBufferBytes<'_> {
7836 fn consume(&mut self, len: usize) {
7837 self.range.start += len;
7838 self.chunk = &self.chunk[len..];
7839
7840 if !self.range.is_empty() && self.chunk.is_empty() {
7841 if let Some(chunk) = self.excerpt_bytes.as_mut().and_then(|bytes| bytes.next()) {
7842 self.chunk = chunk;
7843 } else if self.has_trailing_newline {
7844 self.has_trailing_newline = false;
7845 self.chunk = b"\n";
7846 } else {
7847 self.cursor.next();
7848 if let Some(region) = self.cursor.region() {
7849 let mut excerpt_bytes = region.buffer.bytes_in_range(
7850 region.buffer_range.start
7851 ..(region.buffer_range.start + self.range.end - region.range.start)
7852 .min(region.buffer_range.end),
7853 );
7854 self.chunk = excerpt_bytes.next().unwrap_or(&[]);
7855 self.excerpt_bytes = Some(excerpt_bytes);
7856 self.has_trailing_newline =
7857 region.has_trailing_newline && self.range.end >= region.range.end;
7858 if self.chunk.is_empty() && self.has_trailing_newline {
7859 self.has_trailing_newline = false;
7860 self.chunk = b"\n";
7861 }
7862 }
7863 }
7864 }
7865 }
7866}
7867
7868impl<'a> Iterator for MultiBufferBytes<'a> {
7869 type Item = &'a [u8];
7870
7871 fn next(&mut self) -> Option<Self::Item> {
7872 let chunk = self.chunk;
7873 if chunk.is_empty() {
7874 None
7875 } else {
7876 self.consume(chunk.len());
7877 Some(chunk)
7878 }
7879 }
7880}
7881
7882impl io::Read for MultiBufferBytes<'_> {
7883 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
7884 let len = cmp::min(buf.len(), self.chunk.len());
7885 buf[..len].copy_from_slice(&self.chunk[..len]);
7886 if len > 0 {
7887 self.consume(len);
7888 }
7889 Ok(len)
7890 }
7891}
7892
7893impl io::Read for ReversedMultiBufferBytes<'_> {
7894 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
7895 let len = cmp::min(buf.len(), self.chunk.len());
7896 buf[..len].copy_from_slice(&self.chunk[..len]);
7897 buf[..len].reverse();
7898 if len > 0 {
7899 self.range.end -= len;
7900 self.chunk = &self.chunk[..self.chunk.len() - len];
7901 if !self.range.is_empty()
7902 && self.chunk.is_empty()
7903 && let Some(chunk) = self.chunks.next()
7904 {
7905 self.chunk = chunk.as_bytes();
7906 }
7907 }
7908 Ok(len)
7909 }
7910}
7911
7912impl<'a> Iterator for ExcerptChunks<'a> {
7913 type Item = Chunk<'a>;
7914
7915 fn next(&mut self) -> Option<Self::Item> {
7916 if let Some(chunk) = self.content_chunks.next() {
7917 return Some(chunk);
7918 }
7919
7920 if self.footer_height > 0 {
7921 let text = unsafe { str::from_utf8_unchecked(&NEWLINES[..self.footer_height]) };
7922 let chars = (1 << self.footer_height) - 1;
7923 self.footer_height = 0;
7924 return Some(Chunk {
7925 text,
7926 chars,
7927 ..Default::default()
7928 });
7929 }
7930
7931 None
7932 }
7933}
7934
7935impl ToOffset for Point {
7936 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7937 snapshot.point_to_offset(*self)
7938 }
7939}
7940
7941impl ToOffset for usize {
7942 #[track_caller]
7943 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7944 assert!(
7945 *self <= snapshot.len(),
7946 "offset {} is greater than the snapshot.len() {}",
7947 *self,
7948 snapshot.len(),
7949 );
7950 *self
7951 }
7952}
7953
7954impl ToOffset for OffsetUtf16 {
7955 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7956 snapshot.offset_utf16_to_offset(*self)
7957 }
7958}
7959
7960impl ToOffset for PointUtf16 {
7961 fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
7962 snapshot.point_utf16_to_offset(*self)
7963 }
7964}
7965
7966impl ToOffsetUtf16 for OffsetUtf16 {
7967 fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7968 *self
7969 }
7970}
7971
7972impl ToOffsetUtf16 for usize {
7973 fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
7974 snapshot.offset_to_offset_utf16(*self)
7975 }
7976}
7977
7978impl ToPoint for usize {
7979 fn to_point<'a>(&self, snapshot: &MultiBufferSnapshot) -> Point {
7980 snapshot.offset_to_point(*self)
7981 }
7982}
7983
7984impl ToPoint for Point {
7985 fn to_point<'a>(&self, _: &MultiBufferSnapshot) -> Point {
7986 *self
7987 }
7988}
7989
7990impl ToPointUtf16 for usize {
7991 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
7992 snapshot.offset_to_point_utf16(*self)
7993 }
7994}
7995
7996impl ToPointUtf16 for Point {
7997 fn to_point_utf16<'a>(&self, snapshot: &MultiBufferSnapshot) -> PointUtf16 {
7998 snapshot.point_to_point_utf16(*self)
7999 }
8000}
8001
8002impl ToPointUtf16 for PointUtf16 {
8003 fn to_point_utf16<'a>(&self, _: &MultiBufferSnapshot) -> PointUtf16 {
8004 *self
8005 }
8006}
8007
8008impl From<ExcerptId> for EntityId {
8009 fn from(id: ExcerptId) -> Self {
8010 EntityId::from(id.0 as u64)
8011 }
8012}
8013
8014#[cfg(debug_assertions)]
8015pub mod debug {
8016 use super::*;
8017
8018 pub trait ToMultiBufferDebugRanges {
8019 fn to_multi_buffer_debug_ranges(&self, snapshot: &MultiBufferSnapshot)
8020 -> Vec<Range<usize>>;
8021 }
8022
8023 impl<T: ToOffset> ToMultiBufferDebugRanges for T {
8024 fn to_multi_buffer_debug_ranges(
8025 &self,
8026 snapshot: &MultiBufferSnapshot,
8027 ) -> Vec<Range<usize>> {
8028 [self.to_offset(snapshot)].to_multi_buffer_debug_ranges(snapshot)
8029 }
8030 }
8031
8032 impl<T: ToOffset> ToMultiBufferDebugRanges for Range<T> {
8033 fn to_multi_buffer_debug_ranges(
8034 &self,
8035 snapshot: &MultiBufferSnapshot,
8036 ) -> Vec<Range<usize>> {
8037 [self.start.to_offset(snapshot)..self.end.to_offset(snapshot)]
8038 .to_multi_buffer_debug_ranges(snapshot)
8039 }
8040 }
8041
8042 impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<T> {
8043 fn to_multi_buffer_debug_ranges(
8044 &self,
8045 snapshot: &MultiBufferSnapshot,
8046 ) -> Vec<Range<usize>> {
8047 self.as_slice().to_multi_buffer_debug_ranges(snapshot)
8048 }
8049 }
8050
8051 impl<T: ToOffset> ToMultiBufferDebugRanges for Vec<Range<T>> {
8052 fn to_multi_buffer_debug_ranges(
8053 &self,
8054 snapshot: &MultiBufferSnapshot,
8055 ) -> Vec<Range<usize>> {
8056 self.as_slice().to_multi_buffer_debug_ranges(snapshot)
8057 }
8058 }
8059
8060 impl<T: ToOffset> ToMultiBufferDebugRanges for [T] {
8061 fn to_multi_buffer_debug_ranges(
8062 &self,
8063 snapshot: &MultiBufferSnapshot,
8064 ) -> Vec<Range<usize>> {
8065 self.iter()
8066 .map(|item| {
8067 let offset = item.to_offset(snapshot);
8068 offset..offset
8069 })
8070 .collect()
8071 }
8072 }
8073
8074 impl<T: ToOffset> ToMultiBufferDebugRanges for [Range<T>] {
8075 fn to_multi_buffer_debug_ranges(
8076 &self,
8077 snapshot: &MultiBufferSnapshot,
8078 ) -> Vec<Range<usize>> {
8079 self.iter()
8080 .map(|range| range.start.to_offset(snapshot)..range.end.to_offset(snapshot))
8081 .collect()
8082 }
8083 }
8084}