block_map.rs

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