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