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