block_map.rs

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