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