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