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