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