block_map.rs

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