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