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