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