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