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