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