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