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