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