Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:29:51.391Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

block_map.rs

5105 lines · 200.0 KB · rust
1use super::{
2    Highlights,
3    fold_map::Chunk,
4    wrap_map::{self, WrapEdit, WrapPatch, WrapPoint, WrapSnapshot},
5};
6use crate::{
7    EditorStyle, GutterDimensions,
8    display_map::{Companion, dimensions::RowDelta, wrap_map::WrapRow},
9};
10use collections::{Bound, HashMap, HashSet};
11use gpui::{AnyElement, App, EntityId, Pixels, Window};
12use language::{LanguageAwareStyling, Patch, Point};
13use multi_buffer::{
14    Anchor, ExcerptBoundaryInfo, MultiBuffer, MultiBufferOffset, MultiBufferPoint, MultiBufferRow,
15    MultiBufferSnapshot, RowInfo, ToOffset, ToPoint as _,
16};
17use parking_lot::Mutex;
18use std::{
19    cell::{Cell, RefCell},
20    cmp::{self, Ordering},
21    fmt::Debug,
22    ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeInclusive},
23    sync::{
24        Arc,
25        atomic::{AtomicUsize, Ordering::SeqCst},
26    },
27};
28use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap};
29use text::{BufferId, Edit};
30use ui::{ElementId, IntoElement};
31
32const NEWLINES: &[u8; rope::Chunk::MASK_BITS] = &[b'\n'; _];
33const BULLETS: &[u8; rope::Chunk::MASK_BITS] = &[b'*'; _];
34
35/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
36///
37/// See the [`display_map` module documentation](crate::display_map) for more information.
38pub struct BlockMap {
39    pub(super) wrap_snapshot: RefCell<WrapSnapshot>,
40    next_block_id: AtomicUsize,
41    custom_blocks: Vec<Arc<CustomBlock>>,
42    custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
43    transforms: RefCell<SumTree<Transform>>,
44    buffer_header_height: u32,
45    excerpt_header_height: u32,
46    pub(super) folded_buffers: HashSet<BufferId>,
47    buffers_with_disabled_headers: HashSet<BufferId>,
48    pub(super) deferred_edits: Cell<Patch<WrapRow>>,
49}
50
51pub struct BlockMapReader<'a> {
52    pub blocks: &'a Vec<Arc<CustomBlock>>,
53    pub snapshot: BlockSnapshot,
54}
55
56pub struct BlockMapWriter<'a> {
57    block_map: &'a mut BlockMap,
58    companion: Option<BlockMapWriterCompanion<'a>>,
59}
60
61/// Auxiliary data needed when modifying a BlockMap whose parent DisplayMap has a companion.
62struct BlockMapWriterCompanion<'a> {
63    display_map_id: EntityId,
64    companion_wrap_snapshot: WrapSnapshot,
65    companion: &'a Companion,
66    inverse: Option<BlockMapInverseWriter<'a>>,
67}
68
69struct BlockMapInverseWriter<'a> {
70    companion_multibuffer: &'a MultiBuffer,
71    companion_writer: Box<BlockMapWriter<'a>>,
72}
73
74#[derive(Clone)]
75pub struct BlockSnapshot {
76    pub(super) wrap_snapshot: WrapSnapshot,
77    transforms: SumTree<Transform>,
78    custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
79    pub(super) buffer_header_height: u32,
80    pub(super) excerpt_header_height: u32,
81    pub(super) buffers_with_disabled_headers: HashSet<BufferId>,
82}
83
84impl Deref for BlockSnapshot {
85    type Target = WrapSnapshot;
86
87    fn deref(&self) -> &Self::Target {
88        &self.wrap_snapshot
89    }
90}
91
92#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
93pub struct CustomBlockId(pub usize);
94
95impl From<CustomBlockId> for ElementId {
96    fn from(val: CustomBlockId) -> Self {
97        val.0.into()
98    }
99}
100
101#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
102pub struct SpacerId(pub usize);
103
104/// A zero-indexed point in a text buffer consisting of a row and column
105/// adjusted for inserted blocks, wrapped rows, tabs, folds and inlays.
106#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
107pub struct BlockPoint(pub Point);
108
109#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
110pub struct BlockRow(pub u32);
111
112impl_for_row_types! {
113    BlockRow => RowDelta
114}
115
116impl BlockPoint {
117    pub fn row(&self) -> BlockRow {
118        BlockRow(self.0.row)
119    }
120}
121
122/// Forward-only cursor mapping [`WrapPoint`]s to [`BlockPoint`]s, reusing its
123/// tree position across calls. This is the streaming equivalent of
124/// [`BlockSnapshot::to_block_point`]; callers must provide non-decreasing wrap
125/// points.
126pub struct BlockPointCursor<'a> {
127    snapshot: &'a BlockSnapshot,
128    cursor: sum_tree::Cursor<'a, 'static, Transform, Dimensions<WrapRow, BlockRow>>,
129}
130
131impl BlockPointCursor<'_> {
132    /// Resets the cursor to the start so it can seek backward again.
133    pub fn reset(&mut self) {
134        self.cursor.reset();
135    }
136
137    pub fn map(&mut self, wrap_point: WrapPoint) -> BlockPoint {
138        let cursor = &mut self.cursor;
139        if cursor.did_seek() {
140            cursor.seek_forward(&wrap_point.row(), Bias::Right);
141        } else {
142            cursor.seek(&wrap_point.row(), Bias::Right);
143        }
144        if let Some(transform) = cursor.item() {
145            if transform.block.is_some() {
146                BlockPoint::new(cursor.start().1, 0)
147            } else {
148                let input_start = Point::new(cursor.start().0.0, 0);
149                let output_start = Point::new(cursor.start().1.0, 0);
150                let input_overshoot = wrap_point.0 - input_start;
151                BlockPoint(output_start + input_overshoot)
152            }
153        } else {
154            self.snapshot.max_point()
155        }
156    }
157}
158
159pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
160
161/// Where to place a block.
162#[derive(Clone, Debug, Eq, PartialEq)]
163pub enum BlockPlacement<T> {
164    /// Place the block above the given position.
165    Above(T),
166    /// Place the block below the given position.
167    Below(T),
168    /// Place the block next the given position.
169    Near(T),
170    /// Replace the given range of positions with the block.
171    Replace(RangeInclusive<T>),
172}
173
174impl<T> BlockPlacement<T> {
175    pub fn start(&self) -> &T {
176        match self {
177            BlockPlacement::Above(position) => position,
178            BlockPlacement::Below(position) => position,
179            BlockPlacement::Near(position) => position,
180            BlockPlacement::Replace(range) => range.start(),
181        }
182    }
183
184    fn end(&self) -> &T {
185        match self {
186            BlockPlacement::Above(position) => position,
187            BlockPlacement::Below(position) => position,
188            BlockPlacement::Near(position) => position,
189            BlockPlacement::Replace(range) => range.end(),
190        }
191    }
192
193    pub fn as_ref(&self) -> BlockPlacement<&T> {
194        match self {
195            BlockPlacement::Above(position) => BlockPlacement::Above(position),
196            BlockPlacement::Below(position) => BlockPlacement::Below(position),
197            BlockPlacement::Near(position) => BlockPlacement::Near(position),
198            BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()),
199        }
200    }
201
202    pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> {
203        match self {
204            BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
205            BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
206            BlockPlacement::Near(position) => BlockPlacement::Near(f(position)),
207            BlockPlacement::Replace(range) => {
208                let (start, end) = range.into_inner();
209                BlockPlacement::Replace(f(start)..=f(end))
210            }
211        }
212    }
213
214    fn tie_break(&self) -> u8 {
215        match self {
216            BlockPlacement::Replace(_) => 0,
217            BlockPlacement::Above(_) => 1,
218            BlockPlacement::Near(_) => 2,
219            BlockPlacement::Below(_) => 3,
220        }
221    }
222}
223
224impl BlockPlacement<Anchor> {
225    #[ztracing::instrument(skip_all)]
226    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
227        self.start()
228            .cmp(other.start(), buffer)
229            .then_with(|| other.end().cmp(self.end(), buffer))
230            .then_with(|| self.tie_break().cmp(&other.tie_break()))
231    }
232
233    #[ztracing::instrument(skip_all)]
234    fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
235        let buffer_snapshot = wrap_snapshot.buffer_snapshot();
236        match self {
237            BlockPlacement::Above(position) => {
238                let mut position = position.to_point(buffer_snapshot);
239                position.column = 0;
240                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
241                Some(BlockPlacement::Above(wrap_row))
242            }
243            BlockPlacement::Near(position) => {
244                let mut position = position.to_point(buffer_snapshot);
245                position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
246                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
247                Some(BlockPlacement::Near(wrap_row))
248            }
249            BlockPlacement::Below(position) => {
250                let mut position = position.to_point(buffer_snapshot);
251                position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
252                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
253                Some(BlockPlacement::Below(wrap_row))
254            }
255            BlockPlacement::Replace(range) => {
256                let mut start = range.start().to_point(buffer_snapshot);
257                let mut end = range.end().to_point(buffer_snapshot);
258                if start == end {
259                    None
260                } else {
261                    start.column = 0;
262                    let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
263                    end.column = buffer_snapshot.line_len(MultiBufferRow(end.row));
264                    let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
265                    Some(BlockPlacement::Replace(start_wrap_row..=end_wrap_row))
266                }
267            }
268        }
269    }
270}
271
272pub struct CustomBlock {
273    pub id: CustomBlockId,
274    pub placement: BlockPlacement<Anchor>,
275    pub height: Option<u32>,
276    style: BlockStyle,
277    render: Arc<Mutex<RenderBlock>>,
278    priority: usize,
279}
280
281#[derive(Clone)]
282pub struct BlockProperties<P> {
283    pub placement: BlockPlacement<P>,
284    // None if the block takes up no space
285    // (e.g. a horizontal line)
286    pub height: Option<u32>,
287    pub style: BlockStyle,
288    pub render: RenderBlock,
289    pub priority: usize,
290}
291
292impl<P: Debug> Debug for BlockProperties<P> {
293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294        f.debug_struct("BlockProperties")
295            .field("placement", &self.placement)
296            .field("height", &self.height)
297            .field("style", &self.style)
298            .finish()
299    }
300}
301
302#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
303pub enum BlockStyle {
304    Fixed,
305    Flex,
306    /// Like `Flex` but doesn't use the gutter:
307    /// - block content scrolls with buffer content
308    /// - doesn't paint in gutter
309    Spacer,
310    Sticky,
311}
312
313#[derive(Debug, Default, Copy, Clone)]
314pub struct EditorMargins {
315    pub gutter: GutterDimensions,
316    pub right: Pixels,
317    pub extended_right: Pixels,
318}
319
320#[derive(gpui::AppContext, gpui::VisualContext)]
321pub struct BlockContext<'a, 'b> {
322    #[window]
323    pub window: &'a mut Window,
324    #[app]
325    pub app: &'b mut App,
326    pub anchor_x: Pixels,
327    pub max_width: Pixels,
328    pub margins: &'b EditorMargins,
329    pub em_width: Pixels,
330    pub line_height: Pixels,
331    pub block_id: BlockId,
332    pub height: u32,
333    pub selected: bool,
334    pub editor_style: &'b EditorStyle,
335    pub indent_guide_padding: Pixels,
336}
337
338#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339pub enum BlockId {
340    ExcerptBoundary(Anchor),
341    FoldedBuffer(BufferId),
342    Custom(CustomBlockId),
343    Spacer(SpacerId),
344}
345
346impl From<BlockId> for ElementId {
347    fn from(value: BlockId) -> Self {
348        match value {
349            BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(),
350            BlockId::ExcerptBoundary(anchor) => anchor.opaque_id().unwrap().into(),
351            BlockId::FoldedBuffer(id) => ("FoldedBuffer", EntityId::from(id.to_proto())).into(),
352            BlockId::Spacer(SpacerId(id)) => ("Spacer", id).into(),
353        }
354    }
355}
356
357impl std::fmt::Display for BlockId {
358    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359        match self {
360            Self::Custom(id) => write!(f, "Block({id:?})"),
361            Self::ExcerptBoundary(id) => write!(f, "ExcerptBoundary({id:?})"),
362            Self::FoldedBuffer(id) => write!(f, "FoldedBuffer({id:?})"),
363            Self::Spacer(id) => write!(f, "Spacer({id:?})"),
364        }
365    }
366}
367
368#[derive(Clone, Debug)]
369struct Transform {
370    summary: TransformSummary,
371    /// When `block` is `None`, the transform is isomorphic and passes input
372    /// wrap rows through as normal text.
373    block: Option<Block>,
374}
375
376#[derive(Clone)]
377pub enum Block {
378    Custom(Arc<CustomBlock>),
379    FoldedBuffer {
380        first_excerpt: ExcerptBoundaryInfo,
381        height: u32,
382    },
383    ExcerptBoundary {
384        excerpt: ExcerptBoundaryInfo,
385        height: u32,
386    },
387    BufferHeader {
388        excerpt: ExcerptBoundaryInfo,
389        height: u32,
390    },
391    Spacer {
392        id: SpacerId,
393        height: u32,
394        is_below: bool,
395    },
396}
397
398impl Block {
399    pub fn id(&self) -> BlockId {
400        match self {
401            Block::Custom(block) => BlockId::Custom(block.id),
402            Block::ExcerptBoundary {
403                excerpt: next_excerpt,
404                ..
405            } => BlockId::ExcerptBoundary(next_excerpt.start_anchor),
406            Block::FoldedBuffer { first_excerpt, .. } => {
407                BlockId::FoldedBuffer(first_excerpt.buffer_id())
408            }
409            Block::BufferHeader {
410                excerpt: next_excerpt,
411                ..
412            } => BlockId::ExcerptBoundary(next_excerpt.start_anchor),
413            Block::Spacer { id, .. } => BlockId::Spacer(*id),
414        }
415    }
416
417    pub fn has_height(&self) -> bool {
418        match self {
419            Block::Custom(block) => block.height.is_some(),
420            Block::ExcerptBoundary { .. }
421            | Block::FoldedBuffer { .. }
422            | Block::BufferHeader { .. }
423            | Block::Spacer { .. } => true,
424        }
425    }
426
427    pub fn height(&self) -> u32 {
428        match self {
429            Block::Custom(block) => block.height.unwrap_or(0),
430            Block::ExcerptBoundary { height, .. }
431            | Block::FoldedBuffer { height, .. }
432            | Block::BufferHeader { height, .. }
433            | Block::Spacer { height, .. } => *height,
434        }
435    }
436
437    pub fn style(&self) -> BlockStyle {
438        match self {
439            Block::Custom(block) => block.style,
440            Block::ExcerptBoundary { .. }
441            | Block::FoldedBuffer { .. }
442            | Block::BufferHeader { .. } => BlockStyle::Sticky,
443            Block::Spacer { .. } => BlockStyle::Spacer,
444        }
445    }
446
447    fn place_above(&self) -> bool {
448        match self {
449            Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)),
450            Block::FoldedBuffer { .. } => false,
451            Block::ExcerptBoundary { .. } => true,
452            Block::BufferHeader { .. } => true,
453            Block::Spacer { is_below, .. } => !*is_below,
454        }
455    }
456
457    pub fn place_near(&self) -> bool {
458        match self {
459            Block::Custom(block) => matches!(block.placement, BlockPlacement::Near(_)),
460            Block::FoldedBuffer { .. } => false,
461            Block::ExcerptBoundary { .. } => false,
462            Block::BufferHeader { .. } => false,
463            Block::Spacer { .. } => false,
464        }
465    }
466
467    fn place_below(&self) -> bool {
468        match self {
469            Block::Custom(block) => matches!(
470                block.placement,
471                BlockPlacement::Below(_) | BlockPlacement::Near(_)
472            ),
473            Block::FoldedBuffer { .. } => false,
474            Block::ExcerptBoundary { .. } => false,
475            Block::BufferHeader { .. } => false,
476            Block::Spacer { is_below, .. } => *is_below,
477        }
478    }
479
480    fn is_replacement(&self) -> bool {
481        match self {
482            Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
483            Block::FoldedBuffer { .. } => true,
484            Block::ExcerptBoundary { .. } => false,
485            Block::BufferHeader { .. } => false,
486            Block::Spacer { .. } => false,
487        }
488    }
489
490    fn is_header(&self) -> bool {
491        match self {
492            Block::Custom(_) => false,
493            Block::FoldedBuffer { .. } => true,
494            Block::ExcerptBoundary { .. } => true,
495            Block::BufferHeader { .. } => true,
496            Block::Spacer { .. } => false,
497        }
498    }
499
500    pub fn is_buffer_header(&self) -> bool {
501        match self {
502            Block::Custom(_) => false,
503            Block::FoldedBuffer { .. } => true,
504            Block::ExcerptBoundary { .. } => false,
505            Block::BufferHeader { .. } => true,
506            Block::Spacer { .. } => false,
507        }
508    }
509}
510
511impl Debug for Block {
512    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
513        match self {
514            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
515            Self::FoldedBuffer {
516                first_excerpt,
517                height,
518            } => f
519                .debug_struct("FoldedBuffer")
520                .field("first_excerpt", &first_excerpt)
521                .field("height", height)
522                .finish(),
523            Self::ExcerptBoundary { excerpt, height } => f
524                .debug_struct("ExcerptBoundary")
525                .field("excerpt", excerpt)
526                .field("height", height)
527                .finish(),
528            Self::BufferHeader { excerpt, height } => f
529                .debug_struct("BufferHeader")
530                .field("excerpt", excerpt)
531                .field("height", height)
532                .finish(),
533            Self::Spacer {
534                id,
535                height,
536                is_below: _,
537            } => f
538                .debug_struct("Spacer")
539                .field("id", id)
540                .field("height", height)
541                .finish(),
542        }
543    }
544}
545
546#[derive(Clone, Debug, Default)]
547struct TransformSummary {
548    input_rows: WrapRow,
549    output_rows: BlockRow,
550    longest_row: BlockRow,
551    longest_row_chars: u32,
552    has_replacement_blocks: bool,
553}
554
555pub struct BlockChunks<'a> {
556    transforms: sum_tree::Cursor<'a, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
557    input_chunks: wrap_map::WrapChunks<'a>,
558    input_chunk: Chunk<'a>,
559    output_row: BlockRow,
560    max_output_row: BlockRow,
561    line_count_overflow: RowDelta,
562    masked: bool,
563}
564
565#[derive(Clone)]
566pub struct BlockRows<'a> {
567    transforms: sum_tree::Cursor<'a, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
568    input_rows: wrap_map::WrapRows<'a>,
569    output_row: BlockRow,
570    started: bool,
571}
572
573#[derive(Clone, Copy)]
574pub struct CompanionView<'a> {
575    display_map_id: EntityId,
576    companion_wrap_snapshot: &'a WrapSnapshot,
577    companion_wrap_edits: &'a WrapPatch,
578    companion: &'a Companion,
579}
580
581impl<'a> CompanionView<'a> {
582    pub(crate) fn new(
583        display_map_id: EntityId,
584        companion_wrap_snapshot: &'a WrapSnapshot,
585        companion_wrap_edits: &'a WrapPatch,
586        companion: &'a Companion,
587    ) -> Self {
588        Self {
589            display_map_id,
590            companion_wrap_snapshot,
591            companion_wrap_edits,
592            companion,
593        }
594    }
595}
596
597impl<'a> From<CompanionViewMut<'a>> for CompanionView<'a> {
598    fn from(view_mut: CompanionViewMut<'a>) -> Self {
599        Self {
600            display_map_id: view_mut.display_map_id,
601            companion_wrap_snapshot: view_mut.companion_wrap_snapshot,
602            companion_wrap_edits: view_mut.companion_wrap_edits,
603            companion: view_mut.companion,
604        }
605    }
606}
607
608impl<'a> From<&'a CompanionViewMut<'a>> for CompanionView<'a> {
609    fn from(view_mut: &'a CompanionViewMut<'a>) -> Self {
610        Self {
611            display_map_id: view_mut.display_map_id,
612            companion_wrap_snapshot: view_mut.companion_wrap_snapshot,
613            companion_wrap_edits: view_mut.companion_wrap_edits,
614            companion: view_mut.companion,
615        }
616    }
617}
618
619pub struct CompanionViewMut<'a> {
620    display_map_id: EntityId,
621    companion_display_map_id: EntityId,
622    companion_wrap_snapshot: &'a WrapSnapshot,
623    companion_wrap_edits: &'a WrapPatch,
624    companion_multibuffer: &'a MultiBuffer,
625    companion_block_map: &'a mut BlockMap,
626    companion: &'a Companion,
627}
628
629impl<'a> CompanionViewMut<'a> {
630    pub(crate) fn new(
631        display_map_id: EntityId,
632        companion_display_map_id: EntityId,
633        companion_wrap_snapshot: &'a WrapSnapshot,
634        companion_wrap_edits: &'a WrapPatch,
635        companion_multibuffer: &'a MultiBuffer,
636        companion: &'a Companion,
637        companion_block_map: &'a mut BlockMap,
638    ) -> Self {
639        Self {
640            display_map_id,
641            companion_display_map_id,
642            companion_wrap_snapshot,
643            companion_wrap_edits,
644            companion_multibuffer,
645            companion,
646            companion_block_map,
647        }
648    }
649}
650
651impl BlockMap {
652    #[ztracing::instrument(skip_all)]
653    pub fn new(
654        wrap_snapshot: WrapSnapshot,
655        buffer_header_height: u32,
656        excerpt_header_height: u32,
657    ) -> Self {
658        let row_count = wrap_snapshot.max_point().row() + WrapRow(1);
659        let mut transforms = SumTree::default();
660        push_isomorphic(&mut transforms, row_count - WrapRow(0), &wrap_snapshot);
661        let map = Self {
662            next_block_id: AtomicUsize::new(0),
663            custom_blocks: Vec::new(),
664            custom_blocks_by_id: TreeMap::default(),
665            folded_buffers: HashSet::default(),
666            buffers_with_disabled_headers: HashSet::default(),
667            transforms: RefCell::new(transforms),
668            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
669            buffer_header_height,
670            excerpt_header_height,
671            deferred_edits: Cell::default(),
672        };
673        map.sync(
674            &wrap_snapshot,
675            Patch::new(vec![Edit {
676                old: WrapRow(0)..row_count,
677                new: WrapRow(0)..row_count,
678            }]),
679            None,
680        );
681        map
682    }
683
684    #[ztracing::instrument(skip_all)]
685    pub(crate) fn read(
686        &self,
687        wrap_snapshot: WrapSnapshot,
688        edits: WrapPatch,
689        companion_view: Option<CompanionView>,
690    ) -> BlockMapReader<'_> {
691        self.sync(&wrap_snapshot, edits, companion_view);
692        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
693        BlockMapReader {
694            blocks: &self.custom_blocks,
695            snapshot: BlockSnapshot {
696                wrap_snapshot,
697                transforms: self.transforms.borrow().clone(),
698                custom_blocks_by_id: self.custom_blocks_by_id.clone(),
699                buffer_header_height: self.buffer_header_height,
700                excerpt_header_height: self.excerpt_header_height,
701                buffers_with_disabled_headers: self.buffers_with_disabled_headers.clone(),
702            },
703        }
704    }
705
706    #[ztracing::instrument(skip_all)]
707    pub(crate) fn write<'a>(
708        &'a mut self,
709        wrap_snapshot: WrapSnapshot,
710        edits: WrapPatch,
711        companion_view: Option<CompanionViewMut<'a>>,
712    ) -> BlockMapWriter<'a> {
713        self.sync(
714            &wrap_snapshot,
715            edits.clone(),
716            companion_view.as_ref().map(CompanionView::from),
717        );
718        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
719        let companion = if let Some(companion_view) = companion_view {
720            companion_view.companion_block_map.sync(
721                companion_view.companion_wrap_snapshot,
722                companion_view.companion_wrap_edits.clone(),
723                Some(CompanionView::new(
724                    companion_view.companion_display_map_id,
725                    &wrap_snapshot,
726                    &edits,
727                    companion_view.companion,
728                )),
729            );
730            *companion_view
731                .companion_block_map
732                .wrap_snapshot
733                .borrow_mut() = companion_view.companion_wrap_snapshot.clone();
734            Some(BlockMapWriterCompanion {
735                display_map_id: companion_view.display_map_id,
736                companion_wrap_snapshot: companion_view.companion_wrap_snapshot.clone(),
737                companion: companion_view.companion,
738                inverse: Some(BlockMapInverseWriter {
739                    companion_multibuffer: companion_view.companion_multibuffer,
740                    companion_writer: Box::new(BlockMapWriter {
741                        block_map: companion_view.companion_block_map,
742                        companion: Some(BlockMapWriterCompanion {
743                            display_map_id: companion_view.companion_display_map_id,
744                            companion_wrap_snapshot: wrap_snapshot,
745                            companion: companion_view.companion,
746                            inverse: None,
747                        }),
748                    }),
749                }),
750            })
751        } else {
752            None
753        };
754        BlockMapWriter {
755            block_map: self,
756            companion,
757        }
758    }
759
760    // Warning: doesn't sync the block map, use advisedly
761    pub(crate) fn insert_block_raw(
762        &mut self,
763        block: BlockProperties<Anchor>,
764        buffer: &MultiBufferSnapshot,
765    ) -> CustomBlockId {
766        let id = CustomBlockId(self.next_block_id.fetch_add(1, SeqCst));
767        let block_ix = match self
768            .custom_blocks
769            .binary_search_by(|probe| probe.placement.cmp(&block.placement, &buffer))
770        {
771            Ok(ix) | Err(ix) => ix,
772        };
773        let new_block = Arc::new(CustomBlock {
774            id,
775            placement: block.placement.clone(),
776            height: block.height,
777            style: block.style,
778            render: Arc::new(Mutex::new(block.render.clone())),
779            priority: block.priority,
780        });
781        self.custom_blocks.insert(block_ix, new_block.clone());
782        self.custom_blocks_by_id.insert(id, new_block);
783        id
784    }
785
786    // Warning: doesn't sync the block map, use advisedly
787    pub(crate) fn retain_blocks_raw(&mut self, pred: &mut dyn FnMut(&Arc<CustomBlock>) -> bool) {
788        let mut ids_to_remove = HashSet::default();
789        self.custom_blocks.retain(|block| {
790            let keep = pred(block);
791            if !keep {
792                ids_to_remove.insert(block.id);
793            }
794            keep
795        });
796        self.custom_blocks_by_id
797            .retain(|id, _| !ids_to_remove.contains(id));
798    }
799
800    // Warning: doesn't sync the block map, use advisedly
801    pub(crate) fn blocks_raw(&self) -> impl Iterator<Item = &Arc<CustomBlock>> {
802        self.custom_blocks.iter()
803    }
804
805    #[ztracing::instrument(skip_all, fields(edits = ?edits))]
806    fn sync(
807        &self,
808        wrap_snapshot: &WrapSnapshot,
809        mut edits: WrapPatch,
810        companion_view: Option<CompanionView>,
811    ) {
812        let buffer = wrap_snapshot.buffer_snapshot();
813
814        edits = self.deferred_edits.take().compose(edits);
815
816        let max_point = wrap_snapshot.max_point();
817
818        // Handle changing the last excerpt if it is empty.
819        if buffer.trailing_excerpt_update_count()
820            != self
821                .wrap_snapshot
822                .borrow()
823                .buffer_snapshot()
824                .trailing_excerpt_update_count()
825        {
826            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
827            let edit_end = max_point.row() + WrapRow(1); // this is end of file
828            edits = edits.compose([WrapEdit {
829                old: edit_start..edit_end,
830                new: edit_start..edit_end,
831            }]);
832        }
833
834        // Pull in companion edits to ensure we recompute spacers in ranges that have changed in the companion.
835        if let Some(CompanionView {
836            companion_wrap_snapshot: companion_new_snapshot,
837            companion_wrap_edits: companion_edits,
838            companion,
839            display_map_id,
840            ..
841        }) = companion_view
842        {
843            let mut companion_edits_in_my_space: Vec<WrapEdit> = companion_edits
844                .clone()
845                .into_inner()
846                .iter()
847                .map(|edit| {
848                    let companion_start = companion_new_snapshot
849                        .to_point(WrapPoint::new(edit.new.start, 0), Bias::Left);
850                    let companion_end = companion_new_snapshot
851                        .to_point(WrapPoint::new(edit.new.end, 0), Bias::Left);
852
853                    let my_start = companion
854                        .convert_point_from_companion(
855                            display_map_id,
856                            wrap_snapshot.buffer_snapshot(),
857                            companion_new_snapshot.buffer_snapshot(),
858                            companion_start,
859                        )
860                        .start;
861                    let my_end = companion
862                        .convert_point_from_companion(
863                            display_map_id,
864                            wrap_snapshot.buffer_snapshot(),
865                            companion_new_snapshot.buffer_snapshot(),
866                            companion_end,
867                        )
868                        .end;
869
870                    let mut my_start = wrap_snapshot.make_wrap_point(my_start, Bias::Left);
871                    let mut my_end = wrap_snapshot.make_wrap_point(my_end, Bias::Left);
872                    // TODO(split-diff) should use trailing_excerpt_update_count for the second case
873                    if my_end.column() > 0 || my_end == max_point {
874                        *my_end.row_mut() += 1;
875                        *my_end.column_mut() = 0;
876                    }
877
878                    // Empty edits won't survive Patch::compose, but we still need to make sure
879                    // we recompute spacers when we get them.
880                    if my_start.row() == my_end.row() {
881                        if my_end.row() <= max_point.row() {
882                            *my_end.row_mut() += 1;
883                            *my_end.column_mut() = 0;
884                        } else if my_start.row() > WrapRow(0) {
885                            *my_start.row_mut() += 1;
886                            *my_start.column_mut() = 0;
887                        }
888                    }
889
890                    WrapEdit {
891                        old: my_start.row()..my_end.row(),
892                        new: my_start.row()..my_end.row(),
893                    }
894                })
895                .collect();
896
897            companion_edits_in_my_space.sort_by_key(|edit| edit.old.start);
898            let mut merged_edits: Vec<WrapEdit> = Vec::new();
899            for edit in companion_edits_in_my_space {
900                if let Some(last) = merged_edits.last_mut() {
901                    if edit.old.start <= last.old.end {
902                        last.old.end = last.old.end.max(edit.old.end);
903                        last.new.end = last.new.end.max(edit.new.end);
904                        continue;
905                    }
906                }
907                merged_edits.push(edit);
908            }
909
910            edits = edits.compose(merged_edits);
911        }
912
913        let edits = edits.into_inner();
914        if edits.is_empty() {
915            return;
916        }
917
918        let mut transforms = self.transforms.borrow_mut();
919        let mut new_transforms = SumTree::default();
920        let mut cursor = transforms.cursor::<WrapRow>(());
921        let mut last_block_ix = 0;
922        let mut blocks_in_edit = Vec::new();
923        let mut edits = edits.into_iter().peekable();
924
925        let mut inlay_point_cursor = wrap_snapshot.inlay_point_cursor();
926        let mut tab_point_cursor = wrap_snapshot.tab_point_cursor();
927        let mut fold_point_cursor = wrap_snapshot.fold_point_cursor();
928        let mut wrap_point_cursor = wrap_snapshot.wrap_point_cursor();
929
930        while let Some(edit) = edits.next() {
931            let span = ztracing::debug_span!("while edits", edit = ?edit);
932            let _enter = span.enter();
933
934            let mut old_start = edit.old.start;
935            let mut new_start = edit.new.start;
936
937            // Only preserve transforms that:
938            // * Strictly precedes this edit
939            // * Isomorphic transforms that end *at* the start of the edit
940            // * Below blocks that end at the start of the edit
941            // However, if we hit a replace block that ends at the start of the edit we want to reconstruct it.
942            new_transforms.append(cursor.slice(&old_start, Bias::Left), ());
943            if let Some(transform) = cursor.item()
944                && transform.summary.input_rows > WrapRow(0)
945                && cursor.end() == old_start
946                && transform.block.as_ref().is_none_or(|b| !b.is_replacement())
947            {
948                // Preserve the transform (push and next)
949                new_transforms.push(transform.clone(), ());
950                cursor.next();
951
952                // Preserve below blocks at start of edit
953                while let Some(transform) = cursor.item() {
954                    if transform.block.as_ref().is_some_and(|b| b.place_below()) {
955                        new_transforms.push(transform.clone(), ());
956                        cursor.next();
957                    } else {
958                        break;
959                    }
960                }
961            }
962
963            // Ensure the edit starts at a transform boundary.
964            // If the edit starts within an isomorphic transform, preserve its prefix
965            // If the edit lands within a replacement block, expand the edit to include the start of the replaced input range
966            // The cursor can sit past the end of the tree when a companion edit
967            // is anchored at the new end-of-file and maps to `old_start` at the
968            // very end of the old transforms; in that case there is no transform
969            // preceding the edit and nothing to preserve.
970            let transform_rows_before_edit = old_start - *cursor.start();
971            if transform_rows_before_edit > RowDelta(0)
972                && let Some(transform) = cursor.item()
973            {
974                if transform.block.is_none() {
975                    // Preserve any portion of the old isomorphic transform that precedes this edit.
976                    push_isomorphic(
977                        &mut new_transforms,
978                        transform_rows_before_edit,
979                        wrap_snapshot,
980                    );
981                } else {
982                    // We landed within a block that replaces some lines, so we
983                    // extend the edit to start at the beginning of the
984                    // replacement.
985                    debug_assert!(transform.summary.input_rows > WrapRow(0));
986                    old_start -= transform_rows_before_edit;
987                    new_start -= transform_rows_before_edit;
988                }
989            }
990
991            // Decide where the edit ends
992            // * It should end at a transform boundary
993            // * Coalesce edits that intersect the same transform
994            let mut old_end = edit.old.end;
995            let mut new_end = edit.new.end;
996            loop {
997                let span = ztracing::debug_span!("decide where edit ends loop");
998                let _enter = span.enter();
999                // Seek to the transform starting at or after the end of the edit
1000                cursor.seek(&old_end, Bias::Left);
1001                cursor.next();
1002
1003                // Extend edit to the end of the discarded transform so it is reconstructed in full
1004                let transform_rows_after_edit = *cursor.start() - old_end;
1005                old_end += transform_rows_after_edit;
1006                new_end += transform_rows_after_edit;
1007
1008                // Combine this edit with any subsequent edits that intersect the same transform.
1009                while let Some(next_edit) = edits.peek() {
1010                    if next_edit.old.start <= *cursor.start() {
1011                        old_end = next_edit.old.end;
1012                        new_end = next_edit.new.end;
1013                        cursor.seek(&old_end, Bias::Left);
1014                        cursor.next();
1015                        edits.next();
1016                    } else {
1017                        break;
1018                    }
1019                }
1020
1021                if *cursor.start() == old_end {
1022                    break;
1023                }
1024            }
1025
1026            // Discard below blocks at the end of the edit. They'll be reconstructed.
1027            while let Some(transform) = cursor.item() {
1028                if transform
1029                    .block
1030                    .as_ref()
1031                    .is_some_and(|b| b.place_below() || matches!(b, Block::Spacer { .. }))
1032                {
1033                    cursor.next();
1034                } else {
1035                    break;
1036                }
1037            }
1038
1039            // Find the blocks within this edited region.
1040            let new_buffer_start = wrap_snapshot.to_point(WrapPoint::new(new_start, 0), Bias::Left);
1041            let start_bound = Bound::Included(new_buffer_start);
1042            let start_block_ix =
1043                match self.custom_blocks[last_block_ix..].binary_search_by(|probe| {
1044                    probe
1045                        .start()
1046                        .to_point(buffer)
1047                        .cmp(&new_buffer_start)
1048                        // Move left until we find the index of the first block starting within this edit
1049                        .then(Ordering::Greater)
1050                }) {
1051                    Ok(ix) | Err(ix) => last_block_ix + ix,
1052                };
1053
1054            let end_bound;
1055            let end_block_ix = if new_end > max_point.row() {
1056                end_bound = Bound::Unbounded;
1057                self.custom_blocks.len()
1058            } else {
1059                let new_buffer_end = wrap_snapshot.to_point(WrapPoint::new(new_end, 0), Bias::Left);
1060                end_bound = Bound::Excluded(new_buffer_end);
1061                match self.custom_blocks[start_block_ix..].binary_search_by(|probe| {
1062                    probe
1063                        .start()
1064                        .to_point(buffer)
1065                        .cmp(&new_buffer_end)
1066                        .then(Ordering::Greater)
1067                }) {
1068                    Ok(ix) | Err(ix) => start_block_ix + ix,
1069                }
1070            };
1071            last_block_ix = end_block_ix;
1072
1073            debug_assert!(blocks_in_edit.is_empty());
1074            // + 8 is chosen arbitrarily to cover some multibuffer headers
1075            blocks_in_edit
1076                .reserve(end_block_ix - start_block_ix + if buffer.is_singleton() { 0 } else { 8 });
1077
1078            blocks_in_edit.extend(
1079                self.custom_blocks[start_block_ix..end_block_ix]
1080                    .iter()
1081                    .filter_map(|block| {
1082                        let placement = block.placement.to_wrap_row(wrap_snapshot)?;
1083                        if !matches!(placement, BlockPlacement::Replace(_))
1084                            && wrap_snapshot.intersects_fold(Point::new(
1085                                block
1086                                    .placement
1087                                    .start()
1088                                    .to_point(wrap_snapshot.buffer_snapshot())
1089                                    .row,
1090                                0,
1091                            ))
1092                        {
1093                            return None;
1094                        }
1095                        if let BlockPlacement::Above(row) = placement
1096                            && row < new_start
1097                        {
1098                            return None;
1099                        }
1100                        Some((placement, Block::Custom(block.clone())))
1101                    }),
1102            );
1103
1104            blocks_in_edit.extend(self.header_and_footer_blocks(
1105                buffer,
1106                (start_bound, end_bound),
1107                |point, bias| {
1108                    wrap_point_cursor
1109                        .map(
1110                            tab_point_cursor.map(
1111                                fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias),
1112                            ),
1113                        )
1114                        .row()
1115                },
1116            ));
1117
1118            if let Some(CompanionView {
1119                companion_wrap_snapshot: companion_snapshot,
1120                companion,
1121                display_map_id,
1122                ..
1123            }) = companion_view
1124            {
1125                blocks_in_edit.extend(self.spacer_blocks(
1126                    (start_bound, end_bound),
1127                    wrap_snapshot,
1128                    companion_snapshot,
1129                    companion,
1130                    display_map_id,
1131                ));
1132            }
1133
1134            BlockMap::sort_blocks(&mut blocks_in_edit);
1135
1136            // For each of these blocks, insert a new isomorphic transform preceding the block,
1137            // and then insert the block itself.
1138            let mut just_processed_folded_buffer = false;
1139            for (block_placement, block) in blocks_in_edit.drain(..) {
1140                let span =
1141                    ztracing::debug_span!("for block in edits", block_height = block.height());
1142                let _enter = span.enter();
1143
1144                let mut summary = TransformSummary {
1145                    input_rows: WrapRow(0),
1146                    output_rows: BlockRow(block.height()),
1147                    longest_row: BlockRow(0),
1148                    longest_row_chars: 0,
1149                    has_replacement_blocks: false,
1150                };
1151
1152                let rows_before_block;
1153                let input_rows = new_transforms.summary().input_rows;
1154                match &block_placement {
1155                    &BlockPlacement::Above(position) => {
1156                        let Some(delta) = position.checked_sub(input_rows) else {
1157                            continue;
1158                        };
1159                        rows_before_block = delta;
1160                        just_processed_folded_buffer = false;
1161                    }
1162                    &BlockPlacement::Near(position) | &BlockPlacement::Below(position) => {
1163                        if just_processed_folded_buffer {
1164                            continue;
1165                        }
1166                        let Some(delta) = (position + RowDelta(1)).checked_sub(input_rows) else {
1167                            continue;
1168                        };
1169                        rows_before_block = delta;
1170                    }
1171                    BlockPlacement::Replace(range) => {
1172                        let Some(delta) = range.start().checked_sub(input_rows) else {
1173                            continue;
1174                        };
1175                        rows_before_block = delta;
1176                        summary.input_rows = WrapRow(1) + (*range.end() - *range.start());
1177                        just_processed_folded_buffer = matches!(block, Block::FoldedBuffer { .. });
1178                    }
1179                }
1180
1181                push_isomorphic(&mut new_transforms, rows_before_block, wrap_snapshot);
1182                new_transforms.push(
1183                    Transform {
1184                        summary,
1185                        block: Some(block),
1186                    },
1187                    (),
1188                );
1189            }
1190
1191            // Insert an isomorphic transform after the final block.
1192            let rows_after_last_block =
1193                RowDelta(new_end.0).saturating_sub(RowDelta(new_transforms.summary().input_rows.0));
1194            push_isomorphic(&mut new_transforms, rows_after_last_block, wrap_snapshot);
1195        }
1196
1197        new_transforms.append(cursor.suffix(), ());
1198        debug_assert_eq!(
1199            new_transforms.summary().input_rows,
1200            wrap_snapshot.max_point().row() + WrapRow(1),
1201        );
1202
1203        drop(cursor);
1204        *transforms = new_transforms;
1205    }
1206
1207    #[ztracing::instrument(skip_all)]
1208    pub fn replace_blocks(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) {
1209        for block in &mut self.custom_blocks {
1210            if let Some(render) = renderers.remove(&block.id) {
1211                *block.render.lock() = render;
1212            }
1213        }
1214    }
1215
1216    /// Guarantees that `wrap_row_for` is called with points in increasing order.
1217    #[ztracing::instrument(skip_all)]
1218    fn header_and_footer_blocks<'a, R, T>(
1219        &'a self,
1220        buffer: &'a multi_buffer::MultiBufferSnapshot,
1221        range: R,
1222        mut wrap_row_for: impl 'a + FnMut(Point, Bias) -> WrapRow,
1223    ) -> impl Iterator<Item = (BlockPlacement<WrapRow>, Block)> + 'a
1224    where
1225        R: RangeBounds<T>,
1226        T: multi_buffer::ToOffset,
1227    {
1228        let mut boundaries = buffer.excerpt_boundaries_in_range(range).peekable();
1229
1230        std::iter::from_fn(move || {
1231            loop {
1232                let excerpt_boundary = boundaries.next()?;
1233                let wrap_row = wrap_row_for(Point::new(excerpt_boundary.row.0, 0), Bias::Left);
1234
1235                let new_buffer_id = match (&excerpt_boundary.prev, &excerpt_boundary.next) {
1236                    (None, next) => Some(next.buffer_id()),
1237                    (Some(prev), next) => {
1238                        if prev.buffer_id() != next.buffer_id() {
1239                            Some(next.buffer_id())
1240                        } else {
1241                            None
1242                        }
1243                    }
1244                };
1245
1246                let mut height = 0;
1247
1248                if let Some(new_buffer_id) = new_buffer_id {
1249                    let first_excerpt = excerpt_boundary.next.clone();
1250                    if self.buffers_with_disabled_headers.contains(&new_buffer_id) {
1251                        continue;
1252                    }
1253                    if self.folded_buffers.contains(&new_buffer_id) && buffer.show_headers() {
1254                        let mut last_excerpt_end_row = first_excerpt.end_row;
1255
1256                        while let Some(next_boundary) = boundaries.peek() {
1257                            if next_boundary.next.buffer_id() == new_buffer_id {
1258                                last_excerpt_end_row = next_boundary.next.end_row;
1259                            } else {
1260                                break;
1261                            }
1262
1263                            boundaries.next();
1264                        }
1265                        let wrap_end_row = wrap_row_for(
1266                            Point::new(
1267                                last_excerpt_end_row.0,
1268                                buffer.line_len(last_excerpt_end_row),
1269                            ),
1270                            Bias::Right,
1271                        );
1272
1273                        return Some((
1274                            BlockPlacement::Replace(wrap_row..=wrap_end_row),
1275                            Block::FoldedBuffer {
1276                                height: height + self.buffer_header_height,
1277                                first_excerpt,
1278                            },
1279                        ));
1280                    }
1281                }
1282
1283                let starts_new_buffer = new_buffer_id.is_some();
1284                let block = if starts_new_buffer && buffer.show_headers() {
1285                    height += self.buffer_header_height;
1286                    Block::BufferHeader {
1287                        excerpt: excerpt_boundary.next,
1288                        height,
1289                    }
1290                } else if excerpt_boundary.prev.is_some() {
1291                    height += self.excerpt_header_height;
1292                    Block::ExcerptBoundary {
1293                        excerpt: excerpt_boundary.next,
1294                        height,
1295                    }
1296                } else {
1297                    continue;
1298                };
1299
1300                return Some((BlockPlacement::Above(wrap_row), block));
1301            }
1302        })
1303    }
1304
1305    fn spacer_blocks(
1306        &self,
1307        bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
1308        wrap_snapshot: &WrapSnapshot,
1309        companion_snapshot: &WrapSnapshot,
1310        companion: &Companion,
1311        display_map_id: EntityId,
1312    ) -> Vec<(BlockPlacement<WrapRow>, Block)> {
1313        let our_buffer = wrap_snapshot.buffer_snapshot();
1314        let companion_buffer = companion_snapshot.buffer_snapshot();
1315
1316        let range = match bounds {
1317            (Bound::Included(start), Bound::Excluded(end)) => start..end,
1318            (Bound::Included(start), Bound::Unbounded) => start..wrap_snapshot.buffer().max_point(),
1319            _ => unreachable!(),
1320        };
1321        let mut patches = companion.convert_rows_to_companion(
1322            display_map_id,
1323            companion_buffer,
1324            our_buffer,
1325            range,
1326        );
1327        if let Some(patch) = patches.last()
1328            && let Bound::Excluded(end) = bounds.1
1329            && end == wrap_snapshot.buffer().max_point()
1330            && patch.source_excerpt_range.is_empty()
1331        {
1332            patches.pop();
1333        }
1334
1335        let mut our_inlay_point_cursor = wrap_snapshot.inlay_point_cursor();
1336        let mut our_fold_point_cursor = wrap_snapshot.fold_point_cursor();
1337        let mut our_tab_point_cursor = wrap_snapshot.tab_point_cursor();
1338        let mut our_wrap_point_cursor = wrap_snapshot.wrap_point_cursor();
1339
1340        let mut our_wrapper = |our_point: Point, bias: Bias| {
1341            our_wrap_point_cursor
1342                .map(our_tab_point_cursor.map(
1343                    our_fold_point_cursor.map(our_inlay_point_cursor.map(our_point, bias), bias),
1344                ))
1345                .row()
1346        };
1347        let mut companion_wrapper = |their_point: Point, bias: Bias| {
1348            // TODO(split-diff) fix companion points being passed in decreasing order
1349            let inlay_point = companion_snapshot
1350                .inlay_snapshot
1351                .inlay_point_cursor()
1352                .map(their_point, bias);
1353            let fold_point = companion_snapshot.to_fold_point(inlay_point, bias);
1354            let tab_point = companion_snapshot.fold_point_to_tab_point(fold_point);
1355            companion_snapshot.tab_point_to_wrap_point(tab_point).row()
1356        };
1357        fn determine_spacer(
1358            our_wrapper: &mut dyn FnMut(Point, Bias) -> WrapRow,
1359            companion_wrapper: &mut dyn FnMut(Point, Bias) -> WrapRow,
1360            our_point: Point,
1361            their_point: Point,
1362            delta: i32,
1363            bias: Bias,
1364        ) -> (i32, Option<(WrapRow, u32)>) {
1365            let our_wrap = our_wrapper(our_point, bias);
1366            let companion_wrap = companion_wrapper(their_point, bias);
1367            let new_delta = companion_wrap.0 as i32 - our_wrap.0 as i32;
1368
1369            let spacer = if new_delta > delta {
1370                let height = (new_delta - delta) as u32;
1371                Some((our_wrap, height))
1372            } else {
1373                None
1374            };
1375            (new_delta, spacer)
1376        }
1377
1378        let mut result = Vec::new();
1379
1380        for excerpt in patches {
1381            let mut source_points = (excerpt.edited_range.start.row..=excerpt.edited_range.end.row)
1382                .map(|row| MultiBufferPoint::new(row, 0))
1383                .chain(if excerpt.edited_range.end.column > 0 {
1384                    Some(excerpt.edited_range.end)
1385                } else {
1386                    None
1387                })
1388                .peekable();
1389            let last_source_point = if excerpt.edited_range.end.column > 0 {
1390                excerpt.edited_range.end
1391            } else {
1392                MultiBufferPoint::new(excerpt.edited_range.end.row, 0)
1393            };
1394
1395            let Some(first_point) = source_points.peek().copied() else {
1396                continue;
1397            };
1398            let edit_for_first_point = excerpt.patch.edit_for_old_position(first_point);
1399
1400            // Because we calculate spacers based on differences in wrap row
1401            // counts between the RHS and LHS for corresponding buffer points,
1402            // we need to calibrate our expectations based on the difference
1403            // in counts before the start of the edit. This difference in
1404            // counts should have been balanced already by spacers above this
1405            // edit, so we only need to insert spacers for when the difference
1406            // in counts diverges from that baseline value.
1407            let (our_baseline, their_baseline) = if edit_for_first_point.old.start < first_point {
1408                // Case 1: We are inside a hunk/group--take the start of the hunk/group on both sides as the baseline.
1409                (
1410                    edit_for_first_point.old.start,
1411                    edit_for_first_point.new.start,
1412                )
1413            } else if first_point.row > excerpt.source_excerpt_range.start.row {
1414                // Case 2: We are not inside a hunk/group--go back by one row to find the baseline.
1415                let prev_point = Point::new(first_point.row - 1, 0);
1416                let edit_for_prev_point = excerpt.patch.edit_for_old_position(prev_point);
1417                (prev_point, edit_for_prev_point.new.end)
1418            } else {
1419                // Case 3: We are at the start of the excerpt--no previous row to use as the baseline.
1420                (first_point, edit_for_first_point.new.start)
1421            };
1422            let our_baseline = our_wrapper(our_baseline, Bias::Left);
1423            let their_baseline = companion_wrapper(
1424                their_baseline.min(excerpt.target_excerpt_range.end),
1425                Bias::Left,
1426            );
1427
1428            let mut delta = their_baseline.0 as i32 - our_baseline.0 as i32;
1429
1430            while let Some(source_point) = source_points.next() {
1431                let mut current_boundary = source_point;
1432                let current_edit = excerpt.patch.edit_for_old_position(current_boundary);
1433                let current_range = current_edit.new;
1434
1435                if current_boundary.column > 0 {
1436                    debug_assert_eq!(current_boundary, excerpt.source_excerpt_range.end);
1437                    break;
1438                }
1439
1440                if current_edit.old.start < current_boundary {
1441                    while let Some(next_point) = source_points.peek().copied() {
1442                        let edit_for_next_point = excerpt.patch.edit_for_old_position(next_point);
1443                        if edit_for_next_point.new.end > current_range.end {
1444                            break;
1445                        }
1446                        current_boundary = next_point;
1447                        source_points.next();
1448                    }
1449
1450                    let (new_delta, spacer) = determine_spacer(
1451                        &mut our_wrapper,
1452                        &mut companion_wrapper,
1453                        current_boundary,
1454                        current_range.end.min(excerpt.target_excerpt_range.end),
1455                        delta,
1456                        Bias::Left,
1457                    );
1458
1459                    delta = new_delta;
1460                    if let Some((wrap_row, height)) = spacer {
1461                        result.push((
1462                            BlockPlacement::Above(wrap_row),
1463                            Block::Spacer {
1464                                id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1465                                height,
1466                                is_below: false,
1467                            },
1468                        ));
1469                    }
1470                    continue;
1471                }
1472
1473                let (delta_at_start, mut spacer_at_start) = determine_spacer(
1474                    &mut our_wrapper,
1475                    &mut companion_wrapper,
1476                    current_boundary,
1477                    current_range.start.min(excerpt.target_excerpt_range.end),
1478                    delta,
1479                    Bias::Left,
1480                );
1481                delta = delta_at_start;
1482
1483                while let Some(next_point) = source_points.peek().copied() {
1484                    let edit_for_next_point = excerpt.patch.edit_for_old_position(next_point);
1485                    if edit_for_next_point.new.end > current_range.end {
1486                        break;
1487                    }
1488
1489                    if let Some((wrap_row, height)) = spacer_at_start.take() {
1490                        result.push((
1491                            BlockPlacement::Above(wrap_row),
1492                            Block::Spacer {
1493                                id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1494                                height,
1495                                is_below: false,
1496                            },
1497                        ));
1498                    }
1499
1500                    current_boundary = next_point;
1501                    source_points.next();
1502                }
1503
1504                if current_boundary.column > 0 {
1505                    debug_assert_eq!(current_boundary, excerpt.source_excerpt_range.end);
1506                    break;
1507                }
1508
1509                let edit_for_current_boundary =
1510                    excerpt.patch.edit_for_old_position(current_boundary);
1511
1512                let spacer_at_end = if current_boundary == edit_for_current_boundary.old.end {
1513                    let (delta_at_end, spacer_at_end) = determine_spacer(
1514                        &mut our_wrapper,
1515                        &mut companion_wrapper,
1516                        current_boundary,
1517                        current_range.end.min(excerpt.target_excerpt_range.end),
1518                        delta,
1519                        Bias::Left,
1520                    );
1521                    delta = delta_at_end;
1522                    spacer_at_end
1523                } else {
1524                    None
1525                };
1526
1527                if let Some((wrap_row, mut height)) = spacer_at_start {
1528                    if let Some((_, additional_height)) = spacer_at_end {
1529                        height += additional_height;
1530                    }
1531                    result.push((
1532                        BlockPlacement::Above(wrap_row),
1533                        Block::Spacer {
1534                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1535                            height,
1536                            is_below: false,
1537                        },
1538                    ));
1539                } else if let Some((wrap_row, height)) = spacer_at_end {
1540                    result.push((
1541                        BlockPlacement::Above(wrap_row),
1542                        Block::Spacer {
1543                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1544                            height,
1545                            is_below: false,
1546                        },
1547                    ));
1548                }
1549            }
1550
1551            if last_source_point == excerpt.source_excerpt_range.end {
1552                let (_new_delta, spacer) = determine_spacer(
1553                    &mut our_wrapper,
1554                    &mut companion_wrapper,
1555                    last_source_point,
1556                    excerpt.target_excerpt_range.end,
1557                    delta,
1558                    Bias::Right,
1559                );
1560                if let Some((wrap_row, height)) = spacer {
1561                    result.push((
1562                        BlockPlacement::Below(wrap_row),
1563                        Block::Spacer {
1564                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1565                            height,
1566                            is_below: true,
1567                        },
1568                    ));
1569                }
1570            }
1571        }
1572
1573        result
1574    }
1575
1576    #[ztracing::instrument(skip_all)]
1577    fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
1578        blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
1579            placement_a
1580                .start()
1581                .cmp(placement_b.start())
1582                .then_with(|| placement_b.end().cmp(placement_a.end()))
1583                .then_with(|| placement_a.tie_break().cmp(&placement_b.tie_break()))
1584                .then_with(|| {
1585                    if block_a.is_header() {
1586                        Ordering::Less
1587                    } else if block_b.is_header() {
1588                        Ordering::Greater
1589                    } else {
1590                        Ordering::Equal
1591                    }
1592                })
1593                .then_with(|| match (block_a, block_b) {
1594                    (
1595                        Block::ExcerptBoundary {
1596                            excerpt: excerpt_a, ..
1597                        }
1598                        | Block::BufferHeader {
1599                            excerpt: excerpt_a, ..
1600                        },
1601                        Block::ExcerptBoundary {
1602                            excerpt: excerpt_b, ..
1603                        }
1604                        | Block::BufferHeader {
1605                            excerpt: excerpt_b, ..
1606                        },
1607                    ) => Some(excerpt_a.start_text_anchor().opaque_id())
1608                        .cmp(&Some(excerpt_b.start_text_anchor().opaque_id())),
1609                    (
1610                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1611                        Block::Spacer { .. } | Block::Custom(_),
1612                    ) => Ordering::Less,
1613                    (
1614                        Block::Spacer { .. } | Block::Custom(_),
1615                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1616                    ) => Ordering::Greater,
1617                    (Block::Spacer { .. }, Block::Custom(_)) => Ordering::Less,
1618                    (Block::Custom(_), Block::Spacer { .. }) => Ordering::Greater,
1619                    (Block::Custom(block_a), Block::Custom(block_b)) => block_a
1620                        .priority
1621                        .cmp(&block_b.priority)
1622                        .then_with(|| block_a.id.cmp(&block_b.id)),
1623                    _ => {
1624                        unreachable!("comparing blocks: {block_a:?} vs {block_b:?}")
1625                    }
1626                })
1627        });
1628        blocks.dedup_by(|right, left| match (left.0.clone(), right.0.clone()) {
1629            (BlockPlacement::Replace(range), BlockPlacement::Above(row))
1630            | (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => range.contains(&row),
1631            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => {
1632                if range_a.end() >= range_b.start() && range_a.start() <= range_b.end() {
1633                    left.0 = BlockPlacement::Replace(
1634                        *range_a.start()..=*range_a.end().max(range_b.end()),
1635                    );
1636                    true
1637                } else {
1638                    false
1639                }
1640            }
1641            _ => false,
1642        });
1643    }
1644}
1645
1646#[ztracing::instrument(skip(tree, wrap_snapshot))]
1647fn push_isomorphic(tree: &mut SumTree<Transform>, rows: RowDelta, wrap_snapshot: &WrapSnapshot) {
1648    if rows == RowDelta(0) {
1649        return;
1650    }
1651
1652    let wrap_row_start = tree.summary().input_rows;
1653    let wrap_row_end = wrap_row_start + rows;
1654    let wrap_summary = wrap_snapshot.text_summary_for_range(wrap_row_start..wrap_row_end);
1655    let summary = TransformSummary {
1656        input_rows: WrapRow(rows.0),
1657        output_rows: BlockRow(rows.0),
1658        longest_row: BlockRow(wrap_summary.longest_row),
1659        longest_row_chars: wrap_summary.longest_row_chars,
1660        has_replacement_blocks: false,
1661    };
1662    let mut merged = false;
1663    tree.update_last(
1664        |last_transform| {
1665            if last_transform.block.is_none() {
1666                last_transform.summary.add_summary(&summary);
1667                merged = true;
1668            }
1669        },
1670        (),
1671    );
1672    if !merged {
1673        tree.push(
1674            Transform {
1675                summary,
1676                block: None,
1677            },
1678            (),
1679        );
1680    }
1681}
1682
1683impl BlockPoint {
1684    pub fn new(row: BlockRow, column: u32) -> Self {
1685        Self(Point::new(row.0, column))
1686    }
1687}
1688
1689impl Deref for BlockPoint {
1690    type Target = Point;
1691
1692    fn deref(&self) -> &Self::Target {
1693        &self.0
1694    }
1695}
1696
1697impl std::ops::DerefMut for BlockPoint {
1698    fn deref_mut(&mut self) -> &mut Self::Target {
1699        &mut self.0
1700    }
1701}
1702
1703impl Deref for BlockMapReader<'_> {
1704    type Target = BlockSnapshot;
1705
1706    fn deref(&self) -> &Self::Target {
1707        &self.snapshot
1708    }
1709}
1710
1711impl DerefMut for BlockMapReader<'_> {
1712    fn deref_mut(&mut self) -> &mut Self::Target {
1713        &mut self.snapshot
1714    }
1715}
1716
1717impl BlockMapReader<'_> {
1718    #[ztracing::instrument(skip_all)]
1719    pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
1720        let block = self.blocks.iter().find(|block| block.id == block_id)?;
1721        let buffer_row = block
1722            .start()
1723            .to_point(self.wrap_snapshot.buffer_snapshot())
1724            .row;
1725        let wrap_row = self
1726            .wrap_snapshot
1727            .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
1728            .row();
1729        let start_wrap_row = self
1730            .wrap_snapshot
1731            .prev_row_boundary(WrapPoint::new(wrap_row, 0));
1732        let end_wrap_row = self
1733            .wrap_snapshot
1734            .next_row_boundary(WrapPoint::new(wrap_row, 0))
1735            .unwrap_or(self.wrap_snapshot.max_point().row() + WrapRow(1));
1736
1737        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
1738        cursor.seek(&start_wrap_row, Bias::Left);
1739        while let Some(transform) = cursor.item() {
1740            if cursor.start().0 > end_wrap_row {
1741                break;
1742            }
1743
1744            if let Some(BlockId::Custom(id)) = transform.block.as_ref().map(|block| block.id())
1745                && id == block_id
1746            {
1747                return Some(cursor.start().1);
1748            }
1749            cursor.next();
1750        }
1751
1752        None
1753    }
1754}
1755
1756pub(crate) fn balancing_block(
1757    my_block: &BlockProperties<Anchor>,
1758    my_snapshot: &MultiBufferSnapshot,
1759    their_snapshot: &MultiBufferSnapshot,
1760    my_display_map_id: EntityId,
1761    companion: &Companion,
1762) -> Option<BlockProperties<Anchor>> {
1763    let my_anchor = my_block.placement.start();
1764    let my_point = my_anchor.to_point(&my_snapshot);
1765    let their_range = companion.convert_point_to_companion(
1766        my_display_map_id,
1767        my_snapshot,
1768        their_snapshot,
1769        my_point,
1770    );
1771    let their_anchor = their_snapshot.anchor_at(their_range.start, my_anchor.bias());
1772    let their_placement = match my_block.placement {
1773        BlockPlacement::Above(_) => BlockPlacement::Above(their_anchor),
1774        BlockPlacement::Below(_) => {
1775            if their_range.is_empty() {
1776                BlockPlacement::Above(their_anchor)
1777            } else {
1778                BlockPlacement::Below(their_anchor)
1779            }
1780        }
1781        // Not supported for balancing
1782        BlockPlacement::Near(_) | BlockPlacement::Replace(_) => return None,
1783    };
1784    Some(BlockProperties {
1785        placement: their_placement,
1786        height: my_block.height,
1787        style: BlockStyle::Spacer,
1788        render: Arc::new(move |cx| {
1789            crate::EditorElement::render_spacer_block(
1790                cx.block_id,
1791                cx.height,
1792                cx.line_height,
1793                cx.indent_guide_padding,
1794                cx.window,
1795                cx.app,
1796            )
1797        }),
1798        priority: my_block.priority,
1799    })
1800}
1801
1802impl BlockMapWriterCompanion<'_> {
1803    fn companion_view(&self) -> CompanionView<'_> {
1804        static EMPTY_PATCH: Patch<WrapRow> = Patch::empty();
1805        CompanionView {
1806            display_map_id: self.display_map_id,
1807            companion_wrap_snapshot: &self.companion_wrap_snapshot,
1808            companion_wrap_edits: &EMPTY_PATCH,
1809            companion: self.companion,
1810        }
1811    }
1812}
1813
1814impl BlockMapWriter<'_> {
1815    #[ztracing::instrument(skip_all)]
1816    pub fn insert(
1817        &mut self,
1818        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
1819    ) -> Vec<CustomBlockId> {
1820        let blocks = blocks.into_iter();
1821        let mut ids = Vec::with_capacity(blocks.size_hint().1.unwrap_or(0));
1822        let mut edits = Patch::default();
1823        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
1824        let buffer = wrap_snapshot.buffer_snapshot();
1825
1826        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1827        let mut companion_blocks = Vec::new();
1828        for block in blocks {
1829            if let BlockPlacement::Replace(_) = &block.placement {
1830                debug_assert!(block.height.unwrap() > 0);
1831            }
1832
1833            let id = self.block_map.insert_block_raw(block.clone(), &buffer);
1834            ids.push(id);
1835
1836            let start = block.placement.start().to_point(&buffer);
1837            let end = block.placement.end().to_point(&buffer);
1838            let start_wrap_row = wrap_snapshot
1839                .make_wrap_point(Point::new(start.row, 0), Bias::Left)
1840                .row();
1841            let end_wrap_row = wrap_snapshot
1842                .make_wrap_point(Point::new(end.row, 0), Bias::Left)
1843                .row();
1844
1845            let (start_row, end_row) = {
1846                previous_wrap_row_range.take_if(|range| {
1847                    !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1848                });
1849                let range = previous_wrap_row_range.get_or_insert_with(|| {
1850                    let start_row =
1851                        wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1852                    let end_row = wrap_snapshot
1853                        .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1854                        .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1855                    start_row..end_row
1856                });
1857                (range.start, range.end)
1858            };
1859
1860            // Insert a matching custom block in the companion (if any)
1861            if let Some(companion) = &mut self.companion
1862                && companion.inverse.is_some()
1863            {
1864                companion_blocks.extend(balancing_block(
1865                    &block,
1866                    &buffer,
1867                    companion.companion_wrap_snapshot.buffer(),
1868                    companion.display_map_id,
1869                    companion.companion,
1870                ));
1871            }
1872
1873            edits = edits.compose([Edit {
1874                old: start_row..end_row,
1875                new: start_row..end_row,
1876            }]);
1877        }
1878
1879        self.block_map.sync(
1880            &wrap_snapshot,
1881            edits,
1882            self.companion
1883                .as_ref()
1884                .map(BlockMapWriterCompanion::companion_view),
1885        );
1886
1887        if let Some(companion) = &mut self.companion
1888            && let Some(inverse) = &mut companion.inverse
1889        {
1890            let companion_ids = inverse.companion_writer.insert(companion_blocks);
1891            companion
1892                .companion
1893                .custom_block_to_balancing_block(companion.display_map_id)
1894                .borrow_mut()
1895                .extend(ids.iter().copied().zip(companion_ids));
1896        }
1897
1898        ids
1899    }
1900
1901    #[ztracing::instrument(skip_all)]
1902    pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
1903        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
1904        let buffer = wrap_snapshot.buffer_snapshot();
1905        let mut edits = Patch::default();
1906        let mut last_block_buffer_row = None;
1907
1908        let mut companion_heights = HashMap::default();
1909        for block in &mut self.block_map.custom_blocks {
1910            if let Some(new_height) = heights.remove(&block.id) {
1911                if let BlockPlacement::Replace(_) = &block.placement {
1912                    debug_assert!(new_height > 0);
1913                }
1914
1915                if block.height != Some(new_height) {
1916                    let new_block = CustomBlock {
1917                        id: block.id,
1918                        placement: block.placement.clone(),
1919                        height: Some(new_height),
1920                        style: block.style,
1921                        render: block.render.clone(),
1922                        priority: block.priority,
1923                    };
1924                    let new_block = Arc::new(new_block);
1925                    *block = new_block.clone();
1926                    self.block_map
1927                        .custom_blocks_by_id
1928                        .insert(block.id, new_block);
1929
1930                    if let Some(companion) = &self.companion
1931                        && companion.inverse.is_some()
1932                        && let Some(companion_block_id) = companion
1933                            .companion
1934                            .custom_block_to_balancing_block(companion.display_map_id)
1935                            .borrow()
1936                            .get(&block.id)
1937                            .copied()
1938                    {
1939                        companion_heights.insert(companion_block_id, new_height);
1940                    }
1941
1942                    let start_row = block.placement.start().to_point(buffer).row;
1943                    let end_row = block.placement.end().to_point(buffer).row;
1944                    if last_block_buffer_row != Some(end_row) {
1945                        last_block_buffer_row = Some(end_row);
1946                        let start_wrap_row = wrap_snapshot
1947                            .make_wrap_point(Point::new(start_row, 0), Bias::Left)
1948                            .row();
1949                        let end_wrap_row = wrap_snapshot
1950                            .make_wrap_point(Point::new(end_row, 0), Bias::Left)
1951                            .row();
1952                        let start =
1953                            wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1954                        let end = wrap_snapshot
1955                            .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1956                            .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1957                        edits.push(Edit {
1958                            old: start..end,
1959                            new: start..end,
1960                        })
1961                    }
1962                }
1963            }
1964        }
1965
1966        self.block_map.sync(
1967            &wrap_snapshot,
1968            edits,
1969            self.companion
1970                .as_ref()
1971                .map(BlockMapWriterCompanion::companion_view),
1972        );
1973        if let Some(companion) = &mut self.companion
1974            && let Some(inverse) = &mut companion.inverse
1975        {
1976            inverse.companion_writer.resize(companion_heights);
1977        }
1978    }
1979
1980    #[ztracing::instrument(skip_all)]
1981    pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
1982        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1983        let buffer = wrap_snapshot.buffer_snapshot();
1984        let mut edits = Patch::default();
1985        let mut last_block_buffer_row = None;
1986        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1987        let mut companion_block_ids: HashSet<CustomBlockId> = HashSet::default();
1988        self.block_map.custom_blocks.retain(|block| {
1989            if block_ids.contains(&block.id) {
1990                let start = block.placement.start().to_point(buffer);
1991                let end = block.placement.end().to_point(buffer);
1992                if last_block_buffer_row != Some(end.row) {
1993                    last_block_buffer_row = Some(end.row);
1994                    let start_wrap_row = wrap_snapshot
1995                        .make_wrap_point(Point::new(start.row, 0), Bias::Left)
1996                        .row();
1997                    let end_wrap_row = wrap_snapshot
1998                        .make_wrap_point(Point::new(end.row, 0), Bias::Left)
1999                        .row();
2000                    let (start_row, end_row) = {
2001                        previous_wrap_row_range.take_if(|range| {
2002                            !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
2003                        });
2004                        let range = previous_wrap_row_range.get_or_insert_with(|| {
2005                            let start_row =
2006                                wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
2007                            let end_row = wrap_snapshot
2008                                .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
2009                                .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
2010                            start_row..end_row
2011                        });
2012                        (range.start, range.end)
2013                    };
2014
2015                    edits.push(Edit {
2016                        old: start_row..end_row,
2017                        new: start_row..end_row,
2018                    })
2019                }
2020                if let Some(companion) = &self.companion
2021                    && companion.inverse.is_some()
2022                {
2023                    companion_block_ids.extend(
2024                        companion
2025                            .companion
2026                            .custom_block_to_balancing_block(companion.display_map_id)
2027                            .borrow()
2028                            .get(&block.id)
2029                            .copied(),
2030                    );
2031                }
2032                false
2033            } else {
2034                true
2035            }
2036        });
2037        self.block_map
2038            .custom_blocks_by_id
2039            .retain(|id, _| !block_ids.contains(id));
2040
2041        self.block_map.sync(
2042            wrap_snapshot,
2043            edits,
2044            self.companion
2045                .as_ref()
2046                .map(BlockMapWriterCompanion::companion_view),
2047        );
2048        if let Some(companion) = &mut self.companion
2049            && let Some(inverse) = &mut companion.inverse
2050        {
2051            companion
2052                .companion
2053                .custom_block_to_balancing_block(companion.display_map_id)
2054                .borrow_mut()
2055                .retain(|id, _| !block_ids.contains(&id));
2056            inverse.companion_writer.remove(companion_block_ids);
2057        }
2058    }
2059
2060    #[ztracing::instrument(skip_all)]
2061    pub fn remove_intersecting_replace_blocks(
2062        &mut self,
2063        ranges: impl IntoIterator<Item = Range<MultiBufferOffset>>,
2064        inclusive: bool,
2065    ) {
2066        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
2067        let mut blocks_to_remove = HashSet::default();
2068        for range in ranges {
2069            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
2070                if matches!(block.placement, BlockPlacement::Replace(_)) {
2071                    blocks_to_remove.insert(block.id);
2072                }
2073            }
2074        }
2075        drop(wrap_snapshot);
2076        self.remove(blocks_to_remove);
2077    }
2078
2079    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId) {
2080        self.block_map
2081            .buffers_with_disabled_headers
2082            .insert(buffer_id);
2083    }
2084
2085    #[ztracing::instrument(skip_all)]
2086    pub fn fold_buffers(
2087        &mut self,
2088        buffer_ids: impl IntoIterator<Item = BufferId>,
2089        multi_buffer: &MultiBuffer,
2090        cx: &App,
2091    ) {
2092        self.fold_or_unfold_buffers(true, buffer_ids, multi_buffer, cx);
2093    }
2094
2095    #[ztracing::instrument(skip_all)]
2096    pub fn unfold_buffers(
2097        &mut self,
2098        buffer_ids: impl IntoIterator<Item = BufferId>,
2099        multi_buffer: &MultiBuffer,
2100        cx: &App,
2101    ) {
2102        self.fold_or_unfold_buffers(false, buffer_ids, multi_buffer, cx);
2103    }
2104
2105    #[ztracing::instrument(skip_all)]
2106    fn fold_or_unfold_buffers(
2107        &mut self,
2108        fold: bool,
2109        buffer_ids: impl IntoIterator<Item = BufferId>,
2110        multi_buffer: &MultiBuffer,
2111        cx: &App,
2112    ) {
2113        let multi_buffer_snapshot = multi_buffer.snapshot(cx);
2114        let mut ranges = Vec::new();
2115        let mut companion_buffer_ids = HashSet::default();
2116        for buffer_id in buffer_ids {
2117            if fold {
2118                self.block_map.folded_buffers.insert(buffer_id);
2119            } else {
2120                self.block_map.folded_buffers.remove(&buffer_id);
2121            }
2122            ranges.extend(multi_buffer_snapshot.range_for_buffer(buffer_id));
2123            if let Some(companion) = &self.companion
2124                && companion.inverse.is_some()
2125            {
2126                if let Some(diff) = multi_buffer_snapshot.diff_for_buffer_id(buffer_id) {
2127                    let companion_buffer_id =
2128                        if companion.companion.is_rhs(companion.display_map_id) {
2129                            diff.base_text().remote_id()
2130                        } else {
2131                            diff.buffer_id()
2132                        };
2133                    companion_buffer_ids.insert(companion_buffer_id);
2134                }
2135            }
2136        }
2137        ranges.sort_unstable_by_key(|range| range.start);
2138
2139        let mut edits = Patch::default();
2140        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
2141        for range in ranges {
2142            let last_edit_row = cmp::min(
2143                wrap_snapshot.make_wrap_point(range.end, Bias::Right).row() + WrapRow(1),
2144                wrap_snapshot.max_point().row(),
2145            ) + WrapRow(1);
2146            let range = wrap_snapshot.make_wrap_point(range.start, Bias::Left).row()..last_edit_row;
2147            edits.push(Edit {
2148                old: range.clone(),
2149                new: range,
2150            });
2151        }
2152
2153        self.block_map.sync(
2154            &wrap_snapshot,
2155            edits.clone(),
2156            self.companion
2157                .as_ref()
2158                .map(BlockMapWriterCompanion::companion_view),
2159        );
2160        if let Some(companion) = &mut self.companion
2161            && let Some(inverse) = &mut companion.inverse
2162        {
2163            inverse.companion_writer.fold_or_unfold_buffers(
2164                fold,
2165                companion_buffer_ids,
2166                inverse.companion_multibuffer,
2167                cx,
2168            );
2169        }
2170    }
2171
2172    #[ztracing::instrument(skip_all)]
2173    fn blocks_intersecting_buffer_range(
2174        &self,
2175        range: Range<MultiBufferOffset>,
2176        inclusive: bool,
2177    ) -> &[Arc<CustomBlock>] {
2178        if range.is_empty() && !inclusive {
2179            return &[];
2180        }
2181        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
2182        let buffer = wrap_snapshot.buffer_snapshot();
2183
2184        let start_block_ix = match self.block_map.custom_blocks.binary_search_by(|block| {
2185            let block_end = block.end().to_offset(buffer);
2186            block_end.cmp(&range.start).then(Ordering::Greater)
2187        }) {
2188            Ok(ix) | Err(ix) => ix,
2189        };
2190        let end_block_ix =
2191            match self.block_map.custom_blocks[start_block_ix..].binary_search_by(|block| {
2192                let block_start = block.start().to_offset(buffer);
2193                block_start.cmp(&range.end).then(if inclusive {
2194                    Ordering::Less
2195                } else {
2196                    Ordering::Greater
2197                })
2198            }) {
2199                Ok(ix) | Err(ix) => ix,
2200            };
2201
2202        &self.block_map.custom_blocks[start_block_ix..][..end_block_ix]
2203    }
2204}
2205
2206impl BlockSnapshot {
2207    #[inline(always)]
2208    pub fn has_replacement_blocks(&self) -> bool {
2209        self.transforms.summary().has_replacement_blocks
2210    }
2211
2212    #[cfg(test)]
2213    #[ztracing::instrument(skip_all)]
2214    pub fn text(&self) -> String {
2215        self.chunks(
2216            BlockRow(0)..self.transforms.summary().output_rows,
2217            LanguageAwareStyling {
2218                tree_sitter: false,
2219                diagnostics: false,
2220            },
2221            false,
2222            Highlights::default(),
2223        )
2224        .map(|chunk| chunk.text)
2225        .collect()
2226    }
2227
2228    #[ztracing::instrument(skip_all)]
2229    pub(crate) fn chunks<'a>(
2230        &'a self,
2231        rows: Range<BlockRow>,
2232        language_aware: LanguageAwareStyling,
2233        masked: bool,
2234        highlights: Highlights<'a>,
2235    ) -> BlockChunks<'a> {
2236        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
2237
2238        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2239        cursor.seek(&rows.start, Bias::Right);
2240        let transform_output_start = cursor.start().0;
2241        let transform_input_start = cursor.start().1;
2242
2243        let mut input_start = transform_input_start;
2244        let mut input_end = transform_input_start;
2245        if let Some(transform) = cursor.item()
2246            && transform.block.is_none()
2247        {
2248            input_start += rows.start - transform_output_start;
2249            input_end += cmp::min(
2250                rows.end - transform_output_start,
2251                RowDelta(transform.summary.input_rows.0),
2252            );
2253        }
2254
2255        BlockChunks {
2256            input_chunks: self.wrap_snapshot.chunks(
2257                input_start..input_end,
2258                language_aware,
2259                highlights,
2260            ),
2261            input_chunk: Default::default(),
2262            transforms: cursor,
2263            output_row: rows.start,
2264            line_count_overflow: RowDelta(0),
2265            max_output_row,
2266            masked,
2267        }
2268    }
2269
2270    #[ztracing::instrument(skip_all)]
2271    pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
2272        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2273        cursor.seek(&start_row, Bias::Right);
2274        let Dimensions(output_start, input_start, _) = cursor.start();
2275        let overshoot = if cursor
2276            .item()
2277            .is_some_and(|transform| transform.block.is_none())
2278        {
2279            start_row - *output_start
2280        } else {
2281            RowDelta(0)
2282        };
2283        let input_start_row = *input_start + overshoot;
2284        BlockRows {
2285            transforms: cursor,
2286            input_rows: self.wrap_snapshot.row_infos(input_start_row),
2287            output_row: start_row,
2288            started: false,
2289        }
2290    }
2291
2292    #[ztracing::instrument(skip_all)]
2293    pub fn blocks_in_range(
2294        &self,
2295        rows: Range<BlockRow>,
2296    ) -> impl Iterator<Item = (BlockRow, &Block)> {
2297        let mut cursor = self.transforms.cursor::<BlockRow>(());
2298        cursor.seek(&rows.start, Bias::Left);
2299        while *cursor.start() < rows.start && cursor.end() <= rows.start {
2300            cursor.next();
2301        }
2302
2303        std::iter::from_fn(move || {
2304            while let Some(transform) = cursor.item() {
2305                let start_row = *cursor.start();
2306                if start_row > rows.end
2307                    || (start_row == rows.end
2308                        && transform
2309                            .block
2310                            .as_ref()
2311                            .is_some_and(|block| block.height() > 0))
2312                {
2313                    break;
2314                }
2315                if let Some(block) = &transform.block {
2316                    cursor.next();
2317                    return Some((start_row, block));
2318                } else {
2319                    cursor.next();
2320                }
2321            }
2322            None
2323        })
2324    }
2325
2326    #[ztracing::instrument(skip_all)]
2327    pub(crate) fn sticky_header_excerpt(&self, position: f64) -> Option<StickyHeaderExcerpt<'_>> {
2328        let top_row = position as u32;
2329        let mut cursor = self.transforms.cursor::<BlockRow>(());
2330        cursor.seek(&BlockRow(top_row), Bias::Right);
2331
2332        while let Some(transform) = cursor.item() {
2333            match &transform.block {
2334                Some(
2335                    Block::ExcerptBoundary { excerpt, .. } | Block::BufferHeader { excerpt, .. },
2336                ) => {
2337                    return Some(StickyHeaderExcerpt { excerpt });
2338                }
2339                Some(block) if block.is_buffer_header() => return None,
2340                _ => {
2341                    cursor.prev();
2342                    continue;
2343                }
2344            }
2345        }
2346
2347        None
2348    }
2349
2350    #[ztracing::instrument(skip_all)]
2351    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
2352        let buffer = self.wrap_snapshot.buffer_snapshot();
2353        let wrap_point = match block_id {
2354            BlockId::Custom(custom_block_id) => {
2355                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
2356                return Some(Block::Custom(custom_block.clone()));
2357            }
2358            BlockId::ExcerptBoundary(start_anchor) => {
2359                let start_point = start_anchor.to_point(&buffer);
2360                self.wrap_snapshot.make_wrap_point(start_point, Bias::Left)
2361            }
2362            BlockId::FoldedBuffer(buffer_id) => self.wrap_snapshot.make_wrap_point(
2363                buffer
2364                    .anchor_in_excerpt(buffer.excerpts_for_buffer(buffer_id).next()?.context.start)?
2365                    .to_point(buffer),
2366                Bias::Left,
2367            ),
2368            BlockId::Spacer(_) => return None,
2369        };
2370        let wrap_row = wrap_point.row();
2371
2372        let mut cursor = self.transforms.cursor::<WrapRow>(());
2373        cursor.seek(&wrap_row, Bias::Left);
2374
2375        while let Some(transform) = cursor.item() {
2376            if let Some(block) = transform.block.as_ref() {
2377                if block.id() == block_id {
2378                    return Some(block.clone());
2379                }
2380            } else if *cursor.start() > wrap_row {
2381                break;
2382            }
2383
2384            cursor.next();
2385        }
2386
2387        None
2388    }
2389
2390    #[ztracing::instrument(skip_all)]
2391    pub fn max_point(&self) -> BlockPoint {
2392        let row = self
2393            .transforms
2394            .summary()
2395            .output_rows
2396            .saturating_sub(RowDelta(1));
2397        BlockPoint::new(row, self.line_len(row))
2398    }
2399
2400    #[ztracing::instrument(skip_all)]
2401    pub fn longest_row(&self) -> BlockRow {
2402        self.transforms.summary().longest_row
2403    }
2404
2405    #[ztracing::instrument(skip_all)]
2406    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
2407        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2408        cursor.seek(&range.start, Bias::Right);
2409
2410        let mut longest_row = range.start;
2411        let mut longest_row_chars = 0;
2412        if let Some(transform) = cursor.item() {
2413            if transform.block.is_none() {
2414                let &Dimensions(output_start, input_start, _) = cursor.start();
2415                let overshoot = range.start - output_start;
2416                let wrap_start_row = input_start + WrapRow(overshoot.0);
2417                let wrap_end_row = cmp::min(
2418                    input_start + WrapRow((range.end - output_start).0),
2419                    cursor.end().1,
2420                );
2421                let summary = self
2422                    .wrap_snapshot
2423                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2424                longest_row = BlockRow(range.start.0 + summary.longest_row);
2425                longest_row_chars = summary.longest_row_chars;
2426            }
2427            cursor.next();
2428        }
2429
2430        let cursor_start_row = cursor.start().0;
2431        if range.end > cursor_start_row {
2432            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right);
2433            if summary.longest_row_chars > longest_row_chars {
2434                longest_row = cursor_start_row + summary.longest_row;
2435                longest_row_chars = summary.longest_row_chars;
2436            }
2437
2438            if let Some(transform) = cursor.item()
2439                && transform.block.is_none()
2440            {
2441                let &Dimensions(output_start, input_start, _) = cursor.start();
2442                let overshoot = range.end - output_start;
2443                let wrap_start_row = input_start;
2444                let wrap_end_row = input_start + overshoot;
2445                let summary = self
2446                    .wrap_snapshot
2447                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2448                if summary.longest_row_chars > longest_row_chars {
2449                    longest_row = output_start + RowDelta(summary.longest_row);
2450                }
2451            }
2452        }
2453
2454        longest_row
2455    }
2456
2457    #[ztracing::instrument(skip_all)]
2458    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
2459        let (start, _, item) =
2460            self.transforms
2461                .find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
2462        if let Some(transform) = item {
2463            let Dimensions(output_start, input_start, _) = start;
2464            let overshoot = row - output_start;
2465            if transform.block.is_some() {
2466                0
2467            } else {
2468                self.wrap_snapshot.line_len(input_start + overshoot)
2469            }
2470        } else if row == BlockRow(0) {
2471            0
2472        } else {
2473            panic!("row out of range");
2474        }
2475    }
2476
2477    #[ztracing::instrument(skip_all)]
2478    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
2479        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2480        item.is_some_and(|t| t.block.is_some())
2481    }
2482
2483    #[ztracing::instrument(skip_all)]
2484    pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
2485        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2486        let Some(transform) = item else {
2487            return false;
2488        };
2489        matches!(transform.block, Some(Block::FoldedBuffer { .. }))
2490    }
2491
2492    #[ztracing::instrument(skip_all)]
2493    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
2494        let wrap_point = self
2495            .wrap_snapshot
2496            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
2497        let (_, _, item) = self
2498            .transforms
2499            .find::<WrapRow, _>((), &wrap_point.row(), Bias::Right);
2500        item.is_some_and(|transform| {
2501            transform
2502                .block
2503                .as_ref()
2504                .is_some_and(|block| block.is_replacement())
2505        })
2506    }
2507
2508    #[ztracing::instrument(skip_all)]
2509    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
2510        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2511        cursor.seek(&BlockRow(point.row), Bias::Right);
2512
2513        let max_input_row = self.transforms.summary().input_rows;
2514        let mut search_left = (bias == Bias::Left && cursor.start().1 > WrapRow(0))
2515            || cursor.end().1 == max_input_row;
2516        let mut reversed = false;
2517
2518        loop {
2519            if let Some(transform) = cursor.item() {
2520                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
2521                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
2522                let output_start = Point::new(output_start_row.0, 0);
2523                let input_start = Point::new(input_start_row.0, 0);
2524                let input_end = Point::new(input_end_row.0, 0);
2525
2526                match transform.block.as_ref() {
2527                    Some(block) => {
2528                        if block.is_replacement()
2529                            && (((bias == Bias::Left || search_left) && output_start <= point.0)
2530                                || (!search_left && output_start >= point.0))
2531                        {
2532                            return BlockPoint(output_start);
2533                        }
2534                    }
2535                    None => {
2536                        let input_point = if point.row >= output_end_row.0 {
2537                            let line_len = self.wrap_snapshot.line_len(input_end_row - RowDelta(1));
2538                            self.wrap_snapshot.clip_point(
2539                                WrapPoint::new(input_end_row - RowDelta(1), line_len),
2540                                bias,
2541                            )
2542                        } else {
2543                            let output_overshoot = point.0.saturating_sub(output_start);
2544                            self.wrap_snapshot
2545                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
2546                        };
2547
2548                        if (input_start..input_end).contains(&input_point.0) {
2549                            let input_overshoot = input_point.0.saturating_sub(input_start);
2550                            return BlockPoint(output_start + input_overshoot);
2551                        }
2552                    }
2553                }
2554
2555                if search_left {
2556                    cursor.prev();
2557                } else {
2558                    cursor.next();
2559                }
2560            } else if reversed {
2561                return self.max_point();
2562            } else {
2563                reversed = true;
2564                search_left = !search_left;
2565                cursor.seek(&BlockRow(point.row), Bias::Right);
2566            }
2567        }
2568    }
2569
2570    pub fn block_point_cursor(&self) -> BlockPointCursor<'_> {
2571        BlockPointCursor {
2572            snapshot: self,
2573            cursor: self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(()),
2574        }
2575    }
2576
2577    #[ztracing::instrument(skip_all)]
2578    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
2579        let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
2580            (),
2581            &wrap_point.row(),
2582            Bias::Right,
2583        );
2584        if let Some(transform) = item {
2585            if transform.block.is_some() {
2586                BlockPoint::new(start.1, 0)
2587            } else {
2588                let Dimensions(input_start_row, output_start_row, _) = start;
2589                let input_start = Point::new(input_start_row.0, 0);
2590                let output_start = Point::new(output_start_row.0, 0);
2591                let input_overshoot = wrap_point.0 - input_start;
2592                BlockPoint(output_start + input_overshoot)
2593            }
2594        } else {
2595            self.max_point()
2596        }
2597    }
2598
2599    #[ztracing::instrument(skip_all)]
2600    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
2601        let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
2602            (),
2603            &BlockRow(block_point.row),
2604            Bias::Right,
2605        );
2606        if let Some(transform) = item {
2607            match transform.block.as_ref() {
2608                Some(block) => {
2609                    if block.place_below() {
2610                        let wrap_row = start.1 - RowDelta(1);
2611                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2612                    } else if block.place_above() {
2613                        WrapPoint::new(start.1, 0)
2614                    } else if bias == Bias::Left {
2615                        WrapPoint::new(start.1, 0)
2616                    } else {
2617                        let wrap_row = end.1 - RowDelta(1);
2618                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2619                    }
2620                }
2621                None => {
2622                    let overshoot = block_point.row() - start.0;
2623                    let wrap_row = start.1 + RowDelta(overshoot.0);
2624                    WrapPoint::new(wrap_row, block_point.column)
2625                }
2626            }
2627        } else {
2628            self.wrap_snapshot.max_point()
2629        }
2630    }
2631}
2632
2633impl BlockChunks<'_> {
2634    /// Go to the next transform
2635    #[ztracing::instrument(skip_all)]
2636    fn advance(&mut self) {
2637        self.input_chunk = Chunk::default();
2638        self.transforms.next();
2639        while let Some(transform) = self.transforms.item() {
2640            if transform
2641                .block
2642                .as_ref()
2643                .is_some_and(|block| block.height() == 0)
2644            {
2645                self.transforms.next();
2646            } else {
2647                break;
2648            }
2649        }
2650
2651        if self
2652            .transforms
2653            .item()
2654            .is_some_and(|transform| transform.block.is_none())
2655        {
2656            let start_input_row = self.transforms.start().1;
2657            let start_output_row = self.transforms.start().0;
2658            if start_output_row < self.max_output_row {
2659                let end_input_row = cmp::min(
2660                    self.transforms.end().1,
2661                    start_input_row + (self.max_output_row - start_output_row),
2662                );
2663                self.input_chunks.seek(start_input_row..end_input_row);
2664            }
2665        }
2666    }
2667}
2668
2669pub struct StickyHeaderExcerpt<'a> {
2670    pub excerpt: &'a ExcerptBoundaryInfo,
2671}
2672
2673impl<'a> Iterator for BlockChunks<'a> {
2674    type Item = Chunk<'a>;
2675
2676    #[ztracing::instrument(skip_all)]
2677    fn next(&mut self) -> Option<Self::Item> {
2678        if self.output_row >= self.max_output_row {
2679            return None;
2680        }
2681
2682        if self.line_count_overflow > RowDelta(0) {
2683            let lines = self.line_count_overflow.0.min(u128::BITS);
2684            self.line_count_overflow.0 -= lines;
2685            self.output_row += RowDelta(lines);
2686            return Some(Chunk {
2687                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines as usize]) },
2688                chars: 1u128.unbounded_shl(lines).wrapping_sub(1),
2689                ..Default::default()
2690            });
2691        }
2692
2693        let transform = self.transforms.item()?;
2694        if transform.block.is_some() {
2695            let block_start = self.transforms.start().0;
2696            let mut block_end = self.transforms.end().0;
2697            self.advance();
2698            if self.transforms.item().is_none() {
2699                block_end -= RowDelta(1);
2700            }
2701
2702            let start_in_block = self.output_row - block_start;
2703            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
2704            let line_count = end_in_block - start_in_block;
2705            let lines = RowDelta(line_count.0.min(u128::BITS));
2706            self.line_count_overflow = line_count - lines;
2707            self.output_row += lines;
2708
2709            return Some(Chunk {
2710                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines.0 as usize]) },
2711                chars: 1u128.unbounded_shl(lines.0).wrapping_sub(1),
2712                ..Default::default()
2713            });
2714        }
2715
2716        if self.input_chunk.text.is_empty() {
2717            if let Some(input_chunk) = self.input_chunks.next() {
2718                self.input_chunk = input_chunk;
2719            } else {
2720                if self.output_row < self.max_output_row {
2721                    self.output_row.0 += 1;
2722                    self.advance();
2723                    if self.transforms.item().is_some() {
2724                        return Some(Chunk {
2725                            text: "\n",
2726                            chars: 1,
2727                            ..Default::default()
2728                        });
2729                    }
2730                }
2731                return None;
2732            }
2733        }
2734
2735        let transform_end = self.transforms.end().0;
2736        let (prefix_rows, prefix_bytes) =
2737            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
2738        self.output_row += prefix_rows;
2739
2740        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
2741        self.input_chunk.text = suffix;
2742        self.input_chunk.tabs >>= prefix_bytes.saturating_sub(1);
2743        self.input_chunk.chars >>= prefix_bytes.saturating_sub(1);
2744        self.input_chunk.newlines >>= prefix_bytes.saturating_sub(1);
2745
2746        let mut tabs = self.input_chunk.tabs;
2747        let mut chars = self.input_chunk.chars;
2748        let mut newlines = self.input_chunk.newlines;
2749
2750        if self.masked {
2751            // Not great for multibyte text because to keep cursor math correct we
2752            // need to have the same number of chars in the input as output.
2753            let chars_count = prefix.chars().count();
2754            let bullet_len = chars_count;
2755            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
2756            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
2757            tabs = 0;
2758            newlines = 0;
2759        }
2760
2761        let chunk = Chunk {
2762            text: prefix,
2763            tabs,
2764            chars,
2765            newlines,
2766            ..self.input_chunk.clone()
2767        };
2768
2769        if self.output_row == transform_end {
2770            self.advance();
2771        }
2772
2773        Some(chunk)
2774    }
2775}
2776
2777impl Iterator for BlockRows<'_> {
2778    type Item = RowInfo;
2779
2780    #[ztracing::instrument(skip_all)]
2781    fn next(&mut self) -> Option<Self::Item> {
2782        if self.started {
2783            self.output_row.0 += 1;
2784        } else {
2785            self.started = true;
2786        }
2787
2788        if self.output_row >= self.transforms.end().0 {
2789            self.transforms.next();
2790            while let Some(transform) = self.transforms.item() {
2791                if transform
2792                    .block
2793                    .as_ref()
2794                    .is_some_and(|block| block.height() == 0)
2795                {
2796                    self.transforms.next();
2797                } else {
2798                    break;
2799                }
2800            }
2801
2802            let transform = self.transforms.item()?;
2803            if transform
2804                .block
2805                .as_ref()
2806                .is_none_or(|block| block.is_replacement())
2807            {
2808                self.input_rows.seek(self.transforms.start().1);
2809            }
2810        }
2811
2812        let transform = self.transforms.item()?;
2813        if transform.block.as_ref().is_none_or(|block| {
2814            block.is_replacement()
2815                && self.transforms.start().0 == self.output_row
2816                && matches!(block, Block::FoldedBuffer { .. }).not()
2817        }) {
2818            self.input_rows.next()
2819        } else {
2820            Some(RowInfo::default())
2821        }
2822    }
2823}
2824
2825impl sum_tree::Item for Transform {
2826    type Summary = TransformSummary;
2827
2828    fn summary(&self, _cx: ()) -> Self::Summary {
2829        let mut summary = self.summary.clone();
2830        summary.has_replacement_blocks = self.block.as_ref().is_some_and(Block::is_replacement);
2831        summary
2832    }
2833}
2834
2835impl sum_tree::ContextLessSummary for TransformSummary {
2836    fn zero() -> Self {
2837        Default::default()
2838    }
2839
2840    fn add_summary(&mut self, summary: &Self) {
2841        if summary.longest_row_chars > self.longest_row_chars {
2842            self.longest_row = self.output_rows + summary.longest_row;
2843            self.longest_row_chars = summary.longest_row_chars;
2844        }
2845        self.input_rows += summary.input_rows;
2846        self.output_rows += summary.output_rows;
2847        self.has_replacement_blocks |= summary.has_replacement_blocks;
2848    }
2849}
2850
2851impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
2852    fn zero(_cx: ()) -> Self {
2853        Default::default()
2854    }
2855
2856    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2857        *self += summary.input_rows;
2858    }
2859}
2860
2861impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
2862    fn zero(_cx: ()) -> Self {
2863        Default::default()
2864    }
2865
2866    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2867        *self += summary.output_rows;
2868    }
2869}
2870
2871impl Deref for BlockContext<'_, '_> {
2872    type Target = App;
2873
2874    fn deref(&self) -> &Self::Target {
2875        self.app
2876    }
2877}
2878
2879impl DerefMut for BlockContext<'_, '_> {
2880    fn deref_mut(&mut self) -> &mut Self::Target {
2881        self.app
2882    }
2883}
2884
2885impl CustomBlock {
2886    #[ztracing::instrument(skip_all)]
2887    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
2888        self.render.lock()(cx)
2889    }
2890
2891    #[ztracing::instrument(skip_all)]
2892    pub fn start(&self) -> Anchor {
2893        *self.placement.start()
2894    }
2895
2896    #[ztracing::instrument(skip_all)]
2897    pub fn end(&self) -> Anchor {
2898        *self.placement.end()
2899    }
2900
2901    pub fn style(&self) -> BlockStyle {
2902        self.style
2903    }
2904
2905    pub fn properties(&self) -> BlockProperties<Anchor> {
2906        BlockProperties {
2907            placement: self.placement.clone(),
2908            height: self.height,
2909            style: self.style,
2910            render: Arc::new(|_| {
2911                // Not used
2912                gpui::Empty.into_any_element()
2913            }),
2914            priority: self.priority,
2915        }
2916    }
2917}
2918
2919impl Debug for CustomBlock {
2920    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2921        f.debug_struct("Block")
2922            .field("id", &self.id)
2923            .field("placement", &self.placement)
2924            .field("height", &self.height)
2925            .field("style", &self.style)
2926            .field("priority", &self.priority)
2927            .finish_non_exhaustive()
2928    }
2929}
2930
2931// Count the number of bytes prior to a target point. If the string doesn't contain the target
2932// point, return its total extent. Otherwise return the target point itself.
2933fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
2934    let mut row = 0;
2935    let mut offset = 0;
2936    for (ix, line) in s.split('\n').enumerate() {
2937        if ix > 0 {
2938            row += 1;
2939            offset += 1;
2940        }
2941        if row >= target.0 {
2942            break;
2943        }
2944        offset += line.len();
2945    }
2946    (RowDelta(row), offset)
2947}
2948
2949#[cfg(test)]
2950mod tests {
2951    use super::*;
2952    use crate::{
2953        display_map::{
2954            Companion, fold_map::FoldMap, fold_map::FoldPlaceholder, inlay_map::InlayMap,
2955            tab_map::TabMap, wrap_map::WrapMap,
2956        },
2957        test::test_font,
2958    };
2959    use buffer_diff::BufferDiff;
2960    use gpui::{App, AppContext as _, Element, div, font, px};
2961    use itertools::Itertools;
2962    use language::{Buffer, Capability, Point};
2963    use multi_buffer::{MultiBuffer, PathKey};
2964    use rand::prelude::*;
2965    use settings::SettingsStore;
2966    use std::env;
2967    use util::RandomCharIter;
2968
2969    #[gpui::test]
2970    fn test_offset_for_row() {
2971        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2972        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2973        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2974        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2975        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2976        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2977        assert_eq!(
2978            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2979            (RowDelta(0), 0)
2980        );
2981        assert_eq!(
2982            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2983            (RowDelta(1), 4)
2984        );
2985        assert_eq!(
2986            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2987            (RowDelta(2), 8)
2988        );
2989        assert_eq!(
2990            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2991            (RowDelta(2), 11)
2992        );
2993    }
2994
2995    #[gpui::test]
2996    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2997        cx.update(init_test);
2998
2999        let text = "aaa\nbbb\nccc\nddd";
3000
3001        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3002        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3003        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
3004        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3005        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3006        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
3007        let (wrap_map, wraps_snapshot) =
3008            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3009        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3010
3011        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3012        let block_ids = writer.insert(vec![
3013            BlockProperties {
3014                style: BlockStyle::Fixed,
3015                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3016                height: Some(1),
3017                render: Arc::new(|_| div().into_any()),
3018                priority: 0,
3019            },
3020            BlockProperties {
3021                style: BlockStyle::Fixed,
3022                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
3023                height: Some(2),
3024                render: Arc::new(|_| div().into_any()),
3025                priority: 0,
3026            },
3027            BlockProperties {
3028                style: BlockStyle::Fixed,
3029                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
3030                height: Some(3),
3031                render: Arc::new(|_| div().into_any()),
3032                priority: 0,
3033            },
3034        ]);
3035
3036        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3037        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3038
3039        let blocks = snapshot
3040            .blocks_in_range(BlockRow(0)..BlockRow(8))
3041            .map(|(start_row, block)| {
3042                let block = block.as_custom().unwrap();
3043                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
3044            })
3045            .collect::<Vec<_>>();
3046
3047        // When multiple blocks are on the same line, the newer blocks appear first.
3048        assert_eq!(
3049            blocks,
3050            &[
3051                (1..2, block_ids[0]),
3052                (2..4, block_ids[1]),
3053                (7..10, block_ids[2]),
3054            ]
3055        );
3056
3057        assert_eq!(
3058            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
3059            BlockPoint::new(BlockRow(0), 3)
3060        );
3061        assert_eq!(
3062            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
3063            BlockPoint::new(BlockRow(4), 0)
3064        );
3065        assert_eq!(
3066            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
3067            BlockPoint::new(BlockRow(6), 3)
3068        );
3069
3070        assert_eq!(
3071            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
3072            WrapPoint::new(WrapRow(0), 3)
3073        );
3074        assert_eq!(
3075            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
3076            WrapPoint::new(WrapRow(1), 0)
3077        );
3078        assert_eq!(
3079            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
3080            WrapPoint::new(WrapRow(1), 0)
3081        );
3082        assert_eq!(
3083            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
3084            WrapPoint::new(WrapRow(3), 3)
3085        );
3086
3087        assert_eq!(
3088            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
3089            BlockPoint::new(BlockRow(0), 3)
3090        );
3091        assert_eq!(
3092            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
3093            BlockPoint::new(BlockRow(4), 0)
3094        );
3095        assert_eq!(
3096            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
3097            BlockPoint::new(BlockRow(0), 3)
3098        );
3099        assert_eq!(
3100            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
3101            BlockPoint::new(BlockRow(4), 0)
3102        );
3103        assert_eq!(
3104            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
3105            BlockPoint::new(BlockRow(4), 0)
3106        );
3107        assert_eq!(
3108            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
3109            BlockPoint::new(BlockRow(4), 0)
3110        );
3111        assert_eq!(
3112            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
3113            BlockPoint::new(BlockRow(6), 3)
3114        );
3115        assert_eq!(
3116            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
3117            BlockPoint::new(BlockRow(6), 3)
3118        );
3119        assert_eq!(
3120            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
3121            BlockPoint::new(BlockRow(6), 3)
3122        );
3123        assert_eq!(
3124            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
3125            BlockPoint::new(BlockRow(6), 3)
3126        );
3127
3128        assert_eq!(
3129            snapshot
3130                .row_infos(BlockRow(0))
3131                .map(|row_info| row_info.buffer_row)
3132                .collect::<Vec<_>>(),
3133            &[
3134                Some(0),
3135                None,
3136                None,
3137                None,
3138                Some(1),
3139                Some(2),
3140                Some(3),
3141                None,
3142                None,
3143                None
3144            ]
3145        );
3146
3147        // Insert a line break, separating two block decorations into separate lines.
3148        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3149            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
3150            buffer.snapshot(cx)
3151        });
3152
3153        let (inlay_snapshot, inlay_edits) =
3154            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
3155        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3156        let (tab_snapshot, tab_edits) =
3157            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
3158        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3159            wrap_map.sync(tab_snapshot, tab_edits, cx)
3160        });
3161        let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3162        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
3163    }
3164
3165    #[gpui::test]
3166    fn test_blocks_hidden_in_folds(cx: &mut gpui::TestAppContext) {
3167        cx.update(init_test);
3168
3169        let text = "line0\nline1\nline2\nline3\nline4";
3170
3171        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3172        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3173        let tab_size = 1.try_into().unwrap();
3174        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3175        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3176        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3177        let (wrap_map, wraps_snapshot) =
3178            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3179        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3180
3181        let above = |row| BlockProperties {
3182            style: BlockStyle::Fixed,
3183            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(row, 0))),
3184            height: Some(1),
3185            render: Arc::new(|_| div().into_any()),
3186            priority: 0,
3187        };
3188        let below = |row| BlockProperties {
3189            placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(row, 0))),
3190            ..above(row)
3191        };
3192
3193        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3194        let block_ids = writer.insert(vec![above(1), above(2), below(2), above(4)]);
3195        let (block_a, block_b, block_c, block_d) =
3196            (block_ids[0], block_ids[1], block_ids[2], block_ids[3]);
3197
3198        let present_blocks =
3199            |block_map: &mut BlockMap, wraps_snapshot: WrapSnapshot, wrap_edits: WrapPatch| {
3200                let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3201                let max_row = snapshot.max_point().row;
3202                snapshot
3203                    .blocks_in_range(BlockRow(0)..BlockRow(max_row + 1))
3204                    .filter_map(|(_, block)| Some(block.as_custom()?.id))
3205                    .collect::<HashSet<_>>()
3206            };
3207
3208        assert_eq!(
3209            present_blocks(&mut block_map, wraps_snapshot, Default::default()),
3210            HashSet::from_iter([block_a, block_b, block_c, block_d]),
3211            "every block is present before folding",
3212        );
3213
3214        // Fold lines 2 and 3 entirely, leaving line1 (the fold start) visible.
3215        let (inlay_snapshot, inlay_edits) = inlay_map.sync(buffer_snapshot.clone(), vec![]);
3216        let (mut fold_writer, _, _) = fold_map.write(inlay_snapshot, inlay_edits);
3217        let (fold_snapshot, fold_edits) = fold_writer.fold(vec![(
3218            Point::new(1, 5)..Point::new(3, 5),
3219            FoldPlaceholder::test(),
3220        )]);
3221        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3222        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3223            wrap_map.sync(tab_snapshot, tab_edits, cx)
3224        });
3225
3226        assert_eq!(
3227            present_blocks(&mut block_map, wraps_snapshot, wrap_edits),
3228            HashSet::from_iter([block_a, block_d]),
3229            "blocks B and C anchored to folded lines are dropped, A (fold-start line) and D (past the fold) stay",
3230        );
3231    }
3232
3233    #[gpui::test]
3234    fn test_multibuffer_headers_and_footers(cx: &mut App) {
3235        init_test(cx);
3236
3237        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
3238        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
3239        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
3240
3241        let multi_buffer = cx.new(|cx| {
3242            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
3243            multi_buffer.set_excerpts_for_path(
3244                PathKey::sorted(0),
3245                buffer1.clone(),
3246                [Point::zero()..buffer1.read(cx).max_point()],
3247                0,
3248                cx,
3249            );
3250            multi_buffer.set_excerpts_for_path(
3251                PathKey::sorted(1),
3252                buffer2.clone(),
3253                [Point::zero()..buffer2.read(cx).max_point()],
3254                0,
3255                cx,
3256            );
3257            multi_buffer.set_excerpts_for_path(
3258                PathKey::sorted(2),
3259                buffer3.clone(),
3260                [Point::zero()..buffer3.read(cx).max_point()],
3261                0,
3262                cx,
3263            );
3264            multi_buffer
3265        });
3266        let excerpt_start_anchors = multi_buffer.read_with(cx, |mb, _| {
3267            let snapshot = mb.snapshot(cx);
3268            snapshot
3269                .excerpts()
3270                .map(|e| snapshot.anchor_in_excerpt(e.context.start).unwrap())
3271                .collect::<Vec<_>>()
3272        });
3273
3274        let font = test_font();
3275        let font_size = px(14.);
3276        let font_id = cx.text_system().resolve_font(&font);
3277        let mut wrap_width = px(0.);
3278        for c in "Buff".chars() {
3279            wrap_width += cx
3280                .text_system()
3281                .advance(font_id, font_size, c)
3282                .unwrap()
3283                .width;
3284        }
3285
3286        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
3287        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
3288        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3289        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3290        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
3291
3292        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3293        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3294
3295        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
3296        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
3297
3298        let blocks: Vec<_> = snapshot
3299            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3300            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
3301            .collect();
3302        assert_eq!(
3303            blocks,
3304            vec![
3305                (0..1, BlockId::ExcerptBoundary(excerpt_start_anchors[0])), // path, header
3306                (3..4, BlockId::ExcerptBoundary(excerpt_start_anchors[1])), // path, header
3307                (6..7, BlockId::ExcerptBoundary(excerpt_start_anchors[2])), // path, header
3308            ]
3309        );
3310    }
3311
3312    #[gpui::test]
3313    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
3314        cx.update(init_test);
3315
3316        let text = "aaa\nbbb\nccc\nddd";
3317
3318        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3319        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3320        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
3321        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3322        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3323        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
3324        let (_wrap_map, wraps_snapshot) =
3325            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3326        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3327
3328        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3329        let block_ids = writer.insert(vec![
3330            BlockProperties {
3331                style: BlockStyle::Fixed,
3332                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3333                height: Some(1),
3334                render: Arc::new(|_| div().into_any()),
3335                priority: 0,
3336            },
3337            BlockProperties {
3338                style: BlockStyle::Fixed,
3339                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
3340                height: Some(2),
3341                render: Arc::new(|_| div().into_any()),
3342                priority: 0,
3343            },
3344            BlockProperties {
3345                style: BlockStyle::Fixed,
3346                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
3347                height: Some(3),
3348                render: Arc::new(|_| div().into_any()),
3349                priority: 0,
3350            },
3351        ]);
3352
3353        {
3354            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3355            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3356
3357            let mut block_map_writer =
3358                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3359
3360            let mut new_heights = HashMap::default();
3361            new_heights.insert(block_ids[0], 2);
3362            block_map_writer.resize(new_heights);
3363            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3364            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3365        }
3366
3367        {
3368            let mut block_map_writer =
3369                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3370
3371            let mut new_heights = HashMap::default();
3372            new_heights.insert(block_ids[0], 1);
3373            block_map_writer.resize(new_heights);
3374
3375            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3376            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3377        }
3378
3379        {
3380            let mut block_map_writer =
3381                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3382
3383            let mut new_heights = HashMap::default();
3384            new_heights.insert(block_ids[0], 0);
3385            block_map_writer.resize(new_heights);
3386
3387            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3388            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
3389        }
3390
3391        {
3392            let mut block_map_writer =
3393                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3394
3395            let mut new_heights = HashMap::default();
3396            new_heights.insert(block_ids[0], 3);
3397            block_map_writer.resize(new_heights);
3398
3399            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3400            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3401        }
3402
3403        {
3404            let mut block_map_writer =
3405                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3406
3407            let mut new_heights = HashMap::default();
3408            new_heights.insert(block_ids[0], 3);
3409            block_map_writer.resize(new_heights);
3410
3411            let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3412            // Same height as before, should remain the same
3413            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3414        }
3415    }
3416
3417    #[gpui::test]
3418    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
3419        cx.update(init_test);
3420
3421        let text = "one two three\nfour five six\nseven eight";
3422
3423        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3424        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3425        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3426        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3427        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3428        let (_, wraps_snapshot) = cx.update(|cx| {
3429            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
3430        });
3431        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3432
3433        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3434        writer.insert(vec![
3435            BlockProperties {
3436                style: BlockStyle::Fixed,
3437                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
3438                render: Arc::new(|_| div().into_any()),
3439                height: Some(1),
3440                priority: 0,
3441            },
3442            BlockProperties {
3443                style: BlockStyle::Fixed,
3444                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
3445                render: Arc::new(|_| div().into_any()),
3446                height: Some(1),
3447                priority: 0,
3448            },
3449        ]);
3450
3451        // Blocks with an 'above' disposition go above their corresponding buffer line.
3452        // Blocks with a 'below' disposition go below their corresponding buffer line.
3453        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3454        assert_eq!(
3455            snapshot.text(),
3456            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
3457        );
3458    }
3459
3460    #[gpui::test]
3461    fn test_insert_and_remove_block_anchored_past_soft_wrap(cx: &mut gpui::TestAppContext) {
3462        cx.update(init_test);
3463
3464        let text = "one two three\nfour\nfive";
3465
3466        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3467        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3468        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3469        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3470        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3471        let (_, wraps_snapshot) = cx.update(|cx| {
3472            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
3473        });
3474        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3475
3476        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3477        let block_id = writer.insert(vec![BlockProperties {
3478            style: BlockStyle::Fixed,
3479            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 12))),
3480            render: Arc::new(|_| div().into_any()),
3481            height: Some(2),
3482            priority: 0,
3483        }])[0];
3484
3485        let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3486        assert_eq!(snapshot.text(), "\n\none two \nthree\nfour\nfive");
3487
3488        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3489        writer.remove(HashSet::from_iter([block_id]));
3490
3491        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3492        assert_eq!(snapshot.text(), "one two \nthree\nfour\nfive");
3493    }
3494
3495    #[gpui::test]
3496    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
3497        cx.update(init_test);
3498
3499        let text = "line1\nline2\nline3\nline4\nline5";
3500
3501        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3502        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
3503        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3504        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3505        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3506        let tab_size = 1.try_into().unwrap();
3507        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3508        let (wrap_map, wraps_snapshot) =
3509            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3510        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3511
3512        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3513        let replace_block_id = writer.insert(vec![BlockProperties {
3514            style: BlockStyle::Fixed,
3515            placement: BlockPlacement::Replace(
3516                buffer_snapshot.anchor_after(Point::new(1, 3))
3517                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
3518            ),
3519            height: Some(4),
3520            render: Arc::new(|_| div().into_any()),
3521            priority: 0,
3522        }])[0];
3523
3524        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3525        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3526
3527        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3528            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
3529            buffer.snapshot(cx)
3530        });
3531        let (inlay_snapshot, inlay_edits) =
3532            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
3533        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3534        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3535        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3536            wrap_map.sync(tab_snapshot, tab_edits, cx)
3537        });
3538        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3539        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3540
3541        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3542            buffer.edit(
3543                [(
3544                    Point::new(1, 5)..Point::new(1, 5),
3545                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
3546                )],
3547                None,
3548                cx,
3549            );
3550            buffer.snapshot(cx)
3551        });
3552        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
3553            buffer_snapshot.clone(),
3554            buffer_subscription.consume().into_inner(),
3555        );
3556        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3557        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3558        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3559            wrap_map.sync(tab_snapshot, tab_edits, cx)
3560        });
3561        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3562        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3563
3564        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
3565        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3566        writer.insert(vec![
3567            BlockProperties {
3568                style: BlockStyle::Fixed,
3569                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
3570                height: Some(1),
3571                render: Arc::new(|_| div().into_any()),
3572                priority: 0,
3573            },
3574            BlockProperties {
3575                style: BlockStyle::Fixed,
3576                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
3577                height: Some(1),
3578                render: Arc::new(|_| div().into_any()),
3579                priority: 0,
3580            },
3581            BlockProperties {
3582                style: BlockStyle::Fixed,
3583                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
3584                height: Some(1),
3585                render: Arc::new(|_| div().into_any()),
3586                priority: 0,
3587            },
3588        ]);
3589        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3590        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3591
3592        // Ensure blocks inserted *inside* replaced region are hidden.
3593        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3594        writer.insert(vec![
3595            BlockProperties {
3596                style: BlockStyle::Fixed,
3597                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
3598                height: Some(1),
3599                render: Arc::new(|_| div().into_any()),
3600                priority: 0,
3601            },
3602            BlockProperties {
3603                style: BlockStyle::Fixed,
3604                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
3605                height: Some(1),
3606                render: Arc::new(|_| div().into_any()),
3607                priority: 0,
3608            },
3609            BlockProperties {
3610                style: BlockStyle::Fixed,
3611                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
3612                height: Some(1),
3613                render: Arc::new(|_| div().into_any()),
3614                priority: 0,
3615            },
3616        ]);
3617        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3618        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3619
3620        // Removing the replace block shows all the hidden blocks again.
3621        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3622        writer.remove(HashSet::from_iter([replace_block_id]));
3623        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3624        assert_eq!(
3625            blocks_snapshot.text(),
3626            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
3627        );
3628    }
3629
3630    #[gpui::test]
3631    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
3632        cx.update(init_test);
3633
3634        let text = "111\n\n222\n\n333\n\n444\n\n555\n\n666";
3635
3636        let buffer = cx.update(|cx| {
3637            let multibuffer = MultiBuffer::build_multi(
3638                [
3639                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
3640                    (
3641                        text,
3642                        vec![
3643                            Point::new(2, 0)..Point::new(2, 3),
3644                            Point::new(4, 0)..Point::new(4, 3),
3645                            Point::new(6, 0)..Point::new(6, 3),
3646                        ],
3647                    ),
3648                    (
3649                        text,
3650                        vec![
3651                            Point::new(8, 0)..Point::new(8, 3),
3652                            Point::new(10, 0)..Point::new(10, 3),
3653                        ],
3654                    ),
3655                ],
3656                cx,
3657            );
3658            assert_eq!(multibuffer.read(cx).snapshot(cx).excerpts().count(), 6);
3659            multibuffer
3660        });
3661        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3662        let buffer_ids = buffer_snapshot
3663            .excerpts()
3664            .map(|excerpt| excerpt.context.start.buffer_id)
3665            .dedup()
3666            .collect::<Vec<_>>();
3667        assert_eq!(buffer_ids.len(), 3);
3668        let buffer_id_1 = buffer_ids[0];
3669        let buffer_id_2 = buffer_ids[1];
3670        let buffer_id_3 = buffer_ids[2];
3671
3672        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3673        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3674        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3675        let (_, wrap_snapshot) =
3676            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3677        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3678        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3679
3680        assert_eq!(
3681            blocks_snapshot.text(),
3682            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
3683        );
3684        assert_eq!(
3685            blocks_snapshot
3686                .row_infos(BlockRow(0))
3687                .map(|i| i.buffer_row)
3688                .collect::<Vec<_>>(),
3689            vec![
3690                None,
3691                None,
3692                Some(0),
3693                None,
3694                None,
3695                Some(2),
3696                None,
3697                Some(4),
3698                None,
3699                Some(6),
3700                None,
3701                None,
3702                Some(8),
3703                None,
3704                Some(10),
3705            ]
3706        );
3707
3708        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3709        let excerpt_blocks_2 = writer.insert(vec![
3710            BlockProperties {
3711                style: BlockStyle::Fixed,
3712                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3713                height: Some(1),
3714                render: Arc::new(|_| div().into_any()),
3715                priority: 0,
3716            },
3717            BlockProperties {
3718                style: BlockStyle::Fixed,
3719                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
3720                height: Some(1),
3721                render: Arc::new(|_| div().into_any()),
3722                priority: 0,
3723            },
3724            BlockProperties {
3725                style: BlockStyle::Fixed,
3726                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
3727                height: Some(1),
3728                render: Arc::new(|_| div().into_any()),
3729                priority: 0,
3730            },
3731        ]);
3732        let excerpt_blocks_3 = writer.insert(vec![
3733            BlockProperties {
3734                style: BlockStyle::Fixed,
3735                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
3736                height: Some(1),
3737                render: Arc::new(|_| div().into_any()),
3738                priority: 0,
3739            },
3740            BlockProperties {
3741                style: BlockStyle::Fixed,
3742                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
3743                height: Some(1),
3744                render: Arc::new(|_| div().into_any()),
3745                priority: 0,
3746            },
3747        ]);
3748
3749        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3750        assert_eq!(
3751            blocks_snapshot.text(),
3752            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3753        );
3754        assert_eq!(
3755            blocks_snapshot
3756                .row_infos(BlockRow(0))
3757                .map(|i| i.buffer_row)
3758                .collect::<Vec<_>>(),
3759            vec![
3760                None,
3761                None,
3762                Some(0),
3763                None,
3764                None,
3765                None,
3766                Some(2),
3767                None,
3768                None,
3769                Some(4),
3770                None,
3771                Some(6),
3772                None,
3773                None,
3774                None,
3775                None,
3776                Some(8),
3777                None,
3778                Some(10),
3779                None,
3780            ]
3781        );
3782
3783        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3784        buffer.read_with(cx, |buffer, cx| {
3785            writer.fold_buffers([buffer_id_1], buffer, cx);
3786        });
3787        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
3788            style: BlockStyle::Fixed,
3789            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
3790            height: Some(1),
3791            render: Arc::new(|_| div().into_any()),
3792            priority: 0,
3793        }]);
3794        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3795        let blocks = blocks_snapshot
3796            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3797            .collect::<Vec<_>>();
3798        for (_, block) in &blocks {
3799            if let BlockId::Custom(custom_block_id) = block.id() {
3800                assert!(
3801                    !excerpt_blocks_1.contains(&custom_block_id),
3802                    "Should have no blocks from the folded buffer"
3803                );
3804                assert!(
3805                    excerpt_blocks_2.contains(&custom_block_id)
3806                        || excerpt_blocks_3.contains(&custom_block_id),
3807                    "Should have only blocks from unfolded buffers"
3808                );
3809            }
3810        }
3811        assert_eq!(
3812            1,
3813            blocks
3814                .iter()
3815                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3816                .count(),
3817            "Should have one folded block, producing a header of the second buffer"
3818        );
3819        assert_eq!(
3820            blocks_snapshot.text(),
3821            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3822        );
3823        assert_eq!(
3824            blocks_snapshot
3825                .row_infos(BlockRow(0))
3826                .map(|i| i.buffer_row)
3827                .collect::<Vec<_>>(),
3828            vec![
3829                None,
3830                None,
3831                None,
3832                None,
3833                None,
3834                Some(2),
3835                None,
3836                None,
3837                Some(4),
3838                None,
3839                Some(6),
3840                None,
3841                None,
3842                None,
3843                None,
3844                Some(8),
3845                None,
3846                Some(10),
3847                None,
3848            ]
3849        );
3850
3851        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3852        buffer.read_with(cx, |buffer, cx| {
3853            writer.fold_buffers([buffer_id_2], buffer, cx);
3854        });
3855        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3856        let blocks = blocks_snapshot
3857            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3858            .collect::<Vec<_>>();
3859        for (_, block) in &blocks {
3860            if let BlockId::Custom(custom_block_id) = block.id() {
3861                assert!(
3862                    !excerpt_blocks_1.contains(&custom_block_id),
3863                    "Should have no blocks from the folded buffer_1"
3864                );
3865                assert!(
3866                    !excerpt_blocks_2.contains(&custom_block_id),
3867                    "Should have no blocks from the folded buffer_2"
3868                );
3869                assert!(
3870                    excerpt_blocks_3.contains(&custom_block_id),
3871                    "Should have only blocks from unfolded buffers"
3872                );
3873            }
3874        }
3875        assert_eq!(
3876            2,
3877            blocks
3878                .iter()
3879                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3880                .count(),
3881            "Should have two folded blocks, producing headers"
3882        );
3883        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
3884        assert_eq!(
3885            blocks_snapshot
3886                .row_infos(BlockRow(0))
3887                .map(|i| i.buffer_row)
3888                .collect::<Vec<_>>(),
3889            vec![
3890                None,
3891                None,
3892                None,
3893                None,
3894                None,
3895                None,
3896                None,
3897                Some(8),
3898                None,
3899                Some(10),
3900                None,
3901            ]
3902        );
3903
3904        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3905        buffer.read_with(cx, |buffer, cx| {
3906            writer.unfold_buffers([buffer_id_1], buffer, cx);
3907        });
3908        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3909        let blocks = blocks_snapshot
3910            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3911            .collect::<Vec<_>>();
3912        for (_, block) in &blocks {
3913            if let BlockId::Custom(custom_block_id) = block.id() {
3914                assert!(
3915                    !excerpt_blocks_2.contains(&custom_block_id),
3916                    "Should have no blocks from the folded buffer_2"
3917                );
3918                assert!(
3919                    excerpt_blocks_1.contains(&custom_block_id)
3920                        || excerpt_blocks_3.contains(&custom_block_id),
3921                    "Should have only blocks from unfolded buffers"
3922                );
3923            }
3924        }
3925        assert_eq!(
3926            1,
3927            blocks
3928                .iter()
3929                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3930                .count(),
3931            "Should be back to a single folded buffer, producing a header for buffer_2"
3932        );
3933        assert_eq!(
3934            blocks_snapshot.text(),
3935            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
3936            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
3937        );
3938        assert_eq!(
3939            blocks_snapshot
3940                .row_infos(BlockRow(0))
3941                .map(|i| i.buffer_row)
3942                .collect::<Vec<_>>(),
3943            vec![
3944                None,
3945                None,
3946                None,
3947                Some(0),
3948                None,
3949                None,
3950                None,
3951                None,
3952                None,
3953                Some(8),
3954                None,
3955                Some(10),
3956                None,
3957            ]
3958        );
3959
3960        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3961        buffer.read_with(cx, |buffer, cx| {
3962            writer.fold_buffers([buffer_id_3], buffer, cx);
3963        });
3964        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3965        let blocks = blocks_snapshot
3966            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3967            .collect::<Vec<_>>();
3968        for (_, block) in &blocks {
3969            if let BlockId::Custom(custom_block_id) = block.id() {
3970                assert!(
3971                    excerpt_blocks_1.contains(&custom_block_id),
3972                    "Should have no blocks from the folded buffer_1"
3973                );
3974                assert!(
3975                    !excerpt_blocks_2.contains(&custom_block_id),
3976                    "Should have only blocks from unfolded buffers"
3977                );
3978                assert!(
3979                    !excerpt_blocks_3.contains(&custom_block_id),
3980                    "Should have only blocks from unfolded buffers"
3981                );
3982            }
3983        }
3984
3985        assert_eq!(
3986            blocks_snapshot.text(),
3987            "\n\n\n111\n\n\n\n",
3988            "Should have a single, first buffer left after folding"
3989        );
3990        assert_eq!(
3991            blocks_snapshot
3992                .row_infos(BlockRow(0))
3993                .map(|i| i.buffer_row)
3994                .collect::<Vec<_>>(),
3995            vec![None, None, None, Some(0), None, None, None, None,]
3996        );
3997    }
3998
3999    #[gpui::test]
4000    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
4001        cx.update(init_test);
4002
4003        let text = "111";
4004
4005        let buffer = cx.update(|cx| {
4006            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
4007        });
4008        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4009        let buffer_ids = buffer_snapshot
4010            .excerpts()
4011            .map(|excerpt| excerpt.context.start.buffer_id)
4012            .dedup()
4013            .collect::<Vec<_>>();
4014        assert_eq!(buffer_ids.len(), 1);
4015        let buffer_id = buffer_ids[0];
4016
4017        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
4018        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4019        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4020        let (_, wrap_snapshot) =
4021            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4022        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
4023        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4024
4025        assert_eq!(blocks_snapshot.text(), "\n\n111");
4026
4027        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4028        buffer.read_with(cx, |buffer, cx| {
4029            writer.fold_buffers([buffer_id], buffer, cx);
4030        });
4031        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4032        let blocks = blocks_snapshot
4033            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
4034            .collect::<Vec<_>>();
4035        assert_eq!(
4036            1,
4037            blocks
4038                .iter()
4039                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
4040                .count(),
4041            "Should have one folded block, producing a header of the second buffer"
4042        );
4043        assert_eq!(blocks_snapshot.text(), "\n");
4044        assert_eq!(
4045            blocks_snapshot
4046                .row_infos(BlockRow(0))
4047                .map(|i| i.buffer_row)
4048                .collect::<Vec<_>>(),
4049            vec![None, None],
4050            "When fully folded, should be no buffer rows"
4051        );
4052    }
4053
4054    #[gpui::test(iterations = 60)]
4055    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
4056        cx.update(init_test);
4057
4058        let operations = env::var("OPERATIONS")
4059            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
4060            .unwrap_or(10);
4061
4062        let wrap_width = if rng.random_bool(0.2) {
4063            None
4064        } else {
4065            Some(px(rng.random_range(0.0..=100.0)))
4066        };
4067        let tab_size = 1.try_into().unwrap();
4068        let font_size = px(14.0);
4069        let buffer_start_header_height = rng.random_range(1..=5);
4070        let excerpt_header_height = rng.random_range(1..=5);
4071
4072        log::info!("Wrap width: {:?}", wrap_width);
4073        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
4074        let is_singleton = rng.random();
4075        let buffer = if is_singleton {
4076            let len = rng.random_range(0..10);
4077            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
4078            log::info!("initial singleton buffer text: {:?}", text);
4079            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
4080        } else {
4081            cx.update(|cx| {
4082                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
4083                log::info!(
4084                    "initial multi-buffer text: {:?}",
4085                    multibuffer.read(cx).read(cx).text()
4086                );
4087                multibuffer
4088            })
4089        };
4090
4091        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4092        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4093        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4094        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4095        let font = test_font();
4096        let (wrap_map, wraps_snapshot) =
4097            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
4098        let mut block_map = BlockMap::new(
4099            wraps_snapshot,
4100            buffer_start_header_height,
4101            excerpt_header_height,
4102        );
4103
4104        for _ in 0..operations {
4105            let mut buffer_edits = Vec::new();
4106            match rng.random_range(0..=100) {
4107                0..=19 => {
4108                    let wrap_width = if rng.random_bool(0.2) {
4109                        None
4110                    } else {
4111                        Some(px(rng.random_range(0.0..=100.0)))
4112                    };
4113                    log::info!("Setting wrap width to {:?}", wrap_width);
4114                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
4115                }
4116                20..=39 => {
4117                    let block_count = rng.random_range(1..=5);
4118                    let block_properties = (0..block_count)
4119                        .map(|_| {
4120                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
4121                            let offset = buffer.clip_offset(
4122                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
4123                                Bias::Left,
4124                            );
4125                            let mut min_height = 0;
4126                            let placement = match rng.random_range(0..3) {
4127                                0 => {
4128                                    min_height = 1;
4129                                    let start = buffer.anchor_after(offset);
4130                                    let end = buffer.anchor_after(buffer.clip_offset(
4131                                        rng.random_range(offset..=buffer.len()),
4132                                        Bias::Left,
4133                                    ));
4134                                    BlockPlacement::Replace(start..=end)
4135                                }
4136                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
4137                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
4138                            };
4139
4140                            let height = rng.random_range(min_height..512);
4141                            BlockProperties {
4142                                style: BlockStyle::Fixed,
4143                                placement,
4144                                height: Some(height),
4145                                render: Arc::new(|_| div().into_any()),
4146                                priority: 0,
4147                            }
4148                        })
4149                        .collect::<Vec<_>>();
4150
4151                    let (inlay_snapshot, inlay_edits) =
4152                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
4153                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4154                    let (tab_snapshot, tab_edits) =
4155                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
4156                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4157                        wrap_map.sync(tab_snapshot, tab_edits, cx)
4158                    });
4159                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
4160                    let block_ids =
4161                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
4162                            placement: props.placement.clone(),
4163                            height: props.height,
4164                            style: props.style,
4165                            render: Arc::new(|_| div().into_any()),
4166                            priority: 0,
4167                        }));
4168
4169                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
4170                        log::info!(
4171                            "inserted block {:?} with height {:?} and id {:?}",
4172                            block_properties
4173                                .placement
4174                                .as_ref()
4175                                .map(|p| p.to_point(&buffer_snapshot)),
4176                            block_properties.height,
4177                            block_id
4178                        );
4179                    }
4180                }
4181                40..=59 if !block_map.custom_blocks.is_empty() => {
4182                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
4183                    let block_ids_to_remove = block_map
4184                        .custom_blocks
4185                        .choose_multiple(&mut rng, block_count)
4186                        .map(|block| block.id)
4187                        .collect::<HashSet<_>>();
4188
4189                    let (inlay_snapshot, inlay_edits) =
4190                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
4191                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4192                    let (tab_snapshot, tab_edits) =
4193                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
4194                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4195                        wrap_map.sync(tab_snapshot, tab_edits, cx)
4196                    });
4197                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
4198                    log::info!(
4199                        "removing {} blocks: {:?}",
4200                        block_ids_to_remove.len(),
4201                        block_ids_to_remove
4202                    );
4203                    block_map.remove(block_ids_to_remove);
4204                }
4205                60..=79 => {
4206                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
4207                        log::info!("Noop fold/unfold operation on a singleton buffer");
4208                        continue;
4209                    }
4210                    let (inlay_snapshot, inlay_edits) =
4211                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
4212                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4213                    let (tab_snapshot, tab_edits) =
4214                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
4215                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4216                        wrap_map.sync(tab_snapshot, tab_edits, cx)
4217                    });
4218                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
4219                    let folded_buffers: Vec<_> =
4220                        block_map.block_map.folded_buffers.iter().cloned().collect();
4221                    let mut unfolded_buffers = buffer_snapshot
4222                        .buffer_ids_for_range(Anchor::Min..Anchor::Max)
4223                        .collect::<Vec<_>>();
4224                    unfolded_buffers.dedup();
4225                    log::debug!("All buffers {unfolded_buffers:?}");
4226                    log::debug!("Folded buffers {folded_buffers:?}");
4227                    unfolded_buffers.retain(|buffer_id| {
4228                        !block_map.block_map.folded_buffers.contains(buffer_id)
4229                    });
4230                    let mut folded_count = folded_buffers.len();
4231                    let mut unfolded_count = unfolded_buffers.len();
4232
4233                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
4234                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
4235                    if !fold && !unfold {
4236                        log::info!(
4237                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4238                        );
4239                        continue;
4240                    }
4241
4242                    buffer.update(cx, |buffer, cx| {
4243                        if fold {
4244                            let buffer_to_fold =
4245                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
4246                            log::info!("Folding {buffer_to_fold:?}");
4247                            let related_excerpts = buffer_snapshot
4248                                .excerpts()
4249                                .filter_map(|excerpt| {
4250                                    if excerpt.context.start.buffer_id == buffer_to_fold {
4251                                        Some((
4252                                            excerpt.context.start,
4253                                            buffer_snapshot
4254                                                .buffer_for_id(buffer_to_fold)
4255                                                .unwrap()
4256                                                .text_for_range(excerpt.context)
4257                                                .collect::<String>(),
4258                                        ))
4259                                    } else {
4260                                        None
4261                                    }
4262                                })
4263                                .collect::<Vec<_>>();
4264                            log::info!(
4265                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
4266                            );
4267                            folded_count += 1;
4268                            unfolded_count -= 1;
4269                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
4270                        }
4271                        if unfold {
4272                            let buffer_to_unfold =
4273                                folded_buffers[rng.random_range(0..folded_buffers.len())];
4274                            log::info!("Unfolding {buffer_to_unfold:?}");
4275                            unfolded_count += 1;
4276                            folded_count -= 1;
4277                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
4278                        }
4279                        log::info!(
4280                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4281                        );
4282                    });
4283                }
4284                _ => {
4285                    buffer.update(cx, |buffer, cx| {
4286                        let mutation_count = rng.random_range(1..=5);
4287                        let subscription = buffer.subscribe();
4288                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
4289                        buffer_snapshot = buffer.snapshot(cx);
4290                        buffer_edits.extend(subscription.consume());
4291                        log::info!("buffer text: {:?}", buffer_snapshot.text());
4292                    });
4293                }
4294            }
4295
4296            let (inlay_snapshot, inlay_edits) =
4297                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
4298            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4299            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
4300            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4301                wrap_map.sync(tab_snapshot, tab_edits, cx)
4302            });
4303            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
4304            assert_eq!(
4305                blocks_snapshot.transforms.summary().input_rows,
4306                wraps_snapshot.max_point().row() + RowDelta(1)
4307            );
4308            log::info!("wrapped text: {:?}", wraps_snapshot.text());
4309            log::info!("blocks text: {:?}", blocks_snapshot.text());
4310
4311            let mut expected_blocks = Vec::new();
4312            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
4313                Some((
4314                    block.placement.to_wrap_row(&wraps_snapshot)?,
4315                    Block::Custom(block.clone()),
4316                ))
4317            }));
4318
4319            let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
4320            let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
4321            let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
4322            let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
4323
4324            // Note that this needs to be synced with the related section in BlockMap::sync
4325            expected_blocks.extend(block_map.header_and_footer_blocks(
4326                &buffer_snapshot,
4327                MultiBufferOffset(0)..,
4328                |point, bias| {
4329                    wrap_point_cursor
4330                        .map(
4331                            tab_point_cursor.map(
4332                                fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias),
4333                            ),
4334                        )
4335                        .row()
4336                },
4337            ));
4338
4339            BlockMap::sort_blocks(&mut expected_blocks);
4340
4341            for (placement, block) in &expected_blocks {
4342                log::info!(
4343                    "Block {:?} placement: {:?} Height: {:?}",
4344                    block.id(),
4345                    placement,
4346                    block.height()
4347                );
4348            }
4349
4350            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
4351
4352            let input_buffer_rows = buffer_snapshot
4353                .row_infos(MultiBufferRow(0))
4354                .map(|row| row.buffer_row)
4355                .collect::<Vec<_>>();
4356            let mut expected_buffer_rows = Vec::new();
4357            let mut expected_text = String::new();
4358            let mut expected_block_positions = Vec::new();
4359            let mut expected_replaced_buffer_rows = HashSet::default();
4360            let input_text = wraps_snapshot.text();
4361
4362            // Loop over the input lines, creating (N - 1) empty lines for
4363            // blocks of height N.
4364            //
4365            // It's important to note that output *starts* as one empty line,
4366            // so we special case row 0 to assume a leading '\n'.
4367            //
4368            // Linehood is the birthright of strings.
4369            let input_text_lines = input_text.split('\n').enumerate().peekable();
4370            let mut block_row = 0;
4371            for (wrap_row, input_line) in input_text_lines {
4372                let wrap_row = WrapRow(wrap_row as u32);
4373                let multibuffer_row = wraps_snapshot
4374                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
4375                    .row;
4376
4377                // Create empty lines for the above block
4378                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4379                    if *placement.start() == wrap_row && block.place_above() {
4380                        let (_, block) = sorted_blocks_iter.next().unwrap();
4381                        expected_block_positions.push((block_row, block.id()));
4382                        if block.height() > 0 {
4383                            let text = "\n".repeat((block.height() - 1) as usize);
4384                            if block_row > 0 {
4385                                expected_text.push('\n')
4386                            }
4387                            expected_text.push_str(&text);
4388                            for _ in 0..block.height() {
4389                                expected_buffer_rows.push(None);
4390                            }
4391                            block_row += block.height();
4392                        }
4393                    } else {
4394                        break;
4395                    }
4396                }
4397
4398                // Skip lines within replace blocks, then create empty lines for the replace block's height
4399                let mut is_in_replace_block = false;
4400                if let Some((BlockPlacement::Replace(replace_range), block)) =
4401                    sorted_blocks_iter.peek()
4402                    && wrap_row >= *replace_range.start()
4403                {
4404                    is_in_replace_block = true;
4405
4406                    if wrap_row == *replace_range.start() {
4407                        if matches!(block, Block::FoldedBuffer { .. }) {
4408                            expected_buffer_rows.push(None);
4409                        } else {
4410                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
4411                        }
4412                    }
4413
4414                    if wrap_row == *replace_range.end() {
4415                        expected_block_positions.push((block_row, block.id()));
4416                        let text = "\n".repeat((block.height() - 1) as usize);
4417                        if block_row > 0 {
4418                            expected_text.push('\n');
4419                        }
4420                        expected_text.push_str(&text);
4421
4422                        for _ in 1..block.height() {
4423                            expected_buffer_rows.push(None);
4424                        }
4425                        block_row += block.height();
4426
4427                        sorted_blocks_iter.next();
4428                    }
4429                }
4430
4431                if is_in_replace_block {
4432                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
4433                } else {
4434                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
4435                    let soft_wrapped = wraps_snapshot
4436                        .to_tab_point(WrapPoint::new(wrap_row, 0))
4437                        .column()
4438                        > 0;
4439                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
4440                    if block_row > 0 {
4441                        expected_text.push('\n');
4442                    }
4443                    expected_text.push_str(input_line);
4444                    block_row += 1;
4445                }
4446
4447                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4448                    if *placement.end() == wrap_row && block.place_below() {
4449                        let (_, block) = sorted_blocks_iter.next().unwrap();
4450                        expected_block_positions.push((block_row, block.id()));
4451                        if block.height() > 0 {
4452                            let text = "\n".repeat((block.height() - 1) as usize);
4453                            if block_row > 0 {
4454                                expected_text.push('\n')
4455                            }
4456                            expected_text.push_str(&text);
4457                            for _ in 0..block.height() {
4458                                expected_buffer_rows.push(None);
4459                            }
4460                            block_row += block.height();
4461                        }
4462                    } else {
4463                        break;
4464                    }
4465                }
4466            }
4467
4468            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
4469            let expected_row_count = expected_lines.len();
4470            log::info!("expected text: {expected_text:?}");
4471
4472            assert_eq!(
4473                blocks_snapshot.max_point().row + 1,
4474                expected_row_count as u32,
4475                "actual row count != expected row count",
4476            );
4477            assert_eq!(
4478                blocks_snapshot.text(),
4479                expected_text,
4480                "actual text != expected text",
4481            );
4482
4483            for start_row in 0..expected_row_count {
4484                let end_row = rng.random_range(start_row + 1..=expected_row_count);
4485                let mut expected_text = expected_lines[start_row..end_row].join("\n");
4486                if end_row < expected_row_count {
4487                    expected_text.push('\n');
4488                }
4489
4490                let actual_text = blocks_snapshot
4491                    .chunks(
4492                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
4493                        LanguageAwareStyling {
4494                            tree_sitter: false,
4495                            diagnostics: false,
4496                        },
4497                        false,
4498                        Highlights::default(),
4499                    )
4500                    .map(|chunk| chunk.text)
4501                    .collect::<String>();
4502                assert_eq!(
4503                    actual_text,
4504                    expected_text,
4505                    "incorrect text starting row row range {:?}",
4506                    start_row..end_row
4507                );
4508                assert_eq!(
4509                    blocks_snapshot
4510                        .row_infos(BlockRow(start_row as u32))
4511                        .map(|row_info| row_info.buffer_row)
4512                        .collect::<Vec<_>>(),
4513                    &expected_buffer_rows[start_row..],
4514                    "incorrect buffer_rows starting at row {:?}",
4515                    start_row
4516                );
4517            }
4518
4519            assert_eq!(
4520                blocks_snapshot
4521                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4522                    .map(|(row, block)| (row.0, block.id()))
4523                    .collect::<Vec<_>>(),
4524                expected_block_positions,
4525                "invalid blocks_in_range({:?})",
4526                0..expected_row_count
4527            );
4528
4529            for (_, expected_block) in
4530                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4531            {
4532                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
4533                assert_eq!(
4534                    actual_block.map(|block| block.id()),
4535                    Some(expected_block.id())
4536                );
4537            }
4538
4539            for (block_row, block_id) in expected_block_positions {
4540                if let BlockId::Custom(block_id) = block_id {
4541                    assert_eq!(
4542                        blocks_snapshot.row_for_block(block_id),
4543                        Some(BlockRow(block_row))
4544                    );
4545                }
4546            }
4547
4548            let mut expected_longest_rows = Vec::new();
4549            let mut longest_line_len = -1_isize;
4550            for (row, line) in expected_lines.iter().enumerate() {
4551                let row = row as u32;
4552
4553                assert_eq!(
4554                    blocks_snapshot.line_len(BlockRow(row)),
4555                    line.len() as u32,
4556                    "invalid line len for row {}",
4557                    row
4558                );
4559
4560                let line_char_count = line.chars().count() as isize;
4561                match line_char_count.cmp(&longest_line_len) {
4562                    Ordering::Less => {}
4563                    Ordering::Equal => expected_longest_rows.push(row),
4564                    Ordering::Greater => {
4565                        longest_line_len = line_char_count;
4566                        expected_longest_rows.clear();
4567                        expected_longest_rows.push(row);
4568                    }
4569                }
4570            }
4571
4572            let longest_row = blocks_snapshot.longest_row();
4573            assert!(
4574                expected_longest_rows.contains(&longest_row.0),
4575                "incorrect longest row {}. expected {:?} with length {}",
4576                longest_row.0,
4577                expected_longest_rows,
4578                longest_line_len,
4579            );
4580
4581            for _ in 0..10 {
4582                let end_row = rng.random_range(1..=expected_lines.len());
4583                let start_row = rng.random_range(0..end_row);
4584
4585                let mut expected_longest_rows_in_range = vec![];
4586                let mut longest_line_len_in_range = 0;
4587
4588                for (row, line) in (start_row as u32..).zip(&expected_lines[start_row..end_row]) {
4589                    let line_char_count = line.chars().count() as isize;
4590                    match line_char_count.cmp(&longest_line_len_in_range) {
4591                        Ordering::Less => {}
4592                        Ordering::Equal => expected_longest_rows_in_range.push(row),
4593                        Ordering::Greater => {
4594                            longest_line_len_in_range = line_char_count;
4595                            expected_longest_rows_in_range.clear();
4596                            expected_longest_rows_in_range.push(row);
4597                        }
4598                    }
4599                }
4600
4601                let longest_row_in_range = blocks_snapshot
4602                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
4603                assert!(
4604                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
4605                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
4606                    longest_row.0,
4607                    start_row..end_row,
4608                    expected_longest_rows_in_range,
4609                    longest_line_len_in_range,
4610                );
4611            }
4612
4613            // Ensure that conversion between block points and wrap points is stable.
4614            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
4615                let wrap_point = WrapPoint::new(WrapRow(row), 0);
4616                let block_point = blocks_snapshot.to_block_point(wrap_point);
4617                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
4618                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
4619                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
4620                assert_eq!(
4621                    blocks_snapshot.to_block_point(right_wrap_point),
4622                    block_point
4623                );
4624            }
4625
4626            let mut block_point = BlockPoint::new(BlockRow(0), 0);
4627            for c in expected_text.chars() {
4628                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
4629                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
4630                assert_eq!(
4631                    blocks_snapshot
4632                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
4633                    left_point,
4634                    "block point: {:?}, wrap point: {:?}",
4635                    block_point,
4636                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
4637                );
4638                assert_eq!(
4639                    left_buffer_point,
4640                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
4641                    "{:?} is not valid in buffer coordinates",
4642                    left_point
4643                );
4644
4645                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
4646                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
4647                assert_eq!(
4648                    blocks_snapshot
4649                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
4650                    right_point,
4651                    "block point: {:?}, wrap point: {:?}",
4652                    block_point,
4653                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
4654                );
4655                assert_eq!(
4656                    right_buffer_point,
4657                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
4658                    "{:?} is not valid in buffer coordinates",
4659                    right_point
4660                );
4661
4662                if c == '\n' {
4663                    block_point.0 += Point::new(1, 0);
4664                } else {
4665                    block_point.column += c.len_utf8() as u32;
4666                }
4667            }
4668
4669            for buffer_row in 0..=buffer_snapshot.max_point().row {
4670                let buffer_row = MultiBufferRow(buffer_row);
4671                assert_eq!(
4672                    blocks_snapshot.is_line_replaced(buffer_row),
4673                    expected_replaced_buffer_rows.contains(&buffer_row),
4674                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
4675                );
4676            }
4677        }
4678    }
4679
4680    #[gpui::test]
4681    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
4682        cx.update(init_test);
4683
4684        let text = "abc\ndef\nghi\njkl\nmno";
4685        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
4686        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4687        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4688        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4689        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4690        let (_wrap_map, wraps_snapshot) =
4691            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4692        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
4693
4694        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4695        let _block_id = writer.insert(vec![BlockProperties {
4696            style: BlockStyle::Fixed,
4697            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
4698            height: Some(1),
4699            render: Arc::new(|_| div().into_any()),
4700            priority: 0,
4701        }])[0];
4702
4703        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
4704        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4705
4706        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4707        writer.remove_intersecting_replace_blocks(
4708            [buffer_snapshot
4709                .anchor_after(Point::new(1, 0))
4710                .to_offset(&buffer_snapshot)
4711                ..buffer_snapshot
4712                    .anchor_after(Point::new(1, 0))
4713                    .to_offset(&buffer_snapshot)],
4714            false,
4715        );
4716        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
4717        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4718    }
4719
4720    #[gpui::test]
4721    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
4722        cx.update(init_test);
4723
4724        let text = "line 1\nline 2\nline 3";
4725        let buffer = cx.update(|cx| {
4726            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
4727        });
4728        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4729        let buffer_ids = buffer_snapshot
4730            .excerpts()
4731            .map(|excerpt| excerpt.context.start.buffer_id)
4732            .dedup()
4733            .collect::<Vec<_>>();
4734        assert_eq!(buffer_ids.len(), 1);
4735        let buffer_id = buffer_ids[0];
4736
4737        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4738        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4739        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4740        let (_, wrap_snapshot) =
4741            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4742        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4743
4744        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4745        writer.insert(vec![BlockProperties {
4746            style: BlockStyle::Fixed,
4747            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
4748            height: Some(1),
4749            render: Arc::new(|_| div().into_any()),
4750            priority: 0,
4751        }]);
4752
4753        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4754        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
4755
4756        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4757        buffer.read_with(cx, |buffer, cx| {
4758            writer.fold_buffers([buffer_id], buffer, cx);
4759        });
4760
4761        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4762        assert_eq!(blocks_snapshot.text(), "");
4763    }
4764
4765    #[gpui::test]
4766    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
4767        cx.update(init_test);
4768
4769        let text = "line 1\nline 2\nline 3\nline 4";
4770        let buffer = cx.update(|cx| {
4771            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
4772        });
4773        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4774        let buffer_ids = buffer_snapshot
4775            .excerpts()
4776            .map(|excerpt| excerpt.context.start.buffer_id)
4777            .dedup()
4778            .collect::<Vec<_>>();
4779        assert_eq!(buffer_ids.len(), 1);
4780        let buffer_id = buffer_ids[0];
4781
4782        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4783        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4784        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4785        let (_, wrap_snapshot) =
4786            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4787        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4788
4789        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4790        writer.insert(vec![BlockProperties {
4791            style: BlockStyle::Fixed,
4792            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
4793            height: Some(1),
4794            render: Arc::new(|_| div().into_any()),
4795            priority: 0,
4796        }]);
4797
4798        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4799        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
4800
4801        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4802        buffer.read_with(cx, |buffer, cx| {
4803            writer.fold_buffers([buffer_id], buffer, cx);
4804        });
4805
4806        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4807        assert_eq!(blocks_snapshot.text(), "");
4808    }
4809
4810    #[gpui::test]
4811    fn test_companion_spacer_blocks(cx: &mut gpui::TestAppContext) {
4812        cx.update(init_test);
4813
4814        let base_text = "aaa\nbbb\nccc\nddd\nddd\nddd\neee\n";
4815        let main_text = "aaa\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n";
4816
4817        let rhs_buffer = cx.new(|cx| Buffer::local(main_text, cx));
4818        let diff = cx.new(|cx| {
4819            BufferDiff::new_with_base_text(base_text, &rhs_buffer.read(cx).text_snapshot(), cx)
4820        });
4821        let lhs_buffer = diff.read_with(cx, |diff, _| diff.base_text_buffer().clone());
4822
4823        let lhs_multibuffer = cx.new(|cx| {
4824            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4825            mb.set_excerpts_for_buffer(
4826                lhs_buffer.clone(),
4827                [Point::zero()..lhs_buffer.read(cx).max_point()],
4828                0,
4829                cx,
4830            );
4831            mb.add_inverted_diff(diff.clone(), rhs_buffer.clone(), cx);
4832            mb
4833        });
4834        let rhs_multibuffer = cx.new(|cx| {
4835            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4836            mb.set_excerpts_for_buffer(
4837                rhs_buffer.clone(),
4838                [Point::zero()..rhs_buffer.read(cx).max_point()],
4839                0,
4840                cx,
4841            );
4842            mb.add_diff(diff.clone(), cx);
4843            mb
4844        });
4845        let subscription =
4846            rhs_multibuffer.update(cx, |rhs_multibuffer, _| rhs_multibuffer.subscribe());
4847
4848        let lhs_buffer_snapshot = cx.update(|cx| lhs_multibuffer.read(cx).snapshot(cx));
4849        let (mut _lhs_inlay_map, lhs_inlay_snapshot) = InlayMap::new(lhs_buffer_snapshot);
4850        let (mut _lhs_fold_map, lhs_fold_snapshot) = FoldMap::new(lhs_inlay_snapshot);
4851        let (mut _lhs_tab_map, lhs_tab_snapshot) =
4852            TabMap::new(lhs_fold_snapshot, 4.try_into().unwrap());
4853        let (_lhs_wrap_map, lhs_wrap_snapshot) =
4854            cx.update(|cx| WrapMap::new(lhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4855        let lhs_block_map = BlockMap::new(lhs_wrap_snapshot.clone(), 0, 0);
4856
4857        let rhs_buffer_snapshot = cx.update(|cx| rhs_multibuffer.read(cx).snapshot(cx));
4858        let (mut rhs_inlay_map, rhs_inlay_snapshot) = InlayMap::new(rhs_buffer_snapshot);
4859        let (mut rhs_fold_map, rhs_fold_snapshot) = FoldMap::new(rhs_inlay_snapshot);
4860        let (mut rhs_tab_map, rhs_tab_snapshot) =
4861            TabMap::new(rhs_fold_snapshot, 4.try_into().unwrap());
4862        let (_rhs_wrap_map, rhs_wrap_snapshot) =
4863            cx.update(|cx| WrapMap::new(rhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4864        let rhs_block_map = BlockMap::new(rhs_wrap_snapshot.clone(), 0, 0);
4865
4866        let rhs_entity_id = rhs_multibuffer.entity_id();
4867
4868        let companion = cx.new(|_| Companion::new(rhs_entity_id));
4869
4870        let rhs_edits = Patch::new(vec![text::Edit {
4871            old: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4872            new: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4873        }]);
4874        let lhs_edits = Patch::new(vec![text::Edit {
4875            old: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4876            new: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4877        }]);
4878
4879        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4880            rhs_block_map.read(
4881                rhs_wrap_snapshot.clone(),
4882                rhs_edits.clone(),
4883                Some(CompanionView::new(
4884                    rhs_entity_id,
4885                    &lhs_wrap_snapshot,
4886                    &lhs_edits,
4887                    companion,
4888                )),
4889            )
4890        });
4891
4892        let lhs_entity_id = lhs_multibuffer.entity_id();
4893        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4894            lhs_block_map.read(
4895                lhs_wrap_snapshot.clone(),
4896                lhs_edits.clone(),
4897                Some(CompanionView::new(
4898                    lhs_entity_id,
4899                    &rhs_wrap_snapshot,
4900                    &rhs_edits,
4901                    companion,
4902                )),
4903            )
4904        });
4905
4906        // LHS:
4907        //   aaa
4908        // - bbb
4909        // - ccc
4910        //   ddd
4911        //   ddd
4912        //   ddd
4913        //   <extra line>
4914        //   <extra line>
4915        //   <extra line>
4916        //   *eee
4917        //
4918        // RHS:
4919        //   aaa
4920        //   <extra line>
4921        //   <extra line>
4922        //   ddd
4923        //   ddd
4924        //   ddd
4925        // + XXX
4926        // + YYY
4927        // + ZZZ
4928        //   eee
4929
4930        assert_eq!(
4931            rhs_snapshot.snapshot.text(),
4932            "aaa\n\n\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4933            "RHS should have 2 spacer lines after 'aaa' to align with LHS's deleted lines"
4934        );
4935
4936        assert_eq!(
4937            lhs_snapshot.snapshot.text(),
4938            "aaa\nbbb\nccc\nddd\nddd\nddd\n\n\n\neee\n",
4939            "LHS should have 3 spacer lines in place of RHS's inserted lines"
4940        );
4941
4942        // LHS:
4943        //   aaa
4944        // - bbb
4945        // - ccc
4946        //   ddd
4947        //   ddd
4948        //   ddd
4949        //   <extra line>
4950        //   <extra line>
4951        //   <extra line>
4952        //   eee
4953        //
4954        // RHS:
4955        //   aaa
4956        //   <extra line>
4957        //   <extra line>
4958        //   ddd
4959        //   foo
4960        //   foo
4961        //   foo
4962        //   ddd
4963        //   ddd
4964        // + XXX
4965        // + YYY
4966        // + ZZZ
4967        //   eee
4968
4969        let rhs_buffer_snapshot = rhs_multibuffer.update(cx, |multibuffer, cx| {
4970            multibuffer.edit(
4971                [(Point::new(2, 0)..Point::new(2, 0), "foo\nfoo\nfoo\n")],
4972                None,
4973                cx,
4974            );
4975            multibuffer.snapshot(cx)
4976        });
4977
4978        let (rhs_inlay_snapshot, rhs_inlay_edits) =
4979            rhs_inlay_map.sync(rhs_buffer_snapshot, subscription.consume().into_inner());
4980        let (rhs_fold_snapshot, rhs_fold_edits) =
4981            rhs_fold_map.read(rhs_inlay_snapshot, rhs_inlay_edits);
4982        let (rhs_tab_snapshot, rhs_tab_edits) =
4983            rhs_tab_map.sync(rhs_fold_snapshot, rhs_fold_edits, 4.try_into().unwrap());
4984        let (rhs_wrap_snapshot, rhs_wrap_edits) = _rhs_wrap_map.update(cx, |wrap_map, cx| {
4985            wrap_map.sync(rhs_tab_snapshot, rhs_tab_edits, cx)
4986        });
4987
4988        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4989            rhs_block_map.read(
4990                rhs_wrap_snapshot.clone(),
4991                rhs_wrap_edits.clone(),
4992                Some(CompanionView::new(
4993                    rhs_entity_id,
4994                    &lhs_wrap_snapshot,
4995                    &Default::default(),
4996                    companion,
4997                )),
4998            )
4999        });
5000
5001        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
5002            lhs_block_map.read(
5003                lhs_wrap_snapshot.clone(),
5004                Default::default(),
5005                Some(CompanionView::new(
5006                    lhs_entity_id,
5007                    &rhs_wrap_snapshot,
5008                    &rhs_wrap_edits,
5009                    companion,
5010                )),
5011            )
5012        });
5013
5014        assert_eq!(
5015            rhs_snapshot.snapshot.text(),
5016            "aaa\n\n\nddd\nfoo\nfoo\nfoo\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
5017            "RHS should have the insertion"
5018        );
5019
5020        assert_eq!(
5021            lhs_snapshot.snapshot.text(),
5022            "aaa\nbbb\nccc\nddd\n\n\n\nddd\nddd\n\n\n\neee\n",
5023            "LHS should have 3 more spacer lines to balance the insertion"
5024        );
5025    }
5026
5027    // Regression test for ZED-9V4: `BlockMap::sync` walks the (old) transform
5028    // tree with a `WrapRow` cursor and used to `cursor.item().unwrap()` for
5029    // every edit, assuming each edit's `old.start` lands strictly inside the
5030    // tree. The companion (split-diff) branch of `sync` can compose an edit
5031    // anchored at the trailing boundary of the old transforms
5032    // (`old.start == input_rows`), at which point the cursor is past the end of
5033    // the tree and `item()` is `None`. That used to abort the process.
5034    #[gpui::test]
5035    fn test_sync_edit_anchored_at_end_of_transforms(cx: &mut gpui::TestAppContext) {
5036        cx.update(init_test);
5037
5038        let buffer = cx.update(|cx| MultiBuffer::build_simple("aaa\nbbb\nccc\n", cx));
5039        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
5040        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
5041
5042        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot);
5043        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
5044        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
5045        let (wrap_map, old_wrap_snapshot) =
5046            cx.update(|cx| WrapMap::new(tab_snapshot, test_font(), px(14.0), None, cx));
5047        let block_map = BlockMap::new(old_wrap_snapshot.clone(), 0, 0);
5048
5049        // The tree now spans exactly `old_end` input rows.
5050        let old_end = old_wrap_snapshot.max_point().row() + RowDelta(1);
5051
5052        // Grow the buffer so the new snapshot is larger than the old transforms.
5053        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
5054            buffer.edit(
5055                [(Point::new(3, 0)..Point::new(3, 0), "ddd\neee\n")],
5056                None,
5057                cx,
5058            );
5059            buffer.snapshot(cx)
5060        });
5061        let (inlay_snapshot, inlay_edits) =
5062            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
5063        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
5064        let (tab_snapshot, tab_edits) =
5065            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
5066        let (new_wrap_snapshot, _) = wrap_map.update(cx, |wrap_map, cx| {
5067            wrap_map.sync(tab_snapshot, tab_edits, cx)
5068        });
5069        let new_end = new_wrap_snapshot.max_point().row() + RowDelta(1);
5070
5071        // An edit anchored exactly at the end of the old transforms, of the shape
5072        // the companion branch of `sync` can produce.
5073        let edits = Patch::new(vec![text::Edit {
5074            old: old_end..old_end,
5075            new: old_end..new_end,
5076        }]);
5077
5078        let snapshot = block_map.read(new_wrap_snapshot, edits, None);
5079        assert_eq!(snapshot.snapshot.text(), "aaa\nbbb\nccc\nddd\neee\n");
5080    }
5081
5082    fn init_test(cx: &mut gpui::App) {
5083        let settings = SettingsStore::test(cx);
5084        cx.set_global(settings);
5085        theme_settings::init(theme::LoadThemes::JustBase, cx);
5086        assets::Assets.load_test_fonts(cx);
5087    }
5088
5089    impl Block {
5090        fn as_custom(&self) -> Option<&CustomBlock> {
5091            match self {
5092                Block::Custom(block) => Some(block),
5093                _ => None,
5094            }
5095        }
5096    }
5097
5098    impl BlockSnapshot {
5099        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
5100            self.wrap_snapshot
5101                .to_point(self.to_wrap_point(point, bias), bias)
5102        }
5103    }
5104}
5105
Served at tenant.openagents/omega Member data and write actions are omitted.