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 multi_buffer_snapshot = multi_buffer.snapshot(cx);
2047        let mut ranges = Vec::new();
2048        let mut companion_buffer_ids = HashSet::default();
2049        for buffer_id in buffer_ids {
2050            if fold {
2051                self.block_map.folded_buffers.insert(buffer_id);
2052            } else {
2053                self.block_map.folded_buffers.remove(&buffer_id);
2054            }
2055            ranges.extend(multi_buffer_snapshot.range_for_buffer(buffer_id));
2056            if let Some(companion) = &self.companion
2057                && companion.inverse.is_some()
2058            {
2059                companion_buffer_ids.extend(
2060                    companion
2061                        .companion
2062                        .buffer_to_companion_buffer(companion.display_map_id)
2063                        .get(&buffer_id)
2064                        .copied(),
2065                )
2066            }
2067        }
2068        ranges.sort_unstable_by_key(|range| range.start);
2069
2070        let mut edits = Patch::default();
2071        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
2072        for range in ranges {
2073            let last_edit_row = cmp::min(
2074                wrap_snapshot.make_wrap_point(range.end, Bias::Right).row() + WrapRow(1),
2075                wrap_snapshot.max_point().row(),
2076            ) + WrapRow(1);
2077            let range = wrap_snapshot.make_wrap_point(range.start, Bias::Left).row()..last_edit_row;
2078            edits.push(Edit {
2079                old: range.clone(),
2080                new: range,
2081            });
2082        }
2083
2084        self.block_map.sync(
2085            &wrap_snapshot,
2086            edits.clone(),
2087            self.companion
2088                .as_ref()
2089                .map(BlockMapWriterCompanion::companion_view),
2090        );
2091        if let Some(companion) = &mut self.companion
2092            && let Some(inverse) = &mut companion.inverse
2093        {
2094            inverse.companion_writer.fold_or_unfold_buffers(
2095                fold,
2096                companion_buffer_ids,
2097                inverse.companion_multibuffer,
2098                cx,
2099            );
2100        }
2101    }
2102
2103    #[ztracing::instrument(skip_all)]
2104    fn blocks_intersecting_buffer_range(
2105        &self,
2106        range: Range<MultiBufferOffset>,
2107        inclusive: bool,
2108    ) -> &[Arc<CustomBlock>] {
2109        if range.is_empty() && !inclusive {
2110            return &[];
2111        }
2112        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
2113        let buffer = wrap_snapshot.buffer_snapshot();
2114
2115        let start_block_ix = match self.block_map.custom_blocks.binary_search_by(|block| {
2116            let block_end = block.end().to_offset(buffer);
2117            block_end.cmp(&range.start).then(Ordering::Greater)
2118        }) {
2119            Ok(ix) | Err(ix) => ix,
2120        };
2121        let end_block_ix =
2122            match self.block_map.custom_blocks[start_block_ix..].binary_search_by(|block| {
2123                let block_start = block.start().to_offset(buffer);
2124                block_start.cmp(&range.end).then(if inclusive {
2125                    Ordering::Less
2126                } else {
2127                    Ordering::Greater
2128                })
2129            }) {
2130                Ok(ix) | Err(ix) => ix,
2131            };
2132
2133        &self.block_map.custom_blocks[start_block_ix..][..end_block_ix]
2134    }
2135}
2136
2137impl BlockSnapshot {
2138    #[cfg(test)]
2139    #[ztracing::instrument(skip_all)]
2140    pub fn text(&self) -> String {
2141        self.chunks(
2142            BlockRow(0)..self.transforms.summary().output_rows,
2143            false,
2144            false,
2145            Highlights::default(),
2146        )
2147        .map(|chunk| chunk.text)
2148        .collect()
2149    }
2150
2151    #[ztracing::instrument(skip_all)]
2152    pub(crate) fn chunks<'a>(
2153        &'a self,
2154        rows: Range<BlockRow>,
2155        language_aware: bool,
2156        masked: bool,
2157        highlights: Highlights<'a>,
2158    ) -> BlockChunks<'a> {
2159        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
2160
2161        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2162        cursor.seek(&rows.start, Bias::Right);
2163        let transform_output_start = cursor.start().0;
2164        let transform_input_start = cursor.start().1;
2165
2166        let mut input_start = transform_input_start;
2167        let mut input_end = transform_input_start;
2168        if let Some(transform) = cursor.item()
2169            && transform.block.is_none()
2170        {
2171            input_start += rows.start - transform_output_start;
2172            input_end += cmp::min(
2173                rows.end - transform_output_start,
2174                RowDelta(transform.summary.input_rows.0),
2175            );
2176        }
2177
2178        BlockChunks {
2179            input_chunks: self.wrap_snapshot.chunks(
2180                input_start..input_end,
2181                language_aware,
2182                highlights,
2183            ),
2184            input_chunk: Default::default(),
2185            transforms: cursor,
2186            output_row: rows.start,
2187            line_count_overflow: RowDelta(0),
2188            max_output_row,
2189            masked,
2190        }
2191    }
2192
2193    #[ztracing::instrument(skip_all)]
2194    pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
2195        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2196        cursor.seek(&start_row, Bias::Right);
2197        let Dimensions(output_start, input_start, _) = cursor.start();
2198        let overshoot = if cursor
2199            .item()
2200            .is_some_and(|transform| transform.block.is_none())
2201        {
2202            start_row - *output_start
2203        } else {
2204            RowDelta(0)
2205        };
2206        let input_start_row = *input_start + overshoot;
2207        BlockRows {
2208            transforms: cursor,
2209            input_rows: self.wrap_snapshot.row_infos(input_start_row),
2210            output_row: start_row,
2211            started: false,
2212        }
2213    }
2214
2215    #[ztracing::instrument(skip_all)]
2216    pub fn blocks_in_range(
2217        &self,
2218        rows: Range<BlockRow>,
2219    ) -> impl Iterator<Item = (BlockRow, &Block)> {
2220        let mut cursor = self.transforms.cursor::<BlockRow>(());
2221        cursor.seek(&rows.start, Bias::Left);
2222        while *cursor.start() < rows.start && cursor.end() <= rows.start {
2223            cursor.next();
2224        }
2225
2226        std::iter::from_fn(move || {
2227            while let Some(transform) = cursor.item() {
2228                let start_row = *cursor.start();
2229                if start_row > rows.end
2230                    || (start_row == rows.end
2231                        && transform
2232                            .block
2233                            .as_ref()
2234                            .is_some_and(|block| block.height() > 0))
2235                {
2236                    break;
2237                }
2238                if let Some(block) = &transform.block {
2239                    cursor.next();
2240                    return Some((start_row, block));
2241                } else {
2242                    cursor.next();
2243                }
2244            }
2245            None
2246        })
2247    }
2248
2249    #[ztracing::instrument(skip_all)]
2250    pub(crate) fn sticky_header_excerpt(&self, position: f64) -> Option<StickyHeaderExcerpt<'_>> {
2251        let top_row = position as u32;
2252        let mut cursor = self.transforms.cursor::<BlockRow>(());
2253        cursor.seek(&BlockRow(top_row), Bias::Right);
2254
2255        while let Some(transform) = cursor.item() {
2256            match &transform.block {
2257                Some(
2258                    Block::ExcerptBoundary { excerpt, .. } | Block::BufferHeader { excerpt, .. },
2259                ) => {
2260                    return Some(StickyHeaderExcerpt { excerpt });
2261                }
2262                Some(block) if block.is_buffer_header() => return None,
2263                _ => {
2264                    cursor.prev();
2265                    continue;
2266                }
2267            }
2268        }
2269
2270        None
2271    }
2272
2273    #[ztracing::instrument(skip_all)]
2274    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
2275        let buffer = self.wrap_snapshot.buffer_snapshot();
2276        let wrap_point = match block_id {
2277            BlockId::Custom(custom_block_id) => {
2278                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
2279                return Some(Block::Custom(custom_block.clone()));
2280            }
2281            BlockId::ExcerptBoundary(start_anchor) => {
2282                let start_point = start_anchor.to_point(&buffer);
2283                self.wrap_snapshot.make_wrap_point(start_point, Bias::Left)
2284            }
2285            BlockId::FoldedBuffer(buffer_id) => self.wrap_snapshot.make_wrap_point(
2286                buffer
2287                    .anchor_in_excerpt(buffer.excerpts_for_buffer(buffer_id).next()?.context.start)?
2288                    .to_point(buffer),
2289                Bias::Left,
2290            ),
2291            BlockId::Spacer(_) => return None,
2292        };
2293        let wrap_row = wrap_point.row();
2294
2295        let mut cursor = self.transforms.cursor::<WrapRow>(());
2296        cursor.seek(&wrap_row, Bias::Left);
2297
2298        while let Some(transform) = cursor.item() {
2299            if let Some(block) = transform.block.as_ref() {
2300                if block.id() == block_id {
2301                    return Some(block.clone());
2302                }
2303            } else if *cursor.start() > wrap_row {
2304                break;
2305            }
2306
2307            cursor.next();
2308        }
2309
2310        None
2311    }
2312
2313    #[ztracing::instrument(skip_all)]
2314    pub fn max_point(&self) -> BlockPoint {
2315        let row = self
2316            .transforms
2317            .summary()
2318            .output_rows
2319            .saturating_sub(RowDelta(1));
2320        BlockPoint::new(row, self.line_len(row))
2321    }
2322
2323    #[ztracing::instrument(skip_all)]
2324    pub fn longest_row(&self) -> BlockRow {
2325        self.transforms.summary().longest_row
2326    }
2327
2328    #[ztracing::instrument(skip_all)]
2329    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
2330        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2331        cursor.seek(&range.start, Bias::Right);
2332
2333        let mut longest_row = range.start;
2334        let mut longest_row_chars = 0;
2335        if let Some(transform) = cursor.item() {
2336            if transform.block.is_none() {
2337                let &Dimensions(output_start, input_start, _) = cursor.start();
2338                let overshoot = range.start - output_start;
2339                let wrap_start_row = input_start + WrapRow(overshoot.0);
2340                let wrap_end_row = cmp::min(
2341                    input_start + WrapRow((range.end - output_start).0),
2342                    cursor.end().1,
2343                );
2344                let summary = self
2345                    .wrap_snapshot
2346                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2347                longest_row = BlockRow(range.start.0 + summary.longest_row);
2348                longest_row_chars = summary.longest_row_chars;
2349            }
2350            cursor.next();
2351        }
2352
2353        let cursor_start_row = cursor.start().0;
2354        if range.end > cursor_start_row {
2355            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right);
2356            if summary.longest_row_chars > longest_row_chars {
2357                longest_row = cursor_start_row + summary.longest_row;
2358                longest_row_chars = summary.longest_row_chars;
2359            }
2360
2361            if let Some(transform) = cursor.item()
2362                && transform.block.is_none()
2363            {
2364                let &Dimensions(output_start, input_start, _) = cursor.start();
2365                let overshoot = range.end - output_start;
2366                let wrap_start_row = input_start;
2367                let wrap_end_row = input_start + overshoot;
2368                let summary = self
2369                    .wrap_snapshot
2370                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2371                if summary.longest_row_chars > longest_row_chars {
2372                    longest_row = output_start + RowDelta(summary.longest_row);
2373                }
2374            }
2375        }
2376
2377        longest_row
2378    }
2379
2380    #[ztracing::instrument(skip_all)]
2381    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
2382        let (start, _, item) =
2383            self.transforms
2384                .find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
2385        if let Some(transform) = item {
2386            let Dimensions(output_start, input_start, _) = start;
2387            let overshoot = row - output_start;
2388            if transform.block.is_some() {
2389                0
2390            } else {
2391                self.wrap_snapshot.line_len(input_start + overshoot)
2392            }
2393        } else if row == BlockRow(0) {
2394            0
2395        } else {
2396            panic!("row out of range");
2397        }
2398    }
2399
2400    #[ztracing::instrument(skip_all)]
2401    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
2402        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2403        item.is_some_and(|t| t.block.is_some())
2404    }
2405
2406    #[ztracing::instrument(skip_all)]
2407    pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
2408        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2409        let Some(transform) = item else {
2410            return false;
2411        };
2412        matches!(transform.block, Some(Block::FoldedBuffer { .. }))
2413    }
2414
2415    #[ztracing::instrument(skip_all)]
2416    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
2417        let wrap_point = self
2418            .wrap_snapshot
2419            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
2420        let (_, _, item) = self
2421            .transforms
2422            .find::<WrapRow, _>((), &wrap_point.row(), Bias::Right);
2423        item.is_some_and(|transform| {
2424            transform
2425                .block
2426                .as_ref()
2427                .is_some_and(|block| block.is_replacement())
2428        })
2429    }
2430
2431    #[ztracing::instrument(skip_all)]
2432    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
2433        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2434        cursor.seek(&BlockRow(point.row), Bias::Right);
2435
2436        let max_input_row = self.transforms.summary().input_rows;
2437        let mut search_left = (bias == Bias::Left && cursor.start().1 > WrapRow(0))
2438            || cursor.end().1 == max_input_row;
2439        let mut reversed = false;
2440
2441        loop {
2442            if let Some(transform) = cursor.item() {
2443                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
2444                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
2445                let output_start = Point::new(output_start_row.0, 0);
2446                let input_start = Point::new(input_start_row.0, 0);
2447                let input_end = Point::new(input_end_row.0, 0);
2448
2449                match transform.block.as_ref() {
2450                    Some(block) => {
2451                        if block.is_replacement()
2452                            && (((bias == Bias::Left || search_left) && output_start <= point.0)
2453                                || (!search_left && output_start >= point.0))
2454                        {
2455                            return BlockPoint(output_start);
2456                        }
2457                    }
2458                    None => {
2459                        let input_point = if point.row >= output_end_row.0 {
2460                            let line_len = self.wrap_snapshot.line_len(input_end_row - RowDelta(1));
2461                            self.wrap_snapshot.clip_point(
2462                                WrapPoint::new(input_end_row - RowDelta(1), line_len),
2463                                bias,
2464                            )
2465                        } else {
2466                            let output_overshoot = point.0.saturating_sub(output_start);
2467                            self.wrap_snapshot
2468                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
2469                        };
2470
2471                        if (input_start..input_end).contains(&input_point.0) {
2472                            let input_overshoot = input_point.0.saturating_sub(input_start);
2473                            return BlockPoint(output_start + input_overshoot);
2474                        }
2475                    }
2476                }
2477
2478                if search_left {
2479                    cursor.prev();
2480                } else {
2481                    cursor.next();
2482                }
2483            } else if reversed {
2484                return self.max_point();
2485            } else {
2486                reversed = true;
2487                search_left = !search_left;
2488                cursor.seek(&BlockRow(point.row), Bias::Right);
2489            }
2490        }
2491    }
2492
2493    #[ztracing::instrument(skip_all)]
2494    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
2495        let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
2496            (),
2497            &wrap_point.row(),
2498            Bias::Right,
2499        );
2500        if let Some(transform) = item {
2501            if transform.block.is_some() {
2502                BlockPoint::new(start.1, 0)
2503            } else {
2504                let Dimensions(input_start_row, output_start_row, _) = start;
2505                let input_start = Point::new(input_start_row.0, 0);
2506                let output_start = Point::new(output_start_row.0, 0);
2507                let input_overshoot = wrap_point.0 - input_start;
2508                BlockPoint(output_start + input_overshoot)
2509            }
2510        } else {
2511            self.max_point()
2512        }
2513    }
2514
2515    #[ztracing::instrument(skip_all)]
2516    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
2517        let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
2518            (),
2519            &BlockRow(block_point.row),
2520            Bias::Right,
2521        );
2522        if let Some(transform) = item {
2523            match transform.block.as_ref() {
2524                Some(block) => {
2525                    if block.place_below() {
2526                        let wrap_row = start.1 - RowDelta(1);
2527                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2528                    } else if block.place_above() {
2529                        WrapPoint::new(start.1, 0)
2530                    } else if bias == Bias::Left {
2531                        WrapPoint::new(start.1, 0)
2532                    } else {
2533                        let wrap_row = end.1 - RowDelta(1);
2534                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2535                    }
2536                }
2537                None => {
2538                    let overshoot = block_point.row() - start.0;
2539                    let wrap_row = start.1 + RowDelta(overshoot.0);
2540                    WrapPoint::new(wrap_row, block_point.column)
2541                }
2542            }
2543        } else {
2544            self.wrap_snapshot.max_point()
2545        }
2546    }
2547}
2548
2549impl BlockChunks<'_> {
2550    /// Go to the next transform
2551    #[ztracing::instrument(skip_all)]
2552    fn advance(&mut self) {
2553        self.input_chunk = Chunk::default();
2554        self.transforms.next();
2555        while let Some(transform) = self.transforms.item() {
2556            if transform
2557                .block
2558                .as_ref()
2559                .is_some_and(|block| block.height() == 0)
2560            {
2561                self.transforms.next();
2562            } else {
2563                break;
2564            }
2565        }
2566
2567        if self
2568            .transforms
2569            .item()
2570            .is_some_and(|transform| transform.block.is_none())
2571        {
2572            let start_input_row = self.transforms.start().1;
2573            let start_output_row = self.transforms.start().0;
2574            if start_output_row < self.max_output_row {
2575                let end_input_row = cmp::min(
2576                    self.transforms.end().1,
2577                    start_input_row + (self.max_output_row - start_output_row),
2578                );
2579                self.input_chunks.seek(start_input_row..end_input_row);
2580            }
2581        }
2582    }
2583}
2584
2585pub struct StickyHeaderExcerpt<'a> {
2586    pub excerpt: &'a ExcerptBoundaryInfo,
2587}
2588
2589impl<'a> Iterator for BlockChunks<'a> {
2590    type Item = Chunk<'a>;
2591
2592    #[ztracing::instrument(skip_all)]
2593    fn next(&mut self) -> Option<Self::Item> {
2594        if self.output_row >= self.max_output_row {
2595            return None;
2596        }
2597
2598        if self.line_count_overflow > RowDelta(0) {
2599            let lines = self.line_count_overflow.0.min(u128::BITS);
2600            self.line_count_overflow.0 -= lines;
2601            self.output_row += RowDelta(lines);
2602            return Some(Chunk {
2603                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines as usize]) },
2604                chars: 1u128.unbounded_shl(lines).wrapping_sub(1),
2605                ..Default::default()
2606            });
2607        }
2608
2609        let transform = self.transforms.item()?;
2610        if transform.block.is_some() {
2611            let block_start = self.transforms.start().0;
2612            let mut block_end = self.transforms.end().0;
2613            self.advance();
2614            if self.transforms.item().is_none() {
2615                block_end -= RowDelta(1);
2616            }
2617
2618            let start_in_block = self.output_row - block_start;
2619            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
2620            let line_count = end_in_block - start_in_block;
2621            let lines = RowDelta(line_count.0.min(u128::BITS));
2622            self.line_count_overflow = line_count - lines;
2623            self.output_row += lines;
2624
2625            return Some(Chunk {
2626                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines.0 as usize]) },
2627                chars: 1u128.unbounded_shl(lines.0).wrapping_sub(1),
2628                ..Default::default()
2629            });
2630        }
2631
2632        if self.input_chunk.text.is_empty() {
2633            if let Some(input_chunk) = self.input_chunks.next() {
2634                self.input_chunk = input_chunk;
2635            } else {
2636                if self.output_row < self.max_output_row {
2637                    self.output_row.0 += 1;
2638                    self.advance();
2639                    if self.transforms.item().is_some() {
2640                        return Some(Chunk {
2641                            text: "\n",
2642                            chars: 1,
2643                            ..Default::default()
2644                        });
2645                    }
2646                }
2647                return None;
2648            }
2649        }
2650
2651        let transform_end = self.transforms.end().0;
2652        let (prefix_rows, prefix_bytes) =
2653            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
2654        self.output_row += prefix_rows;
2655
2656        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
2657        self.input_chunk.text = suffix;
2658        self.input_chunk.tabs >>= prefix_bytes.saturating_sub(1);
2659        self.input_chunk.chars >>= prefix_bytes.saturating_sub(1);
2660        self.input_chunk.newlines >>= prefix_bytes.saturating_sub(1);
2661
2662        let mut tabs = self.input_chunk.tabs;
2663        let mut chars = self.input_chunk.chars;
2664        let mut newlines = self.input_chunk.newlines;
2665
2666        if self.masked {
2667            // Not great for multibyte text because to keep cursor math correct we
2668            // need to have the same number of chars in the input as output.
2669            let chars_count = prefix.chars().count();
2670            let bullet_len = chars_count;
2671            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
2672            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
2673            tabs = 0;
2674            newlines = 0;
2675        }
2676
2677        let chunk = Chunk {
2678            text: prefix,
2679            tabs,
2680            chars,
2681            newlines,
2682            ..self.input_chunk.clone()
2683        };
2684
2685        if self.output_row == transform_end {
2686            self.advance();
2687        }
2688
2689        Some(chunk)
2690    }
2691}
2692
2693impl Iterator for BlockRows<'_> {
2694    type Item = RowInfo;
2695
2696    #[ztracing::instrument(skip_all)]
2697    fn next(&mut self) -> Option<Self::Item> {
2698        if self.started {
2699            self.output_row.0 += 1;
2700        } else {
2701            self.started = true;
2702        }
2703
2704        if self.output_row >= self.transforms.end().0 {
2705            self.transforms.next();
2706            while let Some(transform) = self.transforms.item() {
2707                if transform
2708                    .block
2709                    .as_ref()
2710                    .is_some_and(|block| block.height() == 0)
2711                {
2712                    self.transforms.next();
2713                } else {
2714                    break;
2715                }
2716            }
2717
2718            let transform = self.transforms.item()?;
2719            if transform
2720                .block
2721                .as_ref()
2722                .is_none_or(|block| block.is_replacement())
2723            {
2724                self.input_rows.seek(self.transforms.start().1);
2725            }
2726        }
2727
2728        let transform = self.transforms.item()?;
2729        if transform.block.as_ref().is_none_or(|block| {
2730            block.is_replacement()
2731                && self.transforms.start().0 == self.output_row
2732                && matches!(block, Block::FoldedBuffer { .. }).not()
2733        }) {
2734            self.input_rows.next()
2735        } else {
2736            Some(RowInfo::default())
2737        }
2738    }
2739}
2740
2741impl sum_tree::Item for Transform {
2742    type Summary = TransformSummary;
2743
2744    fn summary(&self, _cx: ()) -> Self::Summary {
2745        self.summary.clone()
2746    }
2747}
2748
2749impl sum_tree::ContextLessSummary for TransformSummary {
2750    fn zero() -> Self {
2751        Default::default()
2752    }
2753
2754    fn add_summary(&mut self, summary: &Self) {
2755        if summary.longest_row_chars > self.longest_row_chars {
2756            self.longest_row = self.output_rows + summary.longest_row;
2757            self.longest_row_chars = summary.longest_row_chars;
2758        }
2759        self.input_rows += summary.input_rows;
2760        self.output_rows += summary.output_rows;
2761    }
2762}
2763
2764impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
2765    fn zero(_cx: ()) -> Self {
2766        Default::default()
2767    }
2768
2769    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2770        *self += summary.input_rows;
2771    }
2772}
2773
2774impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
2775    fn zero(_cx: ()) -> Self {
2776        Default::default()
2777    }
2778
2779    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2780        *self += summary.output_rows;
2781    }
2782}
2783
2784impl Deref for BlockContext<'_, '_> {
2785    type Target = App;
2786
2787    fn deref(&self) -> &Self::Target {
2788        self.app
2789    }
2790}
2791
2792impl DerefMut for BlockContext<'_, '_> {
2793    fn deref_mut(&mut self) -> &mut Self::Target {
2794        self.app
2795    }
2796}
2797
2798impl CustomBlock {
2799    #[ztracing::instrument(skip_all)]
2800    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
2801        self.render.lock()(cx)
2802    }
2803
2804    #[ztracing::instrument(skip_all)]
2805    pub fn start(&self) -> Anchor {
2806        *self.placement.start()
2807    }
2808
2809    #[ztracing::instrument(skip_all)]
2810    pub fn end(&self) -> Anchor {
2811        *self.placement.end()
2812    }
2813
2814    pub fn style(&self) -> BlockStyle {
2815        self.style
2816    }
2817
2818    pub fn properties(&self) -> BlockProperties<Anchor> {
2819        BlockProperties {
2820            placement: self.placement.clone(),
2821            height: self.height,
2822            style: self.style,
2823            render: Arc::new(|_| {
2824                // Not used
2825                gpui::Empty.into_any_element()
2826            }),
2827            priority: self.priority,
2828        }
2829    }
2830}
2831
2832impl Debug for CustomBlock {
2833    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2834        f.debug_struct("Block")
2835            .field("id", &self.id)
2836            .field("placement", &self.placement)
2837            .field("height", &self.height)
2838            .field("style", &self.style)
2839            .field("priority", &self.priority)
2840            .finish_non_exhaustive()
2841    }
2842}
2843
2844// Count the number of bytes prior to a target point. If the string doesn't contain the target
2845// point, return its total extent. Otherwise return the target point itself.
2846fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
2847    let mut row = 0;
2848    let mut offset = 0;
2849    for (ix, line) in s.split('\n').enumerate() {
2850        if ix > 0 {
2851            row += 1;
2852            offset += 1;
2853        }
2854        if row >= target.0 {
2855            break;
2856        }
2857        offset += line.len();
2858    }
2859    (RowDelta(row), offset)
2860}
2861
2862#[cfg(test)]
2863mod tests {
2864    use super::*;
2865    use crate::{
2866        display_map::{
2867            Companion, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
2868        },
2869        split::{convert_lhs_rows_to_rhs, convert_rhs_rows_to_lhs},
2870        test::test_font,
2871    };
2872    use buffer_diff::BufferDiff;
2873    use gpui::{App, AppContext as _, Element, div, font, px};
2874    use itertools::Itertools;
2875    use language::{Buffer, Capability, Point};
2876    use multi_buffer::{MultiBuffer, PathKey};
2877    use rand::prelude::*;
2878    use settings::SettingsStore;
2879    use std::env;
2880    use util::RandomCharIter;
2881
2882    #[gpui::test]
2883    fn test_offset_for_row() {
2884        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2885        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2886        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2887        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2888        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2889        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2890        assert_eq!(
2891            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2892            (RowDelta(0), 0)
2893        );
2894        assert_eq!(
2895            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2896            (RowDelta(1), 4)
2897        );
2898        assert_eq!(
2899            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2900            (RowDelta(2), 8)
2901        );
2902        assert_eq!(
2903            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2904            (RowDelta(2), 11)
2905        );
2906    }
2907
2908    #[gpui::test]
2909    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2910        cx.update(init_test);
2911
2912        let text = "aaa\nbbb\nccc\nddd";
2913
2914        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2915        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2916        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2917        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2918        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2919        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2920        let (wrap_map, wraps_snapshot) =
2921            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2922        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2923
2924        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2925        let block_ids = writer.insert(vec![
2926            BlockProperties {
2927                style: BlockStyle::Fixed,
2928                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2929                height: Some(1),
2930                render: Arc::new(|_| div().into_any()),
2931                priority: 0,
2932            },
2933            BlockProperties {
2934                style: BlockStyle::Fixed,
2935                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2936                height: Some(2),
2937                render: Arc::new(|_| div().into_any()),
2938                priority: 0,
2939            },
2940            BlockProperties {
2941                style: BlockStyle::Fixed,
2942                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2943                height: Some(3),
2944                render: Arc::new(|_| div().into_any()),
2945                priority: 0,
2946            },
2947        ]);
2948
2949        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2950        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2951
2952        let blocks = snapshot
2953            .blocks_in_range(BlockRow(0)..BlockRow(8))
2954            .map(|(start_row, block)| {
2955                let block = block.as_custom().unwrap();
2956                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
2957            })
2958            .collect::<Vec<_>>();
2959
2960        // When multiple blocks are on the same line, the newer blocks appear first.
2961        assert_eq!(
2962            blocks,
2963            &[
2964                (1..2, block_ids[0]),
2965                (2..4, block_ids[1]),
2966                (7..10, block_ids[2]),
2967            ]
2968        );
2969
2970        assert_eq!(
2971            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
2972            BlockPoint::new(BlockRow(0), 3)
2973        );
2974        assert_eq!(
2975            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
2976            BlockPoint::new(BlockRow(4), 0)
2977        );
2978        assert_eq!(
2979            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
2980            BlockPoint::new(BlockRow(6), 3)
2981        );
2982
2983        assert_eq!(
2984            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
2985            WrapPoint::new(WrapRow(0), 3)
2986        );
2987        assert_eq!(
2988            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2989            WrapPoint::new(WrapRow(1), 0)
2990        );
2991        assert_eq!(
2992            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
2993            WrapPoint::new(WrapRow(1), 0)
2994        );
2995        assert_eq!(
2996            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2997            WrapPoint::new(WrapRow(3), 3)
2998        );
2999
3000        assert_eq!(
3001            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
3002            BlockPoint::new(BlockRow(0), 3)
3003        );
3004        assert_eq!(
3005            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
3006            BlockPoint::new(BlockRow(4), 0)
3007        );
3008        assert_eq!(
3009            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
3010            BlockPoint::new(BlockRow(0), 3)
3011        );
3012        assert_eq!(
3013            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
3014            BlockPoint::new(BlockRow(4), 0)
3015        );
3016        assert_eq!(
3017            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
3018            BlockPoint::new(BlockRow(4), 0)
3019        );
3020        assert_eq!(
3021            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
3022            BlockPoint::new(BlockRow(4), 0)
3023        );
3024        assert_eq!(
3025            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
3026            BlockPoint::new(BlockRow(6), 3)
3027        );
3028        assert_eq!(
3029            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
3030            BlockPoint::new(BlockRow(6), 3)
3031        );
3032        assert_eq!(
3033            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
3034            BlockPoint::new(BlockRow(6), 3)
3035        );
3036        assert_eq!(
3037            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
3038            BlockPoint::new(BlockRow(6), 3)
3039        );
3040
3041        assert_eq!(
3042            snapshot
3043                .row_infos(BlockRow(0))
3044                .map(|row_info| row_info.buffer_row)
3045                .collect::<Vec<_>>(),
3046            &[
3047                Some(0),
3048                None,
3049                None,
3050                None,
3051                Some(1),
3052                Some(2),
3053                Some(3),
3054                None,
3055                None,
3056                None
3057            ]
3058        );
3059
3060        // Insert a line break, separating two block decorations into separate lines.
3061        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3062            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
3063            buffer.snapshot(cx)
3064        });
3065
3066        let (inlay_snapshot, inlay_edits) =
3067            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
3068        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3069        let (tab_snapshot, tab_edits) =
3070            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
3071        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3072            wrap_map.sync(tab_snapshot, tab_edits, cx)
3073        });
3074        let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3075        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
3076    }
3077
3078    #[gpui::test]
3079    fn test_multibuffer_headers_and_footers(cx: &mut App) {
3080        init_test(cx);
3081
3082        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
3083        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
3084        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
3085
3086        let multi_buffer = cx.new(|cx| {
3087            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
3088            multi_buffer.set_excerpts_for_path(
3089                PathKey::sorted(0),
3090                buffer1.clone(),
3091                [Point::zero()..buffer1.read(cx).max_point()],
3092                0,
3093                cx,
3094            );
3095            multi_buffer.set_excerpts_for_path(
3096                PathKey::sorted(1),
3097                buffer2.clone(),
3098                [Point::zero()..buffer2.read(cx).max_point()],
3099                0,
3100                cx,
3101            );
3102            multi_buffer.set_excerpts_for_path(
3103                PathKey::sorted(2),
3104                buffer3.clone(),
3105                [Point::zero()..buffer3.read(cx).max_point()],
3106                0,
3107                cx,
3108            );
3109            multi_buffer
3110        });
3111        let excerpt_start_anchors = multi_buffer.read_with(cx, |mb, _| {
3112            let snapshot = mb.snapshot(cx);
3113            snapshot
3114                .excerpts()
3115                .map(|e| snapshot.anchor_in_excerpt(e.context.start).unwrap())
3116                .collect::<Vec<_>>()
3117        });
3118
3119        let font = test_font();
3120        let font_size = px(14.);
3121        let font_id = cx.text_system().resolve_font(&font);
3122        let mut wrap_width = px(0.);
3123        for c in "Buff".chars() {
3124            wrap_width += cx
3125                .text_system()
3126                .advance(font_id, font_size, c)
3127                .unwrap()
3128                .width;
3129        }
3130
3131        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
3132        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
3133        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3134        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3135        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
3136
3137        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3138        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3139
3140        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
3141        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
3142
3143        let blocks: Vec<_> = snapshot
3144            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3145            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
3146            .collect();
3147        assert_eq!(
3148            blocks,
3149            vec![
3150                (0..1, BlockId::ExcerptBoundary(excerpt_start_anchors[0])), // path, header
3151                (3..4, BlockId::ExcerptBoundary(excerpt_start_anchors[1])), // path, header
3152                (6..7, BlockId::ExcerptBoundary(excerpt_start_anchors[2])), // path, header
3153            ]
3154        );
3155    }
3156
3157    #[gpui::test]
3158    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
3159        cx.update(init_test);
3160
3161        let text = "aaa\nbbb\nccc\nddd";
3162
3163        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3164        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3165        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
3166        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3167        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3168        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
3169        let (_wrap_map, wraps_snapshot) =
3170            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3171        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3172
3173        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3174        let block_ids = writer.insert(vec![
3175            BlockProperties {
3176                style: BlockStyle::Fixed,
3177                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3178                height: Some(1),
3179                render: Arc::new(|_| div().into_any()),
3180                priority: 0,
3181            },
3182            BlockProperties {
3183                style: BlockStyle::Fixed,
3184                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
3185                height: Some(2),
3186                render: Arc::new(|_| div().into_any()),
3187                priority: 0,
3188            },
3189            BlockProperties {
3190                style: BlockStyle::Fixed,
3191                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
3192                height: Some(3),
3193                render: Arc::new(|_| div().into_any()),
3194                priority: 0,
3195            },
3196        ]);
3197
3198        {
3199            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3200            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3201
3202            let mut block_map_writer =
3203                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3204
3205            let mut new_heights = HashMap::default();
3206            new_heights.insert(block_ids[0], 2);
3207            block_map_writer.resize(new_heights);
3208            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3209            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3210        }
3211
3212        {
3213            let mut block_map_writer =
3214                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3215
3216            let mut new_heights = HashMap::default();
3217            new_heights.insert(block_ids[0], 1);
3218            block_map_writer.resize(new_heights);
3219
3220            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3221            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3222        }
3223
3224        {
3225            let mut block_map_writer =
3226                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3227
3228            let mut new_heights = HashMap::default();
3229            new_heights.insert(block_ids[0], 0);
3230            block_map_writer.resize(new_heights);
3231
3232            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3233            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
3234        }
3235
3236        {
3237            let mut block_map_writer =
3238                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3239
3240            let mut new_heights = HashMap::default();
3241            new_heights.insert(block_ids[0], 3);
3242            block_map_writer.resize(new_heights);
3243
3244            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3245            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3246        }
3247
3248        {
3249            let mut block_map_writer =
3250                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3251
3252            let mut new_heights = HashMap::default();
3253            new_heights.insert(block_ids[0], 3);
3254            block_map_writer.resize(new_heights);
3255
3256            let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3257            // Same height as before, should remain the same
3258            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3259        }
3260    }
3261
3262    #[gpui::test]
3263    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
3264        cx.update(init_test);
3265
3266        let text = "one two three\nfour five six\nseven eight";
3267
3268        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3269        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3270        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3271        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3272        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3273        let (_, wraps_snapshot) = cx.update(|cx| {
3274            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
3275        });
3276        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3277
3278        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3279        writer.insert(vec![
3280            BlockProperties {
3281                style: BlockStyle::Fixed,
3282                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
3283                render: Arc::new(|_| div().into_any()),
3284                height: Some(1),
3285                priority: 0,
3286            },
3287            BlockProperties {
3288                style: BlockStyle::Fixed,
3289                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
3290                render: Arc::new(|_| div().into_any()),
3291                height: Some(1),
3292                priority: 0,
3293            },
3294        ]);
3295
3296        // Blocks with an 'above' disposition go above their corresponding buffer line.
3297        // Blocks with a 'below' disposition go below their corresponding buffer line.
3298        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3299        assert_eq!(
3300            snapshot.text(),
3301            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
3302        );
3303    }
3304
3305    #[gpui::test]
3306    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
3307        cx.update(init_test);
3308
3309        let text = "line1\nline2\nline3\nline4\nline5";
3310
3311        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3312        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
3313        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3314        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3315        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3316        let tab_size = 1.try_into().unwrap();
3317        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3318        let (wrap_map, wraps_snapshot) =
3319            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3320        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3321
3322        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3323        let replace_block_id = writer.insert(vec![BlockProperties {
3324            style: BlockStyle::Fixed,
3325            placement: BlockPlacement::Replace(
3326                buffer_snapshot.anchor_after(Point::new(1, 3))
3327                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
3328            ),
3329            height: Some(4),
3330            render: Arc::new(|_| div().into_any()),
3331            priority: 0,
3332        }])[0];
3333
3334        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3335        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3336
3337        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3338            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
3339            buffer.snapshot(cx)
3340        });
3341        let (inlay_snapshot, inlay_edits) =
3342            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
3343        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3344        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3345        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3346            wrap_map.sync(tab_snapshot, tab_edits, cx)
3347        });
3348        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3349        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3350
3351        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3352            buffer.edit(
3353                [(
3354                    Point::new(1, 5)..Point::new(1, 5),
3355                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
3356                )],
3357                None,
3358                cx,
3359            );
3360            buffer.snapshot(cx)
3361        });
3362        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
3363            buffer_snapshot.clone(),
3364            buffer_subscription.consume().into_inner(),
3365        );
3366        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3367        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3368        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3369            wrap_map.sync(tab_snapshot, tab_edits, cx)
3370        });
3371        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3372        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3373
3374        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
3375        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3376        writer.insert(vec![
3377            BlockProperties {
3378                style: BlockStyle::Fixed,
3379                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
3380                height: Some(1),
3381                render: Arc::new(|_| div().into_any()),
3382                priority: 0,
3383            },
3384            BlockProperties {
3385                style: BlockStyle::Fixed,
3386                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
3387                height: Some(1),
3388                render: Arc::new(|_| div().into_any()),
3389                priority: 0,
3390            },
3391            BlockProperties {
3392                style: BlockStyle::Fixed,
3393                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
3394                height: Some(1),
3395                render: Arc::new(|_| div().into_any()),
3396                priority: 0,
3397            },
3398        ]);
3399        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3400        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3401
3402        // Ensure blocks inserted *inside* replaced region are hidden.
3403        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3404        writer.insert(vec![
3405            BlockProperties {
3406                style: BlockStyle::Fixed,
3407                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
3408                height: Some(1),
3409                render: Arc::new(|_| div().into_any()),
3410                priority: 0,
3411            },
3412            BlockProperties {
3413                style: BlockStyle::Fixed,
3414                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
3415                height: Some(1),
3416                render: Arc::new(|_| div().into_any()),
3417                priority: 0,
3418            },
3419            BlockProperties {
3420                style: BlockStyle::Fixed,
3421                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
3422                height: Some(1),
3423                render: Arc::new(|_| div().into_any()),
3424                priority: 0,
3425            },
3426        ]);
3427        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3428        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3429
3430        // Removing the replace block shows all the hidden blocks again.
3431        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3432        writer.remove(HashSet::from_iter([replace_block_id]));
3433        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3434        assert_eq!(
3435            blocks_snapshot.text(),
3436            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
3437        );
3438    }
3439
3440    #[gpui::test]
3441    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
3442        cx.update(init_test);
3443
3444        let text = "111\n\n222\n\n333\n\n444\n\n555\n\n666";
3445
3446        let buffer = cx.update(|cx| {
3447            let multibuffer = MultiBuffer::build_multi(
3448                [
3449                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
3450                    (
3451                        text,
3452                        vec![
3453                            Point::new(2, 0)..Point::new(2, 3),
3454                            Point::new(4, 0)..Point::new(4, 3),
3455                            Point::new(6, 0)..Point::new(6, 3),
3456                        ],
3457                    ),
3458                    (
3459                        text,
3460                        vec![
3461                            Point::new(8, 0)..Point::new(8, 3),
3462                            Point::new(10, 0)..Point::new(10, 3),
3463                        ],
3464                    ),
3465                ],
3466                cx,
3467            );
3468            assert_eq!(multibuffer.read(cx).snapshot(cx).excerpts().count(), 6);
3469            multibuffer
3470        });
3471        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3472        let buffer_ids = buffer_snapshot
3473            .excerpts()
3474            .map(|excerpt| excerpt.context.start.buffer_id)
3475            .dedup()
3476            .collect::<Vec<_>>();
3477        assert_eq!(buffer_ids.len(), 3);
3478        let buffer_id_1 = buffer_ids[0];
3479        let buffer_id_2 = buffer_ids[1];
3480        let buffer_id_3 = buffer_ids[2];
3481
3482        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3483        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3484        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3485        let (_, wrap_snapshot) =
3486            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3487        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3488        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3489
3490        assert_eq!(
3491            blocks_snapshot.text(),
3492            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
3493        );
3494        assert_eq!(
3495            blocks_snapshot
3496                .row_infos(BlockRow(0))
3497                .map(|i| i.buffer_row)
3498                .collect::<Vec<_>>(),
3499            vec![
3500                None,
3501                None,
3502                Some(0),
3503                None,
3504                None,
3505                Some(2),
3506                None,
3507                Some(4),
3508                None,
3509                Some(6),
3510                None,
3511                None,
3512                Some(8),
3513                None,
3514                Some(10),
3515            ]
3516        );
3517
3518        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3519        let excerpt_blocks_2 = writer.insert(vec![
3520            BlockProperties {
3521                style: BlockStyle::Fixed,
3522                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3523                height: Some(1),
3524                render: Arc::new(|_| div().into_any()),
3525                priority: 0,
3526            },
3527            BlockProperties {
3528                style: BlockStyle::Fixed,
3529                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
3530                height: Some(1),
3531                render: Arc::new(|_| div().into_any()),
3532                priority: 0,
3533            },
3534            BlockProperties {
3535                style: BlockStyle::Fixed,
3536                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
3537                height: Some(1),
3538                render: Arc::new(|_| div().into_any()),
3539                priority: 0,
3540            },
3541        ]);
3542        let excerpt_blocks_3 = writer.insert(vec![
3543            BlockProperties {
3544                style: BlockStyle::Fixed,
3545                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
3546                height: Some(1),
3547                render: Arc::new(|_| div().into_any()),
3548                priority: 0,
3549            },
3550            BlockProperties {
3551                style: BlockStyle::Fixed,
3552                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
3553                height: Some(1),
3554                render: Arc::new(|_| div().into_any()),
3555                priority: 0,
3556            },
3557        ]);
3558
3559        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3560        assert_eq!(
3561            blocks_snapshot.text(),
3562            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3563        );
3564        assert_eq!(
3565            blocks_snapshot
3566                .row_infos(BlockRow(0))
3567                .map(|i| i.buffer_row)
3568                .collect::<Vec<_>>(),
3569            vec![
3570                None,
3571                None,
3572                Some(0),
3573                None,
3574                None,
3575                None,
3576                Some(2),
3577                None,
3578                None,
3579                Some(4),
3580                None,
3581                Some(6),
3582                None,
3583                None,
3584                None,
3585                None,
3586                Some(8),
3587                None,
3588                Some(10),
3589                None,
3590            ]
3591        );
3592
3593        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3594        buffer.read_with(cx, |buffer, cx| {
3595            writer.fold_buffers([buffer_id_1], buffer, cx);
3596        });
3597        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
3598            style: BlockStyle::Fixed,
3599            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
3600            height: Some(1),
3601            render: Arc::new(|_| div().into_any()),
3602            priority: 0,
3603        }]);
3604        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3605        let blocks = blocks_snapshot
3606            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3607            .collect::<Vec<_>>();
3608        for (_, block) in &blocks {
3609            if let BlockId::Custom(custom_block_id) = block.id() {
3610                assert!(
3611                    !excerpt_blocks_1.contains(&custom_block_id),
3612                    "Should have no blocks from the folded buffer"
3613                );
3614                assert!(
3615                    excerpt_blocks_2.contains(&custom_block_id)
3616                        || excerpt_blocks_3.contains(&custom_block_id),
3617                    "Should have only blocks from unfolded buffers"
3618                );
3619            }
3620        }
3621        assert_eq!(
3622            1,
3623            blocks
3624                .iter()
3625                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3626                .count(),
3627            "Should have one folded block, producing a header of the second buffer"
3628        );
3629        assert_eq!(
3630            blocks_snapshot.text(),
3631            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3632        );
3633        assert_eq!(
3634            blocks_snapshot
3635                .row_infos(BlockRow(0))
3636                .map(|i| i.buffer_row)
3637                .collect::<Vec<_>>(),
3638            vec![
3639                None,
3640                None,
3641                None,
3642                None,
3643                None,
3644                Some(2),
3645                None,
3646                None,
3647                Some(4),
3648                None,
3649                Some(6),
3650                None,
3651                None,
3652                None,
3653                None,
3654                Some(8),
3655                None,
3656                Some(10),
3657                None,
3658            ]
3659        );
3660
3661        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3662        buffer.read_with(cx, |buffer, cx| {
3663            writer.fold_buffers([buffer_id_2], buffer, cx);
3664        });
3665        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3666        let blocks = blocks_snapshot
3667            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3668            .collect::<Vec<_>>();
3669        for (_, block) in &blocks {
3670            if let BlockId::Custom(custom_block_id) = block.id() {
3671                assert!(
3672                    !excerpt_blocks_1.contains(&custom_block_id),
3673                    "Should have no blocks from the folded buffer_1"
3674                );
3675                assert!(
3676                    !excerpt_blocks_2.contains(&custom_block_id),
3677                    "Should have no blocks from the folded buffer_2"
3678                );
3679                assert!(
3680                    excerpt_blocks_3.contains(&custom_block_id),
3681                    "Should have only blocks from unfolded buffers"
3682                );
3683            }
3684        }
3685        assert_eq!(
3686            2,
3687            blocks
3688                .iter()
3689                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3690                .count(),
3691            "Should have two folded blocks, producing headers"
3692        );
3693        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
3694        assert_eq!(
3695            blocks_snapshot
3696                .row_infos(BlockRow(0))
3697                .map(|i| i.buffer_row)
3698                .collect::<Vec<_>>(),
3699            vec![
3700                None,
3701                None,
3702                None,
3703                None,
3704                None,
3705                None,
3706                None,
3707                Some(8),
3708                None,
3709                Some(10),
3710                None,
3711            ]
3712        );
3713
3714        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3715        buffer.read_with(cx, |buffer, cx| {
3716            writer.unfold_buffers([buffer_id_1], buffer, cx);
3717        });
3718        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3719        let blocks = blocks_snapshot
3720            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3721            .collect::<Vec<_>>();
3722        for (_, block) in &blocks {
3723            if let BlockId::Custom(custom_block_id) = block.id() {
3724                assert!(
3725                    !excerpt_blocks_2.contains(&custom_block_id),
3726                    "Should have no blocks from the folded buffer_2"
3727                );
3728                assert!(
3729                    excerpt_blocks_1.contains(&custom_block_id)
3730                        || excerpt_blocks_3.contains(&custom_block_id),
3731                    "Should have only blocks from unfolded buffers"
3732                );
3733            }
3734        }
3735        assert_eq!(
3736            1,
3737            blocks
3738                .iter()
3739                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3740                .count(),
3741            "Should be back to a single folded buffer, producing a header for buffer_2"
3742        );
3743        assert_eq!(
3744            blocks_snapshot.text(),
3745            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
3746            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
3747        );
3748        assert_eq!(
3749            blocks_snapshot
3750                .row_infos(BlockRow(0))
3751                .map(|i| i.buffer_row)
3752                .collect::<Vec<_>>(),
3753            vec![
3754                None,
3755                None,
3756                None,
3757                Some(0),
3758                None,
3759                None,
3760                None,
3761                None,
3762                None,
3763                Some(8),
3764                None,
3765                Some(10),
3766                None,
3767            ]
3768        );
3769
3770        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3771        buffer.read_with(cx, |buffer, cx| {
3772            writer.fold_buffers([buffer_id_3], buffer, cx);
3773        });
3774        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3775        let blocks = blocks_snapshot
3776            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3777            .collect::<Vec<_>>();
3778        for (_, block) in &blocks {
3779            if let BlockId::Custom(custom_block_id) = block.id() {
3780                assert!(
3781                    excerpt_blocks_1.contains(&custom_block_id),
3782                    "Should have no blocks from the folded buffer_1"
3783                );
3784                assert!(
3785                    !excerpt_blocks_2.contains(&custom_block_id),
3786                    "Should have only blocks from unfolded buffers"
3787                );
3788                assert!(
3789                    !excerpt_blocks_3.contains(&custom_block_id),
3790                    "Should have only blocks from unfolded buffers"
3791                );
3792            }
3793        }
3794
3795        assert_eq!(
3796            blocks_snapshot.text(),
3797            "\n\n\n111\n\n\n\n",
3798            "Should have a single, first buffer left after folding"
3799        );
3800        assert_eq!(
3801            blocks_snapshot
3802                .row_infos(BlockRow(0))
3803                .map(|i| i.buffer_row)
3804                .collect::<Vec<_>>(),
3805            vec![None, None, None, Some(0), None, None, None, None,]
3806        );
3807    }
3808
3809    #[gpui::test]
3810    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
3811        cx.update(init_test);
3812
3813        let text = "111";
3814
3815        let buffer = cx.update(|cx| {
3816            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
3817        });
3818        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3819        let buffer_ids = buffer_snapshot
3820            .excerpts()
3821            .map(|excerpt| excerpt.context.start.buffer_id)
3822            .dedup()
3823            .collect::<Vec<_>>();
3824        assert_eq!(buffer_ids.len(), 1);
3825        let buffer_id = buffer_ids[0];
3826
3827        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
3828        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3829        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3830        let (_, wrap_snapshot) =
3831            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3832        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3833        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3834
3835        assert_eq!(blocks_snapshot.text(), "\n\n111");
3836
3837        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3838        buffer.read_with(cx, |buffer, cx| {
3839            writer.fold_buffers([buffer_id], buffer, cx);
3840        });
3841        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3842        let blocks = blocks_snapshot
3843            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3844            .collect::<Vec<_>>();
3845        assert_eq!(
3846            1,
3847            blocks
3848                .iter()
3849                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
3850                .count(),
3851            "Should have one folded block, producing a header of the second buffer"
3852        );
3853        assert_eq!(blocks_snapshot.text(), "\n");
3854        assert_eq!(
3855            blocks_snapshot
3856                .row_infos(BlockRow(0))
3857                .map(|i| i.buffer_row)
3858                .collect::<Vec<_>>(),
3859            vec![None, None],
3860            "When fully folded, should be no buffer rows"
3861        );
3862    }
3863
3864    #[gpui::test(iterations = 60)]
3865    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3866        cx.update(init_test);
3867
3868        let operations = env::var("OPERATIONS")
3869            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3870            .unwrap_or(10);
3871
3872        let wrap_width = if rng.random_bool(0.2) {
3873            None
3874        } else {
3875            Some(px(rng.random_range(0.0..=100.0)))
3876        };
3877        let tab_size = 1.try_into().unwrap();
3878        let font_size = px(14.0);
3879        let buffer_start_header_height = rng.random_range(1..=5);
3880        let excerpt_header_height = rng.random_range(1..=5);
3881
3882        log::info!("Wrap width: {:?}", wrap_width);
3883        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3884        let is_singleton = rng.random();
3885        let buffer = if is_singleton {
3886            let len = rng.random_range(0..10);
3887            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3888            log::info!("initial singleton buffer text: {:?}", text);
3889            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3890        } else {
3891            cx.update(|cx| {
3892                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3893                log::info!(
3894                    "initial multi-buffer text: {:?}",
3895                    multibuffer.read(cx).read(cx).text()
3896                );
3897                multibuffer
3898            })
3899        };
3900
3901        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3902        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3903        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3904        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3905        let font = test_font();
3906        let (wrap_map, wraps_snapshot) =
3907            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3908        let mut block_map = BlockMap::new(
3909            wraps_snapshot,
3910            buffer_start_header_height,
3911            excerpt_header_height,
3912        );
3913
3914        for _ in 0..operations {
3915            let mut buffer_edits = Vec::new();
3916            match rng.random_range(0..=100) {
3917                0..=19 => {
3918                    let wrap_width = if rng.random_bool(0.2) {
3919                        None
3920                    } else {
3921                        Some(px(rng.random_range(0.0..=100.0)))
3922                    };
3923                    log::info!("Setting wrap width to {:?}", wrap_width);
3924                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3925                }
3926                20..=39 => {
3927                    let block_count = rng.random_range(1..=5);
3928                    let block_properties = (0..block_count)
3929                        .map(|_| {
3930                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3931                            let offset = buffer.clip_offset(
3932                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3933                                Bias::Left,
3934                            );
3935                            let mut min_height = 0;
3936                            let placement = match rng.random_range(0..3) {
3937                                0 => {
3938                                    min_height = 1;
3939                                    let start = buffer.anchor_after(offset);
3940                                    let end = buffer.anchor_after(buffer.clip_offset(
3941                                        rng.random_range(offset..=buffer.len()),
3942                                        Bias::Left,
3943                                    ));
3944                                    BlockPlacement::Replace(start..=end)
3945                                }
3946                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3947                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3948                            };
3949
3950                            let height = rng.random_range(min_height..512);
3951                            BlockProperties {
3952                                style: BlockStyle::Fixed,
3953                                placement,
3954                                height: Some(height),
3955                                render: Arc::new(|_| div().into_any()),
3956                                priority: 0,
3957                            }
3958                        })
3959                        .collect::<Vec<_>>();
3960
3961                    let (inlay_snapshot, inlay_edits) =
3962                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3963                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3964                    let (tab_snapshot, tab_edits) =
3965                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3966                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3967                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3968                    });
3969                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3970                    let block_ids =
3971                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3972                            placement: props.placement.clone(),
3973                            height: props.height,
3974                            style: props.style,
3975                            render: Arc::new(|_| div().into_any()),
3976                            priority: 0,
3977                        }));
3978
3979                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3980                        log::info!(
3981                            "inserted block {:?} with height {:?} and id {:?}",
3982                            block_properties
3983                                .placement
3984                                .as_ref()
3985                                .map(|p| p.to_point(&buffer_snapshot)),
3986                            block_properties.height,
3987                            block_id
3988                        );
3989                    }
3990                }
3991                40..=59 if !block_map.custom_blocks.is_empty() => {
3992                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3993                    let block_ids_to_remove = block_map
3994                        .custom_blocks
3995                        .choose_multiple(&mut rng, block_count)
3996                        .map(|block| block.id)
3997                        .collect::<HashSet<_>>();
3998
3999                    let (inlay_snapshot, inlay_edits) =
4000                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
4001                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4002                    let (tab_snapshot, tab_edits) =
4003                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
4004                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4005                        wrap_map.sync(tab_snapshot, tab_edits, cx)
4006                    });
4007                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
4008                    log::info!(
4009                        "removing {} blocks: {:?}",
4010                        block_ids_to_remove.len(),
4011                        block_ids_to_remove
4012                    );
4013                    block_map.remove(block_ids_to_remove);
4014                }
4015                60..=79 => {
4016                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
4017                        log::info!("Noop fold/unfold operation on a singleton buffer");
4018                        continue;
4019                    }
4020                    let (inlay_snapshot, inlay_edits) =
4021                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
4022                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4023                    let (tab_snapshot, tab_edits) =
4024                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
4025                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4026                        wrap_map.sync(tab_snapshot, tab_edits, cx)
4027                    });
4028                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
4029                    let folded_buffers: Vec<_> =
4030                        block_map.block_map.folded_buffers.iter().cloned().collect();
4031                    let mut unfolded_buffers = buffer_snapshot
4032                        .buffer_ids_for_range(Anchor::Min..Anchor::Max)
4033                        .collect::<Vec<_>>();
4034                    unfolded_buffers.dedup();
4035                    log::debug!("All buffers {unfolded_buffers:?}");
4036                    log::debug!("Folded buffers {folded_buffers:?}");
4037                    unfolded_buffers.retain(|buffer_id| {
4038                        !block_map.block_map.folded_buffers.contains(buffer_id)
4039                    });
4040                    let mut folded_count = folded_buffers.len();
4041                    let mut unfolded_count = unfolded_buffers.len();
4042
4043                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
4044                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
4045                    if !fold && !unfold {
4046                        log::info!(
4047                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4048                        );
4049                        continue;
4050                    }
4051
4052                    buffer.update(cx, |buffer, cx| {
4053                        if fold {
4054                            let buffer_to_fold =
4055                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
4056                            log::info!("Folding {buffer_to_fold:?}");
4057                            let related_excerpts = buffer_snapshot
4058                                .excerpts()
4059                                .filter_map(|excerpt| {
4060                                    if excerpt.context.start.buffer_id == buffer_to_fold {
4061                                        Some((
4062                                            excerpt.context.start,
4063                                            buffer_snapshot
4064                                                .buffer_for_id(buffer_to_fold)
4065                                                .unwrap()
4066                                                .text_for_range(excerpt.context)
4067                                                .collect::<String>(),
4068                                        ))
4069                                    } else {
4070                                        None
4071                                    }
4072                                })
4073                                .collect::<Vec<_>>();
4074                            log::info!(
4075                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
4076                            );
4077                            folded_count += 1;
4078                            unfolded_count -= 1;
4079                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
4080                        }
4081                        if unfold {
4082                            let buffer_to_unfold =
4083                                folded_buffers[rng.random_range(0..folded_buffers.len())];
4084                            log::info!("Unfolding {buffer_to_unfold:?}");
4085                            unfolded_count += 1;
4086                            folded_count -= 1;
4087                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
4088                        }
4089                        log::info!(
4090                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4091                        );
4092                    });
4093                }
4094                _ => {
4095                    buffer.update(cx, |buffer, cx| {
4096                        let mutation_count = rng.random_range(1..=5);
4097                        let subscription = buffer.subscribe();
4098                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
4099                        buffer_snapshot = buffer.snapshot(cx);
4100                        buffer_edits.extend(subscription.consume());
4101                        log::info!("buffer text: {:?}", buffer_snapshot.text());
4102                    });
4103                }
4104            }
4105
4106            let (inlay_snapshot, inlay_edits) =
4107                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
4108            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4109            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
4110            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4111                wrap_map.sync(tab_snapshot, tab_edits, cx)
4112            });
4113            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
4114            assert_eq!(
4115                blocks_snapshot.transforms.summary().input_rows,
4116                wraps_snapshot.max_point().row() + RowDelta(1)
4117            );
4118            log::info!("wrapped text: {:?}", wraps_snapshot.text());
4119            log::info!("blocks text: {:?}", blocks_snapshot.text());
4120
4121            let mut expected_blocks = Vec::new();
4122            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
4123                Some((
4124                    block.placement.to_wrap_row(&wraps_snapshot)?,
4125                    Block::Custom(block.clone()),
4126                ))
4127            }));
4128
4129            let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
4130            let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
4131            let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
4132            let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
4133
4134            // Note that this needs to be synced with the related section in BlockMap::sync
4135            expected_blocks.extend(block_map.header_and_footer_blocks(
4136                &buffer_snapshot,
4137                MultiBufferOffset(0)..,
4138                |point, bias| {
4139                    wrap_point_cursor
4140                        .map(
4141                            tab_point_cursor.map(
4142                                fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias),
4143                            ),
4144                        )
4145                        .row()
4146                },
4147            ));
4148
4149            BlockMap::sort_blocks(&mut expected_blocks);
4150
4151            for (placement, block) in &expected_blocks {
4152                log::info!(
4153                    "Block {:?} placement: {:?} Height: {:?}",
4154                    block.id(),
4155                    placement,
4156                    block.height()
4157                );
4158            }
4159
4160            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
4161
4162            let input_buffer_rows = buffer_snapshot
4163                .row_infos(MultiBufferRow(0))
4164                .map(|row| row.buffer_row)
4165                .collect::<Vec<_>>();
4166            let mut expected_buffer_rows = Vec::new();
4167            let mut expected_text = String::new();
4168            let mut expected_block_positions = Vec::new();
4169            let mut expected_replaced_buffer_rows = HashSet::default();
4170            let input_text = wraps_snapshot.text();
4171
4172            // Loop over the input lines, creating (N - 1) empty lines for
4173            // blocks of height N.
4174            //
4175            // It's important to note that output *starts* as one empty line,
4176            // so we special case row 0 to assume a leading '\n'.
4177            //
4178            // Linehood is the birthright of strings.
4179            let input_text_lines = input_text.split('\n').enumerate().peekable();
4180            let mut block_row = 0;
4181            for (wrap_row, input_line) in input_text_lines {
4182                let wrap_row = WrapRow(wrap_row as u32);
4183                let multibuffer_row = wraps_snapshot
4184                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
4185                    .row;
4186
4187                // Create empty lines for the above block
4188                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4189                    if *placement.start() == wrap_row && block.place_above() {
4190                        let (_, block) = sorted_blocks_iter.next().unwrap();
4191                        expected_block_positions.push((block_row, block.id()));
4192                        if block.height() > 0 {
4193                            let text = "\n".repeat((block.height() - 1) as usize);
4194                            if block_row > 0 {
4195                                expected_text.push('\n')
4196                            }
4197                            expected_text.push_str(&text);
4198                            for _ in 0..block.height() {
4199                                expected_buffer_rows.push(None);
4200                            }
4201                            block_row += block.height();
4202                        }
4203                    } else {
4204                        break;
4205                    }
4206                }
4207
4208                // Skip lines within replace blocks, then create empty lines for the replace block's height
4209                let mut is_in_replace_block = false;
4210                if let Some((BlockPlacement::Replace(replace_range), block)) =
4211                    sorted_blocks_iter.peek()
4212                    && wrap_row >= *replace_range.start()
4213                {
4214                    is_in_replace_block = true;
4215
4216                    if wrap_row == *replace_range.start() {
4217                        if matches!(block, Block::FoldedBuffer { .. }) {
4218                            expected_buffer_rows.push(None);
4219                        } else {
4220                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
4221                        }
4222                    }
4223
4224                    if wrap_row == *replace_range.end() {
4225                        expected_block_positions.push((block_row, block.id()));
4226                        let text = "\n".repeat((block.height() - 1) as usize);
4227                        if block_row > 0 {
4228                            expected_text.push('\n');
4229                        }
4230                        expected_text.push_str(&text);
4231
4232                        for _ in 1..block.height() {
4233                            expected_buffer_rows.push(None);
4234                        }
4235                        block_row += block.height();
4236
4237                        sorted_blocks_iter.next();
4238                    }
4239                }
4240
4241                if is_in_replace_block {
4242                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
4243                } else {
4244                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
4245                    let soft_wrapped = wraps_snapshot
4246                        .to_tab_point(WrapPoint::new(wrap_row, 0))
4247                        .column()
4248                        > 0;
4249                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
4250                    if block_row > 0 {
4251                        expected_text.push('\n');
4252                    }
4253                    expected_text.push_str(input_line);
4254                    block_row += 1;
4255                }
4256
4257                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4258                    if *placement.end() == wrap_row && block.place_below() {
4259                        let (_, block) = sorted_blocks_iter.next().unwrap();
4260                        expected_block_positions.push((block_row, block.id()));
4261                        if block.height() > 0 {
4262                            let text = "\n".repeat((block.height() - 1) as usize);
4263                            if block_row > 0 {
4264                                expected_text.push('\n')
4265                            }
4266                            expected_text.push_str(&text);
4267                            for _ in 0..block.height() {
4268                                expected_buffer_rows.push(None);
4269                            }
4270                            block_row += block.height();
4271                        }
4272                    } else {
4273                        break;
4274                    }
4275                }
4276            }
4277
4278            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
4279            let expected_row_count = expected_lines.len();
4280            log::info!("expected text: {expected_text:?}");
4281
4282            assert_eq!(
4283                blocks_snapshot.max_point().row + 1,
4284                expected_row_count as u32,
4285                "actual row count != expected row count",
4286            );
4287            assert_eq!(
4288                blocks_snapshot.text(),
4289                expected_text,
4290                "actual text != expected text",
4291            );
4292
4293            for start_row in 0..expected_row_count {
4294                let end_row = rng.random_range(start_row + 1..=expected_row_count);
4295                let mut expected_text = expected_lines[start_row..end_row].join("\n");
4296                if end_row < expected_row_count {
4297                    expected_text.push('\n');
4298                }
4299
4300                let actual_text = blocks_snapshot
4301                    .chunks(
4302                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
4303                        false,
4304                        false,
4305                        Highlights::default(),
4306                    )
4307                    .map(|chunk| chunk.text)
4308                    .collect::<String>();
4309                assert_eq!(
4310                    actual_text,
4311                    expected_text,
4312                    "incorrect text starting row row range {:?}",
4313                    start_row..end_row
4314                );
4315                assert_eq!(
4316                    blocks_snapshot
4317                        .row_infos(BlockRow(start_row as u32))
4318                        .map(|row_info| row_info.buffer_row)
4319                        .collect::<Vec<_>>(),
4320                    &expected_buffer_rows[start_row..],
4321                    "incorrect buffer_rows starting at row {:?}",
4322                    start_row
4323                );
4324            }
4325
4326            assert_eq!(
4327                blocks_snapshot
4328                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4329                    .map(|(row, block)| (row.0, block.id()))
4330                    .collect::<Vec<_>>(),
4331                expected_block_positions,
4332                "invalid blocks_in_range({:?})",
4333                0..expected_row_count
4334            );
4335
4336            for (_, expected_block) in
4337                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4338            {
4339                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
4340                assert_eq!(
4341                    actual_block.map(|block| block.id()),
4342                    Some(expected_block.id())
4343                );
4344            }
4345
4346            for (block_row, block_id) in expected_block_positions {
4347                if let BlockId::Custom(block_id) = block_id {
4348                    assert_eq!(
4349                        blocks_snapshot.row_for_block(block_id),
4350                        Some(BlockRow(block_row))
4351                    );
4352                }
4353            }
4354
4355            let mut expected_longest_rows = Vec::new();
4356            let mut longest_line_len = -1_isize;
4357            for (row, line) in expected_lines.iter().enumerate() {
4358                let row = row as u32;
4359
4360                assert_eq!(
4361                    blocks_snapshot.line_len(BlockRow(row)),
4362                    line.len() as u32,
4363                    "invalid line len for row {}",
4364                    row
4365                );
4366
4367                let line_char_count = line.chars().count() as isize;
4368                match line_char_count.cmp(&longest_line_len) {
4369                    Ordering::Less => {}
4370                    Ordering::Equal => expected_longest_rows.push(row),
4371                    Ordering::Greater => {
4372                        longest_line_len = line_char_count;
4373                        expected_longest_rows.clear();
4374                        expected_longest_rows.push(row);
4375                    }
4376                }
4377            }
4378
4379            let longest_row = blocks_snapshot.longest_row();
4380            assert!(
4381                expected_longest_rows.contains(&longest_row.0),
4382                "incorrect longest row {}. expected {:?} with length {}",
4383                longest_row.0,
4384                expected_longest_rows,
4385                longest_line_len,
4386            );
4387
4388            for _ in 0..10 {
4389                let end_row = rng.random_range(1..=expected_lines.len());
4390                let start_row = rng.random_range(0..end_row);
4391
4392                let mut expected_longest_rows_in_range = vec![];
4393                let mut longest_line_len_in_range = 0;
4394
4395                let mut row = start_row as u32;
4396                for line in &expected_lines[start_row..end_row] {
4397                    let line_char_count = line.chars().count() as isize;
4398                    match line_char_count.cmp(&longest_line_len_in_range) {
4399                        Ordering::Less => {}
4400                        Ordering::Equal => expected_longest_rows_in_range.push(row),
4401                        Ordering::Greater => {
4402                            longest_line_len_in_range = line_char_count;
4403                            expected_longest_rows_in_range.clear();
4404                            expected_longest_rows_in_range.push(row);
4405                        }
4406                    }
4407                    row += 1;
4408                }
4409
4410                let longest_row_in_range = blocks_snapshot
4411                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
4412                assert!(
4413                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
4414                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
4415                    longest_row.0,
4416                    start_row..end_row,
4417                    expected_longest_rows_in_range,
4418                    longest_line_len_in_range,
4419                );
4420            }
4421
4422            // Ensure that conversion between block points and wrap points is stable.
4423            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
4424                let wrap_point = WrapPoint::new(WrapRow(row), 0);
4425                let block_point = blocks_snapshot.to_block_point(wrap_point);
4426                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
4427                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
4428                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
4429                assert_eq!(
4430                    blocks_snapshot.to_block_point(right_wrap_point),
4431                    block_point
4432                );
4433            }
4434
4435            let mut block_point = BlockPoint::new(BlockRow(0), 0);
4436            for c in expected_text.chars() {
4437                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
4438                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
4439                assert_eq!(
4440                    blocks_snapshot
4441                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
4442                    left_point,
4443                    "block point: {:?}, wrap point: {:?}",
4444                    block_point,
4445                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
4446                );
4447                assert_eq!(
4448                    left_buffer_point,
4449                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
4450                    "{:?} is not valid in buffer coordinates",
4451                    left_point
4452                );
4453
4454                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
4455                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
4456                assert_eq!(
4457                    blocks_snapshot
4458                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
4459                    right_point,
4460                    "block point: {:?}, wrap point: {:?}",
4461                    block_point,
4462                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
4463                );
4464                assert_eq!(
4465                    right_buffer_point,
4466                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
4467                    "{:?} is not valid in buffer coordinates",
4468                    right_point
4469                );
4470
4471                if c == '\n' {
4472                    block_point.0 += Point::new(1, 0);
4473                } else {
4474                    block_point.column += c.len_utf8() as u32;
4475                }
4476            }
4477
4478            for buffer_row in 0..=buffer_snapshot.max_point().row {
4479                let buffer_row = MultiBufferRow(buffer_row);
4480                assert_eq!(
4481                    blocks_snapshot.is_line_replaced(buffer_row),
4482                    expected_replaced_buffer_rows.contains(&buffer_row),
4483                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
4484                );
4485            }
4486        }
4487    }
4488
4489    #[gpui::test]
4490    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
4491        cx.update(init_test);
4492
4493        let text = "abc\ndef\nghi\njkl\nmno";
4494        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
4495        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4496        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4497        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4498        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4499        let (_wrap_map, wraps_snapshot) =
4500            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4501        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
4502
4503        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4504        let _block_id = writer.insert(vec![BlockProperties {
4505            style: BlockStyle::Fixed,
4506            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
4507            height: Some(1),
4508            render: Arc::new(|_| div().into_any()),
4509            priority: 0,
4510        }])[0];
4511
4512        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
4513        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4514
4515        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4516        writer.remove_intersecting_replace_blocks(
4517            [buffer_snapshot
4518                .anchor_after(Point::new(1, 0))
4519                .to_offset(&buffer_snapshot)
4520                ..buffer_snapshot
4521                    .anchor_after(Point::new(1, 0))
4522                    .to_offset(&buffer_snapshot)],
4523            false,
4524        );
4525        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
4526        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4527    }
4528
4529    #[gpui::test]
4530    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
4531        cx.update(init_test);
4532
4533        let text = "line 1\nline 2\nline 3";
4534        let buffer = cx.update(|cx| {
4535            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
4536        });
4537        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4538        let buffer_ids = buffer_snapshot
4539            .excerpts()
4540            .map(|excerpt| excerpt.context.start.buffer_id)
4541            .dedup()
4542            .collect::<Vec<_>>();
4543        assert_eq!(buffer_ids.len(), 1);
4544        let buffer_id = buffer_ids[0];
4545
4546        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4547        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4548        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4549        let (_, wrap_snapshot) =
4550            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4551        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4552
4553        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4554        writer.insert(vec![BlockProperties {
4555            style: BlockStyle::Fixed,
4556            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
4557            height: Some(1),
4558            render: Arc::new(|_| div().into_any()),
4559            priority: 0,
4560        }]);
4561
4562        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4563        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
4564
4565        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4566        buffer.read_with(cx, |buffer, cx| {
4567            writer.fold_buffers([buffer_id], buffer, cx);
4568        });
4569
4570        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4571        assert_eq!(blocks_snapshot.text(), "");
4572    }
4573
4574    #[gpui::test]
4575    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
4576        cx.update(init_test);
4577
4578        let text = "line 1\nline 2\nline 3\nline 4";
4579        let buffer = cx.update(|cx| {
4580            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
4581        });
4582        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4583        let buffer_ids = buffer_snapshot
4584            .excerpts()
4585            .map(|excerpt| excerpt.context.start.buffer_id)
4586            .dedup()
4587            .collect::<Vec<_>>();
4588        assert_eq!(buffer_ids.len(), 1);
4589        let buffer_id = buffer_ids[0];
4590
4591        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4592        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4593        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4594        let (_, wrap_snapshot) =
4595            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4596        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4597
4598        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4599        writer.insert(vec![BlockProperties {
4600            style: BlockStyle::Fixed,
4601            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
4602            height: Some(1),
4603            render: Arc::new(|_| div().into_any()),
4604            priority: 0,
4605        }]);
4606
4607        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4608        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
4609
4610        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4611        buffer.read_with(cx, |buffer, cx| {
4612            writer.fold_buffers([buffer_id], buffer, cx);
4613        });
4614
4615        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4616        assert_eq!(blocks_snapshot.text(), "");
4617    }
4618
4619    #[gpui::test]
4620    fn test_companion_spacer_blocks(cx: &mut gpui::TestAppContext) {
4621        cx.update(init_test);
4622
4623        let base_text = "aaa\nbbb\nccc\nddd\nddd\nddd\neee\n";
4624        let main_text = "aaa\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n";
4625
4626        let rhs_buffer = cx.new(|cx| Buffer::local(main_text, cx));
4627        let diff = cx.new(|cx| {
4628            BufferDiff::new_with_base_text(base_text, &rhs_buffer.read(cx).text_snapshot(), cx)
4629        });
4630        let lhs_buffer = diff.read_with(cx, |diff, _| diff.base_text_buffer().clone());
4631
4632        let lhs_multibuffer = cx.new(|cx| {
4633            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4634            mb.set_excerpts_for_buffer(
4635                lhs_buffer.clone(),
4636                [Point::zero()..lhs_buffer.read(cx).max_point()],
4637                0,
4638                cx,
4639            );
4640            mb.add_inverted_diff(diff.clone(), rhs_buffer.clone(), cx);
4641            mb
4642        });
4643        let rhs_multibuffer = cx.new(|cx| {
4644            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4645            mb.set_excerpts_for_buffer(
4646                rhs_buffer.clone(),
4647                [Point::zero()..rhs_buffer.read(cx).max_point()],
4648                0,
4649                cx,
4650            );
4651            mb.add_diff(diff.clone(), cx);
4652            mb
4653        });
4654        let subscription =
4655            rhs_multibuffer.update(cx, |rhs_multibuffer, _| rhs_multibuffer.subscribe());
4656
4657        let lhs_buffer_snapshot = cx.update(|cx| lhs_multibuffer.read(cx).snapshot(cx));
4658        let (mut _lhs_inlay_map, lhs_inlay_snapshot) = InlayMap::new(lhs_buffer_snapshot);
4659        let (mut _lhs_fold_map, lhs_fold_snapshot) = FoldMap::new(lhs_inlay_snapshot);
4660        let (mut _lhs_tab_map, lhs_tab_snapshot) =
4661            TabMap::new(lhs_fold_snapshot, 4.try_into().unwrap());
4662        let (_lhs_wrap_map, lhs_wrap_snapshot) =
4663            cx.update(|cx| WrapMap::new(lhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4664        let lhs_block_map = BlockMap::new(lhs_wrap_snapshot.clone(), 0, 0);
4665
4666        let rhs_buffer_snapshot = cx.update(|cx| rhs_multibuffer.read(cx).snapshot(cx));
4667        let (mut rhs_inlay_map, rhs_inlay_snapshot) = InlayMap::new(rhs_buffer_snapshot);
4668        let (mut rhs_fold_map, rhs_fold_snapshot) = FoldMap::new(rhs_inlay_snapshot);
4669        let (mut rhs_tab_map, rhs_tab_snapshot) =
4670            TabMap::new(rhs_fold_snapshot, 4.try_into().unwrap());
4671        let (_rhs_wrap_map, rhs_wrap_snapshot) =
4672            cx.update(|cx| WrapMap::new(rhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4673        let rhs_block_map = BlockMap::new(rhs_wrap_snapshot.clone(), 0, 0);
4674
4675        let rhs_entity_id = rhs_multibuffer.entity_id();
4676
4677        let companion = cx.new(|_| {
4678            Companion::new(
4679                rhs_entity_id,
4680                convert_rhs_rows_to_lhs,
4681                convert_lhs_rows_to_rhs,
4682            )
4683        });
4684
4685        let rhs_edits = Patch::new(vec![text::Edit {
4686            old: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4687            new: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4688        }]);
4689        let lhs_edits = Patch::new(vec![text::Edit {
4690            old: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4691            new: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4692        }]);
4693
4694        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4695            rhs_block_map.read(
4696                rhs_wrap_snapshot.clone(),
4697                rhs_edits.clone(),
4698                Some(CompanionView::new(
4699                    rhs_entity_id,
4700                    &lhs_wrap_snapshot,
4701                    &lhs_edits,
4702                    companion,
4703                )),
4704            )
4705        });
4706
4707        let lhs_entity_id = lhs_multibuffer.entity_id();
4708        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4709            lhs_block_map.read(
4710                lhs_wrap_snapshot.clone(),
4711                lhs_edits.clone(),
4712                Some(CompanionView::new(
4713                    lhs_entity_id,
4714                    &rhs_wrap_snapshot,
4715                    &rhs_edits,
4716                    companion,
4717                )),
4718            )
4719        });
4720
4721        // LHS:
4722        //   aaa
4723        // - bbb
4724        // - ccc
4725        //   ddd
4726        //   ddd
4727        //   ddd
4728        //   <extra line>
4729        //   <extra line>
4730        //   <extra line>
4731        //   *eee
4732        //
4733        // RHS:
4734        //   aaa
4735        //   <extra line>
4736        //   <extra line>
4737        //   ddd
4738        //   ddd
4739        //   ddd
4740        // + XXX
4741        // + YYY
4742        // + ZZZ
4743        //   eee
4744
4745        assert_eq!(
4746            rhs_snapshot.snapshot.text(),
4747            "aaa\n\n\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4748            "RHS should have 2 spacer lines after 'aaa' to align with LHS's deleted lines"
4749        );
4750
4751        assert_eq!(
4752            lhs_snapshot.snapshot.text(),
4753            "aaa\nbbb\nccc\nddd\nddd\nddd\n\n\n\neee\n",
4754            "LHS should have 3 spacer lines in place of RHS's inserted lines"
4755        );
4756
4757        // LHS:
4758        //   aaa
4759        // - bbb
4760        // - ccc
4761        //   ddd
4762        //   ddd
4763        //   ddd
4764        //   <extra line>
4765        //   <extra line>
4766        //   <extra line>
4767        //   eee
4768        //
4769        // RHS:
4770        //   aaa
4771        //   <extra line>
4772        //   <extra line>
4773        //   ddd
4774        //   foo
4775        //   foo
4776        //   foo
4777        //   ddd
4778        //   ddd
4779        // + XXX
4780        // + YYY
4781        // + ZZZ
4782        //   eee
4783
4784        let rhs_buffer_snapshot = rhs_multibuffer.update(cx, |multibuffer, cx| {
4785            multibuffer.edit(
4786                [(Point::new(2, 0)..Point::new(2, 0), "foo\nfoo\nfoo\n")],
4787                None,
4788                cx,
4789            );
4790            multibuffer.snapshot(cx)
4791        });
4792
4793        let (rhs_inlay_snapshot, rhs_inlay_edits) =
4794            rhs_inlay_map.sync(rhs_buffer_snapshot, subscription.consume().into_inner());
4795        let (rhs_fold_snapshot, rhs_fold_edits) =
4796            rhs_fold_map.read(rhs_inlay_snapshot, rhs_inlay_edits);
4797        let (rhs_tab_snapshot, rhs_tab_edits) =
4798            rhs_tab_map.sync(rhs_fold_snapshot, rhs_fold_edits, 4.try_into().unwrap());
4799        let (rhs_wrap_snapshot, rhs_wrap_edits) = _rhs_wrap_map.update(cx, |wrap_map, cx| {
4800            wrap_map.sync(rhs_tab_snapshot, rhs_tab_edits, cx)
4801        });
4802
4803        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4804            rhs_block_map.read(
4805                rhs_wrap_snapshot.clone(),
4806                rhs_wrap_edits.clone(),
4807                Some(CompanionView::new(
4808                    rhs_entity_id,
4809                    &lhs_wrap_snapshot,
4810                    &Default::default(),
4811                    companion,
4812                )),
4813            )
4814        });
4815
4816        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4817            lhs_block_map.read(
4818                lhs_wrap_snapshot.clone(),
4819                Default::default(),
4820                Some(CompanionView::new(
4821                    lhs_entity_id,
4822                    &rhs_wrap_snapshot,
4823                    &rhs_wrap_edits,
4824                    companion,
4825                )),
4826            )
4827        });
4828
4829        assert_eq!(
4830            rhs_snapshot.snapshot.text(),
4831            "aaa\n\n\nddd\nfoo\nfoo\nfoo\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4832            "RHS should have the insertion"
4833        );
4834
4835        assert_eq!(
4836            lhs_snapshot.snapshot.text(),
4837            "aaa\nbbb\nccc\nddd\n\n\n\nddd\nddd\n\n\n\neee\n",
4838            "LHS should have 3 more spacer lines to balance the insertion"
4839        );
4840    }
4841
4842    fn init_test(cx: &mut gpui::App) {
4843        let settings = SettingsStore::test(cx);
4844        cx.set_global(settings);
4845        theme_settings::init(theme::LoadThemes::JustBase, cx);
4846        assets::Assets.load_test_fonts(cx);
4847    }
4848
4849    impl Block {
4850        fn as_custom(&self) -> Option<&CustomBlock> {
4851            match self {
4852                Block::Custom(block) => Some(block),
4853                _ => None,
4854            }
4855        }
4856    }
4857
4858    impl BlockSnapshot {
4859        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
4860            self.wrap_snapshot
4861                .to_point(self.to_wrap_point(point, bias), bias)
4862        }
4863    }
4864}