block_map.rs

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