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