block_map.rs

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