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