block_map.rs

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