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