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