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