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