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, Not, Range, RangeBounds, RangeInclusive},
  23    sync::{
  24        Arc,
  25        atomic::{AtomicUsize, Ordering::SeqCst},
  26    },
  27};
  28use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap};
  29use text::{BufferId, Edit};
  30use ui::ElementId;
  31
  32const NEWLINES: &[u8; rope::Chunk::MASK_BITS] = &[b'\n'; _];
  33const BULLETS: &[u8; rope::Chunk::MASK_BITS] = &[b'*'; _];
  34
  35/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
  36///
  37/// See the [`display_map` module documentation](crate::display_map) for more information.
  38pub struct BlockMap {
  39    pub(super) wrap_snapshot: RefCell<WrapSnapshot>,
  40    next_block_id: AtomicUsize,
  41    custom_blocks: Vec<Arc<CustomBlock>>,
  42    custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
  43    transforms: RefCell<SumTree<Transform>>,
  44    buffer_header_height: u32,
  45    excerpt_header_height: u32,
  46    pub(super) folded_buffers: HashSet<BufferId>,
  47    buffers_with_disabled_headers: HashSet<BufferId>,
  48}
  49
  50pub struct BlockMapReader<'a> {
  51    blocks: &'a Vec<Arc<CustomBlock>>,
  52    pub snapshot: BlockSnapshot,
  53}
  54
  55pub struct BlockMapWriter<'a>(&'a mut BlockMap);
  56
  57#[derive(Clone)]
  58pub struct BlockSnapshot {
  59    pub(super) wrap_snapshot: WrapSnapshot,
  60    transforms: SumTree<Transform>,
  61    custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
  62    pub(super) buffer_header_height: u32,
  63    pub(super) excerpt_header_height: u32,
  64}
  65
  66impl Deref for BlockSnapshot {
  67    type Target = WrapSnapshot;
  68
  69    fn deref(&self) -> &Self::Target {
  70        &self.wrap_snapshot
  71    }
  72}
  73
  74#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
  75pub struct CustomBlockId(pub usize);
  76
  77impl From<CustomBlockId> for ElementId {
  78    fn from(val: CustomBlockId) -> Self {
  79        val.0.into()
  80    }
  81}
  82
  83/// A zero-indexed point in a text buffer consisting of a row and column
  84/// adjusted for inserted blocks, wrapped rows, tabs, folds and inlays.
  85#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  86pub struct BlockPoint(pub Point);
  87
  88#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  89pub struct BlockRow(pub u32);
  90
  91impl_for_row_types! {
  92    BlockRow => RowDelta
  93}
  94
  95impl BlockPoint {
  96    pub fn row(&self) -> BlockRow {
  97        BlockRow(self.0.row)
  98    }
  99}
 100
 101pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
 102
 103/// Where to place a block.
 104#[derive(Clone, Debug, Eq, PartialEq)]
 105pub enum BlockPlacement<T> {
 106    /// Place the block above the given position.
 107    Above(T),
 108    /// Place the block below the given position.
 109    Below(T),
 110    /// Place the block next the given position.
 111    Near(T),
 112    /// Replace the given range of positions with the block.
 113    Replace(RangeInclusive<T>),
 114}
 115
 116impl<T> BlockPlacement<T> {
 117    pub fn start(&self) -> &T {
 118        match self {
 119            BlockPlacement::Above(position) => position,
 120            BlockPlacement::Below(position) => position,
 121            BlockPlacement::Near(position) => position,
 122            BlockPlacement::Replace(range) => range.start(),
 123        }
 124    }
 125
 126    fn end(&self) -> &T {
 127        match self {
 128            BlockPlacement::Above(position) => position,
 129            BlockPlacement::Below(position) => position,
 130            BlockPlacement::Near(position) => position,
 131            BlockPlacement::Replace(range) => range.end(),
 132        }
 133    }
 134
 135    pub fn as_ref(&self) -> BlockPlacement<&T> {
 136        match self {
 137            BlockPlacement::Above(position) => BlockPlacement::Above(position),
 138            BlockPlacement::Below(position) => BlockPlacement::Below(position),
 139            BlockPlacement::Near(position) => BlockPlacement::Near(position),
 140            BlockPlacement::Replace(range) => BlockPlacement::Replace(range.start()..=range.end()),
 141        }
 142    }
 143
 144    pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> {
 145        match self {
 146            BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
 147            BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
 148            BlockPlacement::Near(position) => BlockPlacement::Near(f(position)),
 149            BlockPlacement::Replace(range) => {
 150                let (start, end) = range.into_inner();
 151                BlockPlacement::Replace(f(start)..=f(end))
 152            }
 153        }
 154    }
 155
 156    fn tie_break(&self) -> u8 {
 157        match self {
 158            BlockPlacement::Replace(_) => 0,
 159            BlockPlacement::Above(_) => 1,
 160            BlockPlacement::Near(_) => 2,
 161            BlockPlacement::Below(_) => 3,
 162        }
 163    }
 164}
 165
 166impl BlockPlacement<Anchor> {
 167    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
 168        self.start()
 169            .cmp(other.start(), buffer)
 170            .then_with(|| other.end().cmp(self.end(), buffer))
 171            .then_with(|| self.tie_break().cmp(&other.tie_break()))
 172    }
 173
 174    fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
 175        let buffer_snapshot = wrap_snapshot.buffer_snapshot();
 176        match self {
 177            BlockPlacement::Above(position) => {
 178                let mut position = position.to_point(buffer_snapshot);
 179                position.column = 0;
 180                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
 181                Some(BlockPlacement::Above(wrap_row))
 182            }
 183            BlockPlacement::Near(position) => {
 184                let mut position = position.to_point(buffer_snapshot);
 185                position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
 186                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
 187                Some(BlockPlacement::Near(wrap_row))
 188            }
 189            BlockPlacement::Below(position) => {
 190                let mut position = position.to_point(buffer_snapshot);
 191                position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
 192                let wrap_row = wrap_snapshot.make_wrap_point(position, Bias::Left).row();
 193                Some(BlockPlacement::Below(wrap_row))
 194            }
 195            BlockPlacement::Replace(range) => {
 196                let mut start = range.start().to_point(buffer_snapshot);
 197                let mut end = range.end().to_point(buffer_snapshot);
 198                if start == end {
 199                    None
 200                } else {
 201                    start.column = 0;
 202                    let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
 203                    end.column = buffer_snapshot.line_len(MultiBufferRow(end.row));
 204                    let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
 205                    Some(BlockPlacement::Replace(start_wrap_row..=end_wrap_row))
 206                }
 207            }
 208        }
 209    }
 210}
 211
 212pub struct CustomBlock {
 213    pub id: CustomBlockId,
 214    pub placement: BlockPlacement<Anchor>,
 215    pub height: Option<u32>,
 216    style: BlockStyle,
 217    render: Arc<Mutex<RenderBlock>>,
 218    priority: usize,
 219}
 220
 221#[derive(Clone)]
 222pub struct BlockProperties<P> {
 223    pub placement: BlockPlacement<P>,
 224    // None if the block takes up no space
 225    // (e.g. a horizontal line)
 226    pub height: Option<u32>,
 227    pub style: BlockStyle,
 228    pub render: RenderBlock,
 229    pub priority: usize,
 230}
 231
 232impl<P: Debug> Debug for BlockProperties<P> {
 233    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 234        f.debug_struct("BlockProperties")
 235            .field("placement", &self.placement)
 236            .field("height", &self.height)
 237            .field("style", &self.style)
 238            .finish()
 239    }
 240}
 241
 242#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
 243pub enum BlockStyle {
 244    Fixed,
 245    Flex,
 246    Sticky,
 247}
 248
 249#[derive(Debug, Default, Copy, Clone)]
 250pub struct EditorMargins {
 251    pub gutter: GutterDimensions,
 252    pub right: Pixels,
 253}
 254
 255#[derive(gpui::AppContext, gpui::VisualContext)]
 256pub struct BlockContext<'a, 'b> {
 257    #[window]
 258    pub window: &'a mut Window,
 259    #[app]
 260    pub app: &'b mut App,
 261    pub anchor_x: Pixels,
 262    pub max_width: Pixels,
 263    pub margins: &'b EditorMargins,
 264    pub em_width: Pixels,
 265    pub line_height: Pixels,
 266    pub block_id: BlockId,
 267    pub selected: bool,
 268    pub editor_style: &'b EditorStyle,
 269}
 270
 271#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
 272pub enum BlockId {
 273    ExcerptBoundary(ExcerptId),
 274    FoldedBuffer(ExcerptId),
 275    Custom(CustomBlockId),
 276}
 277
 278impl From<BlockId> for ElementId {
 279    fn from(value: BlockId) -> Self {
 280        match value {
 281            BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(),
 282            BlockId::ExcerptBoundary(excerpt_id) => {
 283                ("ExcerptBoundary", EntityId::from(excerpt_id)).into()
 284            }
 285            BlockId::FoldedBuffer(id) => ("FoldedBuffer", EntityId::from(id)).into(),
 286        }
 287    }
 288}
 289
 290impl std::fmt::Display for BlockId {
 291    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 292        match self {
 293            Self::Custom(id) => write!(f, "Block({id:?})"),
 294            Self::ExcerptBoundary(id) => write!(f, "ExcerptHeader({id:?})"),
 295            Self::FoldedBuffer(id) => write!(f, "FoldedBuffer({id:?})"),
 296        }
 297    }
 298}
 299
 300#[derive(Clone, Debug)]
 301struct Transform {
 302    summary: TransformSummary,
 303    block: Option<Block>,
 304}
 305
 306#[derive(Clone)]
 307pub enum Block {
 308    Custom(Arc<CustomBlock>),
 309    FoldedBuffer {
 310        first_excerpt: ExcerptInfo,
 311        height: u32,
 312    },
 313    ExcerptBoundary {
 314        excerpt: ExcerptInfo,
 315        height: u32,
 316    },
 317    BufferHeader {
 318        excerpt: ExcerptInfo,
 319        height: u32,
 320    },
 321}
 322
 323impl Block {
 324    pub fn id(&self) -> BlockId {
 325        match self {
 326            Block::Custom(block) => BlockId::Custom(block.id),
 327            Block::ExcerptBoundary {
 328                excerpt: next_excerpt,
 329                ..
 330            } => BlockId::ExcerptBoundary(next_excerpt.id),
 331            Block::FoldedBuffer { first_excerpt, .. } => BlockId::FoldedBuffer(first_excerpt.id),
 332            Block::BufferHeader {
 333                excerpt: next_excerpt,
 334                ..
 335            } => BlockId::ExcerptBoundary(next_excerpt.id),
 336        }
 337    }
 338
 339    pub fn has_height(&self) -> bool {
 340        match self {
 341            Block::Custom(block) => block.height.is_some(),
 342            Block::ExcerptBoundary { .. }
 343            | Block::FoldedBuffer { .. }
 344            | Block::BufferHeader { .. } => true,
 345        }
 346    }
 347
 348    pub fn height(&self) -> u32 {
 349        match self {
 350            Block::Custom(block) => block.height.unwrap_or(0),
 351            Block::ExcerptBoundary { height, .. }
 352            | Block::FoldedBuffer { height, .. }
 353            | Block::BufferHeader { height, .. } => *height,
 354        }
 355    }
 356
 357    pub fn style(&self) -> BlockStyle {
 358        match self {
 359            Block::Custom(block) => block.style,
 360            Block::ExcerptBoundary { .. }
 361            | Block::FoldedBuffer { .. }
 362            | Block::BufferHeader { .. } => BlockStyle::Sticky,
 363        }
 364    }
 365
 366    fn place_above(&self) -> bool {
 367        match self {
 368            Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)),
 369            Block::FoldedBuffer { .. } => false,
 370            Block::ExcerptBoundary { .. } => true,
 371            Block::BufferHeader { .. } => true,
 372        }
 373    }
 374
 375    pub fn place_near(&self) -> bool {
 376        match self {
 377            Block::Custom(block) => matches!(block.placement, BlockPlacement::Near(_)),
 378            Block::FoldedBuffer { .. } => false,
 379            Block::ExcerptBoundary { .. } => false,
 380            Block::BufferHeader { .. } => false,
 381        }
 382    }
 383
 384    fn place_below(&self) -> bool {
 385        match self {
 386            Block::Custom(block) => matches!(
 387                block.placement,
 388                BlockPlacement::Below(_) | BlockPlacement::Near(_)
 389            ),
 390            Block::FoldedBuffer { .. } => false,
 391            Block::ExcerptBoundary { .. } => false,
 392            Block::BufferHeader { .. } => false,
 393        }
 394    }
 395
 396    fn is_replacement(&self) -> bool {
 397        match self {
 398            Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
 399            Block::FoldedBuffer { .. } => true,
 400            Block::ExcerptBoundary { .. } => false,
 401            Block::BufferHeader { .. } => false,
 402        }
 403    }
 404
 405    fn is_header(&self) -> bool {
 406        match self {
 407            Block::Custom(_) => false,
 408            Block::FoldedBuffer { .. } => true,
 409            Block::ExcerptBoundary { .. } => true,
 410            Block::BufferHeader { .. } => true,
 411        }
 412    }
 413
 414    pub fn is_buffer_header(&self) -> bool {
 415        match self {
 416            Block::Custom(_) => false,
 417            Block::FoldedBuffer { .. } => true,
 418            Block::ExcerptBoundary { .. } => false,
 419            Block::BufferHeader { .. } => true,
 420        }
 421    }
 422}
 423
 424impl Debug for Block {
 425    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 426        match self {
 427            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
 428            Self::FoldedBuffer {
 429                first_excerpt,
 430                height,
 431            } => f
 432                .debug_struct("FoldedBuffer")
 433                .field("first_excerpt", &first_excerpt)
 434                .field("height", height)
 435                .finish(),
 436            Self::ExcerptBoundary { excerpt, height } => f
 437                .debug_struct("ExcerptBoundary")
 438                .field("excerpt", excerpt)
 439                .field("height", height)
 440                .finish(),
 441            Self::BufferHeader { excerpt, height } => f
 442                .debug_struct("BufferHeader")
 443                .field("excerpt", excerpt)
 444                .field("height", height)
 445                .finish(),
 446        }
 447    }
 448}
 449
 450#[derive(Clone, Debug, Default)]
 451struct TransformSummary {
 452    input_rows: WrapRow,
 453    output_rows: BlockRow,
 454    longest_row: BlockRow,
 455    longest_row_chars: u32,
 456}
 457
 458pub struct BlockChunks<'a> {
 459    transforms: sum_tree::Cursor<'a, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
 460    input_chunks: wrap_map::WrapChunks<'a>,
 461    input_chunk: Chunk<'a>,
 462    output_row: BlockRow,
 463    max_output_row: BlockRow,
 464    line_count_overflow: RowDelta,
 465    masked: bool,
 466}
 467
 468#[derive(Clone)]
 469pub struct BlockRows<'a> {
 470    transforms: sum_tree::Cursor<'a, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
 471    input_rows: wrap_map::WrapRows<'a>,
 472    output_row: BlockRow,
 473    started: bool,
 474}
 475
 476impl BlockMap {
 477    pub fn new(
 478        wrap_snapshot: WrapSnapshot,
 479        buffer_header_height: u32,
 480        excerpt_header_height: u32,
 481    ) -> Self {
 482        let row_count = wrap_snapshot.max_point().row() + WrapRow(1);
 483        let mut transforms = SumTree::default();
 484        push_isomorphic(&mut transforms, row_count - WrapRow(0), &wrap_snapshot);
 485        let map = Self {
 486            next_block_id: AtomicUsize::new(0),
 487            custom_blocks: Vec::new(),
 488            custom_blocks_by_id: TreeMap::default(),
 489            folded_buffers: HashSet::default(),
 490            buffers_with_disabled_headers: HashSet::default(),
 491            transforms: RefCell::new(transforms),
 492            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
 493            buffer_header_height,
 494            excerpt_header_height,
 495        };
 496        map.sync(
 497            &wrap_snapshot,
 498            Patch::new(vec![Edit {
 499                old: WrapRow(0)..row_count,
 500                new: WrapRow(0)..row_count,
 501            }]),
 502        );
 503        map
 504    }
 505
 506    pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: WrapPatch) -> BlockMapReader<'_> {
 507        self.sync(&wrap_snapshot, edits);
 508        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
 509        BlockMapReader {
 510            blocks: &self.custom_blocks,
 511            snapshot: BlockSnapshot {
 512                wrap_snapshot,
 513                transforms: self.transforms.borrow().clone(),
 514                custom_blocks_by_id: self.custom_blocks_by_id.clone(),
 515                buffer_header_height: self.buffer_header_height,
 516                excerpt_header_height: self.excerpt_header_height,
 517            },
 518        }
 519    }
 520
 521    pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: WrapPatch) -> BlockMapWriter<'_> {
 522        self.sync(&wrap_snapshot, edits);
 523        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
 524        BlockMapWriter(self)
 525    }
 526
 527    fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: WrapPatch) {
 528        let buffer = wrap_snapshot.buffer_snapshot();
 529
 530        // Handle changing the last excerpt if it is empty.
 531        if buffer.trailing_excerpt_update_count()
 532            != self
 533                .wrap_snapshot
 534                .borrow()
 535                .buffer_snapshot()
 536                .trailing_excerpt_update_count()
 537        {
 538            let max_point = wrap_snapshot.max_point();
 539            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
 540            let edit_end = max_point.row() + WrapRow(1);
 541            edits = edits.compose([WrapEdit {
 542                old: edit_start..edit_end,
 543                new: edit_start..edit_end,
 544            }]);
 545        }
 546
 547        let edits = edits.into_inner();
 548        if edits.is_empty() {
 549            return;
 550        }
 551
 552        let mut transforms = self.transforms.borrow_mut();
 553        let mut new_transforms = SumTree::default();
 554        let mut cursor = transforms.cursor::<WrapRow>(());
 555        let mut last_block_ix = 0;
 556        let mut blocks_in_edit = Vec::new();
 557        let mut edits = edits.into_iter().peekable();
 558
 559        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 transform.block.as_ref().is_none_or(|block| {
1883            block.is_replacement()
1884                && self.transforms.start().0 == self.output_row
1885                && matches!(block, Block::FoldedBuffer { .. }).not()
1886        }) {
1887            self.input_rows.next()
1888        } else {
1889            Some(RowInfo::default())
1890        }
1891    }
1892}
1893
1894impl sum_tree::Item for Transform {
1895    type Summary = TransformSummary;
1896
1897    fn summary(&self, _cx: ()) -> Self::Summary {
1898        self.summary.clone()
1899    }
1900}
1901
1902impl sum_tree::ContextLessSummary for TransformSummary {
1903    fn zero() -> Self {
1904        Default::default()
1905    }
1906
1907    fn add_summary(&mut self, summary: &Self) {
1908        if summary.longest_row_chars > self.longest_row_chars {
1909            self.longest_row = self.output_rows + summary.longest_row;
1910            self.longest_row_chars = summary.longest_row_chars;
1911        }
1912        self.input_rows += summary.input_rows;
1913        self.output_rows += summary.output_rows;
1914    }
1915}
1916
1917impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1918    fn zero(_cx: ()) -> Self {
1919        Default::default()
1920    }
1921
1922    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1923        *self += summary.input_rows;
1924    }
1925}
1926
1927impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1928    fn zero(_cx: ()) -> Self {
1929        Default::default()
1930    }
1931
1932    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1933        *self += summary.output_rows;
1934    }
1935}
1936
1937impl Deref for BlockContext<'_, '_> {
1938    type Target = App;
1939
1940    fn deref(&self) -> &Self::Target {
1941        self.app
1942    }
1943}
1944
1945impl DerefMut for BlockContext<'_, '_> {
1946    fn deref_mut(&mut self) -> &mut Self::Target {
1947        self.app
1948    }
1949}
1950
1951impl CustomBlock {
1952    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1953        self.render.lock()(cx)
1954    }
1955
1956    pub fn start(&self) -> Anchor {
1957        *self.placement.start()
1958    }
1959
1960    pub fn end(&self) -> Anchor {
1961        *self.placement.end()
1962    }
1963
1964    pub fn style(&self) -> BlockStyle {
1965        self.style
1966    }
1967}
1968
1969impl Debug for CustomBlock {
1970    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1971        f.debug_struct("Block")
1972            .field("id", &self.id)
1973            .field("placement", &self.placement)
1974            .field("height", &self.height)
1975            .field("style", &self.style)
1976            .field("priority", &self.priority)
1977            .finish_non_exhaustive()
1978    }
1979}
1980
1981// Count the number of bytes prior to a target point. If the string doesn't contain the target
1982// point, return its total extent. Otherwise return the target point itself.
1983fn offset_for_row(s: &str, target: RowDelta) -> (RowDelta, usize) {
1984    let mut row = 0;
1985    let mut offset = 0;
1986    for (ix, line) in s.split('\n').enumerate() {
1987        if ix > 0 {
1988            row += 1;
1989            offset += 1;
1990        }
1991        if row >= target.0 {
1992            break;
1993        }
1994        offset += line.len();
1995    }
1996    (RowDelta(row), offset)
1997}
1998
1999#[cfg(test)]
2000mod tests {
2001    use super::*;
2002    use crate::{
2003        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap},
2004        test::test_font,
2005    };
2006    use gpui::{App, AppContext as _, Element, div, font, px};
2007    use itertools::Itertools;
2008    use language::{Buffer, Capability};
2009    use multi_buffer::{ExcerptRange, MultiBuffer};
2010    use rand::prelude::*;
2011    use settings::SettingsStore;
2012    use std::env;
2013    use util::RandomCharIter;
2014
2015    #[gpui::test]
2016    fn test_offset_for_row() {
2017        assert_eq!(offset_for_row("", RowDelta(0)), (RowDelta(0), 0));
2018        assert_eq!(offset_for_row("", RowDelta(1)), (RowDelta(0), 0));
2019        assert_eq!(offset_for_row("abcd", RowDelta(0)), (RowDelta(0), 0));
2020        assert_eq!(offset_for_row("abcd", RowDelta(1)), (RowDelta(0), 4));
2021        assert_eq!(offset_for_row("\n", RowDelta(0)), (RowDelta(0), 0));
2022        assert_eq!(offset_for_row("\n", RowDelta(1)), (RowDelta(1), 1));
2023        assert_eq!(
2024            offset_for_row("abc\ndef\nghi", RowDelta(0)),
2025            (RowDelta(0), 0)
2026        );
2027        assert_eq!(
2028            offset_for_row("abc\ndef\nghi", RowDelta(1)),
2029            (RowDelta(1), 4)
2030        );
2031        assert_eq!(
2032            offset_for_row("abc\ndef\nghi", RowDelta(2)),
2033            (RowDelta(2), 8)
2034        );
2035        assert_eq!(
2036            offset_for_row("abc\ndef\nghi", RowDelta(3)),
2037            (RowDelta(2), 11)
2038        );
2039    }
2040
2041    #[gpui::test]
2042    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2043        cx.update(init_test);
2044
2045        let text = "aaa\nbbb\nccc\nddd";
2046
2047        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2048        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2049        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2050        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2051        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2052        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2053        let (wrap_map, wraps_snapshot) =
2054            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2055        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2056
2057        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2058        let block_ids = writer.insert(vec![
2059            BlockProperties {
2060                style: BlockStyle::Fixed,
2061                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2062                height: Some(1),
2063                render: Arc::new(|_| div().into_any()),
2064                priority: 0,
2065            },
2066            BlockProperties {
2067                style: BlockStyle::Fixed,
2068                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2069                height: Some(2),
2070                render: Arc::new(|_| div().into_any()),
2071                priority: 0,
2072            },
2073            BlockProperties {
2074                style: BlockStyle::Fixed,
2075                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2076                height: Some(3),
2077                render: Arc::new(|_| div().into_any()),
2078                priority: 0,
2079            },
2080        ]);
2081
2082        let snapshot = block_map.read(wraps_snapshot, Default::default());
2083        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2084
2085        let blocks = snapshot
2086            .blocks_in_range(BlockRow(0)..BlockRow(8))
2087            .map(|(start_row, block)| {
2088                let block = block.as_custom().unwrap();
2089                (start_row.0..start_row.0 + block.height.unwrap(), block.id)
2090            })
2091            .collect::<Vec<_>>();
2092
2093        // When multiple blocks are on the same line, the newer blocks appear first.
2094        assert_eq!(
2095            blocks,
2096            &[
2097                (1..2, block_ids[0]),
2098                (2..4, block_ids[1]),
2099                (7..10, block_ids[2]),
2100            ]
2101        );
2102
2103        assert_eq!(
2104            snapshot.to_block_point(WrapPoint::new(WrapRow(0), 3)),
2105            BlockPoint::new(BlockRow(0), 3)
2106        );
2107        assert_eq!(
2108            snapshot.to_block_point(WrapPoint::new(WrapRow(1), 0)),
2109            BlockPoint::new(BlockRow(4), 0)
2110        );
2111        assert_eq!(
2112            snapshot.to_block_point(WrapPoint::new(WrapRow(3), 3)),
2113            BlockPoint::new(BlockRow(6), 3)
2114        );
2115
2116        assert_eq!(
2117            snapshot.to_wrap_point(BlockPoint::new(BlockRow(0), 3), Bias::Left),
2118            WrapPoint::new(WrapRow(0), 3)
2119        );
2120        assert_eq!(
2121            snapshot.to_wrap_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2122            WrapPoint::new(WrapRow(1), 0)
2123        );
2124        assert_eq!(
2125            snapshot.to_wrap_point(BlockPoint::new(BlockRow(3), 0), Bias::Left),
2126            WrapPoint::new(WrapRow(1), 0)
2127        );
2128        assert_eq!(
2129            snapshot.to_wrap_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2130            WrapPoint::new(WrapRow(3), 3)
2131        );
2132
2133        assert_eq!(
2134            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Left),
2135            BlockPoint::new(BlockRow(0), 3)
2136        );
2137        assert_eq!(
2138            snapshot.clip_point(BlockPoint::new(BlockRow(1), 0), Bias::Right),
2139            BlockPoint::new(BlockRow(4), 0)
2140        );
2141        assert_eq!(
2142            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Left),
2143            BlockPoint::new(BlockRow(0), 3)
2144        );
2145        assert_eq!(
2146            snapshot.clip_point(BlockPoint::new(BlockRow(1), 1), Bias::Right),
2147            BlockPoint::new(BlockRow(4), 0)
2148        );
2149        assert_eq!(
2150            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Left),
2151            BlockPoint::new(BlockRow(4), 0)
2152        );
2153        assert_eq!(
2154            snapshot.clip_point(BlockPoint::new(BlockRow(4), 0), Bias::Right),
2155            BlockPoint::new(BlockRow(4), 0)
2156        );
2157        assert_eq!(
2158            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Left),
2159            BlockPoint::new(BlockRow(6), 3)
2160        );
2161        assert_eq!(
2162            snapshot.clip_point(BlockPoint::new(BlockRow(6), 3), Bias::Right),
2163            BlockPoint::new(BlockRow(6), 3)
2164        );
2165        assert_eq!(
2166            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Left),
2167            BlockPoint::new(BlockRow(6), 3)
2168        );
2169        assert_eq!(
2170            snapshot.clip_point(BlockPoint::new(BlockRow(7), 0), Bias::Right),
2171            BlockPoint::new(BlockRow(6), 3)
2172        );
2173
2174        assert_eq!(
2175            snapshot
2176                .row_infos(BlockRow(0))
2177                .map(|row_info| row_info.buffer_row)
2178                .collect::<Vec<_>>(),
2179            &[
2180                Some(0),
2181                None,
2182                None,
2183                None,
2184                Some(1),
2185                Some(2),
2186                Some(3),
2187                None,
2188                None,
2189                None
2190            ]
2191        );
2192
2193        // Insert a line break, separating two block decorations into separate lines.
2194        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2195            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2196            buffer.snapshot(cx)
2197        });
2198
2199        let (inlay_snapshot, inlay_edits) =
2200            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2201        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2202        let (tab_snapshot, tab_edits) =
2203            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2204        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2205            wrap_map.sync(tab_snapshot, tab_edits, cx)
2206        });
2207        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
2208        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2209    }
2210
2211    #[gpui::test]
2212    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2213        init_test(cx);
2214
2215        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2216        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2217        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2218
2219        let mut excerpt_ids = Vec::new();
2220        let multi_buffer = cx.new(|cx| {
2221            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2222            excerpt_ids.extend(multi_buffer.push_excerpts(
2223                buffer1.clone(),
2224                [ExcerptRange::new(0..buffer1.read(cx).len())],
2225                cx,
2226            ));
2227            excerpt_ids.extend(multi_buffer.push_excerpts(
2228                buffer2.clone(),
2229                [ExcerptRange::new(0..buffer2.read(cx).len())],
2230                cx,
2231            ));
2232            excerpt_ids.extend(multi_buffer.push_excerpts(
2233                buffer3.clone(),
2234                [ExcerptRange::new(0..buffer3.read(cx).len())],
2235                cx,
2236            ));
2237
2238            multi_buffer
2239        });
2240
2241        let font = test_font();
2242        let font_size = px(14.);
2243        let font_id = cx.text_system().resolve_font(&font);
2244        let mut wrap_width = px(0.);
2245        for c in "Buff".chars() {
2246            wrap_width += cx
2247                .text_system()
2248                .advance(font_id, font_size, c)
2249                .unwrap()
2250                .width;
2251        }
2252
2253        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2254        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
2255        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2256        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2257        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2258
2259        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2260        let snapshot = block_map.read(wraps_snapshot, Default::default());
2261
2262        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2263        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
2264
2265        let blocks: Vec<_> = snapshot
2266            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2267            .map(|(row, block)| (row.0..row.0 + block.height(), block.id()))
2268            .collect();
2269        assert_eq!(
2270            blocks,
2271            vec![
2272                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
2273                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
2274                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
2275            ]
2276        );
2277    }
2278
2279    #[gpui::test]
2280    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2281        cx.update(init_test);
2282
2283        let text = "aaa\nbbb\nccc\nddd";
2284
2285        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2286        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2287        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2288        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2289        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2290        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2291        let (_wrap_map, wraps_snapshot) =
2292            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2293        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2294
2295        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2296        let block_ids = writer.insert(vec![
2297            BlockProperties {
2298                style: BlockStyle::Fixed,
2299                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2300                height: Some(1),
2301                render: Arc::new(|_| div().into_any()),
2302                priority: 0,
2303            },
2304            BlockProperties {
2305                style: BlockStyle::Fixed,
2306                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2307                height: Some(2),
2308                render: Arc::new(|_| div().into_any()),
2309                priority: 0,
2310            },
2311            BlockProperties {
2312                style: BlockStyle::Fixed,
2313                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2314                height: Some(3),
2315                render: Arc::new(|_| div().into_any()),
2316                priority: 0,
2317            },
2318        ]);
2319
2320        {
2321            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2322            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2323
2324            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2325
2326            let mut new_heights = HashMap::default();
2327            new_heights.insert(block_ids[0], 2);
2328            block_map_writer.resize(new_heights);
2329            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2330            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2331        }
2332
2333        {
2334            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2335
2336            let mut new_heights = HashMap::default();
2337            new_heights.insert(block_ids[0], 1);
2338            block_map_writer.resize(new_heights);
2339
2340            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2341            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2342        }
2343
2344        {
2345            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2346
2347            let mut new_heights = HashMap::default();
2348            new_heights.insert(block_ids[0], 0);
2349            block_map_writer.resize(new_heights);
2350
2351            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2352            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2353        }
2354
2355        {
2356            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2357
2358            let mut new_heights = HashMap::default();
2359            new_heights.insert(block_ids[0], 3);
2360            block_map_writer.resize(new_heights);
2361
2362            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2363            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2364        }
2365
2366        {
2367            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2368
2369            let mut new_heights = HashMap::default();
2370            new_heights.insert(block_ids[0], 3);
2371            block_map_writer.resize(new_heights);
2372
2373            let snapshot = block_map.read(wraps_snapshot, Default::default());
2374            // Same height as before, should remain the same
2375            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2376        }
2377    }
2378
2379    #[gpui::test]
2380    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2381        cx.update(init_test);
2382
2383        let text = "one two three\nfour five six\nseven eight";
2384
2385        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2386        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2387        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2388        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2389        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2390        let (_, wraps_snapshot) = cx.update(|cx| {
2391            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
2392        });
2393        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2394
2395        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2396        writer.insert(vec![
2397            BlockProperties {
2398                style: BlockStyle::Fixed,
2399                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2400                render: Arc::new(|_| div().into_any()),
2401                height: Some(1),
2402                priority: 0,
2403            },
2404            BlockProperties {
2405                style: BlockStyle::Fixed,
2406                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2407                render: Arc::new(|_| div().into_any()),
2408                height: Some(1),
2409                priority: 0,
2410            },
2411        ]);
2412
2413        // Blocks with an 'above' disposition go above their corresponding buffer line.
2414        // Blocks with a 'below' disposition go below their corresponding buffer line.
2415        let snapshot = block_map.read(wraps_snapshot, Default::default());
2416        assert_eq!(
2417            snapshot.text(),
2418            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2419        );
2420    }
2421
2422    #[gpui::test]
2423    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2424        cx.update(init_test);
2425
2426        let text = "line1\nline2\nline3\nline4\nline5";
2427
2428        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2429        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2430        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2431        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2432        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2433        let tab_size = 1.try_into().unwrap();
2434        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2435        let (wrap_map, wraps_snapshot) =
2436            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2437        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2438
2439        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2440        let replace_block_id = writer.insert(vec![BlockProperties {
2441            style: BlockStyle::Fixed,
2442            placement: BlockPlacement::Replace(
2443                buffer_snapshot.anchor_after(Point::new(1, 3))
2444                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
2445            ),
2446            height: Some(4),
2447            render: Arc::new(|_| div().into_any()),
2448            priority: 0,
2449        }])[0];
2450
2451        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2452        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2453
2454        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2455            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2456            buffer.snapshot(cx)
2457        });
2458        let (inlay_snapshot, inlay_edits) =
2459            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
2460        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2461        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2462        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2463            wrap_map.sync(tab_snapshot, tab_edits, cx)
2464        });
2465        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits);
2466        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2467
2468        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2469            buffer.edit(
2470                [(
2471                    Point::new(1, 5)..Point::new(1, 5),
2472                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2473                )],
2474                None,
2475                cx,
2476            );
2477            buffer.snapshot(cx)
2478        });
2479        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2480            buffer_snapshot.clone(),
2481            buffer_subscription.consume().into_inner(),
2482        );
2483        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2484        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2485        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2486            wrap_map.sync(tab_snapshot, tab_edits, cx)
2487        });
2488        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2489        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2490
2491        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2492        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2493        writer.insert(vec![
2494            BlockProperties {
2495                style: BlockStyle::Fixed,
2496                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2497                height: Some(1),
2498                render: Arc::new(|_| div().into_any()),
2499                priority: 0,
2500            },
2501            BlockProperties {
2502                style: BlockStyle::Fixed,
2503                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2504                height: Some(1),
2505                render: Arc::new(|_| div().into_any()),
2506                priority: 0,
2507            },
2508            BlockProperties {
2509                style: BlockStyle::Fixed,
2510                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2511                height: Some(1),
2512                render: Arc::new(|_| div().into_any()),
2513                priority: 0,
2514            },
2515        ]);
2516        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2517        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2518
2519        // Ensure blocks inserted *inside* replaced region are hidden.
2520        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2521        writer.insert(vec![
2522            BlockProperties {
2523                style: BlockStyle::Fixed,
2524                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2525                height: Some(1),
2526                render: Arc::new(|_| div().into_any()),
2527                priority: 0,
2528            },
2529            BlockProperties {
2530                style: BlockStyle::Fixed,
2531                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2532                height: Some(1),
2533                render: Arc::new(|_| div().into_any()),
2534                priority: 0,
2535            },
2536            BlockProperties {
2537                style: BlockStyle::Fixed,
2538                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2539                height: Some(1),
2540                render: Arc::new(|_| div().into_any()),
2541                priority: 0,
2542            },
2543        ]);
2544        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2545        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2546
2547        // Removing the replace block shows all the hidden blocks again.
2548        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2549        writer.remove(HashSet::from_iter([replace_block_id]));
2550        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2551        assert_eq!(
2552            blocks_snapshot.text(),
2553            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2554        );
2555    }
2556
2557    #[gpui::test]
2558    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2559        cx.update(init_test);
2560
2561        let text = "111\n222\n333\n444\n555\n666";
2562
2563        let buffer = cx.update(|cx| {
2564            MultiBuffer::build_multi(
2565                [
2566                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2567                    (
2568                        text,
2569                        vec![
2570                            Point::new(1, 0)..Point::new(1, 3),
2571                            Point::new(2, 0)..Point::new(2, 3),
2572                            Point::new(3, 0)..Point::new(3, 3),
2573                        ],
2574                    ),
2575                    (
2576                        text,
2577                        vec![
2578                            Point::new(4, 0)..Point::new(4, 3),
2579                            Point::new(5, 0)..Point::new(5, 3),
2580                        ],
2581                    ),
2582                ],
2583                cx,
2584            )
2585        });
2586        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2587        let buffer_ids = buffer_snapshot
2588            .excerpts()
2589            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2590            .dedup()
2591            .collect::<Vec<_>>();
2592        assert_eq!(buffer_ids.len(), 3);
2593        let buffer_id_1 = buffer_ids[0];
2594        let buffer_id_2 = buffer_ids[1];
2595        let buffer_id_3 = buffer_ids[2];
2596
2597        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2598        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2599        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2600        let (_, wrap_snapshot) =
2601            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2602        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2603        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2604
2605        assert_eq!(
2606            blocks_snapshot.text(),
2607            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
2608        );
2609        assert_eq!(
2610            blocks_snapshot
2611                .row_infos(BlockRow(0))
2612                .map(|i| i.buffer_row)
2613                .collect::<Vec<_>>(),
2614            vec![
2615                None,
2616                None,
2617                Some(0),
2618                None,
2619                None,
2620                Some(1),
2621                None,
2622                Some(2),
2623                None,
2624                Some(3),
2625                None,
2626                None,
2627                Some(4),
2628                None,
2629                Some(5),
2630            ]
2631        );
2632
2633        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2634        let excerpt_blocks_2 = writer.insert(vec![
2635            BlockProperties {
2636                style: BlockStyle::Fixed,
2637                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2638                height: Some(1),
2639                render: Arc::new(|_| div().into_any()),
2640                priority: 0,
2641            },
2642            BlockProperties {
2643                style: BlockStyle::Fixed,
2644                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2645                height: Some(1),
2646                render: Arc::new(|_| div().into_any()),
2647                priority: 0,
2648            },
2649            BlockProperties {
2650                style: BlockStyle::Fixed,
2651                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2652                height: Some(1),
2653                render: Arc::new(|_| div().into_any()),
2654                priority: 0,
2655            },
2656        ]);
2657        let excerpt_blocks_3 = writer.insert(vec![
2658            BlockProperties {
2659                style: BlockStyle::Fixed,
2660                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2661                height: Some(1),
2662                render: Arc::new(|_| div().into_any()),
2663                priority: 0,
2664            },
2665            BlockProperties {
2666                style: BlockStyle::Fixed,
2667                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2668                height: Some(1),
2669                render: Arc::new(|_| div().into_any()),
2670                priority: 0,
2671            },
2672        ]);
2673
2674        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2675        assert_eq!(
2676            blocks_snapshot.text(),
2677            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2678        );
2679        assert_eq!(
2680            blocks_snapshot
2681                .row_infos(BlockRow(0))
2682                .map(|i| i.buffer_row)
2683                .collect::<Vec<_>>(),
2684            vec![
2685                None,
2686                None,
2687                Some(0),
2688                None,
2689                None,
2690                None,
2691                Some(1),
2692                None,
2693                None,
2694                Some(2),
2695                None,
2696                Some(3),
2697                None,
2698                None,
2699                None,
2700                None,
2701                Some(4),
2702                None,
2703                Some(5),
2704                None,
2705            ]
2706        );
2707
2708        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2709        buffer.read_with(cx, |buffer, cx| {
2710            writer.fold_buffers([buffer_id_1], buffer, cx);
2711        });
2712        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2713            style: BlockStyle::Fixed,
2714            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2715            height: Some(1),
2716            render: Arc::new(|_| div().into_any()),
2717            priority: 0,
2718        }]);
2719        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2720        let blocks = blocks_snapshot
2721            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2722            .collect::<Vec<_>>();
2723        for (_, block) in &blocks {
2724            if let BlockId::Custom(custom_block_id) = block.id() {
2725                assert!(
2726                    !excerpt_blocks_1.contains(&custom_block_id),
2727                    "Should have no blocks from the folded buffer"
2728                );
2729                assert!(
2730                    excerpt_blocks_2.contains(&custom_block_id)
2731                        || excerpt_blocks_3.contains(&custom_block_id),
2732                    "Should have only blocks from unfolded buffers"
2733                );
2734            }
2735        }
2736        assert_eq!(
2737            1,
2738            blocks
2739                .iter()
2740                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2741                .count(),
2742            "Should have one folded block, producing a header of the second buffer"
2743        );
2744        assert_eq!(
2745            blocks_snapshot.text(),
2746            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2747        );
2748        assert_eq!(
2749            blocks_snapshot
2750                .row_infos(BlockRow(0))
2751                .map(|i| i.buffer_row)
2752                .collect::<Vec<_>>(),
2753            vec![
2754                None,
2755                None,
2756                None,
2757                None,
2758                None,
2759                Some(1),
2760                None,
2761                None,
2762                Some(2),
2763                None,
2764                Some(3),
2765                None,
2766                None,
2767                None,
2768                None,
2769                Some(4),
2770                None,
2771                Some(5),
2772                None,
2773            ]
2774        );
2775
2776        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2777        buffer.read_with(cx, |buffer, cx| {
2778            writer.fold_buffers([buffer_id_2], buffer, cx);
2779        });
2780        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2781        let blocks = blocks_snapshot
2782            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2783            .collect::<Vec<_>>();
2784        for (_, block) in &blocks {
2785            if let BlockId::Custom(custom_block_id) = block.id() {
2786                assert!(
2787                    !excerpt_blocks_1.contains(&custom_block_id),
2788                    "Should have no blocks from the folded buffer_1"
2789                );
2790                assert!(
2791                    !excerpt_blocks_2.contains(&custom_block_id),
2792                    "Should have no blocks from the folded buffer_2"
2793                );
2794                assert!(
2795                    excerpt_blocks_3.contains(&custom_block_id),
2796                    "Should have only blocks from unfolded buffers"
2797                );
2798            }
2799        }
2800        assert_eq!(
2801            2,
2802            blocks
2803                .iter()
2804                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2805                .count(),
2806            "Should have two folded blocks, producing headers"
2807        );
2808        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
2809        assert_eq!(
2810            blocks_snapshot
2811                .row_infos(BlockRow(0))
2812                .map(|i| i.buffer_row)
2813                .collect::<Vec<_>>(),
2814            vec![
2815                None,
2816                None,
2817                None,
2818                None,
2819                None,
2820                None,
2821                None,
2822                Some(4),
2823                None,
2824                Some(5),
2825                None,
2826            ]
2827        );
2828
2829        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2830        buffer.read_with(cx, |buffer, cx| {
2831            writer.unfold_buffers([buffer_id_1], buffer, cx);
2832        });
2833        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2834        let blocks = blocks_snapshot
2835            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2836            .collect::<Vec<_>>();
2837        for (_, block) in &blocks {
2838            if let BlockId::Custom(custom_block_id) = block.id() {
2839                assert!(
2840                    !excerpt_blocks_2.contains(&custom_block_id),
2841                    "Should have no blocks from the folded buffer_2"
2842                );
2843                assert!(
2844                    excerpt_blocks_1.contains(&custom_block_id)
2845                        || excerpt_blocks_3.contains(&custom_block_id),
2846                    "Should have only blocks from unfolded buffers"
2847                );
2848            }
2849        }
2850        assert_eq!(
2851            1,
2852            blocks
2853                .iter()
2854                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2855                .count(),
2856            "Should be back to a single folded buffer, producing a header for buffer_2"
2857        );
2858        assert_eq!(
2859            blocks_snapshot.text(),
2860            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
2861            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2862        );
2863        assert_eq!(
2864            blocks_snapshot
2865                .row_infos(BlockRow(0))
2866                .map(|i| i.buffer_row)
2867                .collect::<Vec<_>>(),
2868            vec![
2869                None,
2870                None,
2871                None,
2872                Some(0),
2873                None,
2874                None,
2875                None,
2876                None,
2877                None,
2878                Some(4),
2879                None,
2880                Some(5),
2881                None,
2882            ]
2883        );
2884
2885        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2886        buffer.read_with(cx, |buffer, cx| {
2887            writer.fold_buffers([buffer_id_3], buffer, cx);
2888        });
2889        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2890        let blocks = blocks_snapshot
2891            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2892            .collect::<Vec<_>>();
2893        for (_, block) in &blocks {
2894            if let BlockId::Custom(custom_block_id) = block.id() {
2895                assert!(
2896                    excerpt_blocks_1.contains(&custom_block_id),
2897                    "Should have no blocks from the folded buffer_1"
2898                );
2899                assert!(
2900                    !excerpt_blocks_2.contains(&custom_block_id),
2901                    "Should have only blocks from unfolded buffers"
2902                );
2903                assert!(
2904                    !excerpt_blocks_3.contains(&custom_block_id),
2905                    "Should have only blocks from unfolded buffers"
2906                );
2907            }
2908        }
2909
2910        assert_eq!(
2911            blocks_snapshot.text(),
2912            "\n\n\n111\n\n\n\n",
2913            "Should have a single, first buffer left after folding"
2914        );
2915        assert_eq!(
2916            blocks_snapshot
2917                .row_infos(BlockRow(0))
2918                .map(|i| i.buffer_row)
2919                .collect::<Vec<_>>(),
2920            vec![None, None, None, Some(0), None, None, None, None,]
2921        );
2922    }
2923
2924    #[gpui::test]
2925    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2926        cx.update(init_test);
2927
2928        let text = "111";
2929
2930        let buffer = cx.update(|cx| {
2931            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2932        });
2933        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2934        let buffer_ids = buffer_snapshot
2935            .excerpts()
2936            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2937            .dedup()
2938            .collect::<Vec<_>>();
2939        assert_eq!(buffer_ids.len(), 1);
2940        let buffer_id = buffer_ids[0];
2941
2942        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
2943        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2944        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2945        let (_, wrap_snapshot) =
2946            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2947        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2948        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2949
2950        assert_eq!(blocks_snapshot.text(), "\n\n111");
2951
2952        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2953        buffer.read_with(cx, |buffer, cx| {
2954            writer.fold_buffers([buffer_id], buffer, cx);
2955        });
2956        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2957        let blocks = blocks_snapshot
2958            .blocks_in_range(BlockRow(0)..BlockRow(u32::MAX))
2959            .collect::<Vec<_>>();
2960        assert_eq!(
2961            1,
2962            blocks
2963                .iter()
2964                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
2965                .count(),
2966            "Should have one folded block, producing a header of the second buffer"
2967        );
2968        assert_eq!(blocks_snapshot.text(), "\n");
2969        assert_eq!(
2970            blocks_snapshot
2971                .row_infos(BlockRow(0))
2972                .map(|i| i.buffer_row)
2973                .collect::<Vec<_>>(),
2974            vec![None, None],
2975            "When fully folded, should be no buffer rows"
2976        );
2977    }
2978
2979    #[gpui::test(iterations = 100)]
2980    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2981        cx.update(init_test);
2982
2983        let operations = env::var("OPERATIONS")
2984            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2985            .unwrap_or(10);
2986
2987        let wrap_width = if rng.random_bool(0.2) {
2988            None
2989        } else {
2990            Some(px(rng.random_range(0.0..=100.0)))
2991        };
2992        let tab_size = 1.try_into().unwrap();
2993        let font_size = px(14.0);
2994        let buffer_start_header_height = rng.random_range(1..=5);
2995        let excerpt_header_height = rng.random_range(1..=5);
2996
2997        log::info!("Wrap width: {:?}", wrap_width);
2998        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2999        let is_singleton = rng.random();
3000        let buffer = if is_singleton {
3001            let len = rng.random_range(0..10);
3002            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3003            log::info!("initial singleton buffer text: {:?}", text);
3004            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3005        } else {
3006            cx.update(|cx| {
3007                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3008                log::info!(
3009                    "initial multi-buffer text: {:?}",
3010                    multibuffer.read(cx).read(cx).text()
3011                );
3012                multibuffer
3013            })
3014        };
3015
3016        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3017        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3018        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3019        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3020        let font = test_font();
3021        let (wrap_map, wraps_snapshot) =
3022            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3023        let mut block_map = BlockMap::new(
3024            wraps_snapshot,
3025            buffer_start_header_height,
3026            excerpt_header_height,
3027        );
3028
3029        for _ in 0..operations {
3030            let mut buffer_edits = Vec::new();
3031            match rng.random_range(0..=100) {
3032                0..=19 => {
3033                    let wrap_width = if rng.random_bool(0.2) {
3034                        None
3035                    } else {
3036                        Some(px(rng.random_range(0.0..=100.0)))
3037                    };
3038                    log::info!("Setting wrap width to {:?}", wrap_width);
3039                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3040                }
3041                20..=39 => {
3042                    let block_count = rng.random_range(1..=5);
3043                    let block_properties = (0..block_count)
3044                        .map(|_| {
3045                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3046                            let offset =
3047                                buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
3048                            let mut min_height = 0;
3049                            let placement = match rng.random_range(0..3) {
3050                                0 => {
3051                                    min_height = 1;
3052                                    let start = buffer.anchor_after(offset);
3053                                    let end = buffer.anchor_after(buffer.clip_offset(
3054                                        rng.random_range(offset..=buffer.len()),
3055                                        Bias::Left,
3056                                    ));
3057                                    BlockPlacement::Replace(start..=end)
3058                                }
3059                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3060                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3061                            };
3062
3063                            let height = rng.random_range(min_height..512);
3064                            BlockProperties {
3065                                style: BlockStyle::Fixed,
3066                                placement,
3067                                height: Some(height),
3068                                render: Arc::new(|_| div().into_any()),
3069                                priority: 0,
3070                            }
3071                        })
3072                        .collect::<Vec<_>>();
3073
3074                    let (inlay_snapshot, inlay_edits) =
3075                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3076                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3077                    let (tab_snapshot, tab_edits) =
3078                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3079                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3080                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3081                    });
3082                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3083                    let block_ids =
3084                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3085                            placement: props.placement.clone(),
3086                            height: props.height,
3087                            style: props.style,
3088                            render: Arc::new(|_| div().into_any()),
3089                            priority: 0,
3090                        }));
3091
3092                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3093                        log::info!(
3094                            "inserted block {:?} with height {:?} and id {:?}",
3095                            block_properties
3096                                .placement
3097                                .as_ref()
3098                                .map(|p| p.to_point(&buffer_snapshot)),
3099                            block_properties.height,
3100                            block_id
3101                        );
3102                    }
3103                }
3104                40..=59 if !block_map.custom_blocks.is_empty() => {
3105                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3106                    let block_ids_to_remove = block_map
3107                        .custom_blocks
3108                        .choose_multiple(&mut rng, block_count)
3109                        .map(|block| block.id)
3110                        .collect::<HashSet<_>>();
3111
3112                    let (inlay_snapshot, inlay_edits) =
3113                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3114                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3115                    let (tab_snapshot, tab_edits) =
3116                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3117                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3118                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3119                    });
3120                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3121                    log::info!(
3122                        "removing {} blocks: {:?}",
3123                        block_ids_to_remove.len(),
3124                        block_ids_to_remove
3125                    );
3126                    block_map.remove(block_ids_to_remove);
3127                }
3128                60..=79 => {
3129                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3130                        log::info!("Noop fold/unfold operation on a singleton buffer");
3131                        continue;
3132                    }
3133                    let (inlay_snapshot, inlay_edits) =
3134                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3135                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3136                    let (tab_snapshot, tab_edits) =
3137                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3138                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3139                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3140                    });
3141                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3142                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3143                        let folded_buffers = block_map
3144                            .0
3145                            .folded_buffers
3146                            .iter()
3147                            .cloned()
3148                            .collect::<Vec<_>>();
3149                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3150                        unfolded_buffers.dedup();
3151                        log::debug!("All buffers {unfolded_buffers:?}");
3152                        log::debug!("Folded buffers {folded_buffers:?}");
3153                        unfolded_buffers
3154                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3155                        (unfolded_buffers, folded_buffers)
3156                    });
3157                    let mut folded_count = folded_buffers.len();
3158                    let mut unfolded_count = unfolded_buffers.len();
3159
3160                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
3161                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
3162                    if !fold && !unfold {
3163                        log::info!(
3164                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3165                        );
3166                        continue;
3167                    }
3168
3169                    buffer.update(cx, |buffer, cx| {
3170                        if fold {
3171                            let buffer_to_fold =
3172                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
3173                            log::info!("Folding {buffer_to_fold:?}");
3174                            let related_excerpts = buffer_snapshot
3175                                .excerpts()
3176                                .filter_map(|(excerpt_id, buffer, range)| {
3177                                    if buffer.remote_id() == buffer_to_fold {
3178                                        Some((
3179                                            excerpt_id,
3180                                            buffer
3181                                                .text_for_range(range.context)
3182                                                .collect::<String>(),
3183                                        ))
3184                                    } else {
3185                                        None
3186                                    }
3187                                })
3188                                .collect::<Vec<_>>();
3189                            log::info!(
3190                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3191                            );
3192                            folded_count += 1;
3193                            unfolded_count -= 1;
3194                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3195                        }
3196                        if unfold {
3197                            let buffer_to_unfold =
3198                                folded_buffers[rng.random_range(0..folded_buffers.len())];
3199                            log::info!("Unfolding {buffer_to_unfold:?}");
3200                            unfolded_count += 1;
3201                            folded_count -= 1;
3202                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3203                        }
3204                        log::info!(
3205                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3206                        );
3207                    });
3208                }
3209                _ => {
3210                    buffer.update(cx, |buffer, cx| {
3211                        let mutation_count = rng.random_range(1..=5);
3212                        let subscription = buffer.subscribe();
3213                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3214                        buffer_snapshot = buffer.snapshot(cx);
3215                        buffer_edits.extend(subscription.consume());
3216                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3217                    });
3218                }
3219            }
3220
3221            let (inlay_snapshot, inlay_edits) =
3222                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3223            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3224            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3225            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3226                wrap_map.sync(tab_snapshot, tab_edits, cx)
3227            });
3228            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3229            assert_eq!(
3230                blocks_snapshot.transforms.summary().input_rows,
3231                wraps_snapshot.max_point().row() + RowDelta(1)
3232            );
3233            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3234            log::info!("blocks text: {:?}", blocks_snapshot.text());
3235
3236            let mut expected_blocks = Vec::new();
3237            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3238                Some((
3239                    block.placement.to_wrap_row(&wraps_snapshot)?,
3240                    Block::Custom(block.clone()),
3241                ))
3242            }));
3243
3244            // Note that this needs to be synced with the related section in BlockMap::sync
3245            expected_blocks.extend(block_map.header_and_footer_blocks(
3246                &buffer_snapshot,
3247                0..,
3248                &wraps_snapshot,
3249            ));
3250
3251            BlockMap::sort_blocks(&mut expected_blocks);
3252
3253            for (placement, block) in &expected_blocks {
3254                log::info!(
3255                    "Block {:?} placement: {:?} Height: {:?}",
3256                    block.id(),
3257                    placement,
3258                    block.height()
3259                );
3260            }
3261
3262            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3263
3264            let input_buffer_rows = buffer_snapshot
3265                .row_infos(MultiBufferRow(0))
3266                .map(|row| row.buffer_row)
3267                .collect::<Vec<_>>();
3268            let mut expected_buffer_rows = Vec::new();
3269            let mut expected_text = String::new();
3270            let mut expected_block_positions = Vec::new();
3271            let mut expected_replaced_buffer_rows = HashSet::default();
3272            let input_text = wraps_snapshot.text();
3273
3274            // Loop over the input lines, creating (N - 1) empty lines for
3275            // blocks of height N.
3276            //
3277            // It's important to note that output *starts* as one empty line,
3278            // so we special case row 0 to assume a leading '\n'.
3279            //
3280            // Linehood is the birthright of strings.
3281            let input_text_lines = input_text.split('\n').enumerate().peekable();
3282            let mut block_row = 0;
3283            for (wrap_row, input_line) in input_text_lines {
3284                let wrap_row = WrapRow(wrap_row as u32);
3285                let multibuffer_row = wraps_snapshot
3286                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3287                    .row;
3288
3289                // Create empty lines for the above block
3290                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3291                    if *placement.start() == wrap_row && block.place_above() {
3292                        let (_, block) = sorted_blocks_iter.next().unwrap();
3293                        expected_block_positions.push((block_row, block.id()));
3294                        if block.height() > 0 {
3295                            let text = "\n".repeat((block.height() - 1) as usize);
3296                            if block_row > 0 {
3297                                expected_text.push('\n')
3298                            }
3299                            expected_text.push_str(&text);
3300                            for _ in 0..block.height() {
3301                                expected_buffer_rows.push(None);
3302                            }
3303                            block_row += block.height();
3304                        }
3305                    } else {
3306                        break;
3307                    }
3308                }
3309
3310                // Skip lines within replace blocks, then create empty lines for the replace block's height
3311                let mut is_in_replace_block = false;
3312                if let Some((BlockPlacement::Replace(replace_range), block)) =
3313                    sorted_blocks_iter.peek()
3314                    && wrap_row >= *replace_range.start()
3315                {
3316                    is_in_replace_block = true;
3317
3318                    if wrap_row == *replace_range.start() {
3319                        if matches!(block, Block::FoldedBuffer { .. }) {
3320                            expected_buffer_rows.push(None);
3321                        } else {
3322                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
3323                        }
3324                    }
3325
3326                    if wrap_row == *replace_range.end() {
3327                        expected_block_positions.push((block_row, block.id()));
3328                        let text = "\n".repeat((block.height() - 1) as usize);
3329                        if block_row > 0 {
3330                            expected_text.push('\n');
3331                        }
3332                        expected_text.push_str(&text);
3333
3334                        for _ in 1..block.height() {
3335                            expected_buffer_rows.push(None);
3336                        }
3337                        block_row += block.height();
3338
3339                        sorted_blocks_iter.next();
3340                    }
3341                }
3342
3343                if is_in_replace_block {
3344                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3345                } else {
3346                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3347                    let soft_wrapped = wraps_snapshot
3348                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3349                        .column()
3350                        > 0;
3351                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3352                    if block_row > 0 {
3353                        expected_text.push('\n');
3354                    }
3355                    expected_text.push_str(input_line);
3356                    block_row += 1;
3357                }
3358
3359                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3360                    if *placement.end() == wrap_row && block.place_below() {
3361                        let (_, block) = sorted_blocks_iter.next().unwrap();
3362                        expected_block_positions.push((block_row, block.id()));
3363                        if block.height() > 0 {
3364                            let text = "\n".repeat((block.height() - 1) as usize);
3365                            if block_row > 0 {
3366                                expected_text.push('\n')
3367                            }
3368                            expected_text.push_str(&text);
3369                            for _ in 0..block.height() {
3370                                expected_buffer_rows.push(None);
3371                            }
3372                            block_row += block.height();
3373                        }
3374                    } else {
3375                        break;
3376                    }
3377                }
3378            }
3379
3380            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3381            let expected_row_count = expected_lines.len();
3382            log::info!("expected text: {expected_text:?}");
3383
3384            assert_eq!(
3385                blocks_snapshot.max_point().row + 1,
3386                expected_row_count as u32,
3387                "actual row count != expected row count",
3388            );
3389            assert_eq!(
3390                blocks_snapshot.text(),
3391                expected_text,
3392                "actual text != expected text",
3393            );
3394
3395            for start_row in 0..expected_row_count {
3396                let end_row = rng.random_range(start_row + 1..=expected_row_count);
3397                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3398                if end_row < expected_row_count {
3399                    expected_text.push('\n');
3400                }
3401
3402                let actual_text = blocks_snapshot
3403                    .chunks(
3404                        BlockRow(start_row as u32)..BlockRow(end_row as u32),
3405                        false,
3406                        false,
3407                        Highlights::default(),
3408                    )
3409                    .map(|chunk| chunk.text)
3410                    .collect::<String>();
3411                assert_eq!(
3412                    actual_text,
3413                    expected_text,
3414                    "incorrect text starting row row range {:?}",
3415                    start_row..end_row
3416                );
3417                assert_eq!(
3418                    blocks_snapshot
3419                        .row_infos(BlockRow(start_row as u32))
3420                        .map(|row_info| row_info.buffer_row)
3421                        .collect::<Vec<_>>(),
3422                    &expected_buffer_rows[start_row..],
3423                    "incorrect buffer_rows starting at row {:?}",
3424                    start_row
3425                );
3426            }
3427
3428            assert_eq!(
3429                blocks_snapshot
3430                    .blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
3431                    .map(|(row, block)| (row.0, block.id()))
3432                    .collect::<Vec<_>>(),
3433                expected_block_positions,
3434                "invalid blocks_in_range({:?})",
3435                0..expected_row_count
3436            );
3437
3438            for (_, expected_block) in
3439                blocks_snapshot.blocks_in_range(BlockRow(0)..BlockRow(expected_row_count as u32))
3440            {
3441                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3442                assert_eq!(
3443                    actual_block.map(|block| block.id()),
3444                    Some(expected_block.id())
3445                );
3446            }
3447
3448            for (block_row, block_id) in expected_block_positions {
3449                if let BlockId::Custom(block_id) = block_id {
3450                    assert_eq!(
3451                        blocks_snapshot.row_for_block(block_id),
3452                        Some(BlockRow(block_row))
3453                    );
3454                }
3455            }
3456
3457            let mut expected_longest_rows = Vec::new();
3458            let mut longest_line_len = -1_isize;
3459            for (row, line) in expected_lines.iter().enumerate() {
3460                let row = row as u32;
3461
3462                assert_eq!(
3463                    blocks_snapshot.line_len(BlockRow(row)),
3464                    line.len() as u32,
3465                    "invalid line len for row {}",
3466                    row
3467                );
3468
3469                let line_char_count = line.chars().count() as isize;
3470                match line_char_count.cmp(&longest_line_len) {
3471                    Ordering::Less => {}
3472                    Ordering::Equal => expected_longest_rows.push(row),
3473                    Ordering::Greater => {
3474                        longest_line_len = line_char_count;
3475                        expected_longest_rows.clear();
3476                        expected_longest_rows.push(row);
3477                    }
3478                }
3479            }
3480
3481            let longest_row = blocks_snapshot.longest_row();
3482            assert!(
3483                expected_longest_rows.contains(&longest_row.0),
3484                "incorrect longest row {}. expected {:?} with length {}",
3485                longest_row.0,
3486                expected_longest_rows,
3487                longest_line_len,
3488            );
3489
3490            for _ in 0..10 {
3491                let end_row = rng.random_range(1..=expected_lines.len());
3492                let start_row = rng.random_range(0..end_row);
3493
3494                let mut expected_longest_rows_in_range = vec![];
3495                let mut longest_line_len_in_range = 0;
3496
3497                let mut row = start_row as u32;
3498                for line in &expected_lines[start_row..end_row] {
3499                    let line_char_count = line.chars().count() as isize;
3500                    match line_char_count.cmp(&longest_line_len_in_range) {
3501                        Ordering::Less => {}
3502                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3503                        Ordering::Greater => {
3504                            longest_line_len_in_range = line_char_count;
3505                            expected_longest_rows_in_range.clear();
3506                            expected_longest_rows_in_range.push(row);
3507                        }
3508                    }
3509                    row += 1;
3510                }
3511
3512                let longest_row_in_range = blocks_snapshot
3513                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3514                assert!(
3515                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3516                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3517                    longest_row.0,
3518                    start_row..end_row,
3519                    expected_longest_rows_in_range,
3520                    longest_line_len_in_range,
3521                );
3522            }
3523
3524            // Ensure that conversion between block points and wrap points is stable.
3525            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row().0 {
3526                let wrap_point = WrapPoint::new(WrapRow(row), 0);
3527                let block_point = blocks_snapshot.to_block_point(wrap_point);
3528                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3529                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3530                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3531                assert_eq!(
3532                    blocks_snapshot.to_block_point(right_wrap_point),
3533                    block_point
3534                );
3535            }
3536
3537            let mut block_point = BlockPoint::new(BlockRow(0), 0);
3538            for c in expected_text.chars() {
3539                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3540                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3541                assert_eq!(
3542                    blocks_snapshot
3543                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3544                    left_point,
3545                    "block point: {:?}, wrap point: {:?}",
3546                    block_point,
3547                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3548                );
3549                assert_eq!(
3550                    left_buffer_point,
3551                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3552                    "{:?} is not valid in buffer coordinates",
3553                    left_point
3554                );
3555
3556                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3557                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3558                assert_eq!(
3559                    blocks_snapshot
3560                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3561                    right_point,
3562                    "block point: {:?}, wrap point: {:?}",
3563                    block_point,
3564                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3565                );
3566                assert_eq!(
3567                    right_buffer_point,
3568                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3569                    "{:?} is not valid in buffer coordinates",
3570                    right_point
3571                );
3572
3573                if c == '\n' {
3574                    block_point.0 += Point::new(1, 0);
3575                } else {
3576                    block_point.column += c.len_utf8() as u32;
3577                }
3578            }
3579
3580            for buffer_row in 0..=buffer_snapshot.max_point().row {
3581                let buffer_row = MultiBufferRow(buffer_row);
3582                assert_eq!(
3583                    blocks_snapshot.is_line_replaced(buffer_row),
3584                    expected_replaced_buffer_rows.contains(&buffer_row),
3585                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3586                );
3587            }
3588        }
3589    }
3590
3591    #[gpui::test]
3592    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
3593        cx.update(init_test);
3594
3595        let text = "abc\ndef\nghi\njkl\nmno";
3596        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3597        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3598        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3599        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3600        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3601        let (_wrap_map, wraps_snapshot) =
3602            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3603        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3604
3605        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3606        let _block_id = writer.insert(vec![BlockProperties {
3607            style: BlockStyle::Fixed,
3608            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3609            height: Some(1),
3610            render: Arc::new(|_| div().into_any()),
3611            priority: 0,
3612        }])[0];
3613
3614        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
3615        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3616
3617        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3618        writer.remove_intersecting_replace_blocks(
3619            [buffer_snapshot
3620                .anchor_after(Point::new(1, 0))
3621                .to_offset(&buffer_snapshot)
3622                ..buffer_snapshot
3623                    .anchor_after(Point::new(1, 0))
3624                    .to_offset(&buffer_snapshot)],
3625            false,
3626        );
3627        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
3628        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3629    }
3630
3631    #[gpui::test]
3632    fn test_folded_buffer_with_near_blocks(cx: &mut gpui::TestAppContext) {
3633        cx.update(init_test);
3634
3635        let text = "line 1\nline 2\nline 3";
3636        let buffer = cx.update(|cx| {
3637            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(2, 6)])], cx)
3638        });
3639        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3640        let buffer_ids = buffer_snapshot
3641            .excerpts()
3642            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3643            .dedup()
3644            .collect::<Vec<_>>();
3645        assert_eq!(buffer_ids.len(), 1);
3646        let buffer_id = buffer_ids[0];
3647
3648        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3649        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3650        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3651        let (_, wrap_snapshot) =
3652            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3653        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
3654
3655        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
3656        writer.insert(vec![BlockProperties {
3657            style: BlockStyle::Fixed,
3658            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(0, 0))),
3659            height: Some(1),
3660            render: Arc::new(|_| div().into_any()),
3661            priority: 0,
3662        }]);
3663
3664        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
3665        assert_eq!(blocks_snapshot.text(), "\nline 1\n\nline 2\nline 3");
3666
3667        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
3668        buffer.read_with(cx, |buffer, cx| {
3669            writer.fold_buffers([buffer_id], buffer, cx);
3670        });
3671
3672        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
3673        assert_eq!(blocks_snapshot.text(), "");
3674    }
3675
3676    #[gpui::test]
3677    fn test_folded_buffer_with_near_blocks_on_last_line(cx: &mut gpui::TestAppContext) {
3678        cx.update(init_test);
3679
3680        let text = "line 1\nline 2\nline 3\nline 4";
3681        let buffer = cx.update(|cx| {
3682            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(3, 6)])], cx)
3683        });
3684        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3685        let buffer_ids = buffer_snapshot
3686            .excerpts()
3687            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
3688            .dedup()
3689            .collect::<Vec<_>>();
3690        assert_eq!(buffer_ids.len(), 1);
3691        let buffer_id = buffer_ids[0];
3692
3693        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3694        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
3695        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3696        let (_, wrap_snapshot) =
3697            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3698        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 1, 1);
3699
3700        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
3701        writer.insert(vec![BlockProperties {
3702            style: BlockStyle::Fixed,
3703            placement: BlockPlacement::Near(buffer_snapshot.anchor_after(Point::new(3, 6))),
3704            height: Some(1),
3705            render: Arc::new(|_| div().into_any()),
3706            priority: 0,
3707        }]);
3708
3709        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
3710        assert_eq!(blocks_snapshot.text(), "\nline 1\nline 2\nline 3\nline 4\n");
3711
3712        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
3713        buffer.read_with(cx, |buffer, cx| {
3714            writer.fold_buffers([buffer_id], buffer, cx);
3715        });
3716
3717        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
3718        assert_eq!(blocks_snapshot.text(), "");
3719    }
3720
3721    fn init_test(cx: &mut gpui::App) {
3722        let settings = SettingsStore::test(cx);
3723        cx.set_global(settings);
3724        theme::init(theme::LoadThemes::JustBase, cx);
3725        assets::Assets.load_test_fonts(cx);
3726    }
3727
3728    impl Block {
3729        fn as_custom(&self) -> Option<&CustomBlock> {
3730            match self {
3731                Block::Custom(block) => Some(block),
3732                _ => None,
3733            }
3734        }
3735    }
3736
3737    impl BlockSnapshot {
3738        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3739            self.wrap_snapshot
3740                .to_point(self.to_wrap_point(point, bias), bias)
3741        }
3742    }
3743}