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