block_map.rs

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