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 our_wrapper = |our_point: Point| {
1114            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        };
1120        let mut companion_wrapper = |their_point: Point| {
1121            companion_wrap_point_cursor
1122                .map(
1123                    companion_tab_point_cursor.map(
1124                        companion_fold_point_cursor
1125                            .map(companion_inlay_point_cursor.map(their_point), Bias::Left),
1126                    ),
1127                )
1128                .row()
1129        };
1130        fn determine_spacer(
1131            our_wrapper: &mut impl FnMut(Point) -> WrapRow,
1132            companion_wrapper: &mut impl FnMut(Point) -> WrapRow,
1133            our_point: Point,
1134            their_point: Point,
1135            delta: i32,
1136        ) -> (i32, Option<(WrapRow, u32)>) {
1137            let our_wrap = our_wrapper(our_point);
1138            let companion_wrap = companion_wrapper(their_point);
1139            let new_delta = companion_wrap.0 as i32 - our_wrap.0 as i32;
1140
1141            let spacer = if new_delta > delta {
1142                let height = (new_delta - delta) as u32;
1143                Some((our_wrap, height))
1144            } else {
1145                None
1146            };
1147            (new_delta, spacer)
1148        }
1149
1150        let mut result = Vec::new();
1151
1152        for excerpt in patches {
1153            let mut source_points = (excerpt.edited_range.start.row..=excerpt.edited_range.end.row)
1154                .map(|row| MultiBufferPoint::new(row, 0))
1155                .chain(if excerpt.edited_range.end.column > 0 {
1156                    Some(excerpt.edited_range.end)
1157                } else {
1158                    None
1159                })
1160                .peekable();
1161            let last_source_point = if excerpt.edited_range.end.column > 0 {
1162                excerpt.edited_range.end
1163            } else {
1164                MultiBufferPoint::new(excerpt.edited_range.end.row, 0)
1165            };
1166
1167            let Some(first_point) = source_points.peek().copied() else {
1168                continue;
1169            };
1170            let edit_for_first_point = excerpt.patch.edit_for_old_position(first_point);
1171
1172            // Because we calculate spacers based on differences in wrap row
1173            // counts between the RHS and LHS for corresponding buffer points,
1174            // we need to calibrate our expectations based on the difference
1175            // in counts before the start of the edit. This difference in
1176            // counts should have been balanced already by spacers above this
1177            // edit, so we only need to insert spacers for when the difference
1178            // in counts diverges from that baseline value.
1179            let (our_baseline, their_baseline) = if edit_for_first_point.old.start < first_point {
1180                // Case 1: We are inside a hunk/group--take the start of the hunk/group on both sides as the baseline.
1181                (
1182                    edit_for_first_point.old.start,
1183                    edit_for_first_point.new.start,
1184                )
1185            } else if first_point.row > excerpt.source_excerpt_range.start.row {
1186                // Case 2: We are not inside a hunk/group--go back by one row to find the baseline.
1187                let prev_point = Point::new(first_point.row - 1, 0);
1188                let edit_for_prev_point = excerpt.patch.edit_for_old_position(prev_point);
1189                (prev_point, edit_for_prev_point.new.end)
1190            } else {
1191                // Case 3: We are at the start of the excerpt--no previous row to use as the baseline.
1192                (first_point, edit_for_first_point.new.start)
1193            };
1194            let our_baseline = our_wrapper(our_baseline);
1195            let their_baseline = companion_wrapper(their_baseline);
1196
1197            let mut delta = their_baseline.0 as i32 - our_baseline.0 as i32;
1198
1199            // 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.
1200            if edit_for_first_point.old.start < first_point {
1201                let mut current_boundary = first_point;
1202                let current_range = edit_for_first_point.new;
1203                while let Some(next_point) = source_points.peek().cloned() {
1204                    let edit_for_next_point = excerpt.patch.edit_for_old_position(next_point);
1205                    if edit_for_next_point.new.end > current_range.end {
1206                        break;
1207                    }
1208                    source_points.next();
1209                    current_boundary = next_point;
1210                }
1211
1212                let (new_delta, spacer) = determine_spacer(
1213                    &mut our_wrapper,
1214                    &mut companion_wrapper,
1215                    current_boundary,
1216                    current_range.end,
1217                    delta,
1218                );
1219
1220                delta = new_delta;
1221                if let Some((wrap_row, height)) = spacer {
1222                    result.push((
1223                        BlockPlacement::Above(wrap_row),
1224                        Block::Spacer {
1225                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1226                            height,
1227                            is_below: false,
1228                        },
1229                    ));
1230                }
1231            }
1232
1233            // Main loop: process one hunk/group at a time, possibly inserting spacers before and after.
1234            while let Some(source_point) = source_points.next() {
1235                let mut current_boundary = source_point;
1236                let current_range = excerpt.patch.edit_for_old_position(current_boundary).new;
1237
1238                // This can only occur at the end of an excerpt.
1239                if current_boundary.column > 0 {
1240                    debug_assert_eq!(current_boundary, excerpt.source_excerpt_range.end);
1241                    break;
1242                }
1243
1244                // Align the two sides at the start of this group.
1245                let (delta_at_start, mut spacer_at_start) = determine_spacer(
1246                    &mut our_wrapper,
1247                    &mut companion_wrapper,
1248                    current_boundary,
1249                    current_range.start,
1250                    delta,
1251                );
1252                delta = delta_at_start;
1253
1254                while let Some(next_point) = source_points.peek().copied() {
1255                    let edit_for_next_point = excerpt.patch.edit_for_old_position(next_point);
1256                    if edit_for_next_point.new.end > current_range.end {
1257                        break;
1258                    }
1259
1260                    if let Some((wrap_row, height)) = spacer_at_start.take() {
1261                        result.push((
1262                            BlockPlacement::Above(wrap_row),
1263                            Block::Spacer {
1264                                id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1265                                height,
1266                                is_below: false,
1267                            },
1268                        ));
1269                    }
1270
1271                    current_boundary = next_point;
1272                    source_points.next();
1273                }
1274
1275                // This can only occur at the end of an excerpt.
1276                if current_boundary.column > 0 {
1277                    debug_assert_eq!(current_boundary, excerpt.source_excerpt_range.end);
1278                    break;
1279                }
1280
1281                let (delta_at_end, spacer_at_end) = determine_spacer(
1282                    &mut our_wrapper,
1283                    &mut companion_wrapper,
1284                    current_boundary,
1285                    current_range.end,
1286                    delta,
1287                );
1288                delta = delta_at_end;
1289
1290                if let Some((wrap_row, mut height)) = spacer_at_start {
1291                    if let Some((_, additional_height)) = spacer_at_end {
1292                        height += additional_height;
1293                    }
1294                    result.push((
1295                        BlockPlacement::Above(wrap_row),
1296                        Block::Spacer {
1297                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1298                            height,
1299                            is_below: false,
1300                        },
1301                    ));
1302                } else if let Some((wrap_row, height)) = spacer_at_end {
1303                    result.push((
1304                        BlockPlacement::Above(wrap_row),
1305                        Block::Spacer {
1306                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1307                            height,
1308                            is_below: false,
1309                        },
1310                    ));
1311                }
1312            }
1313
1314            if last_source_point == excerpt.source_excerpt_range.end {
1315                let (_new_delta, spacer) = determine_spacer(
1316                    &mut our_wrapper,
1317                    &mut companion_wrapper,
1318                    last_source_point,
1319                    excerpt.target_excerpt_range.end,
1320                    delta,
1321                );
1322                if let Some((wrap_row, height)) = spacer {
1323                    result.push((
1324                        BlockPlacement::Below(wrap_row),
1325                        Block::Spacer {
1326                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1327                            height,
1328                            is_below: true,
1329                        },
1330                    ));
1331                }
1332            }
1333        }
1334
1335        result
1336    }
1337
1338    #[ztracing::instrument(skip_all)]
1339    fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
1340        blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
1341            placement_a
1342                .start()
1343                .cmp(placement_b.start())
1344                .then_with(|| placement_b.end().cmp(placement_a.end()))
1345                .then_with(|| placement_a.tie_break().cmp(&placement_b.tie_break()))
1346                .then_with(|| {
1347                    if block_a.is_header() {
1348                        Ordering::Less
1349                    } else if block_b.is_header() {
1350                        Ordering::Greater
1351                    } else {
1352                        Ordering::Equal
1353                    }
1354                })
1355                .then_with(|| match (block_a, block_b) {
1356                    (
1357                        Block::ExcerptBoundary {
1358                            excerpt: excerpt_a, ..
1359                        }
1360                        | Block::BufferHeader {
1361                            excerpt: excerpt_a, ..
1362                        },
1363                        Block::ExcerptBoundary {
1364                            excerpt: excerpt_b, ..
1365                        }
1366                        | Block::BufferHeader {
1367                            excerpt: excerpt_b, ..
1368                        },
1369                    ) => Some(excerpt_a.id).cmp(&Some(excerpt_b.id)),
1370                    (
1371                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1372                        Block::Spacer { .. } | Block::Custom(_),
1373                    ) => Ordering::Less,
1374                    (
1375                        Block::Spacer { .. } | Block::Custom(_),
1376                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1377                    ) => Ordering::Greater,
1378                    (Block::Spacer { .. }, Block::Custom(_)) => Ordering::Less,
1379                    (Block::Custom(_), Block::Spacer { .. }) => Ordering::Greater,
1380                    (Block::Custom(block_a), Block::Custom(block_b)) => block_a
1381                        .priority
1382                        .cmp(&block_b.priority)
1383                        .then_with(|| block_a.id.cmp(&block_b.id)),
1384                    _ => {
1385                        unreachable!("comparing blocks: {block_a:?} vs {block_b:?}")
1386                    }
1387                })
1388        });
1389        blocks.dedup_by(|right, left| match (left.0.clone(), right.0.clone()) {
1390            (BlockPlacement::Replace(range), BlockPlacement::Above(row))
1391            | (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => range.contains(&row),
1392            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => {
1393                if range_a.end() >= range_b.start() && range_a.start() <= range_b.end() {
1394                    left.0 = BlockPlacement::Replace(
1395                        *range_a.start()..=*range_a.end().max(range_b.end()),
1396                    );
1397                    true
1398                } else {
1399                    false
1400                }
1401            }
1402            _ => false,
1403        });
1404    }
1405}
1406
1407#[ztracing::instrument(skip(tree, wrap_snapshot))]
1408fn push_isomorphic(tree: &mut SumTree<Transform>, rows: RowDelta, wrap_snapshot: &WrapSnapshot) {
1409    if rows == RowDelta(0) {
1410        return;
1411    }
1412
1413    let wrap_row_start = tree.summary().input_rows;
1414    let wrap_row_end = wrap_row_start + rows;
1415    let wrap_summary = wrap_snapshot.text_summary_for_range(wrap_row_start..wrap_row_end);
1416    let summary = TransformSummary {
1417        input_rows: WrapRow(rows.0),
1418        output_rows: BlockRow(rows.0),
1419        longest_row: BlockRow(wrap_summary.longest_row),
1420        longest_row_chars: wrap_summary.longest_row_chars,
1421    };
1422    let mut merged = false;
1423    tree.update_last(
1424        |last_transform| {
1425            if last_transform.block.is_none() {
1426                last_transform.summary.add_summary(&summary);
1427                merged = true;
1428            }
1429        },
1430        (),
1431    );
1432    if !merged {
1433        tree.push(
1434            Transform {
1435                summary,
1436                block: None,
1437            },
1438            (),
1439        );
1440    }
1441}
1442
1443impl BlockPoint {
1444    pub fn new(row: BlockRow, column: u32) -> Self {
1445        Self(Point::new(row.0, column))
1446    }
1447}
1448
1449impl Deref for BlockPoint {
1450    type Target = Point;
1451
1452    fn deref(&self) -> &Self::Target {
1453        &self.0
1454    }
1455}
1456
1457impl std::ops::DerefMut for BlockPoint {
1458    fn deref_mut(&mut self) -> &mut Self::Target {
1459        &mut self.0
1460    }
1461}
1462
1463impl Deref for BlockMapReader<'_> {
1464    type Target = BlockSnapshot;
1465
1466    fn deref(&self) -> &Self::Target {
1467        &self.snapshot
1468    }
1469}
1470
1471impl DerefMut for BlockMapReader<'_> {
1472    fn deref_mut(&mut self) -> &mut Self::Target {
1473        &mut self.snapshot
1474    }
1475}
1476
1477impl BlockMapReader<'_> {
1478    #[ztracing::instrument(skip_all)]
1479    pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
1480        let block = self.blocks.iter().find(|block| block.id == block_id)?;
1481        let buffer_row = block
1482            .start()
1483            .to_point(self.wrap_snapshot.buffer_snapshot())
1484            .row;
1485        let wrap_row = self
1486            .wrap_snapshot
1487            .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
1488            .row();
1489        let start_wrap_row = self
1490            .wrap_snapshot
1491            .prev_row_boundary(WrapPoint::new(wrap_row, 0));
1492        let end_wrap_row = self
1493            .wrap_snapshot
1494            .next_row_boundary(WrapPoint::new(wrap_row, 0))
1495            .unwrap_or(self.wrap_snapshot.max_point().row() + WrapRow(1));
1496
1497        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
1498        cursor.seek(&start_wrap_row, Bias::Left);
1499        while let Some(transform) = cursor.item() {
1500            if cursor.start().0 > end_wrap_row {
1501                break;
1502            }
1503
1504            if let Some(BlockId::Custom(id)) = transform.block.as_ref().map(|block| block.id())
1505                && id == block_id
1506            {
1507                return Some(cursor.start().1);
1508            }
1509            cursor.next();
1510        }
1511
1512        None
1513    }
1514}
1515
1516impl BlockMapWriter<'_> {
1517    #[ztracing::instrument(skip_all)]
1518    pub fn insert(
1519        &mut self,
1520        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
1521    ) -> Vec<CustomBlockId> {
1522        let blocks = blocks.into_iter();
1523        let mut ids = Vec::with_capacity(blocks.size_hint().1.unwrap_or(0));
1524        let mut edits = Patch::default();
1525        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1526        let buffer = wrap_snapshot.buffer_snapshot();
1527
1528        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1529        for block in blocks {
1530            if let BlockPlacement::Replace(_) = &block.placement {
1531                debug_assert!(block.height.unwrap() > 0);
1532            }
1533
1534            let id = CustomBlockId(self.block_map.next_block_id.fetch_add(1, SeqCst));
1535            ids.push(id);
1536
1537            let start = block.placement.start().to_point(buffer);
1538            let end = block.placement.end().to_point(buffer);
1539            let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1540            let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1541
1542            let (start_row, end_row) = {
1543                previous_wrap_row_range.take_if(|range| {
1544                    !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1545                });
1546                let range = previous_wrap_row_range.get_or_insert_with(|| {
1547                    let start_row =
1548                        wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1549                    let end_row = wrap_snapshot
1550                        .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1551                        .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1552                    start_row..end_row
1553                });
1554                (range.start, range.end)
1555            };
1556            let block_ix = match self
1557                .block_map
1558                .custom_blocks
1559                .binary_search_by(|probe| probe.placement.cmp(&block.placement, buffer))
1560            {
1561                Ok(ix) | Err(ix) => ix,
1562            };
1563            let new_block = Arc::new(CustomBlock {
1564                id,
1565                placement: block.placement,
1566                height: block.height,
1567                render: Arc::new(Mutex::new(block.render)),
1568                style: block.style,
1569                priority: block.priority,
1570            });
1571            self.block_map
1572                .custom_blocks
1573                .insert(block_ix, new_block.clone());
1574            self.block_map.custom_blocks_by_id.insert(id, new_block);
1575
1576            edits = edits.compose([Edit {
1577                old: start_row..end_row,
1578                new: start_row..end_row,
1579            }]);
1580        }
1581
1582        let default_patch = Patch::default();
1583        self.block_map.sync(
1584            wrap_snapshot,
1585            edits,
1586            self.companion.as_deref().map(
1587                |&CompanionView {
1588                     entity_id,
1589                     wrap_snapshot,
1590                     companion,
1591                     ..
1592                 }| {
1593                    CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
1594                },
1595            ),
1596        );
1597        ids
1598    }
1599
1600    #[ztracing::instrument(skip_all)]
1601    pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
1602        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1603        let buffer = wrap_snapshot.buffer_snapshot();
1604        let mut edits = Patch::default();
1605        let mut last_block_buffer_row = None;
1606
1607        for block in &mut self.block_map.custom_blocks {
1608            if let Some(new_height) = heights.remove(&block.id) {
1609                if let BlockPlacement::Replace(_) = &block.placement {
1610                    debug_assert!(new_height > 0);
1611                }
1612
1613                if block.height != Some(new_height) {
1614                    let new_block = CustomBlock {
1615                        id: block.id,
1616                        placement: block.placement.clone(),
1617                        height: Some(new_height),
1618                        style: block.style,
1619                        render: block.render.clone(),
1620                        priority: block.priority,
1621                    };
1622                    let new_block = Arc::new(new_block);
1623                    *block = new_block.clone();
1624                    self.block_map
1625                        .custom_blocks_by_id
1626                        .insert(block.id, new_block);
1627
1628                    let start_row = block.placement.start().to_point(buffer).row;
1629                    let end_row = block.placement.end().to_point(buffer).row;
1630                    if last_block_buffer_row != Some(end_row) {
1631                        last_block_buffer_row = Some(end_row);
1632                        let start_wrap_row = wrap_snapshot
1633                            .make_wrap_point(Point::new(start_row, 0), Bias::Left)
1634                            .row();
1635                        let end_wrap_row = wrap_snapshot
1636                            .make_wrap_point(Point::new(end_row, 0), Bias::Left)
1637                            .row();
1638                        let start =
1639                            wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1640                        let end = wrap_snapshot
1641                            .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1642                            .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1643                        edits.push(Edit {
1644                            old: start..end,
1645                            new: start..end,
1646                        })
1647                    }
1648                }
1649            }
1650        }
1651
1652        let default_patch = Patch::default();
1653        self.block_map.sync(
1654            wrap_snapshot,
1655            edits,
1656            self.companion.as_deref().map(
1657                |&CompanionView {
1658                     entity_id,
1659                     wrap_snapshot,
1660                     companion,
1661                     ..
1662                 }| {
1663                    CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
1664                },
1665            ),
1666        );
1667    }
1668
1669    #[ztracing::instrument(skip_all)]
1670    pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
1671        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1672        let buffer = wrap_snapshot.buffer_snapshot();
1673        let mut edits = Patch::default();
1674        let mut last_block_buffer_row = None;
1675        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1676        self.block_map.custom_blocks.retain(|block| {
1677            if block_ids.contains(&block.id) {
1678                let start = block.placement.start().to_point(buffer);
1679                let end = block.placement.end().to_point(buffer);
1680                if last_block_buffer_row != Some(end.row) {
1681                    last_block_buffer_row = Some(end.row);
1682                    let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1683                    let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1684                    let (start_row, end_row) = {
1685                        previous_wrap_row_range.take_if(|range| {
1686                            !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1687                        });
1688                        let range = previous_wrap_row_range.get_or_insert_with(|| {
1689                            let start_row =
1690                                wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1691                            let end_row = wrap_snapshot
1692                                .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1693                                .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1694                            start_row..end_row
1695                        });
1696                        (range.start, range.end)
1697                    };
1698
1699                    edits.push(Edit {
1700                        old: start_row..end_row,
1701                        new: start_row..end_row,
1702                    })
1703                }
1704                false
1705            } else {
1706                true
1707            }
1708        });
1709        self.block_map
1710            .custom_blocks_by_id
1711            .retain(|id, _| !block_ids.contains(id));
1712        let default_patch = Patch::default();
1713        self.block_map.sync(
1714            wrap_snapshot,
1715            edits,
1716            self.companion.as_deref().map(
1717                |&CompanionView {
1718                     entity_id,
1719                     wrap_snapshot,
1720                     companion,
1721                     ..
1722                 }| {
1723                    CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
1724                },
1725            ),
1726        );
1727    }
1728
1729    #[ztracing::instrument(skip_all)]
1730    pub fn remove_intersecting_replace_blocks(
1731        &mut self,
1732        ranges: impl IntoIterator<Item = Range<MultiBufferOffset>>,
1733        inclusive: bool,
1734    ) {
1735        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
1736        let mut blocks_to_remove = HashSet::default();
1737        for range in ranges {
1738            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
1739                if matches!(block.placement, BlockPlacement::Replace(_)) {
1740                    blocks_to_remove.insert(block.id);
1741                }
1742            }
1743        }
1744        drop(wrap_snapshot);
1745        self.remove(blocks_to_remove);
1746    }
1747
1748    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId) {
1749        self.block_map
1750            .buffers_with_disabled_headers
1751            .insert(buffer_id);
1752    }
1753
1754    #[ztracing::instrument(skip_all)]
1755    pub fn fold_buffers(
1756        &mut self,
1757        buffer_ids: impl IntoIterator<Item = BufferId>,
1758        multi_buffer: &MultiBuffer,
1759        cx: &App,
1760    ) {
1761        self.fold_or_unfold_buffers(true, buffer_ids, multi_buffer, cx);
1762    }
1763
1764    #[ztracing::instrument(skip_all)]
1765    pub fn unfold_buffers(
1766        &mut self,
1767        buffer_ids: impl IntoIterator<Item = BufferId>,
1768        multi_buffer: &MultiBuffer,
1769        cx: &App,
1770    ) {
1771        self.fold_or_unfold_buffers(false, buffer_ids, multi_buffer, cx);
1772    }
1773
1774    #[ztracing::instrument(skip_all)]
1775    fn fold_or_unfold_buffers(
1776        &mut self,
1777        fold: bool,
1778        buffer_ids: impl IntoIterator<Item = BufferId>,
1779        multi_buffer: &MultiBuffer,
1780        cx: &App,
1781    ) {
1782        let mut ranges = Vec::new();
1783        for buffer_id in buffer_ids {
1784            if fold {
1785                self.block_map.folded_buffers.insert(buffer_id);
1786            } else {
1787                self.block_map.folded_buffers.remove(&buffer_id);
1788            }
1789            ranges.extend(multi_buffer.excerpt_ranges_for_buffer(buffer_id, cx));
1790        }
1791        ranges.sort_unstable_by_key(|range| range.start);
1792
1793        let mut edits = Patch::default();
1794        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
1795        for range in ranges {
1796            let last_edit_row = cmp::min(
1797                wrap_snapshot.make_wrap_point(range.end, Bias::Right).row() + WrapRow(1),
1798                wrap_snapshot.max_point().row(),
1799            ) + WrapRow(1);
1800            let range = wrap_snapshot.make_wrap_point(range.start, Bias::Left).row()..last_edit_row;
1801            edits.push(Edit {
1802                old: range.clone(),
1803                new: range,
1804            });
1805        }
1806
1807        let default_patch = Patch::default();
1808        self.block_map.sync(
1809            &wrap_snapshot,
1810            edits,
1811            self.companion.as_deref().map(
1812                |&CompanionView {
1813                     entity_id,
1814                     wrap_snapshot,
1815                     companion,
1816                     ..
1817                 }| {
1818                    CompanionView::new(entity_id, wrap_snapshot, &default_patch, companion)
1819                },
1820            ),
1821        );
1822    }
1823
1824    #[ztracing::instrument(skip_all)]
1825    fn blocks_intersecting_buffer_range(
1826        &self,
1827        range: Range<MultiBufferOffset>,
1828        inclusive: bool,
1829    ) -> &[Arc<CustomBlock>] {
1830        if range.is_empty() && !inclusive {
1831            return &[];
1832        }
1833        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
1834        let buffer = wrap_snapshot.buffer_snapshot();
1835
1836        let start_block_ix = match self.block_map.custom_blocks.binary_search_by(|block| {
1837            let block_end = block.end().to_offset(buffer);
1838            block_end.cmp(&range.start).then(Ordering::Greater)
1839        }) {
1840            Ok(ix) | Err(ix) => ix,
1841        };
1842        let end_block_ix =
1843            match self.block_map.custom_blocks[start_block_ix..].binary_search_by(|block| {
1844                let block_start = block.start().to_offset(buffer);
1845                block_start.cmp(&range.end).then(if inclusive {
1846                    Ordering::Less
1847                } else {
1848                    Ordering::Greater
1849                })
1850            }) {
1851                Ok(ix) | Err(ix) => ix,
1852            };
1853
1854        &self.block_map.custom_blocks[start_block_ix..][..end_block_ix]
1855    }
1856}
1857
1858impl BlockSnapshot {
1859    #[cfg(test)]
1860    #[ztracing::instrument(skip_all)]
1861    pub fn text(&self) -> String {
1862        self.chunks(
1863            BlockRow(0)..self.transforms.summary().output_rows,
1864            false,
1865            false,
1866            Highlights::default(),
1867        )
1868        .map(|chunk| chunk.text)
1869        .collect()
1870    }
1871
1872    #[ztracing::instrument(skip_all)]
1873    pub(crate) fn chunks<'a>(
1874        &'a self,
1875        rows: Range<BlockRow>,
1876        language_aware: bool,
1877        masked: bool,
1878        highlights: Highlights<'a>,
1879    ) -> BlockChunks<'a> {
1880        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
1881
1882        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1883        cursor.seek(&rows.start, Bias::Right);
1884        let transform_output_start = cursor.start().0;
1885        let transform_input_start = cursor.start().1;
1886
1887        let mut input_start = transform_input_start;
1888        let mut input_end = transform_input_start;
1889        if let Some(transform) = cursor.item()
1890            && transform.block.is_none()
1891        {
1892            input_start += rows.start - transform_output_start;
1893            input_end += cmp::min(
1894                rows.end - transform_output_start,
1895                RowDelta(transform.summary.input_rows.0),
1896            );
1897        }
1898
1899        BlockChunks {
1900            input_chunks: self.wrap_snapshot.chunks(
1901                input_start..input_end,
1902                language_aware,
1903                highlights,
1904            ),
1905            input_chunk: Default::default(),
1906            transforms: cursor,
1907            output_row: rows.start,
1908            line_count_overflow: RowDelta(0),
1909            max_output_row,
1910            masked,
1911        }
1912    }
1913
1914    #[ztracing::instrument(skip_all)]
1915    pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
1916        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1917        cursor.seek(&start_row, Bias::Right);
1918        let Dimensions(output_start, input_start, _) = cursor.start();
1919        let overshoot = if cursor
1920            .item()
1921            .is_some_and(|transform| transform.block.is_none())
1922        {
1923            start_row - *output_start
1924        } else {
1925            RowDelta(0)
1926        };
1927        let input_start_row = *input_start + overshoot;
1928        BlockRows {
1929            transforms: cursor,
1930            input_rows: self.wrap_snapshot.row_infos(input_start_row),
1931            output_row: start_row,
1932            started: false,
1933        }
1934    }
1935
1936    #[ztracing::instrument(skip_all)]
1937    pub fn blocks_in_range(
1938        &self,
1939        rows: Range<BlockRow>,
1940    ) -> impl Iterator<Item = (BlockRow, &Block)> {
1941        let mut cursor = self.transforms.cursor::<BlockRow>(());
1942        cursor.seek(&rows.start, Bias::Left);
1943        while *cursor.start() < rows.start && cursor.end() <= rows.start {
1944            cursor.next();
1945        }
1946
1947        std::iter::from_fn(move || {
1948            while let Some(transform) = cursor.item() {
1949                let start_row = *cursor.start();
1950                if start_row > rows.end
1951                    || (start_row == rows.end
1952                        && transform
1953                            .block
1954                            .as_ref()
1955                            .is_some_and(|block| block.height() > 0))
1956                {
1957                    break;
1958                }
1959                if let Some(block) = &transform.block {
1960                    cursor.next();
1961                    return Some((start_row, block));
1962                } else {
1963                    cursor.next();
1964                }
1965            }
1966            None
1967        })
1968    }
1969
1970    #[ztracing::instrument(skip_all)]
1971    pub(crate) fn sticky_header_excerpt(&self, position: f64) -> Option<StickyHeaderExcerpt<'_>> {
1972        let top_row = position as u32;
1973        let mut cursor = self.transforms.cursor::<BlockRow>(());
1974        cursor.seek(&BlockRow(top_row), Bias::Right);
1975
1976        while let Some(transform) = cursor.item() {
1977            match &transform.block {
1978                Some(
1979                    Block::ExcerptBoundary { excerpt, .. } | Block::BufferHeader { excerpt, .. },
1980                ) => {
1981                    return Some(StickyHeaderExcerpt { excerpt });
1982                }
1983                Some(block) if block.is_buffer_header() => return None,
1984                _ => {
1985                    cursor.prev();
1986                    continue;
1987                }
1988            }
1989        }
1990
1991        None
1992    }
1993
1994    #[ztracing::instrument(skip_all)]
1995    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1996        let buffer = self.wrap_snapshot.buffer_snapshot();
1997        let wrap_point = match block_id {
1998            BlockId::Custom(custom_block_id) => {
1999                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
2000                return Some(Block::Custom(custom_block.clone()));
2001            }
2002            BlockId::ExcerptBoundary(next_excerpt_id) => {
2003                let excerpt_range = buffer.range_for_excerpt(next_excerpt_id)?;
2004                self.wrap_snapshot
2005                    .make_wrap_point(excerpt_range.start, Bias::Left)
2006            }
2007            BlockId::FoldedBuffer(excerpt_id) => self
2008                .wrap_snapshot
2009                .make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left),
2010            BlockId::Spacer(_) => return None,
2011        };
2012        let wrap_row = wrap_point.row();
2013
2014        let mut cursor = self.transforms.cursor::<WrapRow>(());
2015        cursor.seek(&wrap_row, Bias::Left);
2016
2017        while let Some(transform) = cursor.item() {
2018            if let Some(block) = transform.block.as_ref() {
2019                if block.id() == block_id {
2020                    return Some(block.clone());
2021                }
2022            } else if *cursor.start() > wrap_row {
2023                break;
2024            }
2025
2026            cursor.next();
2027        }
2028
2029        None
2030    }
2031
2032    #[ztracing::instrument(skip_all)]
2033    pub fn max_point(&self) -> BlockPoint {
2034        let row = self
2035            .transforms
2036            .summary()
2037            .output_rows
2038            .saturating_sub(RowDelta(1));
2039        BlockPoint::new(row, self.line_len(row))
2040    }
2041
2042    #[ztracing::instrument(skip_all)]
2043    pub fn longest_row(&self) -> BlockRow {
2044        self.transforms.summary().longest_row
2045    }
2046
2047    #[ztracing::instrument(skip_all)]
2048    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
2049        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2050        cursor.seek(&range.start, Bias::Right);
2051
2052        let mut longest_row = range.start;
2053        let mut longest_row_chars = 0;
2054        if let Some(transform) = cursor.item() {
2055            if transform.block.is_none() {
2056                let &Dimensions(output_start, input_start, _) = cursor.start();
2057                let overshoot = range.start - output_start;
2058                let wrap_start_row = input_start + WrapRow(overshoot.0);
2059                let wrap_end_row = cmp::min(
2060                    input_start + WrapRow((range.end - output_start).0),
2061                    cursor.end().1,
2062                );
2063                let summary = self
2064                    .wrap_snapshot
2065                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2066                longest_row = BlockRow(range.start.0 + summary.longest_row);
2067                longest_row_chars = summary.longest_row_chars;
2068            }
2069            cursor.next();
2070        }
2071
2072        let cursor_start_row = cursor.start().0;
2073        if range.end > cursor_start_row {
2074            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right);
2075            if summary.longest_row_chars > longest_row_chars {
2076                longest_row = cursor_start_row + summary.longest_row;
2077                longest_row_chars = summary.longest_row_chars;
2078            }
2079
2080            if let Some(transform) = cursor.item()
2081                && transform.block.is_none()
2082            {
2083                let &Dimensions(output_start, input_start, _) = cursor.start();
2084                let overshoot = range.end - output_start;
2085                let wrap_start_row = input_start;
2086                let wrap_end_row = input_start + overshoot;
2087                let summary = self
2088                    .wrap_snapshot
2089                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2090                if summary.longest_row_chars > longest_row_chars {
2091                    longest_row = output_start + RowDelta(summary.longest_row);
2092                }
2093            }
2094        }
2095
2096        longest_row
2097    }
2098
2099    #[ztracing::instrument(skip_all)]
2100    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
2101        let (start, _, item) =
2102            self.transforms
2103                .find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
2104        if let Some(transform) = item {
2105            let Dimensions(output_start, input_start, _) = start;
2106            let overshoot = row - output_start;
2107            if transform.block.is_some() {
2108                0
2109            } else {
2110                self.wrap_snapshot.line_len(input_start + overshoot)
2111            }
2112        } else if row == BlockRow(0) {
2113            0
2114        } else {
2115            panic!("row out of range");
2116        }
2117    }
2118
2119    #[ztracing::instrument(skip_all)]
2120    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
2121        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2122        item.is_some_and(|t| t.block.is_some())
2123    }
2124
2125    #[ztracing::instrument(skip_all)]
2126    pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
2127        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2128        let Some(transform) = item else {
2129            return false;
2130        };
2131        matches!(transform.block, Some(Block::FoldedBuffer { .. }))
2132    }
2133
2134    #[ztracing::instrument(skip_all)]
2135    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
2136        let wrap_point = self
2137            .wrap_snapshot
2138            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
2139        let (_, _, item) = self
2140            .transforms
2141            .find::<WrapRow, _>((), &wrap_point.row(), Bias::Right);
2142        item.is_some_and(|transform| {
2143            transform
2144                .block
2145                .as_ref()
2146                .is_some_and(|block| block.is_replacement())
2147        })
2148    }
2149
2150    #[ztracing::instrument(skip_all)]
2151    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
2152        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2153        cursor.seek(&BlockRow(point.row), Bias::Right);
2154
2155        let max_input_row = self.transforms.summary().input_rows;
2156        let mut search_left = (bias == Bias::Left && cursor.start().1 > WrapRow(0))
2157            || cursor.end().1 == max_input_row;
2158        let mut reversed = false;
2159
2160        loop {
2161            if let Some(transform) = cursor.item() {
2162                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
2163                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
2164                let output_start = Point::new(output_start_row.0, 0);
2165                let input_start = Point::new(input_start_row.0, 0);
2166                let input_end = Point::new(input_end_row.0, 0);
2167
2168                match transform.block.as_ref() {
2169                    Some(block) => {
2170                        if block.is_replacement()
2171                            && (((bias == Bias::Left || search_left) && output_start <= point.0)
2172                                || (!search_left && output_start >= point.0))
2173                        {
2174                            return BlockPoint(output_start);
2175                        }
2176                    }
2177                    None => {
2178                        let input_point = if point.row >= output_end_row.0 {
2179                            let line_len = self.wrap_snapshot.line_len(input_end_row - RowDelta(1));
2180                            self.wrap_snapshot.clip_point(
2181                                WrapPoint::new(input_end_row - RowDelta(1), line_len),
2182                                bias,
2183                            )
2184                        } else {
2185                            let output_overshoot = point.0.saturating_sub(output_start);
2186                            self.wrap_snapshot
2187                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
2188                        };
2189
2190                        if (input_start..input_end).contains(&input_point.0) {
2191                            let input_overshoot = input_point.0.saturating_sub(input_start);
2192                            return BlockPoint(output_start + input_overshoot);
2193                        }
2194                    }
2195                }
2196
2197                if search_left {
2198                    cursor.prev();
2199                } else {
2200                    cursor.next();
2201                }
2202            } else if reversed {
2203                return self.max_point();
2204            } else {
2205                reversed = true;
2206                search_left = !search_left;
2207                cursor.seek(&BlockRow(point.row), Bias::Right);
2208            }
2209        }
2210    }
2211
2212    #[ztracing::instrument(skip_all)]
2213    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
2214        let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
2215            (),
2216            &wrap_point.row(),
2217            Bias::Right,
2218        );
2219        if let Some(transform) = item {
2220            if transform.block.is_some() {
2221                BlockPoint::new(start.1, 0)
2222            } else {
2223                let Dimensions(input_start_row, output_start_row, _) = start;
2224                let input_start = Point::new(input_start_row.0, 0);
2225                let output_start = Point::new(output_start_row.0, 0);
2226                let input_overshoot = wrap_point.0 - input_start;
2227                BlockPoint(output_start + input_overshoot)
2228            }
2229        } else {
2230            self.max_point()
2231        }
2232    }
2233
2234    #[ztracing::instrument(skip_all)]
2235    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
2236        let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
2237            (),
2238            &BlockRow(block_point.row),
2239            Bias::Right,
2240        );
2241        if let Some(transform) = item {
2242            match transform.block.as_ref() {
2243                Some(block) => {
2244                    if block.place_below() {
2245                        let wrap_row = start.1 - RowDelta(1);
2246                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2247                    } else if block.place_above() {
2248                        WrapPoint::new(start.1, 0)
2249                    } else if bias == Bias::Left {
2250                        WrapPoint::new(start.1, 0)
2251                    } else {
2252                        let wrap_row = end.1 - RowDelta(1);
2253                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2254                    }
2255                }
2256                None => {
2257                    let overshoot = block_point.row() - start.0;
2258                    let wrap_row = start.1 + RowDelta(overshoot.0);
2259                    WrapPoint::new(wrap_row, block_point.column)
2260                }
2261            }
2262        } else {
2263            self.wrap_snapshot.max_point()
2264        }
2265    }
2266}
2267
2268impl BlockChunks<'_> {
2269    /// Go to the next transform
2270    #[ztracing::instrument(skip_all)]
2271    fn advance(&mut self) {
2272        self.input_chunk = Chunk::default();
2273        self.transforms.next();
2274        while let Some(transform) = self.transforms.item() {
2275            if transform
2276                .block
2277                .as_ref()
2278                .is_some_and(|block| block.height() == 0)
2279            {
2280                self.transforms.next();
2281            } else {
2282                break;
2283            }
2284        }
2285
2286        if self
2287            .transforms
2288            .item()
2289            .is_some_and(|transform| transform.block.is_none())
2290        {
2291            let start_input_row = self.transforms.start().1;
2292            let start_output_row = self.transforms.start().0;
2293            if start_output_row < self.max_output_row {
2294                let end_input_row = cmp::min(
2295                    self.transforms.end().1,
2296                    start_input_row + (self.max_output_row - start_output_row),
2297                );
2298                self.input_chunks.seek(start_input_row..end_input_row);
2299            }
2300        }
2301    }
2302}
2303
2304pub struct StickyHeaderExcerpt<'a> {
2305    pub excerpt: &'a ExcerptInfo,
2306}
2307
2308impl<'a> Iterator for BlockChunks<'a> {
2309    type Item = Chunk<'a>;
2310
2311    #[ztracing::instrument(skip_all)]
2312    fn next(&mut self) -> Option<Self::Item> {
2313        if self.output_row >= self.max_output_row {
2314            return None;
2315        }
2316
2317        if self.line_count_overflow > RowDelta(0) {
2318            let lines = self.line_count_overflow.0.min(u128::BITS);
2319            self.line_count_overflow.0 -= lines;
2320            self.output_row += RowDelta(lines);
2321            return Some(Chunk {
2322                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines as usize]) },
2323                chars: 1u128.unbounded_shl(lines).wrapping_sub(1),
2324                ..Default::default()
2325            });
2326        }
2327
2328        let transform = self.transforms.item()?;
2329        if transform.block.is_some() {
2330            let block_start = self.transforms.start().0;
2331            let mut block_end = self.transforms.end().0;
2332            self.advance();
2333            if self.transforms.item().is_none() {
2334                block_end -= RowDelta(1);
2335            }
2336
2337            let start_in_block = self.output_row - block_start;
2338            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
2339            let line_count = end_in_block - start_in_block;
2340            let lines = RowDelta(line_count.0.min(u128::BITS));
2341            self.line_count_overflow = line_count - lines;
2342            self.output_row += lines;
2343
2344            return Some(Chunk {
2345                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines.0 as usize]) },
2346                chars: 1u128.unbounded_shl(lines.0).wrapping_sub(1),
2347                ..Default::default()
2348            });
2349        }
2350
2351        if self.input_chunk.text.is_empty() {
2352            if let Some(input_chunk) = self.input_chunks.next() {
2353                self.input_chunk = input_chunk;
2354            } else {
2355                if self.output_row < self.max_output_row {
2356                    self.output_row.0 += 1;
2357                    self.advance();
2358                    if self.transforms.item().is_some() {
2359                        return Some(Chunk {
2360                            text: "\n",
2361                            chars: 1,
2362                            ..Default::default()
2363                        });
2364                    }
2365                }
2366                return None;
2367            }
2368        }
2369
2370        let transform_end = self.transforms.end().0;
2371        let (prefix_rows, prefix_bytes) =
2372            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
2373        self.output_row += prefix_rows;
2374
2375        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
2376        self.input_chunk.text = suffix;
2377        self.input_chunk.tabs >>= prefix_bytes.saturating_sub(1);
2378        self.input_chunk.chars >>= prefix_bytes.saturating_sub(1);
2379
2380        let mut tabs = self.input_chunk.tabs;
2381        let mut chars = self.input_chunk.chars;
2382
2383        if self.masked {
2384            // Not great for multibyte text because to keep cursor math correct we
2385            // need to have the same number of chars in the input as output.
2386            let chars_count = prefix.chars().count();
2387            let bullet_len = chars_count;
2388            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
2389            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
2390            tabs = 0;
2391        }
2392
2393        let chunk = Chunk {
2394            text: prefix,
2395            tabs,
2396            chars,
2397            ..self.input_chunk.clone()
2398        };
2399
2400        if self.output_row == transform_end {
2401            self.advance();
2402        }
2403
2404        Some(chunk)
2405    }
2406}
2407
2408impl Iterator for BlockRows<'_> {
2409    type Item = RowInfo;
2410
2411    #[ztracing::instrument(skip_all)]
2412    fn next(&mut self) -> Option<Self::Item> {
2413        if self.started {
2414            self.output_row.0 += 1;
2415        } else {
2416            self.started = true;
2417        }
2418
2419        if self.output_row >= self.transforms.end().0 {
2420            self.transforms.next();
2421            while let Some(transform) = self.transforms.item() {
2422                if transform
2423                    .block
2424                    .as_ref()
2425                    .is_some_and(|block| block.height() == 0)
2426                {
2427                    self.transforms.next();
2428                } else {
2429                    break;
2430                }
2431            }
2432
2433            let transform = self.transforms.item()?;
2434            if transform
2435                .block
2436                .as_ref()
2437                .is_none_or(|block| block.is_replacement())
2438            {
2439                self.input_rows.seek(self.transforms.start().1);
2440            }
2441        }
2442
2443        let transform = self.transforms.item()?;
2444        if transform.block.as_ref().is_none_or(|block| {
2445            block.is_replacement()
2446                && self.transforms.start().0 == self.output_row
2447                && matches!(block, Block::FoldedBuffer { .. }).not()
2448        }) {
2449            self.input_rows.next()
2450        } else {
2451            Some(RowInfo::default())
2452        }
2453    }
2454}
2455
2456impl sum_tree::Item for Transform {
2457    type Summary = TransformSummary;
2458
2459    fn summary(&self, _cx: ()) -> Self::Summary {
2460        self.summary.clone()
2461    }
2462}
2463
2464impl sum_tree::ContextLessSummary for TransformSummary {
2465    fn zero() -> Self {
2466        Default::default()
2467    }
2468
2469    fn add_summary(&mut self, summary: &Self) {
2470        if summary.longest_row_chars > self.longest_row_chars {
2471            self.longest_row = self.output_rows + summary.longest_row;
2472            self.longest_row_chars = summary.longest_row_chars;
2473        }
2474        self.input_rows += summary.input_rows;
2475        self.output_rows += summary.output_rows;
2476    }
2477}
2478
2479impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
2480    fn zero(_cx: ()) -> Self {
2481        Default::default()
2482    }
2483
2484    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2485        *self += summary.input_rows;
2486    }
2487}
2488
2489impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
2490    fn zero(_cx: ()) -> Self {
2491        Default::default()
2492    }
2493
2494    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2495        *self += summary.output_rows;
2496    }
2497}
2498
2499impl Deref for BlockContext<'_, '_> {
2500    type Target = App;
2501
2502    fn deref(&self) -> &Self::Target {
2503        self.app
2504    }
2505}
2506
2507impl DerefMut for BlockContext<'_, '_> {
2508    fn deref_mut(&mut self) -> &mut Self::Target {
2509        self.app
2510    }
2511}
2512
2513impl CustomBlock {
2514    #[ztracing::instrument(skip_all)]
2515    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
2516        self.render.lock()(cx)
2517    }
2518
2519    #[ztracing::instrument(skip_all)]
2520    pub fn start(&self) -> Anchor {
2521        *self.placement.start()
2522    }
2523
2524    #[ztracing::instrument(skip_all)]
2525    pub fn end(&self) -> Anchor {
2526        *self.placement.end()
2527    }
2528
2529    pub fn style(&self) -> BlockStyle {
2530        self.style
2531    }
2532}
2533
2534impl Debug for CustomBlock {
2535    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2536        f.debug_struct("Block")
2537            .field("id", &self.id)
2538            .field("placement", &self.placement)
2539            .field("height", &self.height)
2540            .field("style", &self.style)
2541            .field("priority", &self.priority)
2542            .finish_non_exhaustive()
2543    }
2544}
2545
2546// Count the number of bytes prior to a target point. If the string doesn't contain the target
2547// point, return its total extent. Otherwise return the target point itself.
2548fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
2549    let mut row = 0;
2550    let mut offset = 0;
2551    for (ix, line) in s.split('\n').enumerate() {
2552        if ix > 0 {
2553            row += 1;
2554            offset += 1;
2555        }
2556        if row >= target.0 {
2557            break;
2558        }
2559        offset += line.len();
2560    }
2561    (RowDelta(row), offset)
2562}
2563
2564#[cfg(test)]
2565mod tests {
2566    use super::*;
2567    use crate::{
2568        display_map::{
2569            Companion, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
2570        },
2571        split::{convert_lhs_rows_to_rhs, convert_rhs_rows_to_lhs},
2572        test::test_font,
2573    };
2574    use buffer_diff::BufferDiff;
2575    use gpui::{App, AppContext as _, Element, div, font, px};
2576    use itertools::Itertools;
2577    use language::{Buffer, Capability};
2578    use multi_buffer::{ExcerptRange, MultiBuffer};
2579    use rand::prelude::*;
2580    use settings::SettingsStore;
2581    use std::env;
2582    use util::RandomCharIter;
2583
2584    #[gpui::test]
2585    fn test_offset_for_row() {
2586        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2587        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2588        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2589        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2590        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2591        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2592        assert_eq!(
2593            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2594            (RowDelta(0), 0)
2595        );
2596        assert_eq!(
2597            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2598            (RowDelta(1), 4)
2599        );
2600        assert_eq!(
2601            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2602            (RowDelta(2), 8)
2603        );
2604        assert_eq!(
2605            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2606            (RowDelta(2), 11)
2607        );
2608    }
2609
2610    #[gpui::test]
2611    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2612        cx.update(init_test);
2613
2614        let text = "aaa\nbbb\nccc\nddd";
2615
2616        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2617        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2618        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2619        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2620        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2621        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2622        let (wrap_map, wraps_snapshot) =
2623            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2624        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2625
2626        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2627        let block_ids = writer.insert(vec![
2628            BlockProperties {
2629                style: BlockStyle::Fixed,
2630                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2631                height: Some(1),
2632                render: Arc::new(|_| div().into_any()),
2633                priority: 0,
2634            },
2635            BlockProperties {
2636                style: BlockStyle::Fixed,
2637                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2638                height: Some(2),
2639                render: Arc::new(|_| div().into_any()),
2640                priority: 0,
2641            },
2642            BlockProperties {
2643                style: BlockStyle::Fixed,
2644                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2645                height: Some(3),
2646                render: Arc::new(|_| div().into_any()),
2647                priority: 0,
2648            },
2649        ]);
2650
2651        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2652        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2653
2654        let blocks = snapshot
2655            .blocks_in_range(BlockRow(0)..BlockRow(8))
2656            .map(|(start_row, block)| {
2657                let block = block.as_custom().unwrap();
2658                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
2659            })
2660            .collect::<Vec<_>>();
2661
2662        // When multiple blocks are on the same line, the newer blocks appear first.
2663        assert_eq!(
2664            blocks,
2665            &[
2666                (1..2, block_ids[0]),
2667                (2..4, block_ids[1]),
2668                (7..10, block_ids[2]),
2669            ]
2670        );
2671
2672        assert_eq!(
2673            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
2674            BlockPoint::new(BlockRow(0), 3)
2675        );
2676        assert_eq!(
2677            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
2678            BlockPoint::new(BlockRow(4), 0)
2679        );
2680        assert_eq!(
2681            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
2682            BlockPoint::new(BlockRow(6), 3)
2683        );
2684
2685        assert_eq!(
2686            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
2687            WrapPoint::new(WrapRow(0), 3)
2688        );
2689        assert_eq!(
2690            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2691            WrapPoint::new(WrapRow(1), 0)
2692        );
2693        assert_eq!(
2694            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
2695            WrapPoint::new(WrapRow(1), 0)
2696        );
2697        assert_eq!(
2698            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2699            WrapPoint::new(WrapRow(3), 3)
2700        );
2701
2702        assert_eq!(
2703            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2704            BlockPoint::new(BlockRow(0), 3)
2705        );
2706        assert_eq!(
2707            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
2708            BlockPoint::new(BlockRow(4), 0)
2709        );
2710        assert_eq!(
2711            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
2712            BlockPoint::new(BlockRow(0), 3)
2713        );
2714        assert_eq!(
2715            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
2716            BlockPoint::new(BlockRow(4), 0)
2717        );
2718        assert_eq!(
2719            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
2720            BlockPoint::new(BlockRow(4), 0)
2721        );
2722        assert_eq!(
2723            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
2724            BlockPoint::new(BlockRow(4), 0)
2725        );
2726        assert_eq!(
2727            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
2728            BlockPoint::new(BlockRow(6), 3)
2729        );
2730        assert_eq!(
2731            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
2732            BlockPoint::new(BlockRow(6), 3)
2733        );
2734        assert_eq!(
2735            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2736            BlockPoint::new(BlockRow(6), 3)
2737        );
2738        assert_eq!(
2739            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
2740            BlockPoint::new(BlockRow(6), 3)
2741        );
2742
2743        assert_eq!(
2744            snapshot
2745                .row_infos(BlockRow(0))
2746                .map(|row_info| row_info.buffer_row)
2747                .collect::<Vec<_>>(),
2748            &[
2749                Some(0),
2750                None,
2751                None,
2752                None,
2753                Some(1),
2754                Some(2),
2755                Some(3),
2756                None,
2757                None,
2758                None
2759            ]
2760        );
2761
2762        // Insert a line break, separating two block decorations into separate lines.
2763        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2764            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2765            buffer.snapshot(cx)
2766        });
2767
2768        let (inlay_snapshot, inlay_edits) =
2769            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2770        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2771        let (tab_snapshot, tab_edits) =
2772            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2773        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2774            wrap_map.sync(tab_snapshot, tab_edits, cx)
2775        });
2776        let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
2777        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2778    }
2779
2780    #[gpui::test]
2781    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2782        init_test(cx);
2783
2784        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2785        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2786        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2787
2788        let mut excerpt_ids = Vec::new();
2789        let multi_buffer = cx.new(|cx| {
2790            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2791            excerpt_ids.extend(multi_buffer.push_excerpts(
2792                buffer1.clone(),
2793                [ExcerptRange::new(0..buffer1.read(cx).len())],
2794                cx,
2795            ));
2796            excerpt_ids.extend(multi_buffer.push_excerpts(
2797                buffer2.clone(),
2798                [ExcerptRange::new(0..buffer2.read(cx).len())],
2799                cx,
2800            ));
2801            excerpt_ids.extend(multi_buffer.push_excerpts(
2802                buffer3.clone(),
2803                [ExcerptRange::new(0..buffer3.read(cx).len())],
2804                cx,
2805            ));
2806
2807            multi_buffer
2808        });
2809
2810        let font = test_font();
2811        let font_size = px(14.);
2812        let font_id = cx.text_system().resolve_font(&font);
2813        let mut wrap_width = px(0.);
2814        for c in "Buff".chars() {
2815            wrap_width += cx
2816                .text_system()
2817                .advance(font_id, font_size, c)
2818                .unwrap()
2819                .width;
2820        }
2821
2822        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2823        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
2824        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2825        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2826        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2827
2828        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2829        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2830
2831        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2832        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
2833
2834        let blocks: Vec<_> = snapshot
2835            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2836            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
2837            .collect();
2838        assert_eq!(
2839            blocks,
2840            vec![
2841                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
2842                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
2843                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
2844            ]
2845        );
2846    }
2847
2848    #[gpui::test]
2849    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2850        cx.update(init_test);
2851
2852        let text = "aaa\nbbb\nccc\nddd";
2853
2854        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2855        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2856        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2857        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2858        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2859        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2860        let (_wrap_map, wraps_snapshot) =
2861            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2862        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2863
2864        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2865        let block_ids = writer.insert(vec![
2866            BlockProperties {
2867                style: BlockStyle::Fixed,
2868                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2869                height: Some(1),
2870                render: Arc::new(|_| div().into_any()),
2871                priority: 0,
2872            },
2873            BlockProperties {
2874                style: BlockStyle::Fixed,
2875                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2876                height: Some(2),
2877                render: Arc::new(|_| div().into_any()),
2878                priority: 0,
2879            },
2880            BlockProperties {
2881                style: BlockStyle::Fixed,
2882                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2883                height: Some(3),
2884                render: Arc::new(|_| div().into_any()),
2885                priority: 0,
2886            },
2887        ]);
2888
2889        {
2890            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
2891            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2892
2893            let mut block_map_writer =
2894                block_map.write(wraps_snapshot.clone(), Default::default(), None);
2895
2896            let mut new_heights = HashMap::default();
2897            new_heights.insert(block_ids[0], 2);
2898            block_map_writer.resize(new_heights);
2899            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
2900            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2901        }
2902
2903        {
2904            let mut block_map_writer =
2905                block_map.write(wraps_snapshot.clone(), Default::default(), None);
2906
2907            let mut new_heights = HashMap::default();
2908            new_heights.insert(block_ids[0], 1);
2909            block_map_writer.resize(new_heights);
2910
2911            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
2912            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2913        }
2914
2915        {
2916            let mut block_map_writer =
2917                block_map.write(wraps_snapshot.clone(), Default::default(), None);
2918
2919            let mut new_heights = HashMap::default();
2920            new_heights.insert(block_ids[0], 0);
2921            block_map_writer.resize(new_heights);
2922
2923            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
2924            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2925        }
2926
2927        {
2928            let mut block_map_writer =
2929                block_map.write(wraps_snapshot.clone(), Default::default(), None);
2930
2931            let mut new_heights = HashMap::default();
2932            new_heights.insert(block_ids[0], 3);
2933            block_map_writer.resize(new_heights);
2934
2935            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
2936            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2937        }
2938
2939        {
2940            let mut block_map_writer =
2941                block_map.write(wraps_snapshot.clone(), Default::default(), None);
2942
2943            let mut new_heights = HashMap::default();
2944            new_heights.insert(block_ids[0], 3);
2945            block_map_writer.resize(new_heights);
2946
2947            let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2948            // Same height as before, should remain the same
2949            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2950        }
2951    }
2952
2953    #[gpui::test]
2954    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2955        cx.update(init_test);
2956
2957        let text = "one two three\nfour five six\nseven eight";
2958
2959        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2960        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2961        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2962        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2963        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2964        let (_, wraps_snapshot) = cx.update(|cx| {
2965            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
2966        });
2967        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2968
2969        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2970        writer.insert(vec![
2971            BlockProperties {
2972                style: BlockStyle::Fixed,
2973                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2974                render: Arc::new(|_| div().into_any()),
2975                height: Some(1),
2976                priority: 0,
2977            },
2978            BlockProperties {
2979                style: BlockStyle::Fixed,
2980                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2981                render: Arc::new(|_| div().into_any()),
2982                height: Some(1),
2983                priority: 0,
2984            },
2985        ]);
2986
2987        // Blocks with an 'above' disposition go above their corresponding buffer line.
2988        // Blocks with a 'below' disposition go below their corresponding buffer line.
2989        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2990        assert_eq!(
2991            snapshot.text(),
2992            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2993        );
2994    }
2995
2996    #[gpui::test]
2997    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2998        cx.update(init_test);
2999
3000        let text = "line1\nline2\nline3\nline4\nline5";
3001
3002        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3003        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
3004        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3005        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3006        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3007        let tab_size = 1.try_into().unwrap();
3008        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3009        let (wrap_map, wraps_snapshot) =
3010            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3011        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3012
3013        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3014        let replace_block_id = writer.insert(vec![BlockProperties {
3015            style: BlockStyle::Fixed,
3016            placement: BlockPlacement::Replace(
3017                buffer_snapshot.anchor_after(Point::new(1, 3))
3018                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
3019            ),
3020            height: Some(4),
3021            render: Arc::new(|_| div().into_any()),
3022            priority: 0,
3023        }])[0];
3024
3025        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3026        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3027
3028        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3029            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
3030            buffer.snapshot(cx)
3031        });
3032        let (inlay_snapshot, inlay_edits) =
3033            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
3034        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3035        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3036        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3037            wrap_map.sync(tab_snapshot, tab_edits, cx)
3038        });
3039        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3040        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3041
3042        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3043            buffer.edit(
3044                [(
3045                    Point::new(1, 5)..Point::new(1, 5),
3046                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
3047                )],
3048                None,
3049                cx,
3050            );
3051            buffer.snapshot(cx)
3052        });
3053        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
3054            buffer_snapshot.clone(),
3055            buffer_subscription.consume().into_inner(),
3056        );
3057        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3058        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3059        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3060            wrap_map.sync(tab_snapshot, tab_edits, cx)
3061        });
3062        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3063        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3064
3065        // Blocks inserted right above the start or right below the end of the 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::Above(buffer_snapshot.anchor_after(Point::new(0, 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(1, 3))),
3078                height: Some(1),
3079                render: Arc::new(|_| div().into_any()),
3080                priority: 0,
3081            },
3082            BlockProperties {
3083                style: BlockStyle::Fixed,
3084                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
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        // Ensure blocks inserted *inside* replaced region are hidden.
3094        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3095        writer.insert(vec![
3096            BlockProperties {
3097                style: BlockStyle::Fixed,
3098                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
3099                height: Some(1),
3100                render: Arc::new(|_| div().into_any()),
3101                priority: 0,
3102            },
3103            BlockProperties {
3104                style: BlockStyle::Fixed,
3105                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
3106                height: Some(1),
3107                render: Arc::new(|_| div().into_any()),
3108                priority: 0,
3109            },
3110            BlockProperties {
3111                style: BlockStyle::Fixed,
3112                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
3113                height: Some(1),
3114                render: Arc::new(|_| div().into_any()),
3115                priority: 0,
3116            },
3117        ]);
3118        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3119        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3120
3121        // Removing the replace block shows all the hidden blocks again.
3122        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3123        writer.remove(HashSet::from_iter([replace_block_id]));
3124        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3125        assert_eq!(
3126            blocks_snapshot.text(),
3127            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
3128        );
3129    }
3130
3131    #[gpui::test]
3132    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
3133        cx.update(init_test);
3134
3135        let text = "111\n222\n333\n444\n555\n666";
3136
3137        let buffer = cx.update(|cx| {
3138            MultiBuffer::build_multi(
3139                [
3140                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
3141                    (
3142                        text,
3143                        vec![
3144                            Point::new(1, 0)..Point::new(1, 3),
3145                            Point::new(2, 0)..Point::new(2, 3),
3146                            Point::new(3, 0)..Point::new(3, 3),
3147                        ],
3148                    ),
3149                    (
3150                        text,
3151                        vec![
3152                            Point::new(4, 0)..Point::new(4, 3),
3153                            Point::new(5, 0)..Point::new(5, 3),
3154                        ],
3155                    ),
3156                ],
3157                cx,
3158            )
3159        });
3160        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3161        let buffer_ids = buffer_snapshot
3162            .excerpts()
3163            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3164            .dedup()
3165            .collect::<Vec<_>>();
3166        assert_eq!(buffer_ids.len(), 3);
3167        let buffer_id_1 = buffer_ids[0];
3168        let buffer_id_2 = buffer_ids[1];
3169        let buffer_id_3 = buffer_ids[2];
3170
3171        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3172        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3173        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3174        let (_, wrap_snapshot) =
3175            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3176        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3177        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3178
3179        assert_eq!(
3180            blocks_snapshot.text(),
3181            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
3182        );
3183        assert_eq!(
3184            blocks_snapshot
3185                .row_infos(BlockRow(0))
3186                .map(|i| i.buffer_row)
3187                .collect::<Vec<_>>(),
3188            vec![
3189                None,
3190                None,
3191                Some(0),
3192                None,
3193                None,
3194                Some(1),
3195                None,
3196                Some(2),
3197                None,
3198                Some(3),
3199                None,
3200                None,
3201                Some(4),
3202                None,
3203                Some(5),
3204            ]
3205        );
3206
3207        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3208        let excerpt_blocks_2 = writer.insert(vec![
3209            BlockProperties {
3210                style: BlockStyle::Fixed,
3211                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3212                height: Some(1),
3213                render: Arc::new(|_| div().into_any()),
3214                priority: 0,
3215            },
3216            BlockProperties {
3217                style: BlockStyle::Fixed,
3218                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
3219                height: Some(1),
3220                render: Arc::new(|_| div().into_any()),
3221                priority: 0,
3222            },
3223            BlockProperties {
3224                style: BlockStyle::Fixed,
3225                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
3226                height: Some(1),
3227                render: Arc::new(|_| div().into_any()),
3228                priority: 0,
3229            },
3230        ]);
3231        let excerpt_blocks_3 = writer.insert(vec![
3232            BlockProperties {
3233                style: BlockStyle::Fixed,
3234                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
3235                height: Some(1),
3236                render: Arc::new(|_| div().into_any()),
3237                priority: 0,
3238            },
3239            BlockProperties {
3240                style: BlockStyle::Fixed,
3241                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
3242                height: Some(1),
3243                render: Arc::new(|_| div().into_any()),
3244                priority: 0,
3245            },
3246        ]);
3247
3248        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3249        assert_eq!(
3250            blocks_snapshot.text(),
3251            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3252        );
3253        assert_eq!(
3254            blocks_snapshot
3255                .row_infos(BlockRow(0))
3256                .map(|i| i.buffer_row)
3257                .collect::<Vec<_>>(),
3258            vec![
3259                None,
3260                None,
3261                Some(0),
3262                None,
3263                None,
3264                None,
3265                Some(1),
3266                None,
3267                None,
3268                Some(2),
3269                None,
3270                Some(3),
3271                None,
3272                None,
3273                None,
3274                None,
3275                Some(4),
3276                None,
3277                Some(5),
3278                None,
3279            ]
3280        );
3281
3282        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3283        buffer.read_with(cx, |buffer, cx| {
3284            writer.fold_buffers([buffer_id_1], buffer, cx);
3285        });
3286        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
3287            style: BlockStyle::Fixed,
3288            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
3289            height: Some(1),
3290            render: Arc::new(|_| div().into_any()),
3291            priority: 0,
3292        }]);
3293        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3294        let blocks = blocks_snapshot
3295            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3296            .collect::<Vec<_>>();
3297        for (_, block) in &blocks {
3298            if let BlockId::Custom(custom_block_id) = block.id() {
3299                assert!(
3300                    !excerpt_blocks_1.contains(&custom_block_id),
3301                    "Should have no blocks from the folded buffer"
3302                );
3303                assert!(
3304                    excerpt_blocks_2.contains(&custom_block_id)
3305                        || excerpt_blocks_3.contains(&custom_block_id),
3306                    "Should have only blocks from unfolded buffers"
3307                );
3308            }
3309        }
3310        assert_eq!(
3311            1,
3312            blocks
3313                .iter()
3314                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3315                .count(),
3316            "Should have one folded block, producing a header of the second buffer"
3317        );
3318        assert_eq!(
3319            blocks_snapshot.text(),
3320            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3321        );
3322        assert_eq!(
3323            blocks_snapshot
3324                .row_infos(BlockRow(0))
3325                .map(|i| i.buffer_row)
3326                .collect::<Vec<_>>(),
3327            vec![
3328                None,
3329                None,
3330                None,
3331                None,
3332                None,
3333                Some(1),
3334                None,
3335                None,
3336                Some(2),
3337                None,
3338                Some(3),
3339                None,
3340                None,
3341                None,
3342                None,
3343                Some(4),
3344                None,
3345                Some(5),
3346                None,
3347            ]
3348        );
3349
3350        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3351        buffer.read_with(cx, |buffer, cx| {
3352            writer.fold_buffers([buffer_id_2], buffer, cx);
3353        });
3354        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3355        let blocks = blocks_snapshot
3356            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3357            .collect::<Vec<_>>();
3358        for (_, block) in &blocks {
3359            if let BlockId::Custom(custom_block_id) = block.id() {
3360                assert!(
3361                    !excerpt_blocks_1.contains(&custom_block_id),
3362                    "Should have no blocks from the folded buffer_1"
3363                );
3364                assert!(
3365                    !excerpt_blocks_2.contains(&custom_block_id),
3366                    "Should have no blocks from the folded buffer_2"
3367                );
3368                assert!(
3369                    excerpt_blocks_3.contains(&custom_block_id),
3370                    "Should have only blocks from unfolded buffers"
3371                );
3372            }
3373        }
3374        assert_eq!(
3375            2,
3376            blocks
3377                .iter()
3378                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3379                .count(),
3380            "Should have two folded blocks, producing headers"
3381        );
3382        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
3383        assert_eq!(
3384            blocks_snapshot
3385                .row_infos(BlockRow(0))
3386                .map(|i| i.buffer_row)
3387                .collect::<Vec<_>>(),
3388            vec![
3389                None,
3390                None,
3391                None,
3392                None,
3393                None,
3394                None,
3395                None,
3396                Some(4),
3397                None,
3398                Some(5),
3399                None,
3400            ]
3401        );
3402
3403        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3404        buffer.read_with(cx, |buffer, cx| {
3405            writer.unfold_buffers([buffer_id_1], buffer, cx);
3406        });
3407        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3408        let blocks = blocks_snapshot
3409            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3410            .collect::<Vec<_>>();
3411        for (_, block) in &blocks {
3412            if let BlockId::Custom(custom_block_id) = block.id() {
3413                assert!(
3414                    !excerpt_blocks_2.contains(&custom_block_id),
3415                    "Should have no blocks from the folded buffer_2"
3416                );
3417                assert!(
3418                    excerpt_blocks_1.contains(&custom_block_id)
3419                        || excerpt_blocks_3.contains(&custom_block_id),
3420                    "Should have only blocks from unfolded buffers"
3421                );
3422            }
3423        }
3424        assert_eq!(
3425            1,
3426            blocks
3427                .iter()
3428                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3429                .count(),
3430            "Should be back to a single folded buffer, producing a header for buffer_2"
3431        );
3432        assert_eq!(
3433            blocks_snapshot.text(),
3434            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
3435            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
3436        );
3437        assert_eq!(
3438            blocks_snapshot
3439                .row_infos(BlockRow(0))
3440                .map(|i| i.buffer_row)
3441                .collect::<Vec<_>>(),
3442            vec![
3443                None,
3444                None,
3445                None,
3446                Some(0),
3447                None,
3448                None,
3449                None,
3450                None,
3451                None,
3452                Some(4),
3453                None,
3454                Some(5),
3455                None,
3456            ]
3457        );
3458
3459        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3460        buffer.read_with(cx, |buffer, cx| {
3461            writer.fold_buffers([buffer_id_3], buffer, cx);
3462        });
3463        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3464        let blocks = blocks_snapshot
3465            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3466            .collect::<Vec<_>>();
3467        for (_, block) in &blocks {
3468            if let BlockId::Custom(custom_block_id) = block.id() {
3469                assert!(
3470                    excerpt_blocks_1.contains(&custom_block_id),
3471                    "Should have no blocks from the folded buffer_1"
3472                );
3473                assert!(
3474                    !excerpt_blocks_2.contains(&custom_block_id),
3475                    "Should have only blocks from unfolded buffers"
3476                );
3477                assert!(
3478                    !excerpt_blocks_3.contains(&custom_block_id),
3479                    "Should have only blocks from unfolded buffers"
3480                );
3481            }
3482        }
3483
3484        assert_eq!(
3485            blocks_snapshot.text(),
3486            "\n\n\n111\n\n\n\n",
3487            "Should have a single, first buffer left after folding"
3488        );
3489        assert_eq!(
3490            blocks_snapshot
3491                .row_infos(BlockRow(0))
3492                .map(|i| i.buffer_row)
3493                .collect::<Vec<_>>(),
3494            vec![None, None, None, Some(0), None, None, None, None,]
3495        );
3496    }
3497
3498    #[gpui::test]
3499    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
3500        cx.update(init_test);
3501
3502        let text = "111";
3503
3504        let buffer = cx.update(|cx| {
3505            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
3506        });
3507        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3508        let buffer_ids = buffer_snapshot
3509            .excerpts()
3510            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3511            .dedup()
3512            .collect::<Vec<_>>();
3513        assert_eq!(buffer_ids.len(), 1);
3514        let buffer_id = buffer_ids[0];
3515
3516        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
3517        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3518        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3519        let (_, wrap_snapshot) =
3520            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3521        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3522        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3523
3524        assert_eq!(blocks_snapshot.text(), "\n\n111");
3525
3526        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3527        buffer.read_with(cx, |buffer, cx| {
3528            writer.fold_buffers([buffer_id], buffer, cx);
3529        });
3530        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3531        let blocks = blocks_snapshot
3532            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3533            .collect::<Vec<_>>();
3534        assert_eq!(
3535            1,
3536            blocks
3537                .iter()
3538                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
3539                .count(),
3540            "Should have one folded block, producing a header of the second buffer"
3541        );
3542        assert_eq!(blocks_snapshot.text(), "\n");
3543        assert_eq!(
3544            blocks_snapshot
3545                .row_infos(BlockRow(0))
3546                .map(|i| i.buffer_row)
3547                .collect::<Vec<_>>(),
3548            vec![None, None],
3549            "When fully folded, should be no buffer rows"
3550        );
3551    }
3552
3553    #[gpui::test(iterations = 60)]
3554    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3555        cx.update(init_test);
3556
3557        let operations = env::var("OPERATIONS")
3558            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3559            .unwrap_or(10);
3560
3561        let wrap_width = if rng.random_bool(0.2) {
3562            None
3563        } else {
3564            Some(px(rng.random_range(0.0..=100.0)))
3565        };
3566        let tab_size = 1.try_into().unwrap();
3567        let font_size = px(14.0);
3568        let buffer_start_header_height = rng.random_range(1..=5);
3569        let excerpt_header_height = rng.random_range(1..=5);
3570
3571        log::info!("Wrap width: {:?}", wrap_width);
3572        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3573        let is_singleton = rng.random();
3574        let buffer = if is_singleton {
3575            let len = rng.random_range(0..10);
3576            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3577            log::info!("initial singleton buffer text: {:?}", text);
3578            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3579        } else {
3580            cx.update(|cx| {
3581                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3582                log::info!(
3583                    "initial multi-buffer text: {:?}",
3584                    multibuffer.read(cx).read(cx).text()
3585                );
3586                multibuffer
3587            })
3588        };
3589
3590        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3591        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3592        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3593        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3594        let font = test_font();
3595        let (wrap_map, wraps_snapshot) =
3596            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3597        let mut block_map = BlockMap::new(
3598            wraps_snapshot,
3599            buffer_start_header_height,
3600            excerpt_header_height,
3601        );
3602
3603        for _ in 0..operations {
3604            let mut buffer_edits = Vec::new();
3605            match rng.random_range(0..=100) {
3606                0..=19 => {
3607                    let wrap_width = if rng.random_bool(0.2) {
3608                        None
3609                    } else {
3610                        Some(px(rng.random_range(0.0..=100.0)))
3611                    };
3612                    log::info!("Setting wrap width to {:?}", wrap_width);
3613                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3614                }
3615                20..=39 => {
3616                    let block_count = rng.random_range(1..=5);
3617                    let block_properties = (0..block_count)
3618                        .map(|_| {
3619                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3620                            let offset = buffer.clip_offset(
3621                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3622                                Bias::Left,
3623                            );
3624                            let mut min_height = 0;
3625                            let placement = match rng.random_range(0..3) {
3626                                0 => {
3627                                    min_height = 1;
3628                                    let start = buffer.anchor_after(offset);
3629                                    let end = buffer.anchor_after(buffer.clip_offset(
3630                                        rng.random_range(offset..=buffer.len()),
3631                                        Bias::Left,
3632                                    ));
3633                                    BlockPlacement::Replace(start..=end)
3634                                }
3635                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3636                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3637                            };
3638
3639                            let height = rng.random_range(min_height..512);
3640                            BlockProperties {
3641                                style: BlockStyle::Fixed,
3642                                placement,
3643                                height: Some(height),
3644                                render: Arc::new(|_| div().into_any()),
3645                                priority: 0,
3646                            }
3647                        })
3648                        .collect::<Vec<_>>();
3649
3650                    let (inlay_snapshot, inlay_edits) =
3651                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3652                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3653                    let (tab_snapshot, tab_edits) =
3654                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3655                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3656                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3657                    });
3658                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3659                    let block_ids =
3660                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3661                            placement: props.placement.clone(),
3662                            height: props.height,
3663                            style: props.style,
3664                            render: Arc::new(|_| div().into_any()),
3665                            priority: 0,
3666                        }));
3667
3668                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3669                        log::info!(
3670                            "inserted block {:?} with height {:?} and id {:?}",
3671                            block_properties
3672                                .placement
3673                                .as_ref()
3674                                .map(|p| p.to_point(&buffer_snapshot)),
3675                            block_properties.height,
3676                            block_id
3677                        );
3678                    }
3679                }
3680                40..=59 if !block_map.custom_blocks.is_empty() => {
3681                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3682                    let block_ids_to_remove = block_map
3683                        .custom_blocks
3684                        .choose_multiple(&mut rng, block_count)
3685                        .map(|block| block.id)
3686                        .collect::<HashSet<_>>();
3687
3688                    let (inlay_snapshot, inlay_edits) =
3689                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3690                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3691                    let (tab_snapshot, tab_edits) =
3692                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3693                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3694                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3695                    });
3696                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3697                    log::info!(
3698                        "removing {} blocks: {:?}",
3699                        block_ids_to_remove.len(),
3700                        block_ids_to_remove
3701                    );
3702                    block_map.remove(block_ids_to_remove);
3703                }
3704                60..=79 => {
3705                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3706                        log::info!("Noop fold/unfold operation on a singleton buffer");
3707                        continue;
3708                    }
3709                    let (inlay_snapshot, inlay_edits) =
3710                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3711                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3712                    let (tab_snapshot, tab_edits) =
3713                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3714                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3715                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3716                    });
3717                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3718                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3719                        let folded_buffers: Vec<_> =
3720                            block_map.block_map.folded_buffers.iter().cloned().collect();
3721                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3722                        unfolded_buffers.dedup();
3723                        log::debug!("All buffers {unfolded_buffers:?}");
3724                        log::debug!("Folded buffers {folded_buffers:?}");
3725                        unfolded_buffers.retain(|buffer_id| {
3726                            !block_map.block_map.folded_buffers.contains(buffer_id)
3727                        });
3728                        (unfolded_buffers, folded_buffers)
3729                    });
3730                    let mut folded_count = folded_buffers.len();
3731                    let mut unfolded_count = unfolded_buffers.len();
3732
3733                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
3734                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
3735                    if !fold && !unfold {
3736                        log::info!(
3737                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3738                        );
3739                        continue;
3740                    }
3741
3742                    buffer.update(cx, |buffer, cx| {
3743                        if fold {
3744                            let buffer_to_fold =
3745                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
3746                            log::info!("Folding {buffer_to_fold:?}");
3747                            let related_excerpts = buffer_snapshot
3748                                .excerpts()
3749                                .filter_map(|(excerpt_id, buffer, range)| {
3750                                    if buffer.remote_id() == buffer_to_fold {
3751                                        Some((
3752                                            excerpt_id,
3753                                            buffer
3754                                                .text_for_range(range.context)
3755                                                .collect::<String>(),
3756                                        ))
3757                                    } else {
3758                                        None
3759                                    }
3760                                })
3761                                .collect::<Vec<_>>();
3762                            log::info!(
3763                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3764                            );
3765                            folded_count += 1;
3766                            unfolded_count -= 1;
3767                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3768                        }
3769                        if unfold {
3770                            let buffer_to_unfold =
3771                                folded_buffers[rng.random_range(0..folded_buffers.len())];
3772                            log::info!("Unfolding {buffer_to_unfold:?}");
3773                            unfolded_count += 1;
3774                            folded_count -= 1;
3775                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3776                        }
3777                        log::info!(
3778                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3779                        );
3780                    });
3781                }
3782                _ => {
3783                    buffer.update(cx, |buffer, cx| {
3784                        let mutation_count = rng.random_range(1..=5);
3785                        let subscription = buffer.subscribe();
3786                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3787                        buffer_snapshot = buffer.snapshot(cx);
3788                        buffer_edits.extend(subscription.consume());
3789                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3790                    });
3791                }
3792            }
3793
3794            let (inlay_snapshot, inlay_edits) =
3795                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3796            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3797            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3798            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3799                wrap_map.sync(tab_snapshot, tab_edits, cx)
3800            });
3801            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3802            assert_eq!(
3803                blocks_snapshot.transforms.summary().input_rows,
3804                wraps_snapshot.max_point().row() + RowDelta(1)
3805            );
3806            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3807            log::info!("blocks text: {:?}", blocks_snapshot.text());
3808
3809            let mut expected_blocks = Vec::new();
3810            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3811                Some((
3812                    block.placement.to_wrap_row(&wraps_snapshot)?,
3813                    Block::Custom(block.clone()),
3814                ))
3815            }));
3816
3817            let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
3818            let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
3819            let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
3820            let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
3821
3822            // Note that this needs to be synced with the related section in BlockMap::sync
3823            expected_blocks.extend(block_map.header_and_footer_blocks(
3824                &buffer_snapshot,
3825                MultiBufferOffset(0)..,
3826                |point, bias| {
3827                    wrap_point_cursor
3828                        .map(
3829                            tab_point_cursor
3830                                .map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)),
3831                        )
3832                        .row()
3833                },
3834            ));
3835
3836            BlockMap::sort_blocks(&mut expected_blocks);
3837
3838            for (placement, block) in &expected_blocks {
3839                log::info!(
3840                    "Block {:?} placement: {:?} Height: {:?}",
3841                    block.id(),
3842                    placement,
3843                    block.height()
3844                );
3845            }
3846
3847            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3848
3849            let input_buffer_rows = buffer_snapshot
3850                .row_infos(MultiBufferRow(0))
3851                .map(|row| row.buffer_row)
3852                .collect::<Vec<_>>();
3853            let mut expected_buffer_rows = Vec::new();
3854            let mut expected_text = String::new();
3855            let mut expected_block_positions = Vec::new();
3856            let mut expected_replaced_buffer_rows = HashSet::default();
3857            let input_text = wraps_snapshot.text();
3858
3859            // Loop over the input lines, creating (N - 1) empty lines for
3860            // blocks of height N.
3861            //
3862            // It's important to note that output *starts* as one empty line,
3863            // so we special case row 0 to assume a leading '\n'.
3864            //
3865            // Linehood is the birthright of strings.
3866            let input_text_lines = input_text.split('\n').enumerate().peekable();
3867            let mut block_row = 0;
3868            for (wrap_row, input_line) in input_text_lines {
3869                let wrap_row = WrapRow(wrap_row as u32);
3870                let multibuffer_row = wraps_snapshot
3871                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3872                    .row;
3873
3874                // Create empty lines for the above block
3875                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3876                    if *placement.start() == wrap_row && block.place_above() {
3877                        let (_, block) = sorted_blocks_iter.next().unwrap();
3878                        expected_block_positions.push((block_row, block.id()));
3879                        if block.height() > 0 {
3880                            let text = "\n".repeat((block.height() - 1) as usize);
3881                            if block_row > 0 {
3882                                expected_text.push('\n')
3883                            }
3884                            expected_text.push_str(&text);
3885                            for _ in 0..block.height() {
3886                                expected_buffer_rows.push(None);
3887                            }
3888                            block_row += block.height();
3889                        }
3890                    } else {
3891                        break;
3892                    }
3893                }
3894
3895                // Skip lines within replace blocks, then create empty lines for the replace block's height
3896                let mut is_in_replace_block = false;
3897                if let Some((BlockPlacement::Replace(replace_range), block)) =
3898                    sorted_blocks_iter.peek()
3899                    && wrap_row >= *replace_range.start()
3900                {
3901                    is_in_replace_block = true;
3902
3903                    if wrap_row == *replace_range.start() {
3904                        if matches!(block, Block::FoldedBuffer { .. }) {
3905                            expected_buffer_rows.push(None);
3906                        } else {
3907                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
3908                        }
3909                    }
3910
3911                    if wrap_row == *replace_range.end() {
3912                        expected_block_positions.push((block_row, block.id()));
3913                        let text = "\n".repeat((block.height() - 1) as usize);
3914                        if block_row > 0 {
3915                            expected_text.push('\n');
3916                        }
3917                        expected_text.push_str(&text);
3918
3919                        for _ in 1..block.height() {
3920                            expected_buffer_rows.push(None);
3921                        }
3922                        block_row += block.height();
3923
3924                        sorted_blocks_iter.next();
3925                    }
3926                }
3927
3928                if is_in_replace_block {
3929                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3930                } else {
3931                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3932                    let soft_wrapped = wraps_snapshot
3933                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3934                        .column()
3935                        > 0;
3936                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3937                    if block_row > 0 {
3938                        expected_text.push('\n');
3939                    }
3940                    expected_text.push_str(input_line);
3941                    block_row += 1;
3942                }
3943
3944                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3945                    if *placement.end() == wrap_row && block.place_below() {
3946                        let (_, block) = sorted_blocks_iter.next().unwrap();
3947                        expected_block_positions.push((block_row, block.id()));
3948                        if block.height() > 0 {
3949                            let text = "\n".repeat((block.height() - 1) as usize);
3950                            if block_row > 0 {
3951                                expected_text.push('\n')
3952                            }
3953                            expected_text.push_str(&text);
3954                            for _ in 0..block.height() {
3955                                expected_buffer_rows.push(None);
3956                            }
3957                            block_row += block.height();
3958                        }
3959                    } else {
3960                        break;
3961                    }
3962                }
3963            }
3964
3965            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3966            let expected_row_count = expected_lines.len();
3967            log::info!("expected text: {expected_text:?}");
3968
3969            assert_eq!(
3970                blocks_snapshot.max_point().row + 1,
3971                expected_row_count as u32,
3972                "actual row count != expected row count",
3973            );
3974            assert_eq!(
3975                blocks_snapshot.text(),
3976                expected_text,
3977                "actual text != expected text",
3978            );
3979
3980            for start_row in 0..expected_row_count {
3981                let end_row = rng.random_range(start_row + 1..=expected_row_count);
3982                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3983                if end_row < expected_row_count {
3984                    expected_text.push('\n');
3985                }
3986
3987                let actual_text = blocks_snapshot
3988                    .chunks(
3989                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
3990                        false,
3991                        false,
3992                        Highlights::default(),
3993                    )
3994                    .map(|chunk| chunk.text)
3995                    .collect::<String>();
3996                assert_eq!(
3997                    actual_text,
3998                    expected_text,
3999                    "incorrect text starting row row range {:?}",
4000                    start_row..end_row
4001                );
4002                assert_eq!(
4003                    blocks_snapshot
4004                        .row_infos(BlockRow(start_row as u32))
4005                        .map(|row_info| row_info.buffer_row)
4006                        .collect::<Vec<_>>(),
4007                    &expected_buffer_rows[start_row..],
4008                    "incorrect buffer_rows starting at row {:?}",
4009                    start_row
4010                );
4011            }
4012
4013            assert_eq!(
4014                blocks_snapshot
4015                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4016                    .map(|(row, block)| (row.0, block.id()))
4017                    .collect::<Vec<_>>(),
4018                expected_block_positions,
4019                "invalid blocks_in_range({:?})",
4020                0..expected_row_count
4021            );
4022
4023            for (_, expected_block) in
4024                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4025            {
4026                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
4027                assert_eq!(
4028                    actual_block.map(|block| block.id()),
4029                    Some(expected_block.id())
4030                );
4031            }
4032
4033            for (block_row, block_id) in expected_block_positions {
4034                if let BlockId::Custom(block_id) = block_id {
4035                    assert_eq!(
4036                        blocks_snapshot.row_for_block(block_id),
4037                        Some(BlockRow(block_row))
4038                    );
4039                }
4040            }
4041
4042            let mut expected_longest_rows = Vec::new();
4043            let mut longest_line_len = -1_isize;
4044            for (row, line) in expected_lines.iter().enumerate() {
4045                let row = row as u32;
4046
4047                assert_eq!(
4048                    blocks_snapshot.line_len(BlockRow(row)),
4049                    line.len() as u32,
4050                    "invalid line len for row {}",
4051                    row
4052                );
4053
4054                let line_char_count = line.chars().count() as isize;
4055                match line_char_count.cmp(&longest_line_len) {
4056                    Ordering::Less => {}
4057                    Ordering::Equal => expected_longest_rows.push(row),
4058                    Ordering::Greater => {
4059                        longest_line_len = line_char_count;
4060                        expected_longest_rows.clear();
4061                        expected_longest_rows.push(row);
4062                    }
4063                }
4064            }
4065
4066            let longest_row = blocks_snapshot.longest_row();
4067            assert!(
4068                expected_longest_rows.contains(&longest_row.0),
4069                "incorrect longest row {}. expected {:?} with length {}",
4070                longest_row.0,
4071                expected_longest_rows,
4072                longest_line_len,
4073            );
4074
4075            for _ in 0..10 {
4076                let end_row = rng.random_range(1..=expected_lines.len());
4077                let start_row = rng.random_range(0..end_row);
4078
4079                let mut expected_longest_rows_in_range = vec![];
4080                let mut longest_line_len_in_range = 0;
4081
4082                let mut row = start_row as u32;
4083                for line in &expected_lines[start_row..end_row] {
4084                    let line_char_count = line.chars().count() as isize;
4085                    match line_char_count.cmp(&longest_line_len_in_range) {
4086                        Ordering::Less => {}
4087                        Ordering::Equal => expected_longest_rows_in_range.push(row),
4088                        Ordering::Greater => {
4089                            longest_line_len_in_range = line_char_count;
4090                            expected_longest_rows_in_range.clear();
4091                            expected_longest_rows_in_range.push(row);
4092                        }
4093                    }
4094                    row += 1;
4095                }
4096
4097                let longest_row_in_range = blocks_snapshot
4098                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
4099                assert!(
4100                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
4101                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
4102                    longest_row.0,
4103                    start_row..end_row,
4104                    expected_longest_rows_in_range,
4105                    longest_line_len_in_range,
4106                );
4107            }
4108
4109            // Ensure that conversion between block points and wrap points is stable.
4110            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
4111                let wrap_point = WrapPoint::new(WrapRow(row), 0);
4112                let block_point = blocks_snapshot.to_block_point(wrap_point);
4113                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
4114                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
4115                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
4116                assert_eq!(
4117                    blocks_snapshot.to_block_point(right_wrap_point),
4118                    block_point
4119                );
4120            }
4121
4122            let mut block_point = BlockPoint::new(BlockRow(0), 0);
4123            for c in expected_text.chars() {
4124                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
4125                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
4126                assert_eq!(
4127                    blocks_snapshot
4128                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
4129                    left_point,
4130                    "block point: {:?}, wrap point: {:?}",
4131                    block_point,
4132                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
4133                );
4134                assert_eq!(
4135                    left_buffer_point,
4136                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
4137                    "{:?} is not valid in buffer coordinates",
4138                    left_point
4139                );
4140
4141                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
4142                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
4143                assert_eq!(
4144                    blocks_snapshot
4145                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
4146                    right_point,
4147                    "block point: {:?}, wrap point: {:?}",
4148                    block_point,
4149                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
4150                );
4151                assert_eq!(
4152                    right_buffer_point,
4153                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
4154                    "{:?} is not valid in buffer coordinates",
4155                    right_point
4156                );
4157
4158                if c == '\n' {
4159                    block_point.0 += Point::new(1, 0);
4160                } else {
4161                    block_point.column += c.len_utf8() as u32;
4162                }
4163            }
4164
4165            for buffer_row in 0..=buffer_snapshot.max_point().row {
4166                let buffer_row = MultiBufferRow(buffer_row);
4167                assert_eq!(
4168                    blocks_snapshot.is_line_replaced(buffer_row),
4169                    expected_replaced_buffer_rows.contains(&buffer_row),
4170                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
4171                );
4172            }
4173        }
4174    }
4175
4176    #[gpui::test]
4177    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
4178        cx.update(init_test);
4179
4180        let text = "abc\ndef\nghi\njkl\nmno";
4181        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
4182        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4183        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4184        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4185        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4186        let (_wrap_map, wraps_snapshot) =
4187            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4188        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
4189
4190        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4191        let _block_id = writer.insert(vec![BlockProperties {
4192            style: BlockStyle::Fixed,
4193            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
4194            height: Some(1),
4195            render: Arc::new(|_| div().into_any()),
4196            priority: 0,
4197        }])[0];
4198
4199        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
4200        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4201
4202        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4203        writer.remove_intersecting_replace_blocks(
4204            [buffer_snapshot
4205                .anchor_after(Point::new(1, 0))
4206                .to_offset(&buffer_snapshot)
4207                ..buffer_snapshot
4208                    .anchor_after(Point::new(1, 0))
4209                    .to_offset(&buffer_snapshot)],
4210            false,
4211        );
4212        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
4213        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4214    }
4215
4216    #[gpui::test]
4217    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
4218        cx.update(init_test);
4219
4220        let text = "line 1\nline 2\nline 3";
4221        let buffer = cx.update(|cx| {
4222            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
4223        });
4224        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4225        let buffer_ids = buffer_snapshot
4226            .excerpts()
4227            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4228            .dedup()
4229            .collect::<Vec<_>>();
4230        assert_eq!(buffer_ids.len(), 1);
4231        let buffer_id = buffer_ids[0];
4232
4233        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4234        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4235        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4236        let (_, wrap_snapshot) =
4237            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4238        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4239
4240        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4241        writer.insert(vec![BlockProperties {
4242            style: BlockStyle::Fixed,
4243            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
4244            height: Some(1),
4245            render: Arc::new(|_| div().into_any()),
4246            priority: 0,
4247        }]);
4248
4249        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4250        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
4251
4252        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4253        buffer.read_with(cx, |buffer, cx| {
4254            writer.fold_buffers([buffer_id], buffer, cx);
4255        });
4256
4257        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4258        assert_eq!(blocks_snapshot.text(), "");
4259    }
4260
4261    #[gpui::test]
4262    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
4263        cx.update(init_test);
4264
4265        let text = "line 1\nline 2\nline 3\nline 4";
4266        let buffer = cx.update(|cx| {
4267            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
4268        });
4269        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4270        let buffer_ids = buffer_snapshot
4271            .excerpts()
4272            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4273            .dedup()
4274            .collect::<Vec<_>>();
4275        assert_eq!(buffer_ids.len(), 1);
4276        let buffer_id = buffer_ids[0];
4277
4278        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4279        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4280        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4281        let (_, wrap_snapshot) =
4282            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4283        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4284
4285        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4286        writer.insert(vec![BlockProperties {
4287            style: BlockStyle::Fixed,
4288            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
4289            height: Some(1),
4290            render: Arc::new(|_| div().into_any()),
4291            priority: 0,
4292        }]);
4293
4294        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4295        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
4296
4297        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4298        buffer.read_with(cx, |buffer, cx| {
4299            writer.fold_buffers([buffer_id], buffer, cx);
4300        });
4301
4302        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4303        assert_eq!(blocks_snapshot.text(), "");
4304    }
4305
4306    #[gpui::test]
4307    fn test_companion_spacer_blocks(cx: &mut gpui::TestAppContext) {
4308        cx.update(init_test);
4309
4310        let base_text = "aaa\nbbb\nccc\nddd\nddd\nddd\neee\n";
4311        let main_text = "aaa\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n";
4312
4313        let rhs_buffer = cx.new(|cx| Buffer::local(main_text, cx));
4314        let diff = cx.new(|cx| {
4315            BufferDiff::new_with_base_text(base_text, &rhs_buffer.read(cx).text_snapshot(), cx)
4316        });
4317        let lhs_buffer = diff.read_with(cx, |diff, _| diff.base_text_buffer());
4318
4319        let lhs_multibuffer = cx.new(|cx| {
4320            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4321            mb.push_excerpts(
4322                lhs_buffer.clone(),
4323                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4324                cx,
4325            );
4326            mb.add_inverted_diff(diff.clone(), cx);
4327            mb
4328        });
4329        let rhs_multibuffer = cx.new(|cx| {
4330            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4331            mb.push_excerpts(
4332                rhs_buffer.clone(),
4333                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4334                cx,
4335            );
4336            mb.add_diff(diff.clone(), cx);
4337            mb
4338        });
4339        let subscription =
4340            rhs_multibuffer.update(cx, |rhs_multibuffer, _| rhs_multibuffer.subscribe());
4341
4342        let lhs_excerpt_id =
4343            lhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4344        let rhs_excerpt_id =
4345            rhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4346
4347        let lhs_buffer_snapshot = cx.update(|cx| lhs_multibuffer.read(cx).snapshot(cx));
4348        let (mut _lhs_inlay_map, lhs_inlay_snapshot) = InlayMap::new(lhs_buffer_snapshot);
4349        let (mut _lhs_fold_map, lhs_fold_snapshot) = FoldMap::new(lhs_inlay_snapshot);
4350        let (mut _lhs_tab_map, lhs_tab_snapshot) =
4351            TabMap::new(lhs_fold_snapshot, 4.try_into().unwrap());
4352        let (_lhs_wrap_map, lhs_wrap_snapshot) =
4353            cx.update(|cx| WrapMap::new(lhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4354        let lhs_block_map = BlockMap::new(lhs_wrap_snapshot.clone(), 0, 0);
4355
4356        let rhs_buffer_snapshot = cx.update(|cx| rhs_multibuffer.read(cx).snapshot(cx));
4357        let (mut rhs_inlay_map, rhs_inlay_snapshot) = InlayMap::new(rhs_buffer_snapshot);
4358        let (mut rhs_fold_map, rhs_fold_snapshot) = FoldMap::new(rhs_inlay_snapshot);
4359        let (mut rhs_tab_map, rhs_tab_snapshot) =
4360            TabMap::new(rhs_fold_snapshot, 4.try_into().unwrap());
4361        let (_rhs_wrap_map, rhs_wrap_snapshot) =
4362            cx.update(|cx| WrapMap::new(rhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4363        let rhs_block_map = BlockMap::new(rhs_wrap_snapshot.clone(), 0, 0);
4364
4365        let rhs_entity_id = rhs_multibuffer.entity_id();
4366
4367        let companion = cx.new(|_| {
4368            let mut c = Companion::new(
4369                rhs_entity_id,
4370                Default::default(),
4371                convert_rhs_rows_to_lhs,
4372                convert_lhs_rows_to_rhs,
4373            );
4374            c.add_excerpt_mapping(lhs_excerpt_id, rhs_excerpt_id);
4375            c
4376        });
4377
4378        let rhs_edits = Patch::new(vec![text::Edit {
4379            old: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4380            new: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4381        }]);
4382        let lhs_edits = Patch::new(vec![text::Edit {
4383            old: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4384            new: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4385        }]);
4386
4387        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4388            rhs_block_map.read(
4389                rhs_wrap_snapshot.clone(),
4390                rhs_edits.clone(),
4391                Some(CompanionView::new(
4392                    rhs_entity_id,
4393                    &lhs_wrap_snapshot,
4394                    &lhs_edits,
4395                    companion,
4396                )),
4397            )
4398        });
4399
4400        let lhs_entity_id = lhs_multibuffer.entity_id();
4401        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4402            lhs_block_map.read(
4403                lhs_wrap_snapshot.clone(),
4404                lhs_edits.clone(),
4405                Some(CompanionView::new(
4406                    lhs_entity_id,
4407                    &rhs_wrap_snapshot,
4408                    &rhs_edits,
4409                    companion,
4410                )),
4411            )
4412        });
4413
4414        // LHS:
4415        //   aaa
4416        // - bbb
4417        // - ccc
4418        //   ddd
4419        //   ddd
4420        //   ddd
4421        //   <extra line>
4422        //   <extra line>
4423        //   <extra line>
4424        //   *eee
4425        //
4426        // RHS:
4427        //   aaa
4428        //   <extra line>
4429        //   <extra line>
4430        //   ddd
4431        //   ddd
4432        //   ddd
4433        // + XXX
4434        // + YYY
4435        // + ZZZ
4436        //   eee
4437
4438        assert_eq!(
4439            rhs_snapshot.snapshot.text(),
4440            "aaa\n\n\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4441            "RHS should have 2 spacer lines after 'aaa' to align with LHS's deleted lines"
4442        );
4443
4444        assert_eq!(
4445            lhs_snapshot.snapshot.text(),
4446            "aaa\nbbb\nccc\nddd\nddd\nddd\n\n\n\neee\n",
4447            "LHS should have 3 spacer lines in place of RHS's inserted lines"
4448        );
4449
4450        // LHS:
4451        //   aaa
4452        // - bbb
4453        // - ccc
4454        //   ddd
4455        //   ddd
4456        //   ddd
4457        //   <extra line>
4458        //   <extra line>
4459        //   <extra line>
4460        //   eee
4461        //
4462        // RHS:
4463        //   aaa
4464        //   <extra line>
4465        //   <extra line>
4466        //   ddd
4467        //   foo
4468        //   foo
4469        //   foo
4470        //   ddd
4471        //   ddd
4472        // + XXX
4473        // + YYY
4474        // + ZZZ
4475        //   eee
4476
4477        let rhs_buffer_snapshot = rhs_multibuffer.update(cx, |multibuffer, cx| {
4478            multibuffer.edit(
4479                [(Point::new(2, 0)..Point::new(2, 0), "foo\nfoo\nfoo\n")],
4480                None,
4481                cx,
4482            );
4483            multibuffer.snapshot(cx)
4484        });
4485
4486        let (rhs_inlay_snapshot, rhs_inlay_edits) =
4487            rhs_inlay_map.sync(rhs_buffer_snapshot, subscription.consume().into_inner());
4488        let (rhs_fold_snapshot, rhs_fold_edits) =
4489            rhs_fold_map.read(rhs_inlay_snapshot, rhs_inlay_edits);
4490        let (rhs_tab_snapshot, rhs_tab_edits) =
4491            rhs_tab_map.sync(rhs_fold_snapshot, rhs_fold_edits, 4.try_into().unwrap());
4492        let (rhs_wrap_snapshot, rhs_wrap_edits) = _rhs_wrap_map.update(cx, |wrap_map, cx| {
4493            wrap_map.sync(rhs_tab_snapshot, rhs_tab_edits, cx)
4494        });
4495
4496        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4497            rhs_block_map.read(
4498                rhs_wrap_snapshot.clone(),
4499                rhs_wrap_edits.clone(),
4500                Some(CompanionView::new(
4501                    rhs_entity_id,
4502                    &lhs_wrap_snapshot,
4503                    &Default::default(),
4504                    companion,
4505                )),
4506            )
4507        });
4508
4509        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4510            lhs_block_map.read(
4511                lhs_wrap_snapshot.clone(),
4512                Default::default(),
4513                Some(CompanionView::new(
4514                    lhs_entity_id,
4515                    &rhs_wrap_snapshot,
4516                    &rhs_wrap_edits,
4517                    companion,
4518                )),
4519            )
4520        });
4521
4522        assert_eq!(
4523            rhs_snapshot.snapshot.text(),
4524            "aaa\n\n\nddd\nfoo\nfoo\nfoo\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4525            "RHS should have the insertion"
4526        );
4527
4528        assert_eq!(
4529            lhs_snapshot.snapshot.text(),
4530            "aaa\nbbb\nccc\nddd\n\n\n\nddd\nddd\n\n\n\neee\n",
4531            "LHS should have 3 more spacer lines to balance the insertion"
4532        );
4533    }
4534
4535    fn init_test(cx: &mut gpui::App) {
4536        let settings = SettingsStore::test(cx);
4537        cx.set_global(settings);
4538        theme::init(theme::LoadThemes::JustBase, cx);
4539        assets::Assets.load_test_fonts(cx);
4540    }
4541
4542    impl Block {
4543        fn as_custom(&self) -> Option<&CustomBlock> {
4544            match self {
4545                Block::Custom(block) => Some(block),
4546                _ => None,
4547            }
4548        }
4549    }
4550
4551    impl BlockSnapshot {
4552        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
4553            self.wrap_snapshot
4554                .to_point(self.to_wrap_point(point, bias), bias)
4555        }
4556    }
4557}