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