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 edit_for_current_boundary =
1341                    excerpt.patch.edit_for_old_position(current_boundary);
1342
1343                let spacer_at_end = if current_boundary == edit_for_current_boundary.old.end {
1344                    let (delta_at_end, spacer_at_end) = determine_spacer(
1345                        &mut our_wrapper,
1346                        &mut companion_wrapper,
1347                        current_boundary,
1348                        current_range.end,
1349                        delta,
1350                    );
1351                    delta = delta_at_end;
1352                    spacer_at_end
1353                } else {
1354                    None
1355                };
1356
1357                if let Some((wrap_row, mut height)) = spacer_at_start {
1358                    if let Some((_, additional_height)) = spacer_at_end {
1359                        height += additional_height;
1360                    }
1361                    result.push((
1362                        BlockPlacement::Above(wrap_row),
1363                        Block::Spacer {
1364                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1365                            height,
1366                            is_below: false,
1367                        },
1368                    ));
1369                } else if let Some((wrap_row, height)) = spacer_at_end {
1370                    result.push((
1371                        BlockPlacement::Above(wrap_row),
1372                        Block::Spacer {
1373                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1374                            height,
1375                            is_below: false,
1376                        },
1377                    ));
1378                }
1379            }
1380
1381            if last_source_point == excerpt.source_excerpt_range.end {
1382                let (_new_delta, spacer) = determine_spacer(
1383                    &mut our_wrapper,
1384                    &mut companion_wrapper,
1385                    last_source_point,
1386                    excerpt.target_excerpt_range.end,
1387                    delta,
1388                );
1389                if let Some((wrap_row, height)) = spacer {
1390                    result.push((
1391                        BlockPlacement::Below(wrap_row),
1392                        Block::Spacer {
1393                            id: SpacerId(self.next_block_id.fetch_add(1, SeqCst)),
1394                            height,
1395                            is_below: true,
1396                        },
1397                    ));
1398                }
1399            }
1400        }
1401
1402        result
1403    }
1404
1405    #[ztracing::instrument(skip_all)]
1406    fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
1407        blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
1408            placement_a
1409                .start()
1410                .cmp(placement_b.start())
1411                .then_with(|| placement_b.end().cmp(placement_a.end()))
1412                .then_with(|| placement_a.tie_break().cmp(&placement_b.tie_break()))
1413                .then_with(|| {
1414                    if block_a.is_header() {
1415                        Ordering::Less
1416                    } else if block_b.is_header() {
1417                        Ordering::Greater
1418                    } else {
1419                        Ordering::Equal
1420                    }
1421                })
1422                .then_with(|| match (block_a, block_b) {
1423                    (
1424                        Block::ExcerptBoundary {
1425                            excerpt: excerpt_a, ..
1426                        }
1427                        | Block::BufferHeader {
1428                            excerpt: excerpt_a, ..
1429                        },
1430                        Block::ExcerptBoundary {
1431                            excerpt: excerpt_b, ..
1432                        }
1433                        | Block::BufferHeader {
1434                            excerpt: excerpt_b, ..
1435                        },
1436                    ) => Some(excerpt_a.id).cmp(&Some(excerpt_b.id)),
1437                    (
1438                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1439                        Block::Spacer { .. } | Block::Custom(_),
1440                    ) => Ordering::Less,
1441                    (
1442                        Block::Spacer { .. } | Block::Custom(_),
1443                        Block::ExcerptBoundary { .. } | Block::BufferHeader { .. },
1444                    ) => Ordering::Greater,
1445                    (Block::Spacer { .. }, Block::Custom(_)) => Ordering::Less,
1446                    (Block::Custom(_), Block::Spacer { .. }) => Ordering::Greater,
1447                    (Block::Custom(block_a), Block::Custom(block_b)) => block_a
1448                        .priority
1449                        .cmp(&block_b.priority)
1450                        .then_with(|| block_a.id.cmp(&block_b.id)),
1451                    _ => {
1452                        unreachable!("comparing blocks: {block_a:?} vs {block_b:?}")
1453                    }
1454                })
1455        });
1456        blocks.dedup_by(|right, left| match (left.0.clone(), right.0.clone()) {
1457            (BlockPlacement::Replace(range), BlockPlacement::Above(row))
1458            | (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => range.contains(&row),
1459            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => {
1460                if range_a.end() >= range_b.start() && range_a.start() <= range_b.end() {
1461                    left.0 = BlockPlacement::Replace(
1462                        *range_a.start()..=*range_a.end().max(range_b.end()),
1463                    );
1464                    true
1465                } else {
1466                    false
1467                }
1468            }
1469            _ => false,
1470        });
1471    }
1472
1473    pub(crate) fn insert_custom_block_into_companion(
1474        &mut self,
1475        entity_id: EntityId,
1476        snapshot: &WrapSnapshot,
1477        block: &CustomBlock,
1478        companion_snapshot: &MultiBufferSnapshot,
1479        companion: &mut Companion,
1480    ) {
1481        let their_anchor = block.placement.start();
1482        let their_point = their_anchor.to_point(companion_snapshot);
1483        let my_patches = companion.convert_rows_to_companion(
1484            entity_id,
1485            snapshot.buffer_snapshot(),
1486            companion_snapshot,
1487            (Bound::Included(their_point), Bound::Included(their_point)),
1488        );
1489        let my_excerpt = my_patches
1490            .first()
1491            .expect("at least one companion excerpt exists");
1492        let my_range = my_excerpt.patch.edit_for_old_position(their_point).new;
1493        let my_point = my_range.start;
1494        let anchor = snapshot.buffer_snapshot().anchor_before(my_point);
1495        let height = block.height.unwrap_or(1);
1496        let new_block = BlockProperties {
1497            placement: BlockPlacement::Above(anchor),
1498            height: Some(height),
1499            style: BlockStyle::Sticky,
1500            render: Arc::new(move |cx| {
1501                crate::EditorElement::render_spacer_block(
1502                    cx.block_id,
1503                    cx.height,
1504                    cx.line_height,
1505                    cx.window,
1506                    cx.app,
1507                )
1508            }),
1509            priority: 0,
1510        };
1511        log::debug!("Inserting matching companion custom block: {block:#?} => {new_block:#?}");
1512        let new_block_id = self
1513            .write(snapshot.clone(), Patch::default(), None)
1514            .insert([new_block])[0];
1515        if companion.is_rhs(entity_id) {
1516            companion.add_custom_block_mapping(block.id, new_block_id);
1517        } else {
1518            companion.add_custom_block_mapping(new_block_id, block.id);
1519        }
1520    }
1521}
1522
1523#[ztracing::instrument(skip(tree, wrap_snapshot))]
1524fn push_isomorphic(tree: &mut SumTree<Transform>, rows: RowDelta, wrap_snapshot: &WrapSnapshot) {
1525    if rows == RowDelta(0) {
1526        return;
1527    }
1528
1529    let wrap_row_start = tree.summary().input_rows;
1530    let wrap_row_end = wrap_row_start + rows;
1531    let wrap_summary = wrap_snapshot.text_summary_for_range(wrap_row_start..wrap_row_end);
1532    let summary = TransformSummary {
1533        input_rows: WrapRow(rows.0),
1534        output_rows: BlockRow(rows.0),
1535        longest_row: BlockRow(wrap_summary.longest_row),
1536        longest_row_chars: wrap_summary.longest_row_chars,
1537    };
1538    let mut merged = false;
1539    tree.update_last(
1540        |last_transform| {
1541            if last_transform.block.is_none() {
1542                last_transform.summary.add_summary(&summary);
1543                merged = true;
1544            }
1545        },
1546        (),
1547    );
1548    if !merged {
1549        tree.push(
1550            Transform {
1551                summary,
1552                block: None,
1553            },
1554            (),
1555        );
1556    }
1557}
1558
1559impl BlockPoint {
1560    pub fn new(row: BlockRow, column: u32) -> Self {
1561        Self(Point::new(row.0, column))
1562    }
1563}
1564
1565impl Deref for BlockPoint {
1566    type Target = Point;
1567
1568    fn deref(&self) -> &Self::Target {
1569        &self.0
1570    }
1571}
1572
1573impl std::ops::DerefMut for BlockPoint {
1574    fn deref_mut(&mut self) -> &mut Self::Target {
1575        &mut self.0
1576    }
1577}
1578
1579impl Deref for BlockMapReader<'_> {
1580    type Target = BlockSnapshot;
1581
1582    fn deref(&self) -> &Self::Target {
1583        &self.snapshot
1584    }
1585}
1586
1587impl DerefMut for BlockMapReader<'_> {
1588    fn deref_mut(&mut self) -> &mut Self::Target {
1589        &mut self.snapshot
1590    }
1591}
1592
1593impl BlockMapReader<'_> {
1594    #[ztracing::instrument(skip_all)]
1595    pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
1596        let block = self.blocks.iter().find(|block| block.id == block_id)?;
1597        let buffer_row = block
1598            .start()
1599            .to_point(self.wrap_snapshot.buffer_snapshot())
1600            .row;
1601        let wrap_row = self
1602            .wrap_snapshot
1603            .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
1604            .row();
1605        let start_wrap_row = self
1606            .wrap_snapshot
1607            .prev_row_boundary(WrapPoint::new(wrap_row, 0));
1608        let end_wrap_row = self
1609            .wrap_snapshot
1610            .next_row_boundary(WrapPoint::new(wrap_row, 0))
1611            .unwrap_or(self.wrap_snapshot.max_point().row() + WrapRow(1));
1612
1613        let mut cursor = self.transforms.cursor::<Dimensions<WrapRow, BlockRow>>(());
1614        cursor.seek(&start_wrap_row, Bias::Left);
1615        while let Some(transform) = cursor.item() {
1616            if cursor.start().0 > end_wrap_row {
1617                break;
1618            }
1619
1620            if let Some(BlockId::Custom(id)) = transform.block.as_ref().map(|block| block.id())
1621                && id == block_id
1622            {
1623                return Some(cursor.start().1);
1624            }
1625            cursor.next();
1626        }
1627
1628        None
1629    }
1630}
1631
1632impl BlockMapWriter<'_> {
1633    #[ztracing::instrument(skip_all)]
1634    pub fn insert(
1635        &mut self,
1636        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
1637    ) -> Vec<CustomBlockId> {
1638        let blocks = blocks.into_iter();
1639        let mut ids = Vec::with_capacity(blocks.size_hint().1.unwrap_or(0));
1640        let mut edits = Patch::default();
1641        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1642        let buffer = wrap_snapshot.buffer_snapshot();
1643
1644        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1645        for block in blocks {
1646            if let BlockPlacement::Replace(_) = &block.placement {
1647                debug_assert!(block.height.unwrap() > 0);
1648            }
1649
1650            let id = CustomBlockId(self.block_map.next_block_id.fetch_add(1, SeqCst));
1651            ids.push(id);
1652
1653            let start = block.placement.start().to_point(buffer);
1654            let end = block.placement.end().to_point(buffer);
1655            let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1656            let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1657
1658            let (start_row, end_row) = {
1659                previous_wrap_row_range.take_if(|range| {
1660                    !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1661                });
1662                let range = previous_wrap_row_range.get_or_insert_with(|| {
1663                    let start_row =
1664                        wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1665                    let end_row = wrap_snapshot
1666                        .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1667                        .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1668                    start_row..end_row
1669                });
1670                (range.start, range.end)
1671            };
1672            let block_ix = match self
1673                .block_map
1674                .custom_blocks
1675                .binary_search_by(|probe| probe.placement.cmp(&block.placement, buffer))
1676            {
1677                Ok(ix) | Err(ix) => ix,
1678            };
1679            let new_block = Arc::new(CustomBlock {
1680                id,
1681                placement: block.placement.clone(),
1682                height: block.height,
1683                render: Arc::new(Mutex::new(block.render)),
1684                style: block.style,
1685                priority: block.priority,
1686            });
1687            self.block_map
1688                .custom_blocks
1689                .insert(block_ix, new_block.clone());
1690            self.block_map
1691                .custom_blocks_by_id
1692                .insert(id, new_block.clone());
1693
1694            // Insert a matching custom block in the companion (if any)
1695            if let Some(CompanionViewMut {
1696                entity_id: their_entity_id,
1697                wrap_snapshot: their_snapshot,
1698                block_map: their_block_map,
1699                companion,
1700                ..
1701            }) = self.companion.as_deref_mut()
1702            {
1703                their_block_map.insert_custom_block_into_companion(
1704                    *their_entity_id,
1705                    their_snapshot,
1706                    &new_block,
1707                    buffer,
1708                    companion,
1709                );
1710            }
1711
1712            edits = edits.compose([Edit {
1713                old: start_row..end_row,
1714                new: start_row..end_row,
1715            }]);
1716        }
1717
1718        let default_patch = Patch::default();
1719        self.block_map.sync(
1720            wrap_snapshot,
1721            edits,
1722            self.companion.as_deref().map(
1723                |CompanionViewMut {
1724                     entity_id,
1725                     wrap_snapshot,
1726                     companion,
1727                     ..
1728                 }| {
1729                    CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
1730                },
1731            ),
1732        );
1733        ids
1734    }
1735
1736    #[ztracing::instrument(skip_all)]
1737    pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
1738        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1739        let buffer = wrap_snapshot.buffer_snapshot();
1740        let mut edits = Patch::default();
1741        let mut last_block_buffer_row = None;
1742
1743        for block in &mut self.block_map.custom_blocks {
1744            if let Some(new_height) = heights.remove(&block.id) {
1745                if let BlockPlacement::Replace(_) = &block.placement {
1746                    debug_assert!(new_height > 0);
1747                }
1748
1749                if block.height != Some(new_height) {
1750                    let new_block = CustomBlock {
1751                        id: block.id,
1752                        placement: block.placement.clone(),
1753                        height: Some(new_height),
1754                        style: block.style,
1755                        render: block.render.clone(),
1756                        priority: block.priority,
1757                    };
1758                    let new_block = Arc::new(new_block);
1759                    *block = new_block.clone();
1760                    self.block_map
1761                        .custom_blocks_by_id
1762                        .insert(block.id, new_block);
1763
1764                    let start_row = block.placement.start().to_point(buffer).row;
1765                    let end_row = block.placement.end().to_point(buffer).row;
1766                    if last_block_buffer_row != Some(end_row) {
1767                        last_block_buffer_row = Some(end_row);
1768                        let start_wrap_row = wrap_snapshot
1769                            .make_wrap_point(Point::new(start_row, 0), Bias::Left)
1770                            .row();
1771                        let end_wrap_row = wrap_snapshot
1772                            .make_wrap_point(Point::new(end_row, 0), Bias::Left)
1773                            .row();
1774                        let start =
1775                            wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1776                        let end = wrap_snapshot
1777                            .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1778                            .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1779                        edits.push(Edit {
1780                            old: start..end,
1781                            new: start..end,
1782                        })
1783                    }
1784                }
1785            }
1786        }
1787
1788        let default_patch = Patch::default();
1789        self.block_map.sync(
1790            wrap_snapshot,
1791            edits,
1792            self.companion.as_deref().map(
1793                |CompanionViewMut {
1794                     entity_id,
1795                     wrap_snapshot,
1796                     companion,
1797                     ..
1798                 }| {
1799                    CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
1800                },
1801            ),
1802        );
1803    }
1804
1805    #[ztracing::instrument(skip_all)]
1806    pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
1807        let wrap_snapshot = &*self.block_map.wrap_snapshot.borrow();
1808        let buffer = wrap_snapshot.buffer_snapshot();
1809        let mut edits = Patch::default();
1810        let mut last_block_buffer_row = None;
1811        let mut previous_wrap_row_range: Option<Range<WrapRow>> = None;
1812        self.block_map.custom_blocks.retain(|block| {
1813            if block_ids.contains(&block.id) {
1814                let start = block.placement.start().to_point(buffer);
1815                let end = block.placement.end().to_point(buffer);
1816                if last_block_buffer_row != Some(end.row) {
1817                    last_block_buffer_row = Some(end.row);
1818                    let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1819                    let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1820                    let (start_row, end_row) = {
1821                        previous_wrap_row_range.take_if(|range| {
1822                            !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1823                        });
1824                        let range = previous_wrap_row_range.get_or_insert_with(|| {
1825                            let start_row =
1826                                wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1827                            let end_row = wrap_snapshot
1828                                .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1829                                .unwrap_or(wrap_snapshot.max_point().row() + WrapRow(1));
1830                            start_row..end_row
1831                        });
1832                        (range.start, range.end)
1833                    };
1834
1835                    edits.push(Edit {
1836                        old: start_row..end_row,
1837                        new: start_row..end_row,
1838                    })
1839                }
1840                false
1841            } else {
1842                true
1843            }
1844        });
1845        self.block_map
1846            .custom_blocks_by_id
1847            .retain(|id, _| !block_ids.contains(id));
1848
1849        if let Some(CompanionViewMut {
1850            entity_id: their_entity_id,
1851            wrap_snapshot: their_snapshot,
1852            companion,
1853            block_map: their_block_map,
1854            ..
1855        }) = self.companion.as_deref_mut()
1856        {
1857            let their_block_ids: HashSet<_> = block_ids
1858                .iter()
1859                .filter_map(|my_block_id| {
1860                    let mapping = companion.companion_custom_block_to_custom_block(*their_entity_id);
1861                    let their_block_id =
1862                        mapping.get(my_block_id)?;
1863                    log::debug!("Removing custom block in the companion with id {their_block_id:?} for mine {my_block_id:?}");
1864                    Some(*their_block_id)
1865                })
1866                .collect();
1867            for (lhs_id, rhs_id) in block_ids.iter().zip(their_block_ids.iter()) {
1868                if !companion.is_rhs(*their_entity_id) {
1869                    companion.remove_custom_block_mapping(lhs_id, rhs_id);
1870                } else {
1871                    companion.remove_custom_block_mapping(rhs_id, lhs_id);
1872                }
1873            }
1874            their_block_map
1875                .write(their_snapshot.clone(), Patch::default(), None)
1876                .remove(their_block_ids);
1877        }
1878
1879        let default_patch = Patch::default();
1880        self.block_map.sync(
1881            wrap_snapshot,
1882            edits,
1883            self.companion.as_deref().map(
1884                |CompanionViewMut {
1885                     entity_id,
1886                     wrap_snapshot,
1887                     companion,
1888                     ..
1889                 }| {
1890                    CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
1891                },
1892            ),
1893        );
1894    }
1895
1896    #[ztracing::instrument(skip_all)]
1897    pub fn remove_intersecting_replace_blocks(
1898        &mut self,
1899        ranges: impl IntoIterator<Item = Range<MultiBufferOffset>>,
1900        inclusive: bool,
1901    ) {
1902        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
1903        let mut blocks_to_remove = HashSet::default();
1904        for range in ranges {
1905            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
1906                if matches!(block.placement, BlockPlacement::Replace(_)) {
1907                    blocks_to_remove.insert(block.id);
1908                }
1909            }
1910        }
1911        drop(wrap_snapshot);
1912        self.remove(blocks_to_remove);
1913    }
1914
1915    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId) {
1916        self.block_map
1917            .buffers_with_disabled_headers
1918            .insert(buffer_id);
1919    }
1920
1921    #[ztracing::instrument(skip_all)]
1922    pub fn fold_buffers(
1923        &mut self,
1924        buffer_ids: impl IntoIterator<Item = BufferId>,
1925        multi_buffer: &MultiBuffer,
1926        cx: &App,
1927    ) {
1928        self.fold_or_unfold_buffers(true, buffer_ids, multi_buffer, cx);
1929    }
1930
1931    #[ztracing::instrument(skip_all)]
1932    pub fn unfold_buffers(
1933        &mut self,
1934        buffer_ids: impl IntoIterator<Item = BufferId>,
1935        multi_buffer: &MultiBuffer,
1936        cx: &App,
1937    ) {
1938        self.fold_or_unfold_buffers(false, buffer_ids, multi_buffer, cx);
1939    }
1940
1941    #[ztracing::instrument(skip_all)]
1942    fn fold_or_unfold_buffers(
1943        &mut self,
1944        fold: bool,
1945        buffer_ids: impl IntoIterator<Item = BufferId>,
1946        multi_buffer: &MultiBuffer,
1947        cx: &App,
1948    ) {
1949        let mut ranges = Vec::new();
1950        for buffer_id in buffer_ids {
1951            if fold {
1952                self.block_map.folded_buffers.insert(buffer_id);
1953            } else {
1954                self.block_map.folded_buffers.remove(&buffer_id);
1955            }
1956            ranges.extend(multi_buffer.excerpt_ranges_for_buffer(buffer_id, cx));
1957        }
1958        ranges.sort_unstable_by_key(|range| range.start);
1959
1960        let mut edits = Patch::default();
1961        let wrap_snapshot = self.block_map.wrap_snapshot.borrow().clone();
1962        for range in ranges {
1963            let last_edit_row = cmp::min(
1964                wrap_snapshot.make_wrap_point(range.end, Bias::Right).row() + WrapRow(1),
1965                wrap_snapshot.max_point().row(),
1966            ) + WrapRow(1);
1967            let range = wrap_snapshot.make_wrap_point(range.start, Bias::Left).row()..last_edit_row;
1968            edits.push(Edit {
1969                old: range.clone(),
1970                new: range,
1971            });
1972        }
1973
1974        let default_patch = Patch::default();
1975        self.block_map.sync(
1976            &wrap_snapshot,
1977            edits,
1978            self.companion.as_deref().map(
1979                |CompanionViewMut {
1980                     entity_id,
1981                     wrap_snapshot,
1982                     companion,
1983                     ..
1984                 }| {
1985                    CompanionView::new(*entity_id, wrap_snapshot, &default_patch, companion)
1986                },
1987            ),
1988        );
1989    }
1990
1991    #[ztracing::instrument(skip_all)]
1992    fn blocks_intersecting_buffer_range(
1993        &self,
1994        range: Range<MultiBufferOffset>,
1995        inclusive: bool,
1996    ) -> &[Arc<CustomBlock>] {
1997        if range.is_empty() && !inclusive {
1998            return &[];
1999        }
2000        let wrap_snapshot = self.block_map.wrap_snapshot.borrow();
2001        let buffer = wrap_snapshot.buffer_snapshot();
2002
2003        let start_block_ix = match self.block_map.custom_blocks.binary_search_by(|block| {
2004            let block_end = block.end().to_offset(buffer);
2005            block_end.cmp(&range.start).then(Ordering::Greater)
2006        }) {
2007            Ok(ix) | Err(ix) => ix,
2008        };
2009        let end_block_ix =
2010            match self.block_map.custom_blocks[start_block_ix..].binary_search_by(|block| {
2011                let block_start = block.start().to_offset(buffer);
2012                block_start.cmp(&range.end).then(if inclusive {
2013                    Ordering::Less
2014                } else {
2015                    Ordering::Greater
2016                })
2017            }) {
2018                Ok(ix) | Err(ix) => ix,
2019            };
2020
2021        &self.block_map.custom_blocks[start_block_ix..][..end_block_ix]
2022    }
2023}
2024
2025impl BlockSnapshot {
2026    #[cfg(test)]
2027    #[ztracing::instrument(skip_all)]
2028    pub fn text(&self) -> String {
2029        self.chunks(
2030            BlockRow(0)..self.transforms.summary().output_rows,
2031            false,
2032            false,
2033            Highlights::default(),
2034        )
2035        .map(|chunk| chunk.text)
2036        .collect()
2037    }
2038
2039    #[ztracing::instrument(skip_all)]
2040    pub(crate) fn chunks<'a>(
2041        &'a self,
2042        rows: Range<BlockRow>,
2043        language_aware: bool,
2044        masked: bool,
2045        highlights: Highlights<'a>,
2046    ) -> BlockChunks<'a> {
2047        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
2048
2049        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2050        cursor.seek(&rows.start, Bias::Right);
2051        let transform_output_start = cursor.start().0;
2052        let transform_input_start = cursor.start().1;
2053
2054        let mut input_start = transform_input_start;
2055        let mut input_end = transform_input_start;
2056        if let Some(transform) = cursor.item()
2057            && transform.block.is_none()
2058        {
2059            input_start += rows.start - transform_output_start;
2060            input_end += cmp::min(
2061                rows.end - transform_output_start,
2062                RowDelta(transform.summary.input_rows.0),
2063            );
2064        }
2065
2066        BlockChunks {
2067            input_chunks: self.wrap_snapshot.chunks(
2068                input_start..input_end,
2069                language_aware,
2070                highlights,
2071            ),
2072            input_chunk: Default::default(),
2073            transforms: cursor,
2074            output_row: rows.start,
2075            line_count_overflow: RowDelta(0),
2076            max_output_row,
2077            masked,
2078        }
2079    }
2080
2081    #[ztracing::instrument(skip_all)]
2082    pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
2083        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2084        cursor.seek(&start_row, Bias::Right);
2085        let Dimensions(output_start, input_start, _) = cursor.start();
2086        let overshoot = if cursor
2087            .item()
2088            .is_some_and(|transform| transform.block.is_none())
2089        {
2090            start_row - *output_start
2091        } else {
2092            RowDelta(0)
2093        };
2094        let input_start_row = *input_start + overshoot;
2095        BlockRows {
2096            transforms: cursor,
2097            input_rows: self.wrap_snapshot.row_infos(input_start_row),
2098            output_row: start_row,
2099            started: false,
2100        }
2101    }
2102
2103    #[ztracing::instrument(skip_all)]
2104    pub fn blocks_in_range(
2105        &self,
2106        rows: Range<BlockRow>,
2107    ) -> impl Iterator<Item = (BlockRow, &Block)> {
2108        let mut cursor = self.transforms.cursor::<BlockRow>(());
2109        cursor.seek(&rows.start, Bias::Left);
2110        while *cursor.start() < rows.start && cursor.end() <= rows.start {
2111            cursor.next();
2112        }
2113
2114        std::iter::from_fn(move || {
2115            while let Some(transform) = cursor.item() {
2116                let start_row = *cursor.start();
2117                if start_row > rows.end
2118                    || (start_row == rows.end
2119                        && transform
2120                            .block
2121                            .as_ref()
2122                            .is_some_and(|block| block.height() > 0))
2123                {
2124                    break;
2125                }
2126                if let Some(block) = &transform.block {
2127                    cursor.next();
2128                    return Some((start_row, block));
2129                } else {
2130                    cursor.next();
2131                }
2132            }
2133            None
2134        })
2135    }
2136
2137    #[ztracing::instrument(skip_all)]
2138    pub(crate) fn sticky_header_excerpt(&self, position: f64) -> Option<StickyHeaderExcerpt<'_>> {
2139        let top_row = position as u32;
2140        let mut cursor = self.transforms.cursor::<BlockRow>(());
2141        cursor.seek(&BlockRow(top_row), Bias::Right);
2142
2143        while let Some(transform) = cursor.item() {
2144            match &transform.block {
2145                Some(
2146                    Block::ExcerptBoundary { excerpt, .. } | Block::BufferHeader { excerpt, .. },
2147                ) => {
2148                    return Some(StickyHeaderExcerpt { excerpt });
2149                }
2150                Some(block) if block.is_buffer_header() => return None,
2151                _ => {
2152                    cursor.prev();
2153                    continue;
2154                }
2155            }
2156        }
2157
2158        None
2159    }
2160
2161    #[ztracing::instrument(skip_all)]
2162    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
2163        let buffer = self.wrap_snapshot.buffer_snapshot();
2164        let wrap_point = match block_id {
2165            BlockId::Custom(custom_block_id) => {
2166                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
2167                return Some(Block::Custom(custom_block.clone()));
2168            }
2169            BlockId::ExcerptBoundary(next_excerpt_id) => {
2170                let excerpt_range = buffer.range_for_excerpt(next_excerpt_id)?;
2171                self.wrap_snapshot
2172                    .make_wrap_point(excerpt_range.start, Bias::Left)
2173            }
2174            BlockId::FoldedBuffer(excerpt_id) => self
2175                .wrap_snapshot
2176                .make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left),
2177            BlockId::Spacer(_) => return None,
2178        };
2179        let wrap_row = wrap_point.row();
2180
2181        let mut cursor = self.transforms.cursor::<WrapRow>(());
2182        cursor.seek(&wrap_row, Bias::Left);
2183
2184        while let Some(transform) = cursor.item() {
2185            if let Some(block) = transform.block.as_ref() {
2186                if block.id() == block_id {
2187                    return Some(block.clone());
2188                }
2189            } else if *cursor.start() > wrap_row {
2190                break;
2191            }
2192
2193            cursor.next();
2194        }
2195
2196        None
2197    }
2198
2199    #[ztracing::instrument(skip_all)]
2200    pub fn max_point(&self) -> BlockPoint {
2201        let row = self
2202            .transforms
2203            .summary()
2204            .output_rows
2205            .saturating_sub(RowDelta(1));
2206        BlockPoint::new(row, self.line_len(row))
2207    }
2208
2209    #[ztracing::instrument(skip_all)]
2210    pub fn longest_row(&self) -> BlockRow {
2211        self.transforms.summary().longest_row
2212    }
2213
2214    #[ztracing::instrument(skip_all)]
2215    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
2216        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2217        cursor.seek(&range.start, Bias::Right);
2218
2219        let mut longest_row = range.start;
2220        let mut longest_row_chars = 0;
2221        if let Some(transform) = cursor.item() {
2222            if transform.block.is_none() {
2223                let &Dimensions(output_start, input_start, _) = cursor.start();
2224                let overshoot = range.start - output_start;
2225                let wrap_start_row = input_start + WrapRow(overshoot.0);
2226                let wrap_end_row = cmp::min(
2227                    input_start + WrapRow((range.end - output_start).0),
2228                    cursor.end().1,
2229                );
2230                let summary = self
2231                    .wrap_snapshot
2232                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2233                longest_row = BlockRow(range.start.0 + summary.longest_row);
2234                longest_row_chars = summary.longest_row_chars;
2235            }
2236            cursor.next();
2237        }
2238
2239        let cursor_start_row = cursor.start().0;
2240        if range.end > cursor_start_row {
2241            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right);
2242            if summary.longest_row_chars > longest_row_chars {
2243                longest_row = cursor_start_row + summary.longest_row;
2244                longest_row_chars = summary.longest_row_chars;
2245            }
2246
2247            if let Some(transform) = cursor.item()
2248                && transform.block.is_none()
2249            {
2250                let &Dimensions(output_start, input_start, _) = cursor.start();
2251                let overshoot = range.end - output_start;
2252                let wrap_start_row = input_start;
2253                let wrap_end_row = input_start + overshoot;
2254                let summary = self
2255                    .wrap_snapshot
2256                    .text_summary_for_range(wrap_start_row..wrap_end_row);
2257                if summary.longest_row_chars > longest_row_chars {
2258                    longest_row = output_start + RowDelta(summary.longest_row);
2259                }
2260            }
2261        }
2262
2263        longest_row
2264    }
2265
2266    #[ztracing::instrument(skip_all)]
2267    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
2268        let (start, _, item) =
2269            self.transforms
2270                .find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
2271        if let Some(transform) = item {
2272            let Dimensions(output_start, input_start, _) = start;
2273            let overshoot = row - output_start;
2274            if transform.block.is_some() {
2275                0
2276            } else {
2277                self.wrap_snapshot.line_len(input_start + overshoot)
2278            }
2279        } else if row == BlockRow(0) {
2280            0
2281        } else {
2282            panic!("row out of range");
2283        }
2284    }
2285
2286    #[ztracing::instrument(skip_all)]
2287    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
2288        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2289        item.is_some_and(|t| t.block.is_some())
2290    }
2291
2292    #[ztracing::instrument(skip_all)]
2293    pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
2294        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
2295        let Some(transform) = item else {
2296            return false;
2297        };
2298        matches!(transform.block, Some(Block::FoldedBuffer { .. }))
2299    }
2300
2301    #[ztracing::instrument(skip_all)]
2302    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
2303        let wrap_point = self
2304            .wrap_snapshot
2305            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
2306        let (_, _, item) = self
2307            .transforms
2308            .find::<WrapRow, _>((), &wrap_point.row(), Bias::Right);
2309        item.is_some_and(|transform| {
2310            transform
2311                .block
2312                .as_ref()
2313                .is_some_and(|block| block.is_replacement())
2314        })
2315    }
2316
2317    #[ztracing::instrument(skip_all)]
2318    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
2319        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
2320        cursor.seek(&BlockRow(point.row), Bias::Right);
2321
2322        let max_input_row = self.transforms.summary().input_rows;
2323        let mut search_left = (bias == Bias::Left && cursor.start().1 > WrapRow(0))
2324            || cursor.end().1 == max_input_row;
2325        let mut reversed = false;
2326
2327        loop {
2328            if let Some(transform) = cursor.item() {
2329                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
2330                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
2331                let output_start = Point::new(output_start_row.0, 0);
2332                let input_start = Point::new(input_start_row.0, 0);
2333                let input_end = Point::new(input_end_row.0, 0);
2334
2335                match transform.block.as_ref() {
2336                    Some(block) => {
2337                        if block.is_replacement()
2338                            && (((bias == Bias::Left || search_left) && output_start <= point.0)
2339                                || (!search_left && output_start >= point.0))
2340                        {
2341                            return BlockPoint(output_start);
2342                        }
2343                    }
2344                    None => {
2345                        let input_point = if point.row >= output_end_row.0 {
2346                            let line_len = self.wrap_snapshot.line_len(input_end_row - RowDelta(1));
2347                            self.wrap_snapshot.clip_point(
2348                                WrapPoint::new(input_end_row - RowDelta(1), line_len),
2349                                bias,
2350                            )
2351                        } else {
2352                            let output_overshoot = point.0.saturating_sub(output_start);
2353                            self.wrap_snapshot
2354                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
2355                        };
2356
2357                        if (input_start..input_end).contains(&input_point.0) {
2358                            let input_overshoot = input_point.0.saturating_sub(input_start);
2359                            return BlockPoint(output_start + input_overshoot);
2360                        }
2361                    }
2362                }
2363
2364                if search_left {
2365                    cursor.prev();
2366                } else {
2367                    cursor.next();
2368                }
2369            } else if reversed {
2370                return self.max_point();
2371            } else {
2372                reversed = true;
2373                search_left = !search_left;
2374                cursor.seek(&BlockRow(point.row), Bias::Right);
2375            }
2376        }
2377    }
2378
2379    #[ztracing::instrument(skip_all)]
2380    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
2381        let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
2382            (),
2383            &wrap_point.row(),
2384            Bias::Right,
2385        );
2386        if let Some(transform) = item {
2387            if transform.block.is_some() {
2388                BlockPoint::new(start.1, 0)
2389            } else {
2390                let Dimensions(input_start_row, output_start_row, _) = start;
2391                let input_start = Point::new(input_start_row.0, 0);
2392                let output_start = Point::new(output_start_row.0, 0);
2393                let input_overshoot = wrap_point.0 - input_start;
2394                BlockPoint(output_start + input_overshoot)
2395            }
2396        } else {
2397            self.max_point()
2398        }
2399    }
2400
2401    #[ztracing::instrument(skip_all)]
2402    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
2403        let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
2404            (),
2405            &BlockRow(block_point.row),
2406            Bias::Right,
2407        );
2408        if let Some(transform) = item {
2409            match transform.block.as_ref() {
2410                Some(block) => {
2411                    if block.place_below() {
2412                        let wrap_row = start.1 - RowDelta(1);
2413                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2414                    } else if block.place_above() {
2415                        WrapPoint::new(start.1, 0)
2416                    } else if bias == Bias::Left {
2417                        WrapPoint::new(start.1, 0)
2418                    } else {
2419                        let wrap_row = end.1 - RowDelta(1);
2420                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
2421                    }
2422                }
2423                None => {
2424                    let overshoot = block_point.row() - start.0;
2425                    let wrap_row = start.1 + RowDelta(overshoot.0);
2426                    WrapPoint::new(wrap_row, block_point.column)
2427                }
2428            }
2429        } else {
2430            self.wrap_snapshot.max_point()
2431        }
2432    }
2433}
2434
2435impl BlockChunks<'_> {
2436    /// Go to the next transform
2437    #[ztracing::instrument(skip_all)]
2438    fn advance(&mut self) {
2439        self.input_chunk = Chunk::default();
2440        self.transforms.next();
2441        while let Some(transform) = self.transforms.item() {
2442            if transform
2443                .block
2444                .as_ref()
2445                .is_some_and(|block| block.height() == 0)
2446            {
2447                self.transforms.next();
2448            } else {
2449                break;
2450            }
2451        }
2452
2453        if self
2454            .transforms
2455            .item()
2456            .is_some_and(|transform| transform.block.is_none())
2457        {
2458            let start_input_row = self.transforms.start().1;
2459            let start_output_row = self.transforms.start().0;
2460            if start_output_row < self.max_output_row {
2461                let end_input_row = cmp::min(
2462                    self.transforms.end().1,
2463                    start_input_row + (self.max_output_row - start_output_row),
2464                );
2465                self.input_chunks.seek(start_input_row..end_input_row);
2466            }
2467        }
2468    }
2469}
2470
2471pub struct StickyHeaderExcerpt<'a> {
2472    pub excerpt: &'a ExcerptInfo,
2473}
2474
2475impl<'a> Iterator for BlockChunks<'a> {
2476    type Item = Chunk<'a>;
2477
2478    #[ztracing::instrument(skip_all)]
2479    fn next(&mut self) -> Option<Self::Item> {
2480        if self.output_row >= self.max_output_row {
2481            return None;
2482        }
2483
2484        if self.line_count_overflow > RowDelta(0) {
2485            let lines = self.line_count_overflow.0.min(u128::BITS);
2486            self.line_count_overflow.0 -= lines;
2487            self.output_row += RowDelta(lines);
2488            return Some(Chunk {
2489                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines as usize]) },
2490                chars: 1u128.unbounded_shl(lines).wrapping_sub(1),
2491                ..Default::default()
2492            });
2493        }
2494
2495        let transform = self.transforms.item()?;
2496        if transform.block.is_some() {
2497            let block_start = self.transforms.start().0;
2498            let mut block_end = self.transforms.end().0;
2499            self.advance();
2500            if self.transforms.item().is_none() {
2501                block_end -= RowDelta(1);
2502            }
2503
2504            let start_in_block = self.output_row - block_start;
2505            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
2506            let line_count = end_in_block - start_in_block;
2507            let lines = RowDelta(line_count.0.min(u128::BITS));
2508            self.line_count_overflow = line_count - lines;
2509            self.output_row += lines;
2510
2511            return Some(Chunk {
2512                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..lines.0 as usize]) },
2513                chars: 1u128.unbounded_shl(lines.0).wrapping_sub(1),
2514                ..Default::default()
2515            });
2516        }
2517
2518        if self.input_chunk.text.is_empty() {
2519            if let Some(input_chunk) = self.input_chunks.next() {
2520                self.input_chunk = input_chunk;
2521            } else {
2522                if self.output_row < self.max_output_row {
2523                    self.output_row.0 += 1;
2524                    self.advance();
2525                    if self.transforms.item().is_some() {
2526                        return Some(Chunk {
2527                            text: "\n",
2528                            chars: 1,
2529                            ..Default::default()
2530                        });
2531                    }
2532                }
2533                return None;
2534            }
2535        }
2536
2537        let transform_end = self.transforms.end().0;
2538        let (prefix_rows, prefix_bytes) =
2539            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
2540        self.output_row += prefix_rows;
2541
2542        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
2543        self.input_chunk.text = suffix;
2544        self.input_chunk.tabs >>= prefix_bytes.saturating_sub(1);
2545        self.input_chunk.chars >>= prefix_bytes.saturating_sub(1);
2546
2547        let mut tabs = self.input_chunk.tabs;
2548        let mut chars = self.input_chunk.chars;
2549
2550        if self.masked {
2551            // Not great for multibyte text because to keep cursor math correct we
2552            // need to have the same number of chars in the input as output.
2553            let chars_count = prefix.chars().count();
2554            let bullet_len = chars_count;
2555            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
2556            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
2557            tabs = 0;
2558        }
2559
2560        let chunk = Chunk {
2561            text: prefix,
2562            tabs,
2563            chars,
2564            ..self.input_chunk.clone()
2565        };
2566
2567        if self.output_row == transform_end {
2568            self.advance();
2569        }
2570
2571        Some(chunk)
2572    }
2573}
2574
2575impl Iterator for BlockRows<'_> {
2576    type Item = RowInfo;
2577
2578    #[ztracing::instrument(skip_all)]
2579    fn next(&mut self) -> Option<Self::Item> {
2580        if self.started {
2581            self.output_row.0 += 1;
2582        } else {
2583            self.started = true;
2584        }
2585
2586        if self.output_row >= self.transforms.end().0 {
2587            self.transforms.next();
2588            while let Some(transform) = self.transforms.item() {
2589                if transform
2590                    .block
2591                    .as_ref()
2592                    .is_some_and(|block| block.height() == 0)
2593                {
2594                    self.transforms.next();
2595                } else {
2596                    break;
2597                }
2598            }
2599
2600            let transform = self.transforms.item()?;
2601            if transform
2602                .block
2603                .as_ref()
2604                .is_none_or(|block| block.is_replacement())
2605            {
2606                self.input_rows.seek(self.transforms.start().1);
2607            }
2608        }
2609
2610        let transform = self.transforms.item()?;
2611        if transform.block.as_ref().is_none_or(|block| {
2612            block.is_replacement()
2613                && self.transforms.start().0 == self.output_row
2614                && matches!(block, Block::FoldedBuffer { .. }).not()
2615        }) {
2616            self.input_rows.next()
2617        } else {
2618            Some(RowInfo::default())
2619        }
2620    }
2621}
2622
2623impl sum_tree::Item for Transform {
2624    type Summary = TransformSummary;
2625
2626    fn summary(&self, _cx: ()) -> Self::Summary {
2627        self.summary.clone()
2628    }
2629}
2630
2631impl sum_tree::ContextLessSummary for TransformSummary {
2632    fn zero() -> Self {
2633        Default::default()
2634    }
2635
2636    fn add_summary(&mut self, summary: &Self) {
2637        if summary.longest_row_chars > self.longest_row_chars {
2638            self.longest_row = self.output_rows + summary.longest_row;
2639            self.longest_row_chars = summary.longest_row_chars;
2640        }
2641        self.input_rows += summary.input_rows;
2642        self.output_rows += summary.output_rows;
2643    }
2644}
2645
2646impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
2647    fn zero(_cx: ()) -> Self {
2648        Default::default()
2649    }
2650
2651    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2652        *self += summary.input_rows;
2653    }
2654}
2655
2656impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
2657    fn zero(_cx: ()) -> Self {
2658        Default::default()
2659    }
2660
2661    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
2662        *self += summary.output_rows;
2663    }
2664}
2665
2666impl Deref for BlockContext<'_, '_> {
2667    type Target = App;
2668
2669    fn deref(&self) -> &Self::Target {
2670        self.app
2671    }
2672}
2673
2674impl DerefMut for BlockContext<'_, '_> {
2675    fn deref_mut(&mut self) -> &mut Self::Target {
2676        self.app
2677    }
2678}
2679
2680impl CustomBlock {
2681    #[ztracing::instrument(skip_all)]
2682    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
2683        self.render.lock()(cx)
2684    }
2685
2686    #[ztracing::instrument(skip_all)]
2687    pub fn start(&self) -> Anchor {
2688        *self.placement.start()
2689    }
2690
2691    #[ztracing::instrument(skip_all)]
2692    pub fn end(&self) -> Anchor {
2693        *self.placement.end()
2694    }
2695
2696    pub fn style(&self) -> BlockStyle {
2697        self.style
2698    }
2699}
2700
2701impl Debug for CustomBlock {
2702    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2703        f.debug_struct("Block")
2704            .field("id", &self.id)
2705            .field("placement", &self.placement)
2706            .field("height", &self.height)
2707            .field("style", &self.style)
2708            .field("priority", &self.priority)
2709            .finish_non_exhaustive()
2710    }
2711}
2712
2713// Count the number of bytes prior to a target point. If the string doesn't contain the target
2714// point, return its total extent. Otherwise return the target point itself.
2715fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
2716    let mut row = 0;
2717    let mut offset = 0;
2718    for (ix, line) in s.split('\n').enumerate() {
2719        if ix > 0 {
2720            row += 1;
2721            offset += 1;
2722        }
2723        if row >= target.0 {
2724            break;
2725        }
2726        offset += line.len();
2727    }
2728    (RowDelta(row), offset)
2729}
2730
2731#[cfg(test)]
2732mod tests {
2733    use super::*;
2734    use crate::{
2735        display_map::{
2736            Companion, fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
2737        },
2738        split::{convert_lhs_rows_to_rhs, convert_rhs_rows_to_lhs},
2739        test::test_font,
2740    };
2741    use buffer_diff::BufferDiff;
2742    use gpui::{App, AppContext as _, Element, div, font, px};
2743    use itertools::Itertools;
2744    use language::{Buffer, Capability};
2745    use multi_buffer::{ExcerptRange, MultiBuffer};
2746    use rand::prelude::*;
2747    use settings::SettingsStore;
2748    use std::env;
2749    use util::RandomCharIter;
2750
2751    #[gpui::test]
2752    fn test_offset_for_row() {
2753        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2754        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2755        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2756        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2757        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2758        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2759        assert_eq!(
2760            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2761            (RowDelta(0), 0)
2762        );
2763        assert_eq!(
2764            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2765            (RowDelta(1), 4)
2766        );
2767        assert_eq!(
2768            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2769            (RowDelta(2), 8)
2770        );
2771        assert_eq!(
2772            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2773            (RowDelta(2), 11)
2774        );
2775    }
2776
2777    #[gpui::test]
2778    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2779        cx.update(init_test);
2780
2781        let text = "aaa\nbbb\nccc\nddd";
2782
2783        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2784        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2785        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2786        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2787        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2788        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2789        let (wrap_map, wraps_snapshot) =
2790            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2791        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2792
2793        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
2794        let block_ids = writer.insert(vec![
2795            BlockProperties {
2796                style: BlockStyle::Fixed,
2797                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2798                height: Some(1),
2799                render: Arc::new(|_| div().into_any()),
2800                priority: 0,
2801            },
2802            BlockProperties {
2803                style: BlockStyle::Fixed,
2804                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2805                height: Some(2),
2806                render: Arc::new(|_| div().into_any()),
2807                priority: 0,
2808            },
2809            BlockProperties {
2810                style: BlockStyle::Fixed,
2811                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2812                height: Some(3),
2813                render: Arc::new(|_| div().into_any()),
2814                priority: 0,
2815            },
2816        ]);
2817
2818        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2819        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2820
2821        let blocks = snapshot
2822            .blocks_in_range(BlockRow(0)..BlockRow(8))
2823            .map(|(start_row, block)| {
2824                let block = block.as_custom().unwrap();
2825                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
2826            })
2827            .collect::<Vec<_>>();
2828
2829        // When multiple blocks are on the same line, the newer blocks appear first.
2830        assert_eq!(
2831            blocks,
2832            &[
2833                (1..2, block_ids[0]),
2834                (2..4, block_ids[1]),
2835                (7..10, block_ids[2]),
2836            ]
2837        );
2838
2839        assert_eq!(
2840            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
2841            BlockPoint::new(BlockRow(0), 3)
2842        );
2843        assert_eq!(
2844            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
2845            BlockPoint::new(BlockRow(4), 0)
2846        );
2847        assert_eq!(
2848            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
2849            BlockPoint::new(BlockRow(6), 3)
2850        );
2851
2852        assert_eq!(
2853            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
2854            WrapPoint::new(WrapRow(0), 3)
2855        );
2856        assert_eq!(
2857            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2858            WrapPoint::new(WrapRow(1), 0)
2859        );
2860        assert_eq!(
2861            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
2862            WrapPoint::new(WrapRow(1), 0)
2863        );
2864        assert_eq!(
2865            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2866            WrapPoint::new(WrapRow(3), 3)
2867        );
2868
2869        assert_eq!(
2870            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2871            BlockPoint::new(BlockRow(0), 3)
2872        );
2873        assert_eq!(
2874            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
2875            BlockPoint::new(BlockRow(4), 0)
2876        );
2877        assert_eq!(
2878            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
2879            BlockPoint::new(BlockRow(0), 3)
2880        );
2881        assert_eq!(
2882            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
2883            BlockPoint::new(BlockRow(4), 0)
2884        );
2885        assert_eq!(
2886            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
2887            BlockPoint::new(BlockRow(4), 0)
2888        );
2889        assert_eq!(
2890            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
2891            BlockPoint::new(BlockRow(4), 0)
2892        );
2893        assert_eq!(
2894            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
2895            BlockPoint::new(BlockRow(6), 3)
2896        );
2897        assert_eq!(
2898            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
2899            BlockPoint::new(BlockRow(6), 3)
2900        );
2901        assert_eq!(
2902            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2903            BlockPoint::new(BlockRow(6), 3)
2904        );
2905        assert_eq!(
2906            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
2907            BlockPoint::new(BlockRow(6), 3)
2908        );
2909
2910        assert_eq!(
2911            snapshot
2912                .row_infos(BlockRow(0))
2913                .map(|row_info| row_info.buffer_row)
2914                .collect::<Vec<_>>(),
2915            &[
2916                Some(0),
2917                None,
2918                None,
2919                None,
2920                Some(1),
2921                Some(2),
2922                Some(3),
2923                None,
2924                None,
2925                None
2926            ]
2927        );
2928
2929        // Insert a line break, separating two block decorations into separate lines.
2930        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2931            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2932            buffer.snapshot(cx)
2933        });
2934
2935        let (inlay_snapshot, inlay_edits) =
2936            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2937        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2938        let (tab_snapshot, tab_edits) =
2939            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2940        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2941            wrap_map.sync(tab_snapshot, tab_edits, cx)
2942        });
2943        let snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
2944        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2945    }
2946
2947    #[gpui::test]
2948    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2949        init_test(cx);
2950
2951        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2952        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2953        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2954
2955        let mut excerpt_ids = Vec::new();
2956        let multi_buffer = cx.new(|cx| {
2957            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2958            excerpt_ids.extend(multi_buffer.push_excerpts(
2959                buffer1.clone(),
2960                [ExcerptRange::new(0..buffer1.read(cx).len())],
2961                cx,
2962            ));
2963            excerpt_ids.extend(multi_buffer.push_excerpts(
2964                buffer2.clone(),
2965                [ExcerptRange::new(0..buffer2.read(cx).len())],
2966                cx,
2967            ));
2968            excerpt_ids.extend(multi_buffer.push_excerpts(
2969                buffer3.clone(),
2970                [ExcerptRange::new(0..buffer3.read(cx).len())],
2971                cx,
2972            ));
2973
2974            multi_buffer
2975        });
2976
2977        let font = test_font();
2978        let font_size = px(14.);
2979        let font_id = cx.text_system().resolve_font(&font);
2980        let mut wrap_width = px(0.);
2981        for c in "Buff".chars() {
2982            wrap_width += cx
2983                .text_system()
2984                .advance(font_id, font_size, c)
2985                .unwrap()
2986                .width;
2987        }
2988
2989        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2990        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
2991        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2992        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2993        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2994
2995        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2996        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
2997
2998        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2999        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
3000
3001        let blocks: Vec<_> = snapshot
3002            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3003            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
3004            .collect();
3005        assert_eq!(
3006            blocks,
3007            vec![
3008                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
3009                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
3010                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
3011            ]
3012        );
3013    }
3014
3015    #[gpui::test]
3016    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
3017        cx.update(init_test);
3018
3019        let text = "aaa\nbbb\nccc\nddd";
3020
3021        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3022        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3023        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
3024        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3025        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3026        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
3027        let (_wrap_map, wraps_snapshot) =
3028            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3029        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3030
3031        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3032        let block_ids = writer.insert(vec![
3033            BlockProperties {
3034                style: BlockStyle::Fixed,
3035                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3036                height: Some(1),
3037                render: Arc::new(|_| div().into_any()),
3038                priority: 0,
3039            },
3040            BlockProperties {
3041                style: BlockStyle::Fixed,
3042                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
3043                height: Some(2),
3044                render: Arc::new(|_| div().into_any()),
3045                priority: 0,
3046            },
3047            BlockProperties {
3048                style: BlockStyle::Fixed,
3049                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
3050                height: Some(3),
3051                render: Arc::new(|_| div().into_any()),
3052                priority: 0,
3053            },
3054        ]);
3055
3056        {
3057            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3058            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3059
3060            let mut block_map_writer =
3061                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3062
3063            let mut new_heights = HashMap::default();
3064            new_heights.insert(block_ids[0], 2);
3065            block_map_writer.resize(new_heights);
3066            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3067            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3068        }
3069
3070        {
3071            let mut block_map_writer =
3072                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3073
3074            let mut new_heights = HashMap::default();
3075            new_heights.insert(block_ids[0], 1);
3076            block_map_writer.resize(new_heights);
3077
3078            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3079            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
3080        }
3081
3082        {
3083            let mut block_map_writer =
3084                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3085
3086            let mut new_heights = HashMap::default();
3087            new_heights.insert(block_ids[0], 0);
3088            block_map_writer.resize(new_heights);
3089
3090            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3091            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
3092        }
3093
3094        {
3095            let mut block_map_writer =
3096                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3097
3098            let mut new_heights = HashMap::default();
3099            new_heights.insert(block_ids[0], 3);
3100            block_map_writer.resize(new_heights);
3101
3102            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3103            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3104        }
3105
3106        {
3107            let mut block_map_writer =
3108                block_map.write(wraps_snapshot.clone(), Default::default(), None);
3109
3110            let mut new_heights = HashMap::default();
3111            new_heights.insert(block_ids[0], 3);
3112            block_map_writer.resize(new_heights);
3113
3114            let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3115            // Same height as before, should remain the same
3116            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
3117        }
3118    }
3119
3120    #[gpui::test]
3121    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
3122        cx.update(init_test);
3123
3124        let text = "one two three\nfour five six\nseven eight";
3125
3126        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3127        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3128        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3129        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3130        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3131        let (_, wraps_snapshot) = cx.update(|cx| {
3132            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
3133        });
3134        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3135
3136        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3137        writer.insert(vec![
3138            BlockProperties {
3139                style: BlockStyle::Fixed,
3140                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
3141                render: Arc::new(|_| div().into_any()),
3142                height: Some(1),
3143                priority: 0,
3144            },
3145            BlockProperties {
3146                style: BlockStyle::Fixed,
3147                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
3148                render: Arc::new(|_| div().into_any()),
3149                height: Some(1),
3150                priority: 0,
3151            },
3152        ]);
3153
3154        // Blocks with an 'above' disposition go above their corresponding buffer line.
3155        // Blocks with a 'below' disposition go below their corresponding buffer line.
3156        let snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3157        assert_eq!(
3158            snapshot.text(),
3159            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
3160        );
3161    }
3162
3163    #[gpui::test]
3164    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
3165        cx.update(init_test);
3166
3167        let text = "line1\nline2\nline3\nline4\nline5";
3168
3169        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3170        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
3171        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3172        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3173        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3174        let tab_size = 1.try_into().unwrap();
3175        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
3176        let (wrap_map, wraps_snapshot) =
3177            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3178        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3179
3180        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3181        let replace_block_id = writer.insert(vec![BlockProperties {
3182            style: BlockStyle::Fixed,
3183            placement: BlockPlacement::Replace(
3184                buffer_snapshot.anchor_after(Point::new(1, 3))
3185                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
3186            ),
3187            height: Some(4),
3188            render: Arc::new(|_| div().into_any()),
3189            priority: 0,
3190        }])[0];
3191
3192        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3193        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3194
3195        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3196            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
3197            buffer.snapshot(cx)
3198        });
3199        let (inlay_snapshot, inlay_edits) =
3200            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
3201        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3202        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3203        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3204            wrap_map.sync(tab_snapshot, tab_edits, cx)
3205        });
3206        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits, None);
3207        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3208
3209        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
3210            buffer.edit(
3211                [(
3212                    Point::new(1, 5)..Point::new(1, 5),
3213                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
3214                )],
3215                None,
3216                cx,
3217            );
3218            buffer.snapshot(cx)
3219        });
3220        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
3221            buffer_snapshot.clone(),
3222            buffer_subscription.consume().into_inner(),
3223        );
3224        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3225        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3226        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3227            wrap_map.sync(tab_snapshot, tab_edits, cx)
3228        });
3229        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3230        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
3231
3232        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
3233        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3234        writer.insert(vec![
3235            BlockProperties {
3236                style: BlockStyle::Fixed,
3237                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
3238                height: Some(1),
3239                render: Arc::new(|_| div().into_any()),
3240                priority: 0,
3241            },
3242            BlockProperties {
3243                style: BlockStyle::Fixed,
3244                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
3245                height: Some(1),
3246                render: Arc::new(|_| div().into_any()),
3247                priority: 0,
3248            },
3249            BlockProperties {
3250                style: BlockStyle::Fixed,
3251                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
3252                height: Some(1),
3253                render: Arc::new(|_| div().into_any()),
3254                priority: 0,
3255            },
3256        ]);
3257        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3258        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3259
3260        // Ensure blocks inserted *inside* replaced region are hidden.
3261        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3262        writer.insert(vec![
3263            BlockProperties {
3264                style: BlockStyle::Fixed,
3265                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
3266                height: Some(1),
3267                render: Arc::new(|_| div().into_any()),
3268                priority: 0,
3269            },
3270            BlockProperties {
3271                style: BlockStyle::Fixed,
3272                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
3273                height: Some(1),
3274                render: Arc::new(|_| div().into_any()),
3275                priority: 0,
3276            },
3277            BlockProperties {
3278                style: BlockStyle::Fixed,
3279                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
3280                height: Some(1),
3281                render: Arc::new(|_| div().into_any()),
3282                priority: 0,
3283            },
3284        ]);
3285        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
3286        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
3287
3288        // Removing the replace block shows all the hidden blocks again.
3289        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
3290        writer.remove(HashSet::from_iter([replace_block_id]));
3291        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
3292        assert_eq!(
3293            blocks_snapshot.text(),
3294            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
3295        );
3296    }
3297
3298    #[gpui::test]
3299    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
3300        cx.update(init_test);
3301
3302        let text = "111\n222\n333\n444\n555\n666";
3303
3304        let buffer = cx.update(|cx| {
3305            MultiBuffer::build_multi(
3306                [
3307                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
3308                    (
3309                        text,
3310                        vec![
3311                            Point::new(1, 0)..Point::new(1, 3),
3312                            Point::new(2, 0)..Point::new(2, 3),
3313                            Point::new(3, 0)..Point::new(3, 3),
3314                        ],
3315                    ),
3316                    (
3317                        text,
3318                        vec![
3319                            Point::new(4, 0)..Point::new(4, 3),
3320                            Point::new(5, 0)..Point::new(5, 3),
3321                        ],
3322                    ),
3323                ],
3324                cx,
3325            )
3326        });
3327        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3328        let buffer_ids = buffer_snapshot
3329            .excerpts()
3330            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3331            .dedup()
3332            .collect::<Vec<_>>();
3333        assert_eq!(buffer_ids.len(), 3);
3334        let buffer_id_1 = buffer_ids[0];
3335        let buffer_id_2 = buffer_ids[1];
3336        let buffer_id_3 = buffer_ids[2];
3337
3338        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3339        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3340        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3341        let (_, wrap_snapshot) =
3342            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3343        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3344        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3345
3346        assert_eq!(
3347            blocks_snapshot.text(),
3348            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
3349        );
3350        assert_eq!(
3351            blocks_snapshot
3352                .row_infos(BlockRow(0))
3353                .map(|i| i.buffer_row)
3354                .collect::<Vec<_>>(),
3355            vec![
3356                None,
3357                None,
3358                Some(0),
3359                None,
3360                None,
3361                Some(1),
3362                None,
3363                Some(2),
3364                None,
3365                Some(3),
3366                None,
3367                None,
3368                Some(4),
3369                None,
3370                Some(5),
3371            ]
3372        );
3373
3374        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3375        let excerpt_blocks_2 = writer.insert(vec![
3376            BlockProperties {
3377                style: BlockStyle::Fixed,
3378                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3379                height: Some(1),
3380                render: Arc::new(|_| div().into_any()),
3381                priority: 0,
3382            },
3383            BlockProperties {
3384                style: BlockStyle::Fixed,
3385                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
3386                height: Some(1),
3387                render: Arc::new(|_| div().into_any()),
3388                priority: 0,
3389            },
3390            BlockProperties {
3391                style: BlockStyle::Fixed,
3392                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
3393                height: Some(1),
3394                render: Arc::new(|_| div().into_any()),
3395                priority: 0,
3396            },
3397        ]);
3398        let excerpt_blocks_3 = writer.insert(vec![
3399            BlockProperties {
3400                style: BlockStyle::Fixed,
3401                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
3402                height: Some(1),
3403                render: Arc::new(|_| div().into_any()),
3404                priority: 0,
3405            },
3406            BlockProperties {
3407                style: BlockStyle::Fixed,
3408                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
3409                height: Some(1),
3410                render: Arc::new(|_| div().into_any()),
3411                priority: 0,
3412            },
3413        ]);
3414
3415        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3416        assert_eq!(
3417            blocks_snapshot.text(),
3418            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3419        );
3420        assert_eq!(
3421            blocks_snapshot
3422                .row_infos(BlockRow(0))
3423                .map(|i| i.buffer_row)
3424                .collect::<Vec<_>>(),
3425            vec![
3426                None,
3427                None,
3428                Some(0),
3429                None,
3430                None,
3431                None,
3432                Some(1),
3433                None,
3434                None,
3435                Some(2),
3436                None,
3437                Some(3),
3438                None,
3439                None,
3440                None,
3441                None,
3442                Some(4),
3443                None,
3444                Some(5),
3445                None,
3446            ]
3447        );
3448
3449        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3450        buffer.read_with(cx, |buffer, cx| {
3451            writer.fold_buffers([buffer_id_1], buffer, cx);
3452        });
3453        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
3454            style: BlockStyle::Fixed,
3455            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
3456            height: Some(1),
3457            render: Arc::new(|_| div().into_any()),
3458            priority: 0,
3459        }]);
3460        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3461        let blocks = blocks_snapshot
3462            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3463            .collect::<Vec<_>>();
3464        for (_, block) in &blocks {
3465            if let BlockId::Custom(custom_block_id) = block.id() {
3466                assert!(
3467                    !excerpt_blocks_1.contains(&custom_block_id),
3468                    "Should have no blocks from the folded buffer"
3469                );
3470                assert!(
3471                    excerpt_blocks_2.contains(&custom_block_id)
3472                        || excerpt_blocks_3.contains(&custom_block_id),
3473                    "Should have only blocks from unfolded buffers"
3474                );
3475            }
3476        }
3477        assert_eq!(
3478            1,
3479            blocks
3480                .iter()
3481                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3482                .count(),
3483            "Should have one folded block, producing a header of the second buffer"
3484        );
3485        assert_eq!(
3486            blocks_snapshot.text(),
3487            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
3488        );
3489        assert_eq!(
3490            blocks_snapshot
3491                .row_infos(BlockRow(0))
3492                .map(|i| i.buffer_row)
3493                .collect::<Vec<_>>(),
3494            vec![
3495                None,
3496                None,
3497                None,
3498                None,
3499                None,
3500                Some(1),
3501                None,
3502                None,
3503                Some(2),
3504                None,
3505                Some(3),
3506                None,
3507                None,
3508                None,
3509                None,
3510                Some(4),
3511                None,
3512                Some(5),
3513                None,
3514            ]
3515        );
3516
3517        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3518        buffer.read_with(cx, |buffer, cx| {
3519            writer.fold_buffers([buffer_id_2], buffer, cx);
3520        });
3521        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3522        let blocks = blocks_snapshot
3523            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3524            .collect::<Vec<_>>();
3525        for (_, block) in &blocks {
3526            if let BlockId::Custom(custom_block_id) = block.id() {
3527                assert!(
3528                    !excerpt_blocks_1.contains(&custom_block_id),
3529                    "Should have no blocks from the folded buffer_1"
3530                );
3531                assert!(
3532                    !excerpt_blocks_2.contains(&custom_block_id),
3533                    "Should have no blocks from the folded buffer_2"
3534                );
3535                assert!(
3536                    excerpt_blocks_3.contains(&custom_block_id),
3537                    "Should have only blocks from unfolded buffers"
3538                );
3539            }
3540        }
3541        assert_eq!(
3542            2,
3543            blocks
3544                .iter()
3545                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3546                .count(),
3547            "Should have two folded blocks, producing headers"
3548        );
3549        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
3550        assert_eq!(
3551            blocks_snapshot
3552                .row_infos(BlockRow(0))
3553                .map(|i| i.buffer_row)
3554                .collect::<Vec<_>>(),
3555            vec![
3556                None,
3557                None,
3558                None,
3559                None,
3560                None,
3561                None,
3562                None,
3563                Some(4),
3564                None,
3565                Some(5),
3566                None,
3567            ]
3568        );
3569
3570        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3571        buffer.read_with(cx, |buffer, cx| {
3572            writer.unfold_buffers([buffer_id_1], buffer, cx);
3573        });
3574        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3575        let blocks = blocks_snapshot
3576            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3577            .collect::<Vec<_>>();
3578        for (_, block) in &blocks {
3579            if let BlockId::Custom(custom_block_id) = block.id() {
3580                assert!(
3581                    !excerpt_blocks_2.contains(&custom_block_id),
3582                    "Should have no blocks from the folded buffer_2"
3583                );
3584                assert!(
3585                    excerpt_blocks_1.contains(&custom_block_id)
3586                        || excerpt_blocks_3.contains(&custom_block_id),
3587                    "Should have only blocks from unfolded buffers"
3588                );
3589            }
3590        }
3591        assert_eq!(
3592            1,
3593            blocks
3594                .iter()
3595                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
3596                .count(),
3597            "Should be back to a single folded buffer, producing a header for buffer_2"
3598        );
3599        assert_eq!(
3600            blocks_snapshot.text(),
3601            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
3602            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
3603        );
3604        assert_eq!(
3605            blocks_snapshot
3606                .row_infos(BlockRow(0))
3607                .map(|i| i.buffer_row)
3608                .collect::<Vec<_>>(),
3609            vec![
3610                None,
3611                None,
3612                None,
3613                Some(0),
3614                None,
3615                None,
3616                None,
3617                None,
3618                None,
3619                Some(4),
3620                None,
3621                Some(5),
3622                None,
3623            ]
3624        );
3625
3626        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3627        buffer.read_with(cx, |buffer, cx| {
3628            writer.fold_buffers([buffer_id_3], buffer, cx);
3629        });
3630        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3631        let blocks = blocks_snapshot
3632            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3633            .collect::<Vec<_>>();
3634        for (_, block) in &blocks {
3635            if let BlockId::Custom(custom_block_id) = block.id() {
3636                assert!(
3637                    excerpt_blocks_1.contains(&custom_block_id),
3638                    "Should have no blocks from the folded buffer_1"
3639                );
3640                assert!(
3641                    !excerpt_blocks_2.contains(&custom_block_id),
3642                    "Should have only blocks from unfolded buffers"
3643                );
3644                assert!(
3645                    !excerpt_blocks_3.contains(&custom_block_id),
3646                    "Should have only blocks from unfolded buffers"
3647                );
3648            }
3649        }
3650
3651        assert_eq!(
3652            blocks_snapshot.text(),
3653            "\n\n\n111\n\n\n\n",
3654            "Should have a single, first buffer left after folding"
3655        );
3656        assert_eq!(
3657            blocks_snapshot
3658                .row_infos(BlockRow(0))
3659                .map(|i| i.buffer_row)
3660                .collect::<Vec<_>>(),
3661            vec![None, None, None, Some(0), None, None, None, None,]
3662        );
3663    }
3664
3665    #[gpui::test]
3666    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
3667        cx.update(init_test);
3668
3669        let text = "111";
3670
3671        let buffer = cx.update(|cx| {
3672            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
3673        });
3674        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3675        let buffer_ids = buffer_snapshot
3676            .excerpts()
3677            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3678            .dedup()
3679            .collect::<Vec<_>>();
3680        assert_eq!(buffer_ids.len(), 1);
3681        let buffer_id = buffer_ids[0];
3682
3683        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
3684        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3685        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3686        let (_, wrap_snapshot) =
3687            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3688        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
3689        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
3690
3691        assert_eq!(blocks_snapshot.text(), "\n\n111");
3692
3693        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
3694        buffer.read_with(cx, |buffer, cx| {
3695            writer.fold_buffers([buffer_id], buffer, cx);
3696        });
3697        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
3698        let blocks = blocks_snapshot
3699            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
3700            .collect::<Vec<_>>();
3701        assert_eq!(
3702            1,
3703            blocks
3704                .iter()
3705                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
3706                .count(),
3707            "Should have one folded block, producing a header of the second buffer"
3708        );
3709        assert_eq!(blocks_snapshot.text(), "\n");
3710        assert_eq!(
3711            blocks_snapshot
3712                .row_infos(BlockRow(0))
3713                .map(|i| i.buffer_row)
3714                .collect::<Vec<_>>(),
3715            vec![None, None],
3716            "When fully folded, should be no buffer rows"
3717        );
3718    }
3719
3720    #[gpui::test(iterations = 60)]
3721    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3722        cx.update(init_test);
3723
3724        let operations = env::var("OPERATIONS")
3725            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3726            .unwrap_or(10);
3727
3728        let wrap_width = if rng.random_bool(0.2) {
3729            None
3730        } else {
3731            Some(px(rng.random_range(0.0..=100.0)))
3732        };
3733        let tab_size = 1.try_into().unwrap();
3734        let font_size = px(14.0);
3735        let buffer_start_header_height = rng.random_range(1..=5);
3736        let excerpt_header_height = rng.random_range(1..=5);
3737
3738        log::info!("Wrap width: {:?}", wrap_width);
3739        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3740        let is_singleton = rng.random();
3741        let buffer = if is_singleton {
3742            let len = rng.random_range(0..10);
3743            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3744            log::info!("initial singleton buffer text: {:?}", text);
3745            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3746        } else {
3747            cx.update(|cx| {
3748                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3749                log::info!(
3750                    "initial multi-buffer text: {:?}",
3751                    multibuffer.read(cx).read(cx).text()
3752                );
3753                multibuffer
3754            })
3755        };
3756
3757        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3758        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3759        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3760        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3761        let font = test_font();
3762        let (wrap_map, wraps_snapshot) =
3763            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3764        let mut block_map = BlockMap::new(
3765            wraps_snapshot,
3766            buffer_start_header_height,
3767            excerpt_header_height,
3768        );
3769
3770        for _ in 0..operations {
3771            let mut buffer_edits = Vec::new();
3772            match rng.random_range(0..=100) {
3773                0..=19 => {
3774                    let wrap_width = if rng.random_bool(0.2) {
3775                        None
3776                    } else {
3777                        Some(px(rng.random_range(0.0..=100.0)))
3778                    };
3779                    log::info!("Setting wrap width to {:?}", wrap_width);
3780                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3781                }
3782                20..=39 => {
3783                    let block_count = rng.random_range(1..=5);
3784                    let block_properties = (0..block_count)
3785                        .map(|_| {
3786                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3787                            let offset = buffer.clip_offset(
3788                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3789                                Bias::Left,
3790                            );
3791                            let mut min_height = 0;
3792                            let placement = match rng.random_range(0..3) {
3793                                0 => {
3794                                    min_height = 1;
3795                                    let start = buffer.anchor_after(offset);
3796                                    let end = buffer.anchor_after(buffer.clip_offset(
3797                                        rng.random_range(offset..=buffer.len()),
3798                                        Bias::Left,
3799                                    ));
3800                                    BlockPlacement::Replace(start..=end)
3801                                }
3802                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3803                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3804                            };
3805
3806                            let height = rng.random_range(min_height..512);
3807                            BlockProperties {
3808                                style: BlockStyle::Fixed,
3809                                placement,
3810                                height: Some(height),
3811                                render: Arc::new(|_| div().into_any()),
3812                                priority: 0,
3813                            }
3814                        })
3815                        .collect::<Vec<_>>();
3816
3817                    let (inlay_snapshot, inlay_edits) =
3818                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3819                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3820                    let (tab_snapshot, tab_edits) =
3821                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3822                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3823                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3824                    });
3825                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3826                    let block_ids =
3827                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3828                            placement: props.placement.clone(),
3829                            height: props.height,
3830                            style: props.style,
3831                            render: Arc::new(|_| div().into_any()),
3832                            priority: 0,
3833                        }));
3834
3835                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3836                        log::info!(
3837                            "inserted block {:?} with height {:?} and id {:?}",
3838                            block_properties
3839                                .placement
3840                                .as_ref()
3841                                .map(|p| p.to_point(&buffer_snapshot)),
3842                            block_properties.height,
3843                            block_id
3844                        );
3845                    }
3846                }
3847                40..=59 if !block_map.custom_blocks.is_empty() => {
3848                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3849                    let block_ids_to_remove = block_map
3850                        .custom_blocks
3851                        .choose_multiple(&mut rng, block_count)
3852                        .map(|block| block.id)
3853                        .collect::<HashSet<_>>();
3854
3855                    let (inlay_snapshot, inlay_edits) =
3856                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3857                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3858                    let (tab_snapshot, tab_edits) =
3859                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3860                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3861                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3862                    });
3863                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3864                    log::info!(
3865                        "removing {} blocks: {:?}",
3866                        block_ids_to_remove.len(),
3867                        block_ids_to_remove
3868                    );
3869                    block_map.remove(block_ids_to_remove);
3870                }
3871                60..=79 => {
3872                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3873                        log::info!("Noop fold/unfold operation on a singleton buffer");
3874                        continue;
3875                    }
3876                    let (inlay_snapshot, inlay_edits) =
3877                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3878                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3879                    let (tab_snapshot, tab_edits) =
3880                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3881                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3882                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3883                    });
3884                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, None);
3885                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3886                        let folded_buffers: Vec<_> =
3887                            block_map.block_map.folded_buffers.iter().cloned().collect();
3888                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3889                        unfolded_buffers.dedup();
3890                        log::debug!("All buffers {unfolded_buffers:?}");
3891                        log::debug!("Folded buffers {folded_buffers:?}");
3892                        unfolded_buffers.retain(|buffer_id| {
3893                            !block_map.block_map.folded_buffers.contains(buffer_id)
3894                        });
3895                        (unfolded_buffers, folded_buffers)
3896                    });
3897                    let mut folded_count = folded_buffers.len();
3898                    let mut unfolded_count = unfolded_buffers.len();
3899
3900                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
3901                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
3902                    if !fold && !unfold {
3903                        log::info!(
3904                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3905                        );
3906                        continue;
3907                    }
3908
3909                    buffer.update(cx, |buffer, cx| {
3910                        if fold {
3911                            let buffer_to_fold =
3912                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
3913                            log::info!("Folding {buffer_to_fold:?}");
3914                            let related_excerpts = buffer_snapshot
3915                                .excerpts()
3916                                .filter_map(|(excerpt_id, buffer, range)| {
3917                                    if buffer.remote_id() == buffer_to_fold {
3918                                        Some((
3919                                            excerpt_id,
3920                                            buffer
3921                                                .text_for_range(range.context)
3922                                                .collect::<String>(),
3923                                        ))
3924                                    } else {
3925                                        None
3926                                    }
3927                                })
3928                                .collect::<Vec<_>>();
3929                            log::info!(
3930                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3931                            );
3932                            folded_count += 1;
3933                            unfolded_count -= 1;
3934                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3935                        }
3936                        if unfold {
3937                            let buffer_to_unfold =
3938                                folded_buffers[rng.random_range(0..folded_buffers.len())];
3939                            log::info!("Unfolding {buffer_to_unfold:?}");
3940                            unfolded_count += 1;
3941                            folded_count -= 1;
3942                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3943                        }
3944                        log::info!(
3945                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3946                        );
3947                    });
3948                }
3949                _ => {
3950                    buffer.update(cx, |buffer, cx| {
3951                        let mutation_count = rng.random_range(1..=5);
3952                        let subscription = buffer.subscribe();
3953                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3954                        buffer_snapshot = buffer.snapshot(cx);
3955                        buffer_edits.extend(subscription.consume());
3956                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3957                    });
3958                }
3959            }
3960
3961            let (inlay_snapshot, inlay_edits) =
3962                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3963            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3964            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3965            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3966                wrap_map.sync(tab_snapshot, tab_edits, cx)
3967            });
3968            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, None);
3969            assert_eq!(
3970                blocks_snapshot.transforms.summary().input_rows,
3971                wraps_snapshot.max_point().row() + RowDelta(1)
3972            );
3973            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3974            log::info!("blocks text: {:?}", blocks_snapshot.text());
3975
3976            let mut expected_blocks = Vec::new();
3977            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3978                Some((
3979                    block.placement.to_wrap_row(&wraps_snapshot)?,
3980                    Block::Custom(block.clone()),
3981                ))
3982            }));
3983
3984            let mut inlay_point_cursor = wraps_snapshot.inlay_point_cursor();
3985            let mut tab_point_cursor = wraps_snapshot.tab_point_cursor();
3986            let mut fold_point_cursor = wraps_snapshot.fold_point_cursor();
3987            let mut wrap_point_cursor = wraps_snapshot.wrap_point_cursor();
3988
3989            // Note that this needs to be synced with the related section in BlockMap::sync
3990            expected_blocks.extend(block_map.header_and_footer_blocks(
3991                &buffer_snapshot,
3992                MultiBufferOffset(0)..,
3993                |point, bias| {
3994                    wrap_point_cursor
3995                        .map(
3996                            tab_point_cursor
3997                                .map(fold_point_cursor.map(inlay_point_cursor.map(point), bias)),
3998                        )
3999                        .row()
4000                },
4001            ));
4002
4003            BlockMap::sort_blocks(&mut expected_blocks);
4004
4005            for (placement, block) in &expected_blocks {
4006                log::info!(
4007                    "Block {:?} placement: {:?} Height: {:?}",
4008                    block.id(),
4009                    placement,
4010                    block.height()
4011                );
4012            }
4013
4014            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
4015
4016            let input_buffer_rows = buffer_snapshot
4017                .row_infos(MultiBufferRow(0))
4018                .map(|row| row.buffer_row)
4019                .collect::<Vec<_>>();
4020            let mut expected_buffer_rows = Vec::new();
4021            let mut expected_text = String::new();
4022            let mut expected_block_positions = Vec::new();
4023            let mut expected_replaced_buffer_rows = HashSet::default();
4024            let input_text = wraps_snapshot.text();
4025
4026            // Loop over the input lines, creating (N - 1) empty lines for
4027            // blocks of height N.
4028            //
4029            // It's important to note that output *starts* as one empty line,
4030            // so we special case row 0 to assume a leading '\n'.
4031            //
4032            // Linehood is the birthright of strings.
4033            let input_text_lines = input_text.split('\n').enumerate().peekable();
4034            let mut block_row = 0;
4035            for (wrap_row, input_line) in input_text_lines {
4036                let wrap_row = WrapRow(wrap_row as u32);
4037                let multibuffer_row = wraps_snapshot
4038                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
4039                    .row;
4040
4041                // Create empty lines for the above block
4042                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4043                    if *placement.start() == wrap_row && block.place_above() {
4044                        let (_, block) = sorted_blocks_iter.next().unwrap();
4045                        expected_block_positions.push((block_row, block.id()));
4046                        if block.height() > 0 {
4047                            let text = "\n".repeat((block.height() - 1) as usize);
4048                            if block_row > 0 {
4049                                expected_text.push('\n')
4050                            }
4051                            expected_text.push_str(&text);
4052                            for _ in 0..block.height() {
4053                                expected_buffer_rows.push(None);
4054                            }
4055                            block_row += block.height();
4056                        }
4057                    } else {
4058                        break;
4059                    }
4060                }
4061
4062                // Skip lines within replace blocks, then create empty lines for the replace block's height
4063                let mut is_in_replace_block = false;
4064                if let Some((BlockPlacement::Replace(replace_range), block)) =
4065                    sorted_blocks_iter.peek()
4066                    && wrap_row >= *replace_range.start()
4067                {
4068                    is_in_replace_block = true;
4069
4070                    if wrap_row == *replace_range.start() {
4071                        if matches!(block, Block::FoldedBuffer { .. }) {
4072                            expected_buffer_rows.push(None);
4073                        } else {
4074                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
4075                        }
4076                    }
4077
4078                    if wrap_row == *replace_range.end() {
4079                        expected_block_positions.push((block_row, block.id()));
4080                        let text = "\n".repeat((block.height() - 1) as usize);
4081                        if block_row > 0 {
4082                            expected_text.push('\n');
4083                        }
4084                        expected_text.push_str(&text);
4085
4086                        for _ in 1..block.height() {
4087                            expected_buffer_rows.push(None);
4088                        }
4089                        block_row += block.height();
4090
4091                        sorted_blocks_iter.next();
4092                    }
4093                }
4094
4095                if is_in_replace_block {
4096                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
4097                } else {
4098                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
4099                    let soft_wrapped = wraps_snapshot
4100                        .to_tab_point(WrapPoint::new(wrap_row, 0))
4101                        .column()
4102                        > 0;
4103                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
4104                    if block_row > 0 {
4105                        expected_text.push('\n');
4106                    }
4107                    expected_text.push_str(input_line);
4108                    block_row += 1;
4109                }
4110
4111                while let Some((placement, block)) = sorted_blocks_iter.peek() {
4112                    if *placement.end() == wrap_row && block.place_below() {
4113                        let (_, block) = sorted_blocks_iter.next().unwrap();
4114                        expected_block_positions.push((block_row, block.id()));
4115                        if block.height() > 0 {
4116                            let text = "\n".repeat((block.height() - 1) as usize);
4117                            if block_row > 0 {
4118                                expected_text.push('\n')
4119                            }
4120                            expected_text.push_str(&text);
4121                            for _ in 0..block.height() {
4122                                expected_buffer_rows.push(None);
4123                            }
4124                            block_row += block.height();
4125                        }
4126                    } else {
4127                        break;
4128                    }
4129                }
4130            }
4131
4132            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
4133            let expected_row_count = expected_lines.len();
4134            log::info!("expected text: {expected_text:?}");
4135
4136            assert_eq!(
4137                blocks_snapshot.max_point().row + 1,
4138                expected_row_count as u32,
4139                "actual row count != expected row count",
4140            );
4141            assert_eq!(
4142                blocks_snapshot.text(),
4143                expected_text,
4144                "actual text != expected text",
4145            );
4146
4147            for start_row in 0..expected_row_count {
4148                let end_row = rng.random_range(start_row + 1..=expected_row_count);
4149                let mut expected_text = expected_lines[start_row..end_row].join("\n");
4150                if end_row < expected_row_count {
4151                    expected_text.push('\n');
4152                }
4153
4154                let actual_text = blocks_snapshot
4155                    .chunks(
4156                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
4157                        false,
4158                        false,
4159                        Highlights::default(),
4160                    )
4161                    .map(|chunk| chunk.text)
4162                    .collect::<String>();
4163                assert_eq!(
4164                    actual_text,
4165                    expected_text,
4166                    "incorrect text starting row row range {:?}",
4167                    start_row..end_row
4168                );
4169                assert_eq!(
4170                    blocks_snapshot
4171                        .row_infos(BlockRow(start_row as u32))
4172                        .map(|row_info| row_info.buffer_row)
4173                        .collect::<Vec<_>>(),
4174                    &expected_buffer_rows[start_row..],
4175                    "incorrect buffer_rows starting at row {:?}",
4176                    start_row
4177                );
4178            }
4179
4180            assert_eq!(
4181                blocks_snapshot
4182                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4183                    .map(|(row, block)| (row.0, block.id()))
4184                    .collect::<Vec<_>>(),
4185                expected_block_positions,
4186                "invalid blocks_in_range({:?})",
4187                0..expected_row_count
4188            );
4189
4190            for (_, expected_block) in
4191                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
4192            {
4193                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
4194                assert_eq!(
4195                    actual_block.map(|block| block.id()),
4196                    Some(expected_block.id())
4197                );
4198            }
4199
4200            for (block_row, block_id) in expected_block_positions {
4201                if let BlockId::Custom(block_id) = block_id {
4202                    assert_eq!(
4203                        blocks_snapshot.row_for_block(block_id),
4204                        Some(BlockRow(block_row))
4205                    );
4206                }
4207            }
4208
4209            let mut expected_longest_rows = Vec::new();
4210            let mut longest_line_len = -1_isize;
4211            for (row, line) in expected_lines.iter().enumerate() {
4212                let row = row as u32;
4213
4214                assert_eq!(
4215                    blocks_snapshot.line_len(BlockRow(row)),
4216                    line.len() as u32,
4217                    "invalid line len for row {}",
4218                    row
4219                );
4220
4221                let line_char_count = line.chars().count() as isize;
4222                match line_char_count.cmp(&longest_line_len) {
4223                    Ordering::Less => {}
4224                    Ordering::Equal => expected_longest_rows.push(row),
4225                    Ordering::Greater => {
4226                        longest_line_len = line_char_count;
4227                        expected_longest_rows.clear();
4228                        expected_longest_rows.push(row);
4229                    }
4230                }
4231            }
4232
4233            let longest_row = blocks_snapshot.longest_row();
4234            assert!(
4235                expected_longest_rows.contains(&longest_row.0),
4236                "incorrect longest row {}. expected {:?} with length {}",
4237                longest_row.0,
4238                expected_longest_rows,
4239                longest_line_len,
4240            );
4241
4242            for _ in 0..10 {
4243                let end_row = rng.random_range(1..=expected_lines.len());
4244                let start_row = rng.random_range(0..end_row);
4245
4246                let mut expected_longest_rows_in_range = vec![];
4247                let mut longest_line_len_in_range = 0;
4248
4249                let mut row = start_row as u32;
4250                for line in &expected_lines[start_row..end_row] {
4251                    let line_char_count = line.chars().count() as isize;
4252                    match line_char_count.cmp(&longest_line_len_in_range) {
4253                        Ordering::Less => {}
4254                        Ordering::Equal => expected_longest_rows_in_range.push(row),
4255                        Ordering::Greater => {
4256                            longest_line_len_in_range = line_char_count;
4257                            expected_longest_rows_in_range.clear();
4258                            expected_longest_rows_in_range.push(row);
4259                        }
4260                    }
4261                    row += 1;
4262                }
4263
4264                let longest_row_in_range = blocks_snapshot
4265                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
4266                assert!(
4267                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
4268                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
4269                    longest_row.0,
4270                    start_row..end_row,
4271                    expected_longest_rows_in_range,
4272                    longest_line_len_in_range,
4273                );
4274            }
4275
4276            // Ensure that conversion between block points and wrap points is stable.
4277            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
4278                let wrap_point = WrapPoint::new(WrapRow(row), 0);
4279                let block_point = blocks_snapshot.to_block_point(wrap_point);
4280                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
4281                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
4282                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
4283                assert_eq!(
4284                    blocks_snapshot.to_block_point(right_wrap_point),
4285                    block_point
4286                );
4287            }
4288
4289            let mut block_point = BlockPoint::new(BlockRow(0), 0);
4290            for c in expected_text.chars() {
4291                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
4292                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
4293                assert_eq!(
4294                    blocks_snapshot
4295                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
4296                    left_point,
4297                    "block point: {:?}, wrap point: {:?}",
4298                    block_point,
4299                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
4300                );
4301                assert_eq!(
4302                    left_buffer_point,
4303                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
4304                    "{:?} is not valid in buffer coordinates",
4305                    left_point
4306                );
4307
4308                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
4309                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
4310                assert_eq!(
4311                    blocks_snapshot
4312                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
4313                    right_point,
4314                    "block point: {:?}, wrap point: {:?}",
4315                    block_point,
4316                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
4317                );
4318                assert_eq!(
4319                    right_buffer_point,
4320                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
4321                    "{:?} is not valid in buffer coordinates",
4322                    right_point
4323                );
4324
4325                if c == '\n' {
4326                    block_point.0 += Point::new(1, 0);
4327                } else {
4328                    block_point.column += c.len_utf8() as u32;
4329                }
4330            }
4331
4332            for buffer_row in 0..=buffer_snapshot.max_point().row {
4333                let buffer_row = MultiBufferRow(buffer_row);
4334                assert_eq!(
4335                    blocks_snapshot.is_line_replaced(buffer_row),
4336                    expected_replaced_buffer_rows.contains(&buffer_row),
4337                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
4338                );
4339            }
4340        }
4341    }
4342
4343    #[gpui::test]
4344    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
4345        cx.update(init_test);
4346
4347        let text = "abc\ndef\nghi\njkl\nmno";
4348        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
4349        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4350        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4351        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
4352        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4353        let (_wrap_map, wraps_snapshot) =
4354            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4355        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
4356
4357        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4358        let _block_id = writer.insert(vec![BlockProperties {
4359            style: BlockStyle::Fixed,
4360            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
4361            height: Some(1),
4362            render: Arc::new(|_| div().into_any()),
4363            priority: 0,
4364        }])[0];
4365
4366        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default(), None);
4367        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4368
4369        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default(), None);
4370        writer.remove_intersecting_replace_blocks(
4371            [buffer_snapshot
4372                .anchor_after(Point::new(1, 0))
4373                .to_offset(&buffer_snapshot)
4374                ..buffer_snapshot
4375                    .anchor_after(Point::new(1, 0))
4376                    .to_offset(&buffer_snapshot)],
4377            false,
4378        );
4379        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default(), None);
4380        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
4381    }
4382
4383    #[gpui::test]
4384    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
4385        cx.update(init_test);
4386
4387        let text = "line 1\nline 2\nline 3";
4388        let buffer = cx.update(|cx| {
4389            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
4390        });
4391        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4392        let buffer_ids = buffer_snapshot
4393            .excerpts()
4394            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4395            .dedup()
4396            .collect::<Vec<_>>();
4397        assert_eq!(buffer_ids.len(), 1);
4398        let buffer_id = buffer_ids[0];
4399
4400        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4401        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4402        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4403        let (_, wrap_snapshot) =
4404            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4405        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4406
4407        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4408        writer.insert(vec![BlockProperties {
4409            style: BlockStyle::Fixed,
4410            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
4411            height: Some(1),
4412            render: Arc::new(|_| div().into_any()),
4413            priority: 0,
4414        }]);
4415
4416        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4417        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
4418
4419        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4420        buffer.read_with(cx, |buffer, cx| {
4421            writer.fold_buffers([buffer_id], buffer, cx);
4422        });
4423
4424        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4425        assert_eq!(blocks_snapshot.text(), "");
4426    }
4427
4428    #[gpui::test]
4429    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
4430        cx.update(init_test);
4431
4432        let text = "line 1\nline 2\nline 3\nline 4";
4433        let buffer = cx.update(|cx| {
4434            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
4435        });
4436        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
4437        let buffer_ids = buffer_snapshot
4438            .excerpts()
4439            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
4440            .dedup()
4441            .collect::<Vec<_>>();
4442        assert_eq!(buffer_ids.len(), 1);
4443        let buffer_id = buffer_ids[0];
4444
4445        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
4446        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
4447        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
4448        let (_, wrap_snapshot) =
4449            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4450        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
4451
4452        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4453        writer.insert(vec![BlockProperties {
4454            style: BlockStyle::Fixed,
4455            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
4456            height: Some(1),
4457            render: Arc::new(|_| div().into_any()),
4458            priority: 0,
4459        }]);
4460
4461        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default(), None);
4462        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
4463
4464        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default(), None);
4465        buffer.read_with(cx, |buffer, cx| {
4466            writer.fold_buffers([buffer_id], buffer, cx);
4467        });
4468
4469        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default(), None);
4470        assert_eq!(blocks_snapshot.text(), "");
4471    }
4472
4473    #[gpui::test]
4474    fn test_companion_spacer_blocks(cx: &mut gpui::TestAppContext) {
4475        cx.update(init_test);
4476
4477        let base_text = "aaa\nbbb\nccc\nddd\nddd\nddd\neee\n";
4478        let main_text = "aaa\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n";
4479
4480        let rhs_buffer = cx.new(|cx| Buffer::local(main_text, cx));
4481        let diff = cx.new(|cx| {
4482            BufferDiff::new_with_base_text(base_text, &rhs_buffer.read(cx).text_snapshot(), cx)
4483        });
4484        let lhs_buffer = diff.read_with(cx, |diff, _| diff.base_text_buffer());
4485
4486        let lhs_multibuffer = cx.new(|cx| {
4487            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4488            mb.push_excerpts(
4489                lhs_buffer.clone(),
4490                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4491                cx,
4492            );
4493            mb.add_inverted_diff(diff.clone(), cx);
4494            mb
4495        });
4496        let rhs_multibuffer = cx.new(|cx| {
4497            let mut mb = MultiBuffer::new(Capability::ReadWrite);
4498            mb.push_excerpts(
4499                rhs_buffer.clone(),
4500                [ExcerptRange::new(text::Anchor::MIN..text::Anchor::MAX)],
4501                cx,
4502            );
4503            mb.add_diff(diff.clone(), cx);
4504            mb
4505        });
4506        let subscription =
4507            rhs_multibuffer.update(cx, |rhs_multibuffer, _| rhs_multibuffer.subscribe());
4508
4509        let lhs_excerpt_id =
4510            lhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4511        let rhs_excerpt_id =
4512            rhs_multibuffer.read_with(cx, |mb, cx| mb.snapshot(cx).excerpts().next().unwrap().0);
4513
4514        let lhs_buffer_snapshot = cx.update(|cx| lhs_multibuffer.read(cx).snapshot(cx));
4515        let (mut _lhs_inlay_map, lhs_inlay_snapshot) = InlayMap::new(lhs_buffer_snapshot);
4516        let (mut _lhs_fold_map, lhs_fold_snapshot) = FoldMap::new(lhs_inlay_snapshot);
4517        let (mut _lhs_tab_map, lhs_tab_snapshot) =
4518            TabMap::new(lhs_fold_snapshot, 4.try_into().unwrap());
4519        let (_lhs_wrap_map, lhs_wrap_snapshot) =
4520            cx.update(|cx| WrapMap::new(lhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4521        let lhs_block_map = BlockMap::new(lhs_wrap_snapshot.clone(), 0, 0);
4522
4523        let rhs_buffer_snapshot = cx.update(|cx| rhs_multibuffer.read(cx).snapshot(cx));
4524        let (mut rhs_inlay_map, rhs_inlay_snapshot) = InlayMap::new(rhs_buffer_snapshot);
4525        let (mut rhs_fold_map, rhs_fold_snapshot) = FoldMap::new(rhs_inlay_snapshot);
4526        let (mut rhs_tab_map, rhs_tab_snapshot) =
4527            TabMap::new(rhs_fold_snapshot, 4.try_into().unwrap());
4528        let (_rhs_wrap_map, rhs_wrap_snapshot) =
4529            cx.update(|cx| WrapMap::new(rhs_tab_snapshot, font("Helvetica"), px(14.0), None, cx));
4530        let rhs_block_map = BlockMap::new(rhs_wrap_snapshot.clone(), 0, 0);
4531
4532        let rhs_entity_id = rhs_multibuffer.entity_id();
4533
4534        let companion = cx.new(|_| {
4535            let mut c = Companion::new(
4536                rhs_entity_id,
4537                Default::default(),
4538                convert_rhs_rows_to_lhs,
4539                convert_lhs_rows_to_rhs,
4540            );
4541            c.add_excerpt_mapping(lhs_excerpt_id, rhs_excerpt_id);
4542            c
4543        });
4544
4545        let rhs_edits = Patch::new(vec![text::Edit {
4546            old: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4547            new: WrapRow(0)..rhs_wrap_snapshot.max_point().row(),
4548        }]);
4549        let lhs_edits = Patch::new(vec![text::Edit {
4550            old: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4551            new: WrapRow(0)..lhs_wrap_snapshot.max_point().row(),
4552        }]);
4553
4554        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4555            rhs_block_map.read(
4556                rhs_wrap_snapshot.clone(),
4557                rhs_edits.clone(),
4558                Some(CompanionView::new(
4559                    rhs_entity_id,
4560                    &lhs_wrap_snapshot,
4561                    &lhs_edits,
4562                    companion,
4563                )),
4564            )
4565        });
4566
4567        let lhs_entity_id = lhs_multibuffer.entity_id();
4568        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4569            lhs_block_map.read(
4570                lhs_wrap_snapshot.clone(),
4571                lhs_edits.clone(),
4572                Some(CompanionView::new(
4573                    lhs_entity_id,
4574                    &rhs_wrap_snapshot,
4575                    &rhs_edits,
4576                    companion,
4577                )),
4578            )
4579        });
4580
4581        // LHS:
4582        //   aaa
4583        // - bbb
4584        // - ccc
4585        //   ddd
4586        //   ddd
4587        //   ddd
4588        //   <extra line>
4589        //   <extra line>
4590        //   <extra line>
4591        //   *eee
4592        //
4593        // RHS:
4594        //   aaa
4595        //   <extra line>
4596        //   <extra line>
4597        //   ddd
4598        //   ddd
4599        //   ddd
4600        // + XXX
4601        // + YYY
4602        // + ZZZ
4603        //   eee
4604
4605        assert_eq!(
4606            rhs_snapshot.snapshot.text(),
4607            "aaa\n\n\nddd\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4608            "RHS should have 2 spacer lines after 'aaa' to align with LHS's deleted lines"
4609        );
4610
4611        assert_eq!(
4612            lhs_snapshot.snapshot.text(),
4613            "aaa\nbbb\nccc\nddd\nddd\nddd\n\n\n\neee\n",
4614            "LHS should have 3 spacer lines in place of RHS's inserted lines"
4615        );
4616
4617        // LHS:
4618        //   aaa
4619        // - bbb
4620        // - ccc
4621        //   ddd
4622        //   ddd
4623        //   ddd
4624        //   <extra line>
4625        //   <extra line>
4626        //   <extra line>
4627        //   eee
4628        //
4629        // RHS:
4630        //   aaa
4631        //   <extra line>
4632        //   <extra line>
4633        //   ddd
4634        //   foo
4635        //   foo
4636        //   foo
4637        //   ddd
4638        //   ddd
4639        // + XXX
4640        // + YYY
4641        // + ZZZ
4642        //   eee
4643
4644        let rhs_buffer_snapshot = rhs_multibuffer.update(cx, |multibuffer, cx| {
4645            multibuffer.edit(
4646                [(Point::new(2, 0)..Point::new(2, 0), "foo\nfoo\nfoo\n")],
4647                None,
4648                cx,
4649            );
4650            multibuffer.snapshot(cx)
4651        });
4652
4653        let (rhs_inlay_snapshot, rhs_inlay_edits) =
4654            rhs_inlay_map.sync(rhs_buffer_snapshot, subscription.consume().into_inner());
4655        let (rhs_fold_snapshot, rhs_fold_edits) =
4656            rhs_fold_map.read(rhs_inlay_snapshot, rhs_inlay_edits);
4657        let (rhs_tab_snapshot, rhs_tab_edits) =
4658            rhs_tab_map.sync(rhs_fold_snapshot, rhs_fold_edits, 4.try_into().unwrap());
4659        let (rhs_wrap_snapshot, rhs_wrap_edits) = _rhs_wrap_map.update(cx, |wrap_map, cx| {
4660            wrap_map.sync(rhs_tab_snapshot, rhs_tab_edits, cx)
4661        });
4662
4663        let rhs_snapshot = companion.read_with(cx, |companion, _cx| {
4664            rhs_block_map.read(
4665                rhs_wrap_snapshot.clone(),
4666                rhs_wrap_edits.clone(),
4667                Some(CompanionView::new(
4668                    rhs_entity_id,
4669                    &lhs_wrap_snapshot,
4670                    &Default::default(),
4671                    companion,
4672                )),
4673            )
4674        });
4675
4676        let lhs_snapshot = companion.read_with(cx, |companion, _cx| {
4677            lhs_block_map.read(
4678                lhs_wrap_snapshot.clone(),
4679                Default::default(),
4680                Some(CompanionView::new(
4681                    lhs_entity_id,
4682                    &rhs_wrap_snapshot,
4683                    &rhs_wrap_edits,
4684                    companion,
4685                )),
4686            )
4687        });
4688
4689        assert_eq!(
4690            rhs_snapshot.snapshot.text(),
4691            "aaa\n\n\nddd\nfoo\nfoo\nfoo\nddd\nddd\nXXX\nYYY\nZZZ\neee\n",
4692            "RHS should have the insertion"
4693        );
4694
4695        assert_eq!(
4696            lhs_snapshot.snapshot.text(),
4697            "aaa\nbbb\nccc\nddd\n\n\n\nddd\nddd\n\n\n\neee\n",
4698            "LHS should have 3 more spacer lines to balance the insertion"
4699        );
4700    }
4701
4702    fn init_test(cx: &mut gpui::App) {
4703        let settings = SettingsStore::test(cx);
4704        cx.set_global(settings);
4705        theme::init(theme::LoadThemes::JustBase, cx);
4706        assets::Assets.load_test_fonts(cx);
4707    }
4708
4709    impl Block {
4710        fn as_custom(&self) -> Option<&CustomBlock> {
4711            match self {
4712                Block::Custom(block) => Some(block),
4713                _ => None,
4714            }
4715        }
4716    }
4717
4718    impl BlockSnapshot {
4719        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
4720            self.wrap_snapshot
4721                .to_point(self.to_wrap_point(point, bias), bias)
4722        }
4723    }
4724}