block_map.rs

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