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