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, pred: &mut dyn 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 dyn FnMut(Point, Bias) -> WrapRow,
1282            companion_wrapper: &mut dyn 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        self.input_chunk.newlines >>= prefix_bytes.saturating_sub(1);
2642
2643        let mut tabs = self.input_chunk.tabs;
2644        let mut chars = self.input_chunk.chars;
2645        let mut newlines = self.input_chunk.newlines;
2646
2647        if self.masked {
2648            // Not great for multibyte text because to keep cursor math correct we
2649            // need to have the same number of chars in the input as output.
2650            let chars_count = prefix.chars().count();
2651            let bullet_len = chars_count;
2652            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
2653            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
2654            tabs = 0;
2655            newlines = 0;
2656        }
2657
2658        let chunk = Chunk {
2659            text: prefix,
2660            tabs,
2661            chars,
2662            newlines,
2663            ..self.input_chunk.clone()
2664        };
2665
2666        if self.output_row == transform_end {
2667            self.advance();
2668        }
2669
2670        Some(chunk)
2671    }
2672}
2673
2674impl Iterator for BlockRows<'_> {
2675    type Item = RowInfo;
2676
2677    #[ztracing::instrument(skip_all)]
2678    fn next(&mut self) -> Option<Self::Item> {
2679        if self.started {
2680            self.output_row.0 += 1;
2681        } else {
2682            self.started = true;
2683        }
2684
2685        if self.output_row >= self.transforms.end().0 {
2686            self.transforms.next();
2687            while let Some(transform) = self.transforms.item() {
2688                if transform
2689                    .block
2690                    .as_ref()
2691                    .is_some_and(|block| block.height() == 0)
2692                {
2693                    self.transforms.next();
2694                } else {
2695                    break;
2696                }
2697            }
2698
2699            let transform = self.transforms.item()?;
2700            if transform
2701                .block
2702                .as_ref()
2703                .is_none_or(|block| block.is_replacement())
2704            {
2705                self.input_rows.seek(self.transforms.start().1);
2706            }
2707        }
2708
2709        let transform = self.transforms.item()?;
2710        if transform.block.as_ref().is_none_or(|block| {
2711            block.is_replacement()
2712                && self.transforms.start().0 == self.output_row
2713                && matches!(block, Block::FoldedBuffer { .. }).not()
2714        }) {
2715            self.input_rows.next()
2716        } else {
2717            Some(RowInfo::default())
2718        }
2719    }
2720}
2721
2722impl sum_tree::Item for Transform {
2723    type Summary = TransformSummary;
2724
2725    fn summary(&self, _cx: ()) -> Self::Summary {
2726        self.summary.clone()
2727    }
2728}
2729
2730impl sum_tree::ContextLessSummary for TransformSummary {
2731    fn zero() -> Self {
2732        Default::default()
2733    }
2734
2735    fn add_summary(&mut self, summary: &Self) {
2736        if summary.longest_row_chars > self.longest_row_chars {
2737            self.longest_row = self.output_rows + summary.longest_row;
2738            self.longest_row_chars = summary.longest_row_chars;
2739        }
2740        self.input_rows += summary.input_rows;
2741        self.output_rows += summary.output_rows;
2742    }
2743}
2744
2745impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
2746    fn zero(_cx: ()) -> Self {
2747        Default::default()
2748    }
2749
2750    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2751        *self += summary.input_rows;
2752    }
2753}
2754
2755impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
2756    fn zero(_cx: ()) -> Self {
2757        Default::default()
2758    }
2759
2760    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2761        *self += summary.output_rows;
2762    }
2763}
2764
2765impl Deref for BlockContext<'_, '_> {
2766    type Target = App;
2767
2768    fn deref(&self) -> &Self::Target {
2769        self.app
2770    }
2771}
2772
2773impl DerefMut for BlockContext<'_, '_> {
2774    fn deref_mut(&mut self) -> &mut Self::Target {
2775        self.app
2776    }
2777}
2778
2779impl CustomBlock {
2780    #[ztracing::instrument(skip_all)]
2781    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
2782        self.render.lock()(cx)
2783    }
2784
2785    #[ztracing::instrument(skip_all)]
2786    pub fn start(&self) -> Anchor {
2787        *self.placement.start()
2788    }
2789
2790    #[ztracing::instrument(skip_all)]
2791    pub fn end(&self) -> Anchor {
2792        *self.placement.end()
2793    }
2794
2795    pub fn style(&self) -> BlockStyle {
2796        self.style
2797    }
2798
2799    pub fn properties(&self) -> BlockProperties<Anchor> {
2800        BlockProperties {
2801            placement: self.placement.clone(),
2802            height: self.height,
2803            style: self.style,
2804            render: Arc::new(|_| {
2805                // Not used
2806                gpui::Empty.into_any_element()
2807            }),
2808            priority: self.priority,
2809        }
2810    }
2811}
2812
2813impl Debug for CustomBlock {
2814    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2815        f.debug_struct("Block")
2816            .field("id", &self.id)
2817            .field("placement", &self.placement)
2818            .field("height", &self.height)
2819            .field("style", &self.style)
2820            .field("priority", &self.priority)
2821            .finish_non_exhaustive()
2822    }
2823}
2824
2825// Count the number of bytes prior to a target point. If the string doesn't contain the target
2826// point, return its total extent. Otherwise return the target point itself.
2827fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
2828    let mut row = 0;
2829    let mut offset = 0;
2830    for (ix, line) in s.split('\n').enumerate() {
2831        if ix > 0 {
2832            row += 1;
2833            offset += 1;
2834        }
2835        if row >= target.0 {
2836            break;
2837        }
2838        offset += line.len();
2839    }
2840    (RowDelta(row), offset)
2841}
2842
2843#[cfg(test)]
2844mod tests {
2845    use super::*;
2846    use crate::{
2847        display_map::{
2848            Companion, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
2849        },
2850        split::{convert_lhs_rows_to_rhs, convert_rhs_rows_to_lhs},
2851        test::test_font,
2852    };
2853    use buffer_diff::BufferDiff;
2854    use gpui::{App, AppContext as _, Element, div, font, px};
2855    use itertools::Itertools;
2856    use language::{Buffer, Capability};
2857    use multi_buffer::{ExcerptRange, MultiBuffer};
2858    use rand::prelude::*;
2859    use settings::SettingsStore;
2860    use std::env;
2861    use util::RandomCharIter;
2862
2863    #[gpui::test]
2864    fn test_offset_for_row() {
2865        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2866        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2867        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2868        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2869        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2870        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2871        assert_eq!(
2872            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2873            (RowDelta(0), 0)
2874        );
2875        assert_eq!(
2876            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2877            (RowDelta(1), 4)
2878        );
2879        assert_eq!(
2880            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2881            (RowDelta(2), 8)
2882        );
2883        assert_eq!(
2884            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2885            (RowDelta(2), 11)
2886        );
2887    }
2888
2889    #[gpui::test]
2890    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2891        cx.update(init_test);
2892
2893        let text = "aaa\nbbb\nccc\nddd";
2894
2895        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2896        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2897        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2898        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2899        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2900        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2901        let (wrap_map, wraps_snapshot) =
2902            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2903        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2904
2905        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2906        let block_ids = writer.insert(vec![
2907            BlockProperties {
2908                style: BlockStyle::Fixed,
2909                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2910                height: Some(1),
2911                render: Arc::new(|_| div().into_any()),
2912                priority: 0,
2913            },
2914            BlockProperties {
2915                style: BlockStyle::Fixed,
2916                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2917                height: Some(2),
2918                render: Arc::new(|_| div().into_any()),
2919                priority: 0,
2920            },
2921            BlockProperties {
2922                style: BlockStyle::Fixed,
2923                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2924                height: Some(3),
2925                render: Arc::new(|_| div().into_any()),
2926                priority: 0,
2927            },
2928        ]);
2929
2930        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2931        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2932
2933        let blocks = snapshot
2934            .blocks_in_range(BlockRow(0)..BlockRow(8))
2935            .map(|(start_row, block)| {
2936                let block = block.as_custom().unwrap();
2937                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
2938            })
2939            .collect::<Vec<_>>();
2940
2941        // When multiple blocks are on the same line, the newer blocks appear first.
2942        assert_eq!(
2943            blocks,
2944            &[
2945                (1..2, block_ids[0]),
2946                (2..4, block_ids[1]),
2947                (7..10, block_ids[2]),
2948            ]
2949        );
2950
2951        assert_eq!(
2952            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
2953            BlockPoint::new(BlockRow(0), 3)
2954        );
2955        assert_eq!(
2956            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
2957            BlockPoint::new(BlockRow(4), 0)
2958        );
2959        assert_eq!(
2960            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
2961            BlockPoint::new(BlockRow(6), 3)
2962        );
2963
2964        assert_eq!(
2965            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
2966            WrapPoint::new(WrapRow(0), 3)
2967        );
2968        assert_eq!(
2969            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2970            WrapPoint::new(WrapRow(1), 0)
2971        );
2972        assert_eq!(
2973            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
2974            WrapPoint::new(WrapRow(1), 0)
2975        );
2976        assert_eq!(
2977            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2978            WrapPoint::new(WrapRow(3), 3)
2979        );
2980
2981        assert_eq!(
2982            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2983            BlockPoint::new(BlockRow(0), 3)
2984        );
2985        assert_eq!(
2986            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
2987            BlockPoint::new(BlockRow(4), 0)
2988        );
2989        assert_eq!(
2990            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
2991            BlockPoint::new(BlockRow(0), 3)
2992        );
2993        assert_eq!(
2994            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
2995            BlockPoint::new(BlockRow(4), 0)
2996        );
2997        assert_eq!(
2998            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
2999            BlockPoint::new(BlockRow(4), 0)
3000        );
3001        assert_eq!(
3002            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
3003            BlockPoint::new(BlockRow(4), 0)
3004        );
3005        assert_eq!(
3006            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
3007            BlockPoint::new(BlockRow(6), 3)
3008        );
3009        assert_eq!(
3010            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
3011            BlockPoint::new(BlockRow(6), 3)
3012        );
3013        assert_eq!(
3014            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
3015            BlockPoint::new(BlockRow(6), 3)
3016        );
3017        assert_eq!(
3018            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
3019            BlockPoint::new(BlockRow(6), 3)
3020        );
3021
3022        assert_eq!(
3023            snapshot
3024                .row_infos(BlockRow(0))
3025                .map(|row_info| row_info.buffer_row)
3026                .collect::<Vec<_>>(),
3027            &[
3028                Some(0),
3029                None,
3030                None,
3031                None,
3032                Some(1),
3033                Some(2),
3034                Some(3),
3035                None,
3036                None,
3037                None
3038            ]
3039        );
3040
3041        // Insert a line break, separating two block decorations into separate lines.
3042        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3043            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
3044            buffer.snapshot(cx)
3045        });
3046
3047        let (inlay_snapshot, inlay_edits) =
3048            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
3049        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3050        let (tab_snapshot, tab_edits) =
3051            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
3052        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3053            wrap_map.sync(tab_snapshot, tab_edits, cx)
3054        });
3055        let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3056        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
3057    }
3058
3059    #[gpui::test]
3060    fn test_multibuffer_headers_and_footers(cx: &mut App) {
3061        init_test(cx);
3062
3063        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
3064        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
3065        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
3066
3067        let mut excerpt_ids = Vec::new();
3068        let multi_buffer = cx.new(|cx| {
3069            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
3070            excerpt_ids.extend(multi_buffer.push_excerpts(
3071                buffer1.clone(),
3072                [ExcerptRange::new(0..buffer1.read(cx).len())],
3073                cx,
3074            ));
3075            excerpt_ids.extend(multi_buffer.push_excerpts(
3076                buffer2.clone(),
3077                [ExcerptRange::new(0..buffer2.read(cx).len())],
3078                cx,
3079            ));
3080            excerpt_ids.extend(multi_buffer.push_excerpts(
3081                buffer3.clone(),
3082                [ExcerptRange::new(0..buffer3.read(cx).len())],
3083                cx,
3084            ));
3085
3086            multi_buffer
3087        });
3088
3089        let font = test_font();
3090        let font_size = px(14.);
3091        let font_id = cx.text_system().resolve_font(&font);
3092        let mut wrap_width = px(0.);
3093        for c in "Buff".chars() {
3094            wrap_width += cx
3095                .text_system()
3096                .advance(font_id, font_size, c)
3097                .unwrap()
3098                .width;
3099        }
3100
3101        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
3102        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
3103        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3104        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3105        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
3106
3107        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3108        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3109
3110        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
3111        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
3112
3113        let blocks: Vec<_> = snapshot
3114            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3115            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
3116            .collect();
3117        assert_eq!(
3118            blocks,
3119            vec![
3120                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
3121                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
3122                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
3123            ]
3124        );
3125    }
3126
3127    #[gpui::test]
3128    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
3129        cx.update(init_test);
3130
3131        let text = "aaa\nbbb\nccc\nddd";
3132
3133        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3134        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3135        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
3136        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3137        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3138        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
3139        let (_wrap_map, wraps_snapshot) =
3140            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3141        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3142
3143        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3144        let block_ids = writer.insert(vec![
3145            BlockProperties {
3146                style: BlockStyle::Fixed,
3147                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3148                height: Some(1),
3149                render: Arc::new(|_| div().into_any()),
3150                priority: 0,
3151            },
3152            BlockProperties {
3153                style: BlockStyle::Fixed,
3154                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
3155                height: Some(2),
3156                render: Arc::new(|_| div().into_any()),
3157                priority: 0,
3158            },
3159            BlockProperties {
3160                style: BlockStyle::Fixed,
3161                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
3162                height: Some(3),
3163                render: Arc::new(|_| div().into_any()),
3164                priority: 0,
3165            },
3166        ]);
3167
3168        {
3169            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3170            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3171
3172            let mut block_map_writer =
3173                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3174
3175            let mut new_heights = HashMap::default();
3176            new_heights.insert(block_ids[0], 2);
3177            block_map_writer.resize(new_heights);
3178            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3179            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3180        }
3181
3182        {
3183            let mut block_map_writer =
3184                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3185
3186            let mut new_heights = HashMap::default();
3187            new_heights.insert(block_ids[0], 1);
3188            block_map_writer.resize(new_heights);
3189
3190            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3191            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3192        }
3193
3194        {
3195            let mut block_map_writer =
3196                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3197
3198            let mut new_heights = HashMap::default();
3199            new_heights.insert(block_ids[0], 0);
3200            block_map_writer.resize(new_heights);
3201
3202            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3203            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
3204        }
3205
3206        {
3207            let mut block_map_writer =
3208                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3209
3210            let mut new_heights = HashMap::default();
3211            new_heights.insert(block_ids[0], 3);
3212            block_map_writer.resize(new_heights);
3213
3214            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3215            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3216        }
3217
3218        {
3219            let mut block_map_writer =
3220                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3221
3222            let mut new_heights = HashMap::default();
3223            new_heights.insert(block_ids[0], 3);
3224            block_map_writer.resize(new_heights);
3225
3226            let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3227            // Same height as before, should remain the same
3228            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3229        }
3230    }
3231
3232    #[gpui::test]
3233    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
3234        cx.update(init_test);
3235
3236        let text = "one two three\nfour five six\nseven eight";
3237
3238        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3239        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3240        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3241        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3242        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3243        let (_, wraps_snapshot) = cx.update(|cx| {
3244            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
3245        });
3246        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3247
3248        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3249        writer.insert(vec![
3250            BlockProperties {
3251                style: BlockStyle::Fixed,
3252                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
3253                render: Arc::new(|_| div().into_any()),
3254                height: Some(1),
3255                priority: 0,
3256            },
3257            BlockProperties {
3258                style: BlockStyle::Fixed,
3259                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
3260                render: Arc::new(|_| div().into_any()),
3261                height: Some(1),
3262                priority: 0,
3263            },
3264        ]);
3265
3266        // Blocks with an 'above' disposition go above their corresponding buffer line.
3267        // Blocks with a 'below' disposition go below their corresponding buffer line.
3268        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3269        assert_eq!(
3270            snapshot.text(),
3271            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
3272        );
3273    }
3274
3275    #[gpui::test]
3276    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
3277        cx.update(init_test);
3278
3279        let text = "line1\nline2\nline3\nline4\nline5";
3280
3281        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3282        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
3283        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3284        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3285        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3286        let tab_size = 1.try_into().unwrap();
3287        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3288        let (wrap_map, wraps_snapshot) =
3289            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3290        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3291
3292        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3293        let replace_block_id = writer.insert(vec![BlockProperties {
3294            style: BlockStyle::Fixed,
3295            placement: BlockPlacement::Replace(
3296                buffer_snapshot.anchor_after(Point::new(1, 3))
3297                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
3298            ),
3299            height: Some(4),
3300            render: Arc::new(|_| div().into_any()),
3301            priority: 0,
3302        }])[0];
3303
3304        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3305        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3306
3307        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3308            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
3309            buffer.snapshot(cx)
3310        });
3311        let (inlay_snapshot, inlay_edits) =
3312            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
3313        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3314        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3315        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3316            wrap_map.sync(tab_snapshot, tab_edits, cx)
3317        });
3318        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3319        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3320
3321        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3322            buffer.edit(
3323                [(
3324                    Point::new(1, 5)..Point::new(1, 5),
3325                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
3326                )],
3327                None,
3328                cx,
3329            );
3330            buffer.snapshot(cx)
3331        });
3332        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
3333            buffer_snapshot.clone(),
3334            buffer_subscription.consume().into_inner(),
3335        );
3336        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3337        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3338        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3339            wrap_map.sync(tab_snapshot, tab_edits, cx)
3340        });
3341        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3342        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3343
3344        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
3345        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3346        writer.insert(vec![
3347            BlockProperties {
3348                style: BlockStyle::Fixed,
3349                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
3350                height: Some(1),
3351                render: Arc::new(|_| div().into_any()),
3352                priority: 0,
3353            },
3354            BlockProperties {
3355                style: BlockStyle::Fixed,
3356                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
3357                height: Some(1),
3358                render: Arc::new(|_| div().into_any()),
3359                priority: 0,
3360            },
3361            BlockProperties {
3362                style: BlockStyle::Fixed,
3363                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
3364                height: Some(1),
3365                render: Arc::new(|_| div().into_any()),
3366                priority: 0,
3367            },
3368        ]);
3369        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3370        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3371
3372        // Ensure blocks inserted *inside* replaced region are hidden.
3373        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3374        writer.insert(vec![
3375            BlockProperties {
3376                style: BlockStyle::Fixed,
3377                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
3378                height: Some(1),
3379                render: Arc::new(|_| div().into_any()),
3380                priority: 0,
3381            },
3382            BlockProperties {
3383                style: BlockStyle::Fixed,
3384                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
3385                height: Some(1),
3386                render: Arc::new(|_| div().into_any()),
3387                priority: 0,
3388            },
3389            BlockProperties {
3390                style: BlockStyle::Fixed,
3391                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
3392                height: Some(1),
3393                render: Arc::new(|_| div().into_any()),
3394                priority: 0,
3395            },
3396        ]);
3397        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3398        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3399
3400        // Removing the replace block shows all the hidden blocks again.
3401        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3402        writer.remove(HashSet::from_iter([replace_block_id]));
3403        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3404        assert_eq!(
3405            blocks_snapshot.text(),
3406            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
3407        );
3408    }
3409
3410    #[gpui::test]
3411    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
3412        cx.update(init_test);
3413
3414        let text = "111\n222\n333\n444\n555\n666";
3415
3416        let buffer = cx.update(|cx| {
3417            MultiBuffer::build_multi(
3418                [
3419                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
3420                    (
3421                        text,
3422                        vec![
3423                            Point::new(1, 0)..Point::new(1, 3),
3424                            Point::new(2, 0)..Point::new(2, 3),
3425                            Point::new(3, 0)..Point::new(3, 3),
3426                        ],
3427                    ),
3428                    (
3429                        text,
3430                        vec![
3431                            Point::new(4, 0)..Point::new(4, 3),
3432                            Point::new(5, 0)..Point::new(5, 3),
3433                        ],
3434                    ),
3435                ],
3436                cx,
3437            )
3438        });
3439        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3440        let buffer_ids = buffer_snapshot
3441            .excerpts()
3442            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3443            .dedup()
3444            .collect::<Vec<_>>();
3445        assert_eq!(buffer_ids.len(), 3);
3446        let buffer_id_1 = buffer_ids[0];
3447        let buffer_id_2 = buffer_ids[1];
3448        let buffer_id_3 = buffer_ids[2];
3449
3450        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3451        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3452        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3453        let (_, wrap_snapshot) =
3454            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3455        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3456        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3457
3458        assert_eq!(
3459            blocks_snapshot.text(),
3460            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
3461        );
3462        assert_eq!(
3463            blocks_snapshot
3464                .row_infos(BlockRow(0))
3465                .map(|i| i.buffer_row)
3466                .collect::<Vec<_>>(),
3467            vec![
3468                None,
3469                None,
3470                Some(0),
3471                None,
3472                None,
3473                Some(1),
3474                None,
3475                Some(2),
3476                None,
3477                Some(3),
3478                None,
3479                None,
3480                Some(4),
3481                None,
3482                Some(5),
3483            ]
3484        );
3485
3486        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3487        let excerpt_blocks_2 = writer.insert(vec![
3488            BlockProperties {
3489                style: BlockStyle::Fixed,
3490                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3491                height: Some(1),
3492                render: Arc::new(|_| div().into_any()),
3493                priority: 0,
3494            },
3495            BlockProperties {
3496                style: BlockStyle::Fixed,
3497                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
3498                height: Some(1),
3499                render: Arc::new(|_| div().into_any()),
3500                priority: 0,
3501            },
3502            BlockProperties {
3503                style: BlockStyle::Fixed,
3504                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
3505                height: Some(1),
3506                render: Arc::new(|_| div().into_any()),
3507                priority: 0,
3508            },
3509        ]);
3510        let excerpt_blocks_3 = writer.insert(vec![
3511            BlockProperties {
3512                style: BlockStyle::Fixed,
3513                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
3514                height: Some(1),
3515                render: Arc::new(|_| div().into_any()),
3516                priority: 0,
3517            },
3518            BlockProperties {
3519                style: BlockStyle::Fixed,
3520                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
3521                height: Some(1),
3522                render: Arc::new(|_| div().into_any()),
3523                priority: 0,
3524            },
3525        ]);
3526
3527        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3528        assert_eq!(
3529            blocks_snapshot.text(),
3530            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3531        );
3532        assert_eq!(
3533            blocks_snapshot
3534                .row_infos(BlockRow(0))
3535                .map(|i| i.buffer_row)
3536                .collect::<Vec<_>>(),
3537            vec![
3538                None,
3539                None,
3540                Some(0),
3541                None,
3542                None,
3543                None,
3544                Some(1),
3545                None,
3546                None,
3547                Some(2),
3548                None,
3549                Some(3),
3550                None,
3551                None,
3552                None,
3553                None,
3554                Some(4),
3555                None,
3556                Some(5),
3557                None,
3558            ]
3559        );
3560
3561        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3562        buffer.read_with(cx, |buffer, cx| {
3563            writer.fold_buffers([buffer_id_1], buffer, cx);
3564        });
3565        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
3566            style: BlockStyle::Fixed,
3567            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
3568            height: Some(1),
3569            render: Arc::new(|_| div().into_any()),
3570            priority: 0,
3571        }]);
3572        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3573        let blocks = blocks_snapshot
3574            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3575            .collect::<Vec<_>>();
3576        for (_, block) in &blocks {
3577            if let BlockId::Custom(custom_block_id) = block.id() {
3578                assert!(
3579                    !excerpt_blocks_1.contains(&custom_block_id),
3580                    "Should have no blocks from the folded buffer"
3581                );
3582                assert!(
3583                    excerpt_blocks_2.contains(&custom_block_id)
3584                        || excerpt_blocks_3.contains(&custom_block_id),
3585                    "Should have only blocks from unfolded buffers"
3586                );
3587            }
3588        }
3589        assert_eq!(
3590            1,
3591            blocks
3592                .iter()
3593                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3594                .count(),
3595            "Should have one folded block, producing a header of the second buffer"
3596        );
3597        assert_eq!(
3598            blocks_snapshot.text(),
3599            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3600        );
3601        assert_eq!(
3602            blocks_snapshot
3603                .row_infos(BlockRow(0))
3604                .map(|i| i.buffer_row)
3605                .collect::<Vec<_>>(),
3606            vec![
3607                None,
3608                None,
3609                None,
3610                None,
3611                None,
3612                Some(1),
3613                None,
3614                None,
3615                Some(2),
3616                None,
3617                Some(3),
3618                None,
3619                None,
3620                None,
3621                None,
3622                Some(4),
3623                None,
3624                Some(5),
3625                None,
3626            ]
3627        );
3628
3629        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3630        buffer.read_with(cx, |buffer, cx| {
3631            writer.fold_buffers([buffer_id_2], buffer, cx);
3632        });
3633        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3634        let blocks = blocks_snapshot
3635            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3636            .collect::<Vec<_>>();
3637        for (_, block) in &blocks {
3638            if let BlockId::Custom(custom_block_id) = block.id() {
3639                assert!(
3640                    !excerpt_blocks_1.contains(&custom_block_id),
3641                    "Should have no blocks from the folded buffer_1"
3642                );
3643                assert!(
3644                    !excerpt_blocks_2.contains(&custom_block_id),
3645                    "Should have no blocks from the folded buffer_2"
3646                );
3647                assert!(
3648                    excerpt_blocks_3.contains(&custom_block_id),
3649                    "Should have only blocks from unfolded buffers"
3650                );
3651            }
3652        }
3653        assert_eq!(
3654            2,
3655            blocks
3656                .iter()
3657                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3658                .count(),
3659            "Should have two folded blocks, producing headers"
3660        );
3661        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
3662        assert_eq!(
3663            blocks_snapshot
3664                .row_infos(BlockRow(0))
3665                .map(|i| i.buffer_row)
3666                .collect::<Vec<_>>(),
3667            vec![
3668                None,
3669                None,
3670                None,
3671                None,
3672                None,
3673                None,
3674                None,
3675                Some(4),
3676                None,
3677                Some(5),
3678                None,
3679            ]
3680        );
3681
3682        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3683        buffer.read_with(cx, |buffer, cx| {
3684            writer.unfold_buffers([buffer_id_1], buffer, cx);
3685        });
3686        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3687        let blocks = blocks_snapshot
3688            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3689            .collect::<Vec<_>>();
3690        for (_, block) in &blocks {
3691            if let BlockId::Custom(custom_block_id) = block.id() {
3692                assert!(
3693                    !excerpt_blocks_2.contains(&custom_block_id),
3694                    "Should have no blocks from the folded buffer_2"
3695                );
3696                assert!(
3697                    excerpt_blocks_1.contains(&custom_block_id)
3698                        || excerpt_blocks_3.contains(&custom_block_id),
3699                    "Should have only blocks from unfolded buffers"
3700                );
3701            }
3702        }
3703        assert_eq!(
3704            1,
3705            blocks
3706                .iter()
3707                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3708                .count(),
3709            "Should be back to a single folded buffer, producing a header for buffer_2"
3710        );
3711        assert_eq!(
3712            blocks_snapshot.text(),
3713            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
3714            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
3715        );
3716        assert_eq!(
3717            blocks_snapshot
3718                .row_infos(BlockRow(0))
3719                .map(|i| i.buffer_row)
3720                .collect::<Vec<_>>(),
3721            vec![
3722                None,
3723                None,
3724                None,
3725                Some(0),
3726                None,
3727                None,
3728                None,
3729                None,
3730                None,
3731                Some(4),
3732                None,
3733                Some(5),
3734                None,
3735            ]
3736        );
3737
3738        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3739        buffer.read_with(cx, |buffer, cx| {
3740            writer.fold_buffers([buffer_id_3], buffer, cx);
3741        });
3742        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3743        let blocks = blocks_snapshot
3744            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3745            .collect::<Vec<_>>();
3746        for (_, block) in &blocks {
3747            if let BlockId::Custom(custom_block_id) = block.id() {
3748                assert!(
3749                    excerpt_blocks_1.contains(&custom_block_id),
3750                    "Should have no blocks from the folded buffer_1"
3751                );
3752                assert!(
3753                    !excerpt_blocks_2.contains(&custom_block_id),
3754                    "Should have only blocks from unfolded buffers"
3755                );
3756                assert!(
3757                    !excerpt_blocks_3.contains(&custom_block_id),
3758                    "Should have only blocks from unfolded buffers"
3759                );
3760            }
3761        }
3762
3763        assert_eq!(
3764            blocks_snapshot.text(),
3765            "\n\n\n111\n\n\n\n",
3766            "Should have a single, first buffer left after folding"
3767        );
3768        assert_eq!(
3769            blocks_snapshot
3770                .row_infos(BlockRow(0))
3771                .map(|i| i.buffer_row)
3772                .collect::<Vec<_>>(),
3773            vec![None, None, None, Some(0), None, None, None, None,]
3774        );
3775    }
3776
3777    #[gpui::test]
3778    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
3779        cx.update(init_test);
3780
3781        let text = "111";
3782
3783        let buffer = cx.update(|cx| {
3784            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
3785        });
3786        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3787        let buffer_ids = buffer_snapshot
3788            .excerpts()
3789            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3790            .dedup()
3791            .collect::<Vec<_>>();
3792        assert_eq!(buffer_ids.len(), 1);
3793        let buffer_id = buffer_ids[0];
3794
3795        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
3796        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3797        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3798        let (_, wrap_snapshot) =
3799            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3800        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3801        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3802
3803        assert_eq!(blocks_snapshot.text(), "\n\n111");
3804
3805        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3806        buffer.read_with(cx, |buffer, cx| {
3807            writer.fold_buffers([buffer_id], buffer, cx);
3808        });
3809        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3810        let blocks = blocks_snapshot
3811            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3812            .collect::<Vec<_>>();
3813        assert_eq!(
3814            1,
3815            blocks
3816                .iter()
3817                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
3818                .count(),
3819            "Should have one folded block, producing a header of the second buffer"
3820        );
3821        assert_eq!(blocks_snapshot.text(), "\n");
3822        assert_eq!(
3823            blocks_snapshot
3824                .row_infos(BlockRow(0))
3825                .map(|i| i.buffer_row)
3826                .collect::<Vec<_>>(),
3827            vec![None, None],
3828            "When fully folded, should be no buffer rows"
3829        );
3830    }
3831
3832    #[gpui::test(iterations = 60)]
3833    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3834        cx.update(init_test);
3835
3836        let operations = env::var("OPERATIONS")
3837            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3838            .unwrap_or(10);
3839
3840        let wrap_width = if rng.random_bool(0.2) {
3841            None
3842        } else {
3843            Some(px(rng.random_range(0.0..=100.0)))
3844        };
3845        let tab_size = 1.try_into().unwrap();
3846        let font_size = px(14.0);
3847        let buffer_start_header_height = rng.random_range(1..=5);
3848        let excerpt_header_height = rng.random_range(1..=5);
3849
3850        log::info!("Wrap width: {:?}", wrap_width);
3851        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3852        let is_singleton = rng.random();
3853        let buffer = if is_singleton {
3854            let len = rng.random_range(0..10);
3855            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3856            log::info!("initial singleton buffer text: {:?}", text);
3857            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3858        } else {
3859            cx.update(|cx| {
3860                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3861                log::info!(
3862                    "initial multi-buffer text: {:?}",
3863                    multibuffer.read(cx).read(cx).text()
3864                );
3865                multibuffer
3866            })
3867        };
3868
3869        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3870        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3871        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3872        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3873        let font = test_font();
3874        let (wrap_map, wraps_snapshot) =
3875            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3876        let mut block_map = BlockMap::new(
3877            wraps_snapshot,
3878            buffer_start_header_height,
3879            excerpt_header_height,
3880        );
3881
3882        for _ in 0..operations {
3883            let mut buffer_edits = Vec::new();
3884            match rng.random_range(0..=100) {
3885                0..=19 => {
3886                    let wrap_width = if rng.random_bool(0.2) {
3887                        None
3888                    } else {
3889                        Some(px(rng.random_range(0.0..=100.0)))
3890                    };
3891                    log::info!("Setting wrap width to {:?}", wrap_width);
3892                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3893                }
3894                20..=39 => {
3895                    let block_count = rng.random_range(1..=5);
3896                    let block_properties = (0..block_count)
3897                        .map(|_| {
3898                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3899                            let offset = buffer.clip_offset(
3900                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3901                                Bias::Left,
3902                            );
3903                            let mut min_height = 0;
3904                            let placement = match rng.random_range(0..3) {
3905                                0 => {
3906                                    min_height = 1;
3907                                    let start = buffer.anchor_after(offset);
3908                                    let end = buffer.anchor_after(buffer.clip_offset(
3909                                        rng.random_range(offset..=buffer.len()),
3910                                        Bias::Left,
3911                                    ));
3912                                    BlockPlacement::Replace(start..=end)
3913                                }
3914                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3915                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3916                            };
3917
3918                            let height = rng.random_range(min_height..512);
3919                            BlockProperties {
3920                                style: BlockStyle::Fixed,
3921                                placement,
3922                                height: Some(height),
3923                                render: Arc::new(|_| div().into_any()),
3924                                priority: 0,
3925                            }
3926                        })
3927                        .collect::<Vec<_>>();
3928
3929                    let (inlay_snapshot, inlay_edits) =
3930                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3931                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3932                    let (tab_snapshot, tab_edits) =
3933                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3934                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3935                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3936                    });
3937                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3938                    let block_ids =
3939                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3940                            placement: props.placement.clone(),
3941                            height: props.height,
3942                            style: props.style,
3943                            render: Arc::new(|_| div().into_any()),
3944                            priority: 0,
3945                        }));
3946
3947                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3948                        log::info!(
3949                            "inserted block {:?} with height {:?} and id {:?}",
3950                            block_properties
3951                                .placement
3952                                .as_ref()
3953                                .map(|p| p.to_point(&buffer_snapshot)),
3954                            block_properties.height,
3955                            block_id
3956                        );
3957                    }
3958                }
3959                40..=59 if !block_map.custom_blocks.is_empty() => {
3960                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3961                    let block_ids_to_remove = block_map
3962                        .custom_blocks
3963                        .choose_multiple(&mut rng, block_count)
3964                        .map(|block| block.id)
3965                        .collect::<HashSet<_>>();
3966
3967                    let (inlay_snapshot, inlay_edits) =
3968                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3969                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3970                    let (tab_snapshot, tab_edits) =
3971                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3972                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3973                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3974                    });
3975                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3976                    log::info!(
3977                        "removing {} blocks: {:?}",
3978                        block_ids_to_remove.len(),
3979                        block_ids_to_remove
3980                    );
3981                    block_map.remove(block_ids_to_remove);
3982                }
3983                60..=79 => {
3984                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3985                        log::info!("Noop fold/unfold operation on a singleton buffer");
3986                        continue;
3987                    }
3988                    let (inlay_snapshot, inlay_edits) =
3989                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3990                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3991                    let (tab_snapshot, tab_edits) =
3992                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3993                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3994                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3995                    });
3996                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3997                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3998                        let folded_buffers: Vec<_> =
3999                            block_map.block_map.folded_buffers.iter().cloned().collect();
4000                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
4001                        unfolded_buffers.dedup();
4002                        log::debug!("All buffers {unfolded_buffers:?}");
4003                        log::debug!("Folded buffers {folded_buffers:?}");
4004                        unfolded_buffers.retain(|buffer_id| {
4005                            !block_map.block_map.folded_buffers.contains(buffer_id)
4006                        });
4007                        (unfolded_buffers, folded_buffers)
4008                    });
4009                    let mut folded_count = folded_buffers.len();
4010                    let mut unfolded_count = unfolded_buffers.len();
4011
4012                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
4013                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
4014                    if !fold && !unfold {
4015                        log::info!(
4016                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4017                        );
4018                        continue;
4019                    }
4020
4021                    buffer.update(cx, |buffer, cx| {
4022                        if fold {
4023                            let buffer_to_fold =
4024                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
4025                            log::info!("Folding {buffer_to_fold:?}");
4026                            let related_excerpts = buffer_snapshot
4027                                .excerpts()
4028                                .filter_map(|(excerpt_id, buffer, range)| {
4029                                    if buffer.remote_id() == buffer_to_fold {
4030                                        Some((
4031                                            excerpt_id,
4032                                            buffer
4033                                                .text_for_range(range.context)
4034                                                .collect::<String>(),
4035                                        ))
4036                                    } else {
4037                                        None
4038                                    }
4039                                })
4040                                .collect::<Vec<_>>();
4041                            log::info!(
4042                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
4043                            );
4044                            folded_count += 1;
4045                            unfolded_count -= 1;
4046                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
4047                        }
4048                        if unfold {
4049                            let buffer_to_unfold =
4050                                folded_buffers[rng.random_range(0..folded_buffers.len())];
4051                            log::info!("Unfolding {buffer_to_unfold:?}");
4052                            unfolded_count += 1;
4053                            folded_count -= 1;
4054                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
4055                        }
4056                        log::info!(
4057                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
4058                        );
4059                    });
4060                }
4061                _ => {
4062                    buffer.update(cx, |buffer, cx| {
4063                        let mutation_count = rng.random_range(1..=5);
4064                        let subscription = buffer.subscribe();
4065                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
4066                        buffer_snapshot = buffer.snapshot(cx);
4067                        buffer_edits.extend(subscription.consume());
4068                        log::info!("buffer text: {:?}", buffer_snapshot.text());
4069                    });
4070                }
4071            }
4072
4073            let (inlay_snapshot, inlay_edits) =
4074                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
4075            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
4076            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
4077            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
4078                wrap_map.sync(tab_snapshot, tab_edits, cx)
4079            });
4080            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
4081            assert_eq!(
4082                blocks_snapshot.transforms.summary().input_rows,
4083                wraps_snapshot.max_point().row() + RowDelta(1)
4084            );
4085            log::info!("wrapped text: {:?}", wraps_snapshot.text());
4086            log::info!("blocks text: {:?}", blocks_snapshot.text());
4087
4088            let mut expected_blocks = Vec::new();
4089            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
4090                Some((
4091                    block.placement.to_wrap_row(&wraps_snapshot)?,
4092                    Block::Custom(block.clone()),
4093                ))
4094            }));
4095
4096            let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
4097            let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
4098            let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
4099            let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
4100
4101            // Note that this needs to be synced with the related section in BlockMap::sync
4102            expected_blocks.extend(block_map.header_and_footer_blocks(
4103                &buffer_snapshot,
4104                MultiBufferOffset(0)..,
4105                |point, bias| {
4106                    wrap_point_cursor
4107                        .map(
4108                            tab_point_cursor.map(
4109                                fold_point_cursor.map(inlay_point_cursor.map(point, bias), bias),
4110                            ),
4111                        )
4112                        .row()
4113                },
4114            ));
4115
4116            BlockMap::sort_blocks(&mut expected_blocks);
4117
4118            for (placement, block) in &expected_blocks {
4119                log::info!(
4120                    "Block {:?} placement: {:?} Height: {:?}",
4121                    block.id(),
4122                    placement,
4123                    block.height()
4124                );
4125            }
4126
4127            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
4128
4129            let input_buffer_rows = buffer_snapshot
4130                .row_infos(MultiBufferRow(0))
4131                .map(|row| row.buffer_row)
4132                .collect::<Vec<_>>();
4133            let mut expected_buffer_rows = Vec::new();
4134            let mut expected_text = String::new();
4135            let mut expected_block_positions = Vec::new();
4136            let mut expected_replaced_buffer_rows = HashSet::default();
4137            let input_text = wraps_snapshot.text();
4138
4139            // Loop over the input lines, creating (N - 1) empty lines for
4140            // blocks of height N.
4141            //
4142            // It's important to note that output *starts* as one empty line,
4143            // so we special case row 0 to assume a leading '\n'.
4144            //
4145            // Linehood is the birthright of strings.
4146            let input_text_lines = input_text.split('\n').enumerate().peekable();
4147            let mut block_row = 0;
4148            for (wrap_row, input_line) in input_text_lines {
4149                let wrap_row = WrapRow(wrap_row as u32);
4150                let multibuffer_row = wraps_snapshot
4151                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
4152                    .row;
4153
4154                // Create empty lines for the above block
4155                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4156                    if *placement.start() == wrap_row && block.place_above() {
4157                        let (_, block) = sorted_blocks_iter.next().unwrap();
4158                        expected_block_positions.push((block_row, block.id()));
4159                        if block.height() > 0 {
4160                            let text = "\n".repeat((block.height() - 1) as usize);
4161                            if block_row > 0 {
4162                                expected_text.push('\n')
4163                            }
4164                            expected_text.push_str(&text);
4165                            for _ in 0..block.height() {
4166                                expected_buffer_rows.push(None);
4167                            }
4168                            block_row += block.height();
4169                        }
4170                    } else {
4171                        break;
4172                    }
4173                }
4174
4175                // Skip lines within replace blocks, then create empty lines for the replace block's height
4176                let mut is_in_replace_block = false;
4177                if let Some((BlockPlacement::Replace(replace_range), block)) =
4178                    sorted_blocks_iter.peek()
4179                    && wrap_row >= *replace_range.start()
4180                {
4181                    is_in_replace_block = true;
4182
4183                    if wrap_row == *replace_range.start() {
4184                        if matches!(block, Block::FoldedBuffer { .. }) {
4185                            expected_buffer_rows.push(None);
4186                        } else {
4187                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
4188                        }
4189                    }
4190
4191                    if wrap_row == *replace_range.end() {
4192                        expected_block_positions.push((block_row, block.id()));
4193                        let text = "\n".repeat((block.height() - 1) as usize);
4194                        if block_row > 0 {
4195                            expected_text.push('\n');
4196                        }
4197                        expected_text.push_str(&text);
4198
4199                        for _ in 1..block.height() {
4200                            expected_buffer_rows.push(None);
4201                        }
4202                        block_row += block.height();
4203
4204                        sorted_blocks_iter.next();
4205                    }
4206                }
4207
4208                if is_in_replace_block {
4209                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
4210                } else {
4211                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
4212                    let soft_wrapped = wraps_snapshot
4213                        .to_tab_point(WrapPoint::new(wrap_row, 0))
4214                        .column()
4215                        > 0;
4216                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
4217                    if block_row > 0 {
4218                        expected_text.push('\n');
4219                    }
4220                    expected_text.push_str(input_line);
4221                    block_row += 1;
4222                }
4223
4224                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4225                    if *placement.end() == wrap_row && block.place_below() {
4226                        let (_, block) = sorted_blocks_iter.next().unwrap();
4227                        expected_block_positions.push((block_row, block.id()));
4228                        if block.height() > 0 {
4229                            let text = "\n".repeat((block.height() - 1) as usize);
4230                            if block_row > 0 {
4231                                expected_text.push('\n')
4232                            }
4233                            expected_text.push_str(&text);
4234                            for _ in 0..block.height() {
4235                                expected_buffer_rows.push(None);
4236                            }
4237                            block_row += block.height();
4238                        }
4239                    } else {
4240                        break;
4241                    }
4242                }
4243            }
4244
4245            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
4246            let expected_row_count = expected_lines.len();
4247            log::info!("expected text: {expected_text:?}");
4248
4249            assert_eq!(
4250                blocks_snapshot.max_point().row + 1,
4251                expected_row_count as u32,
4252                "actual row count != expected row count",
4253            );
4254            assert_eq!(
4255                blocks_snapshot.text(),
4256                expected_text,
4257                "actual text != expected text",
4258            );
4259
4260            for start_row in 0..expected_row_count {
4261                let end_row = rng.random_range(start_row + 1..=expected_row_count);
4262                let mut expected_text = expected_lines[start_row..end_row].join("\n");
4263                if end_row < expected_row_count {
4264                    expected_text.push('\n');
4265                }
4266
4267                let actual_text = blocks_snapshot
4268                    .chunks(
4269                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
4270                        false,
4271                        false,
4272                        Highlights::default(),
4273                    )
4274                    .map(|chunk| chunk.text)
4275                    .collect::<String>();
4276                assert_eq!(
4277                    actual_text,
4278                    expected_text,
4279                    "incorrect text starting row row range {:?}",
4280                    start_row..end_row
4281                );
4282                assert_eq!(
4283                    blocks_snapshot
4284                        .row_infos(BlockRow(start_row as u32))
4285                        .map(|row_info| row_info.buffer_row)
4286                        .collect::<Vec<_>>(),
4287                    &expected_buffer_rows[start_row..],
4288                    "incorrect buffer_rows starting at row {:?}",
4289                    start_row
4290                );
4291            }
4292
4293            assert_eq!(
4294                blocks_snapshot
4295                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4296                    .map(|(row, block)| (row.0, block.id()))
4297                    .collect::<Vec<_>>(),
4298                expected_block_positions,
4299                "invalid blocks_in_range({:?})",
4300                0..expected_row_count
4301            );
4302
4303            for (_, expected_block) in
4304                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4305            {
4306                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
4307                assert_eq!(
4308                    actual_block.map(|block| block.id()),
4309                    Some(expected_block.id())
4310                );
4311            }
4312
4313            for (block_row, block_id) in expected_block_positions {
4314                if let BlockId::Custom(block_id) = block_id {
4315                    assert_eq!(
4316                        blocks_snapshot.row_for_block(block_id),
4317                        Some(BlockRow(block_row))
4318                    );
4319                }
4320            }
4321
4322            let mut expected_longest_rows = Vec::new();
4323            let mut longest_line_len = -1_isize;
4324            for (row, line) in expected_lines.iter().enumerate() {
4325                let row = row as u32;
4326
4327                assert_eq!(
4328                    blocks_snapshot.line_len(BlockRow(row)),
4329                    line.len() as u32,
4330                    "invalid line len for row {}",
4331                    row
4332                );
4333
4334                let line_char_count = line.chars().count() as isize;
4335                match line_char_count.cmp(&longest_line_len) {
4336                    Ordering::Less => {}
4337                    Ordering::Equal => expected_longest_rows.push(row),
4338                    Ordering::Greater => {
4339                        longest_line_len = line_char_count;
4340                        expected_longest_rows.clear();
4341                        expected_longest_rows.push(row);
4342                    }
4343                }
4344            }
4345
4346            let longest_row = blocks_snapshot.longest_row();
4347            assert!(
4348                expected_longest_rows.contains(&longest_row.0),
4349                "incorrect longest row {}. expected {:?} with length {}",
4350                longest_row.0,
4351                expected_longest_rows,
4352                longest_line_len,
4353            );
4354
4355            for _ in 0..10 {
4356                let end_row = rng.random_range(1..=expected_lines.len());
4357                let start_row = rng.random_range(0..end_row);
4358
4359                let mut expected_longest_rows_in_range = vec![];
4360                let mut longest_line_len_in_range = 0;
4361
4362                let mut row = start_row as u32;
4363                for line in &expected_lines[start_row..end_row] {
4364                    let line_char_count = line.chars().count() as isize;
4365                    match line_char_count.cmp(&longest_line_len_in_range) {
4366                        Ordering::Less => {}
4367                        Ordering::Equal => expected_longest_rows_in_range.push(row),
4368                        Ordering::Greater => {
4369                            longest_line_len_in_range = line_char_count;
4370                            expected_longest_rows_in_range.clear();
4371                            expected_longest_rows_in_range.push(row);
4372                        }
4373                    }
4374                    row += 1;
4375                }
4376
4377                let longest_row_in_range = blocks_snapshot
4378                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
4379                assert!(
4380                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
4381                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
4382                    longest_row.0,
4383                    start_row..end_row,
4384                    expected_longest_rows_in_range,
4385                    longest_line_len_in_range,
4386                );
4387            }
4388
4389            // Ensure that conversion between block points and wrap points is stable.
4390            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
4391                let wrap_point = WrapPoint::new(WrapRow(row), 0);
4392                let block_point = blocks_snapshot.to_block_point(wrap_point);
4393                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
4394                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
4395                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
4396                assert_eq!(
4397                    blocks_snapshot.to_block_point(right_wrap_point),
4398                    block_point
4399                );
4400            }
4401
4402            let mut block_point = BlockPoint::new(BlockRow(0), 0);
4403            for c in expected_text.chars() {
4404                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
4405                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
4406                assert_eq!(
4407                    blocks_snapshot
4408                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
4409                    left_point,
4410                    "block point: {:?}, wrap point: {:?}",
4411                    block_point,
4412                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
4413                );
4414                assert_eq!(
4415                    left_buffer_point,
4416                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
4417                    "{:?} is not valid in buffer coordinates",
4418                    left_point
4419                );
4420
4421                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
4422                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
4423                assert_eq!(
4424                    blocks_snapshot
4425                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
4426                    right_point,
4427                    "block point: {:?}, wrap point: {:?}",
4428                    block_point,
4429                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
4430                );
4431                assert_eq!(
4432                    right_buffer_point,
4433                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
4434                    "{:?} is not valid in buffer coordinates",
4435                    right_point
4436                );
4437
4438                if c == '\n' {
4439                    block_point.0 += Point::new(1, 0);
4440                } else {
4441                    block_point.column += c.len_utf8() as u32;
4442                }
4443            }
4444
4445            for buffer_row in 0..=buffer_snapshot.max_point().row {
4446                let buffer_row = MultiBufferRow(buffer_row);
4447                assert_eq!(
4448                    blocks_snapshot.is_line_replaced(buffer_row),
4449                    expected_replaced_buffer_rows.contains(&buffer_row),
4450                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
4451                );
4452            }
4453        }
4454    }
4455
4456    #[gpui::test]
4457    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
4458        cx.update(init_test);
4459
4460        let text = "abc\ndef\nghi\njkl\nmno";
4461        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
4462        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4463        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4464        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4465        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4466        let (_wrap_map, wraps_snapshot) =
4467            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4468        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
4469
4470        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4471        let _block_id = writer.insert(vec![BlockProperties {
4472            style: BlockStyle::Fixed,
4473            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
4474            height: Some(1),
4475            render: Arc::new(|_| div().into_any()),
4476            priority: 0,
4477        }])[0];
4478
4479        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
4480        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4481
4482        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4483        writer.remove_intersecting_replace_blocks(
4484            [buffer_snapshot
4485                .anchor_after(Point::new(1, 0))
4486                .to_offset(&buffer_snapshot)
4487                ..buffer_snapshot
4488                    .anchor_after(Point::new(1, 0))
4489                    .to_offset(&buffer_snapshot)],
4490            false,
4491        );
4492        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
4493        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4494    }
4495
4496    #[gpui::test]
4497    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
4498        cx.update(init_test);
4499
4500        let text = "line 1\nline 2\nline 3";
4501        let buffer = cx.update(|cx| {
4502            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
4503        });
4504        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4505        let buffer_ids = buffer_snapshot
4506            .excerpts()
4507            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4508            .dedup()
4509            .collect::<Vec<_>>();
4510        assert_eq!(buffer_ids.len(), 1);
4511        let buffer_id = buffer_ids[0];
4512
4513        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4514        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4515        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4516        let (_, wrap_snapshot) =
4517            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4518        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4519
4520        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4521        writer.insert(vec![BlockProperties {
4522            style: BlockStyle::Fixed,
4523            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
4524            height: Some(1),
4525            render: Arc::new(|_| div().into_any()),
4526            priority: 0,
4527        }]);
4528
4529        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4530        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
4531
4532        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4533        buffer.read_with(cx, |buffer, cx| {
4534            writer.fold_buffers([buffer_id], buffer, cx);
4535        });
4536
4537        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4538        assert_eq!(blocks_snapshot.text(), "");
4539    }
4540
4541    #[gpui::test]
4542    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
4543        cx.update(init_test);
4544
4545        let text = "line 1\nline 2\nline 3\nline 4";
4546        let buffer = cx.update(|cx| {
4547            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
4548        });
4549        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4550        let buffer_ids = buffer_snapshot
4551            .excerpts()
4552            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4553            .dedup()
4554            .collect::<Vec<_>>();
4555        assert_eq!(buffer_ids.len(), 1);
4556        let buffer_id = buffer_ids[0];
4557
4558        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4559        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4560        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4561        let (_, wrap_snapshot) =
4562            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4563        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4564
4565        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4566        writer.insert(vec![BlockProperties {
4567            style: BlockStyle::Fixed,
4568            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
4569            height: Some(1),
4570            render: Arc::new(|_| div().into_any()),
4571            priority: 0,
4572        }]);
4573
4574        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4575        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
4576
4577        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4578        buffer.read_with(cx, |buffer, cx| {
4579            writer.fold_buffers([buffer_id], buffer, cx);
4580        });
4581
4582        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4583        assert_eq!(blocks_snapshot.text(), "");
4584    }
4585
4586    #[gpui::test]
4587    fn test_companion_spacer_blocks(cx: &mut gpui::TestAppContext) {
4588        cx.update(init_test);
4589
4590        let base_text = "aaa\nbbb\nccc\nddd\nddd\nddd\neee\n";
4591        let main_text = "aaa\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n";
4592
4593        let rhs_buffer = cx.new(|cx| Buffer::local(main_text, cx));
4594        let diff = cx.new(|cx| {
4595            BufferDiff::new_with_base_text(base_text, &rhs_buffer.read(cx).text_snapshot(), cx)
4596        });
4597        let lhs_buffer = diff.read_with(cx, |diff, _| diff.base_text_buffer());
4598
4599        let lhs_multibuffer = cx.new(|cx| {
4600            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4601            mb.push_excerpts(
4602                lhs_buffer.clone(),
4603                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4604                cx,
4605            );
4606            mb.add_inverted_diff(diff.clone(), cx);
4607            mb
4608        });
4609        let rhs_multibuffer = cx.new(|cx| {
4610            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4611            mb.push_excerpts(
4612                rhs_buffer.clone(),
4613                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4614                cx,
4615            );
4616            mb.add_diff(diff.clone(), cx);
4617            mb
4618        });
4619        let subscription =
4620            rhs_multibuffer.update(cx, |rhs_multibuffer, _| rhs_multibuffer.subscribe());
4621
4622        let lhs_excerpt_id =
4623            lhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4624        let rhs_excerpt_id =
4625            rhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4626
4627        let lhs_buffer_snapshot = cx.update(|cx| lhs_multibuffer.read(cx).snapshot(cx));
4628        let (mut _lhs_inlay_map, lhs_inlay_snapshot) = InlayMap::new(lhs_buffer_snapshot);
4629        let (mut _lhs_fold_map, lhs_fold_snapshot) = FoldMap::new(lhs_inlay_snapshot);
4630        let (mut _lhs_tab_map, lhs_tab_snapshot) =
4631            TabMap::new(lhs_fold_snapshot, 4.try_into().unwrap());
4632        let (_lhs_wrap_map, lhs_wrap_snapshot) =
4633            cx.update(|cx| WrapMap::new(lhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4634        let lhs_block_map = BlockMap::new(lhs_wrap_snapshot.clone(), 0, 0);
4635
4636        let rhs_buffer_snapshot = cx.update(|cx| rhs_multibuffer.read(cx).snapshot(cx));
4637        let (mut rhs_inlay_map, rhs_inlay_snapshot) = InlayMap::new(rhs_buffer_snapshot);
4638        let (mut rhs_fold_map, rhs_fold_snapshot) = FoldMap::new(rhs_inlay_snapshot);
4639        let (mut rhs_tab_map, rhs_tab_snapshot) =
4640            TabMap::new(rhs_fold_snapshot, 4.try_into().unwrap());
4641        let (_rhs_wrap_map, rhs_wrap_snapshot) =
4642            cx.update(|cx| WrapMap::new(rhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4643        let rhs_block_map = BlockMap::new(rhs_wrap_snapshot.clone(), 0, 0);
4644
4645        let rhs_entity_id = rhs_multibuffer.entity_id();
4646
4647        let companion = cx.new(|_| {
4648            let mut c = Companion::new(
4649                rhs_entity_id,
4650                convert_rhs_rows_to_lhs,
4651                convert_lhs_rows_to_rhs,
4652            );
4653            c.add_excerpt_mapping(lhs_excerpt_id, rhs_excerpt_id);
4654            c
4655        });
4656
4657        let rhs_edits = Patch::new(vec![text::Edit {
4658            old: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4659            new: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4660        }]);
4661        let lhs_edits = Patch::new(vec![text::Edit {
4662            old: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4663            new: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4664        }]);
4665
4666        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4667            rhs_block_map.read(
4668                rhs_wrap_snapshot.clone(),
4669                rhs_edits.clone(),
4670                Some(CompanionView::new(
4671                    rhs_entity_id,
4672                    &lhs_wrap_snapshot,
4673                    &lhs_edits,
4674                    companion,
4675                )),
4676            )
4677        });
4678
4679        let lhs_entity_id = lhs_multibuffer.entity_id();
4680        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4681            lhs_block_map.read(
4682                lhs_wrap_snapshot.clone(),
4683                lhs_edits.clone(),
4684                Some(CompanionView::new(
4685                    lhs_entity_id,
4686                    &rhs_wrap_snapshot,
4687                    &rhs_edits,
4688                    companion,
4689                )),
4690            )
4691        });
4692
4693        // LHS:
4694        //   aaa
4695        // - bbb
4696        // - ccc
4697        //   ddd
4698        //   ddd
4699        //   ddd
4700        //   <extra line>
4701        //   <extra line>
4702        //   <extra line>
4703        //   *eee
4704        //
4705        // RHS:
4706        //   aaa
4707        //   <extra line>
4708        //   <extra line>
4709        //   ddd
4710        //   ddd
4711        //   ddd
4712        // + XXX
4713        // + YYY
4714        // + ZZZ
4715        //   eee
4716
4717        assert_eq!(
4718            rhs_snapshot.snapshot.text(),
4719            "aaa\n\n\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4720            "RHS should have 2 spacer lines after 'aaa' to align with LHS's deleted lines"
4721        );
4722
4723        assert_eq!(
4724            lhs_snapshot.snapshot.text(),
4725            "aaa\nbbb\nccc\nddd\nddd\nddd\n\n\n\neee\n",
4726            "LHS should have 3 spacer lines in place of RHS's inserted lines"
4727        );
4728
4729        // LHS:
4730        //   aaa
4731        // - bbb
4732        // - ccc
4733        //   ddd
4734        //   ddd
4735        //   ddd
4736        //   <extra line>
4737        //   <extra line>
4738        //   <extra line>
4739        //   eee
4740        //
4741        // RHS:
4742        //   aaa
4743        //   <extra line>
4744        //   <extra line>
4745        //   ddd
4746        //   foo
4747        //   foo
4748        //   foo
4749        //   ddd
4750        //   ddd
4751        // + XXX
4752        // + YYY
4753        // + ZZZ
4754        //   eee
4755
4756        let rhs_buffer_snapshot = rhs_multibuffer.update(cx, |multibuffer, cx| {
4757            multibuffer.edit(
4758                [(Point::new(2, 0)..Point::new(2, 0), "foo\nfoo\nfoo\n")],
4759                None,
4760                cx,
4761            );
4762            multibuffer.snapshot(cx)
4763        });
4764
4765        let (rhs_inlay_snapshot, rhs_inlay_edits) =
4766            rhs_inlay_map.sync(rhs_buffer_snapshot, subscription.consume().into_inner());
4767        let (rhs_fold_snapshot, rhs_fold_edits) =
4768            rhs_fold_map.read(rhs_inlay_snapshot, rhs_inlay_edits);
4769        let (rhs_tab_snapshot, rhs_tab_edits) =
4770            rhs_tab_map.sync(rhs_fold_snapshot, rhs_fold_edits, 4.try_into().unwrap());
4771        let (rhs_wrap_snapshot, rhs_wrap_edits) = _rhs_wrap_map.update(cx, |wrap_map, cx| {
4772            wrap_map.sync(rhs_tab_snapshot, rhs_tab_edits, cx)
4773        });
4774
4775        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4776            rhs_block_map.read(
4777                rhs_wrap_snapshot.clone(),
4778                rhs_wrap_edits.clone(),
4779                Some(CompanionView::new(
4780                    rhs_entity_id,
4781                    &lhs_wrap_snapshot,
4782                    &Default::default(),
4783                    companion,
4784                )),
4785            )
4786        });
4787
4788        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4789            lhs_block_map.read(
4790                lhs_wrap_snapshot.clone(),
4791                Default::default(),
4792                Some(CompanionView::new(
4793                    lhs_entity_id,
4794                    &rhs_wrap_snapshot,
4795                    &rhs_wrap_edits,
4796                    companion,
4797                )),
4798            )
4799        });
4800
4801        assert_eq!(
4802            rhs_snapshot.snapshot.text(),
4803            "aaa\n\n\nddd\nfoo\nfoo\nfoo\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4804            "RHS should have the insertion"
4805        );
4806
4807        assert_eq!(
4808            lhs_snapshot.snapshot.text(),
4809            "aaa\nbbb\nccc\nddd\n\n\n\nddd\nddd\n\n\n\neee\n",
4810            "LHS should have 3 more spacer lines to balance the insertion"
4811        );
4812    }
4813
4814    fn init_test(cx: &mut gpui::App) {
4815        let settings = SettingsStore::test(cx);
4816        cx.set_global(settings);
4817        theme::init(theme::LoadThemes::JustBase, cx);
4818        assets::Assets.load_test_fonts(cx);
4819    }
4820
4821    impl Block {
4822        fn as_custom(&self) -> Option<&CustomBlock> {
4823            match self {
4824                Block::Custom(block) => Some(block),
4825                _ => None,
4826            }
4827        }
4828    }
4829
4830    impl BlockSnapshot {
4831        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
4832            self.wrap_snapshot
4833                .to_point(self.to_wrap_point(point, bias), bias)
4834        }
4835    }
4836}