block_map.rs

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