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