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<T>(
1197        &mut self,
1198        ranges: impl IntoIterator<Item = Range<T>>,
1199        inclusive: bool,
1200    ) where
1201        T: ToOffset,
1202    {
1203        let wrap_snapshot = self.0.wrap_snapshot.borrow();
1204        let mut blocks_to_remove = HashSet::default();
1205        for range in ranges {
1206            let range = range.start.to_offset(wrap_snapshot.buffer_snapshot())
1207                ..range.end.to_offset(wrap_snapshot.buffer_snapshot());
1208            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
1209                if matches!(block.placement, BlockPlacement::Replace(_)) {
1210                    blocks_to_remove.insert(block.id);
1211                }
1212            }
1213        }
1214        drop(wrap_snapshot);
1215        self.remove(blocks_to_remove);
1216    }
1217
1218    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId) {
1219        self.0.buffers_with_disabled_headers.insert(buffer_id);
1220    }
1221
1222    pub fn fold_buffers(
1223        &mut self,
1224        buffer_ids: impl IntoIterator<Item = BufferId>,
1225        multi_buffer: &MultiBuffer,
1226        cx: &App,
1227    ) {
1228        self.fold_or_unfold_buffers(true, buffer_ids, multi_buffer, cx);
1229    }
1230
1231    pub fn unfold_buffers(
1232        &mut self,
1233        buffer_ids: impl IntoIterator<Item = BufferId>,
1234        multi_buffer: &MultiBuffer,
1235        cx: &App,
1236    ) {
1237        self.fold_or_unfold_buffers(false, buffer_ids, multi_buffer, cx);
1238    }
1239
1240    fn fold_or_unfold_buffers(
1241        &mut self,
1242        fold: bool,
1243        buffer_ids: impl IntoIterator<Item = BufferId>,
1244        multi_buffer: &MultiBuffer,
1245        cx: &App,
1246    ) {
1247        let mut ranges = Vec::new();
1248        for buffer_id in buffer_ids {
1249            if fold {
1250                self.0.folded_buffers.insert(buffer_id);
1251            } else {
1252                self.0.folded_buffers.remove(&buffer_id);
1253            }
1254            ranges.extend(multi_buffer.excerpt_ranges_for_buffer(buffer_id, cx));
1255        }
1256        ranges.sort_unstable_by_key(|range| range.start);
1257
1258        let mut edits = Patch::default();
1259        let wrap_snapshot = self.0.wrap_snapshot.borrow().clone();
1260        for range in ranges {
1261            let last_edit_row = cmp::min(
1262                wrap_snapshot.make_wrap_point(range.end, Bias::Right).row() + 1,
1263                wrap_snapshot.max_point().row(),
1264            ) + 1;
1265            let range = wrap_snapshot.make_wrap_point(range.start, Bias::Left).row()..last_edit_row;
1266            edits.push(Edit {
1267                old: range.clone(),
1268                new: range,
1269            });
1270        }
1271
1272        self.0.sync(&wrap_snapshot, edits);
1273    }
1274
1275    fn blocks_intersecting_buffer_range(
1276        &self,
1277        range: Range<usize>,
1278        inclusive: bool,
1279    ) -> &[Arc<CustomBlock>] {
1280        if range.is_empty() && !inclusive {
1281            return &[];
1282        }
1283        let wrap_snapshot = self.0.wrap_snapshot.borrow();
1284        let buffer = wrap_snapshot.buffer_snapshot();
1285
1286        let start_block_ix = match self.0.custom_blocks.binary_search_by(|block| {
1287            let block_end = block.end().to_offset(buffer);
1288            block_end.cmp(&range.start).then(Ordering::Greater)
1289        }) {
1290            Ok(ix) | Err(ix) => ix,
1291        };
1292        let end_block_ix = match self.0.custom_blocks[start_block_ix..].binary_search_by(|block| {
1293            let block_start = block.start().to_offset(buffer);
1294            block_start.cmp(&range.end).then(if inclusive {
1295                Ordering::Less
1296            } else {
1297                Ordering::Greater
1298            })
1299        }) {
1300            Ok(ix) | Err(ix) => ix,
1301        };
1302
1303        &self.0.custom_blocks[start_block_ix..][..end_block_ix]
1304    }
1305}
1306
1307impl BlockSnapshot {
1308    #[cfg(test)]
1309    pub fn text(&self) -> String {
1310        self.chunks(
1311            0..self.transforms.summary().output_rows,
1312            false,
1313            false,
1314            Highlights::default(),
1315        )
1316        .map(|chunk| chunk.text)
1317        .collect()
1318    }
1319
1320    pub(crate) fn chunks<'a>(
1321        &'a self,
1322        rows: Range<u32>,
1323        language_aware: bool,
1324        masked: bool,
1325        highlights: Highlights<'a>,
1326    ) -> BlockChunks<'a> {
1327        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
1328
1329        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1330        cursor.seek(&BlockRow(rows.start), Bias::Right);
1331        let transform_output_start = cursor.start().0.0;
1332        let transform_input_start = cursor.start().1.0;
1333
1334        let mut input_start = transform_input_start;
1335        let mut input_end = transform_input_start;
1336        if let Some(transform) = cursor.item()
1337            && transform.block.is_none()
1338        {
1339            input_start += rows.start - transform_output_start;
1340            input_end += cmp::min(
1341                rows.end - transform_output_start,
1342                transform.summary.input_rows,
1343            );
1344        }
1345
1346        BlockChunks {
1347            input_chunks: self.wrap_snapshot.chunks(
1348                input_start..input_end,
1349                language_aware,
1350                highlights,
1351            ),
1352            input_chunk: Default::default(),
1353            transforms: cursor,
1354            output_row: rows.start,
1355            max_output_row,
1356            masked,
1357        }
1358    }
1359
1360    pub(super) fn row_infos(&self, start_row: BlockRow) -> BlockRows<'_> {
1361        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1362        cursor.seek(&start_row, Bias::Right);
1363        let Dimensions(output_start, input_start, _) = cursor.start();
1364        let overshoot = if cursor
1365            .item()
1366            .is_some_and(|transform| transform.block.is_none())
1367        {
1368            start_row.0 - output_start.0
1369        } else {
1370            0
1371        };
1372        let input_start_row = input_start.0 + overshoot;
1373        BlockRows {
1374            transforms: cursor,
1375            input_rows: self.wrap_snapshot.row_infos(input_start_row),
1376            output_row: start_row,
1377            started: false,
1378        }
1379    }
1380
1381    pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
1382        let mut cursor = self.transforms.cursor::<BlockRow>(());
1383        cursor.seek(&BlockRow(rows.start), Bias::Left);
1384        while cursor.start().0 < rows.start && cursor.end().0 <= rows.start {
1385            cursor.next();
1386        }
1387
1388        std::iter::from_fn(move || {
1389            while let Some(transform) = cursor.item() {
1390                let start_row = cursor.start().0;
1391                if start_row > rows.end
1392                    || (start_row == rows.end
1393                        && transform
1394                            .block
1395                            .as_ref()
1396                            .is_some_and(|block| block.height() > 0))
1397                {
1398                    break;
1399                }
1400                if let Some(block) = &transform.block {
1401                    cursor.next();
1402                    return Some((start_row, block));
1403                } else {
1404                    cursor.next();
1405                }
1406            }
1407            None
1408        })
1409    }
1410
1411    pub(crate) fn sticky_header_excerpt(&self, position: f64) -> Option<StickyHeaderExcerpt<'_>> {
1412        let top_row = position as u32;
1413        let mut cursor = self.transforms.cursor::<BlockRow>(());
1414        cursor.seek(&BlockRow(top_row), Bias::Right);
1415
1416        while let Some(transform) = cursor.item() {
1417            match &transform.block {
1418                Some(
1419                    Block::ExcerptBoundary { excerpt, .. } | Block::BufferHeader { excerpt, .. },
1420                ) => {
1421                    return Some(StickyHeaderExcerpt { excerpt });
1422                }
1423                Some(block) if block.is_buffer_header() => return None,
1424                _ => {
1425                    cursor.prev();
1426                    continue;
1427                }
1428            }
1429        }
1430
1431        None
1432    }
1433
1434    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1435        let buffer = self.wrap_snapshot.buffer_snapshot();
1436        let wrap_point = match block_id {
1437            BlockId::Custom(custom_block_id) => {
1438                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
1439                return Some(Block::Custom(custom_block.clone()));
1440            }
1441            BlockId::ExcerptBoundary(next_excerpt_id) => {
1442                let excerpt_range = buffer.range_for_excerpt(next_excerpt_id)?;
1443                self.wrap_snapshot
1444                    .make_wrap_point(excerpt_range.start, Bias::Left)
1445            }
1446            BlockId::FoldedBuffer(excerpt_id) => self
1447                .wrap_snapshot
1448                .make_wrap_point(buffer.range_for_excerpt(excerpt_id)?.start, Bias::Left),
1449        };
1450        let wrap_row = WrapRow(wrap_point.row());
1451
1452        let mut cursor = self.transforms.cursor::<WrapRow>(());
1453        cursor.seek(&wrap_row, Bias::Left);
1454
1455        while let Some(transform) = cursor.item() {
1456            if let Some(block) = transform.block.as_ref() {
1457                if block.id() == block_id {
1458                    return Some(block.clone());
1459                }
1460            } else if *cursor.start() > wrap_row {
1461                break;
1462            }
1463
1464            cursor.next();
1465        }
1466
1467        None
1468    }
1469
1470    pub fn max_point(&self) -> BlockPoint {
1471        let row = self.transforms.summary().output_rows.saturating_sub(1);
1472        BlockPoint::new(row, self.line_len(BlockRow(row)))
1473    }
1474
1475    pub fn longest_row(&self) -> u32 {
1476        self.transforms.summary().longest_row
1477    }
1478
1479    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
1480        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1481        cursor.seek(&range.start, Bias::Right);
1482
1483        let mut longest_row = range.start;
1484        let mut longest_row_chars = 0;
1485        if let Some(transform) = cursor.item() {
1486            if transform.block.is_none() {
1487                let Dimensions(output_start, input_start, _) = cursor.start();
1488                let overshoot = range.start.0 - output_start.0;
1489                let wrap_start_row = input_start.0 + overshoot;
1490                let wrap_end_row = cmp::min(
1491                    input_start.0 + (range.end.0 - output_start.0),
1492                    cursor.end().1.0,
1493                );
1494                let summary = self
1495                    .wrap_snapshot
1496                    .text_summary_for_range(wrap_start_row..wrap_end_row);
1497                longest_row = BlockRow(range.start.0 + summary.longest_row);
1498                longest_row_chars = summary.longest_row_chars;
1499            }
1500            cursor.next();
1501        }
1502
1503        let cursor_start_row = cursor.start().0;
1504        if range.end > cursor_start_row {
1505            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right);
1506            if summary.longest_row_chars > longest_row_chars {
1507                longest_row = BlockRow(cursor_start_row.0 + summary.longest_row);
1508                longest_row_chars = summary.longest_row_chars;
1509            }
1510
1511            if let Some(transform) = cursor.item()
1512                && transform.block.is_none()
1513            {
1514                let Dimensions(output_start, input_start, _) = cursor.start();
1515                let overshoot = range.end.0 - output_start.0;
1516                let wrap_start_row = input_start.0;
1517                let wrap_end_row = input_start.0 + overshoot;
1518                let summary = self
1519                    .wrap_snapshot
1520                    .text_summary_for_range(wrap_start_row..wrap_end_row);
1521                if summary.longest_row_chars > longest_row_chars {
1522                    longest_row = BlockRow(output_start.0 + summary.longest_row);
1523                }
1524            }
1525        }
1526
1527        longest_row
1528    }
1529
1530    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1531        let (start, _, item) =
1532            self.transforms
1533                .find::<Dimensions<BlockRow, WrapRow>, _>((), &row, Bias::Right);
1534        if let Some(transform) = item {
1535            let Dimensions(output_start, input_start, _) = start;
1536            let overshoot = row.0 - output_start.0;
1537            if transform.block.is_some() {
1538                0
1539            } else {
1540                self.wrap_snapshot.line_len(input_start.0 + overshoot)
1541            }
1542        } else if row.0 == 0 {
1543            0
1544        } else {
1545            panic!("row out of range");
1546        }
1547    }
1548
1549    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1550        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
1551        item.is_some_and(|t| t.block.is_some())
1552    }
1553
1554    pub(super) fn is_folded_buffer_header(&self, row: BlockRow) -> bool {
1555        let (_, _, item) = self.transforms.find::<BlockRow, _>((), &row, Bias::Right);
1556        let Some(transform) = item else {
1557            return false;
1558        };
1559        matches!(transform.block, Some(Block::FoldedBuffer { .. }))
1560    }
1561
1562    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
1563        let wrap_point = self
1564            .wrap_snapshot
1565            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
1566        let (_, _, item) =
1567            self.transforms
1568                .find::<WrapRow, _>((), &WrapRow(wrap_point.row()), Bias::Right);
1569        item.is_some_and(|transform| {
1570            transform
1571                .block
1572                .as_ref()
1573                .is_some_and(|block| block.is_replacement())
1574        })
1575    }
1576
1577    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1578        let mut cursor = self.transforms.cursor::<Dimensions<BlockRow, WrapRow>>(());
1579        cursor.seek(&BlockRow(point.row), Bias::Right);
1580
1581        let max_input_row = WrapRow(self.transforms.summary().input_rows);
1582        let mut search_left =
1583            (bias == Bias::Left && cursor.start().1.0 > 0) || cursor.end().1 == max_input_row;
1584        let mut reversed = false;
1585
1586        loop {
1587            if let Some(transform) = cursor.item() {
1588                let Dimensions(output_start_row, input_start_row, _) = cursor.start();
1589                let Dimensions(output_end_row, input_end_row, _) = cursor.end();
1590                let output_start = Point::new(output_start_row.0, 0);
1591                let input_start = Point::new(input_start_row.0, 0);
1592                let input_end = Point::new(input_end_row.0, 0);
1593
1594                match transform.block.as_ref() {
1595                    Some(block) => {
1596                        if block.is_replacement()
1597                            && (((bias == Bias::Left || search_left) && output_start <= point.0)
1598                                || (!search_left && output_start >= point.0))
1599                        {
1600                            return BlockPoint(output_start);
1601                        }
1602                    }
1603                    None => {
1604                        let input_point = if point.row >= output_end_row.0 {
1605                            let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1606                            self.wrap_snapshot
1607                                .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1608                        } else {
1609                            let output_overshoot = point.0.saturating_sub(output_start);
1610                            self.wrap_snapshot
1611                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
1612                        };
1613
1614                        if (input_start..input_end).contains(&input_point.0) {
1615                            let input_overshoot = input_point.0.saturating_sub(input_start);
1616                            return BlockPoint(output_start + input_overshoot);
1617                        }
1618                    }
1619                }
1620
1621                if search_left {
1622                    cursor.prev();
1623                } else {
1624                    cursor.next();
1625                }
1626            } else if reversed {
1627                return self.max_point();
1628            } else {
1629                reversed = true;
1630                search_left = !search_left;
1631                cursor.seek(&BlockRow(point.row), Bias::Right);
1632            }
1633        }
1634    }
1635
1636    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1637        let (start, _, item) = self.transforms.find::<Dimensions<WrapRow, BlockRow>, _>(
1638            (),
1639            &WrapRow(wrap_point.row()),
1640            Bias::Right,
1641        );
1642        if let Some(transform) = item {
1643            if transform.block.is_some() {
1644                BlockPoint::new(start.1.0, 0)
1645            } else {
1646                let Dimensions(input_start_row, output_start_row, _) = start;
1647                let input_start = Point::new(input_start_row.0, 0);
1648                let output_start = Point::new(output_start_row.0, 0);
1649                let input_overshoot = wrap_point.0 - input_start;
1650                BlockPoint(output_start + input_overshoot)
1651            }
1652        } else {
1653            self.max_point()
1654        }
1655    }
1656
1657    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
1658        let (start, end, item) = self.transforms.find::<Dimensions<BlockRow, WrapRow>, _>(
1659            (),
1660            &BlockRow(block_point.row),
1661            Bias::Right,
1662        );
1663        if let Some(transform) = item {
1664            match transform.block.as_ref() {
1665                Some(block) => {
1666                    if block.place_below() {
1667                        let wrap_row = start.1.0 - 1;
1668                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1669                    } else if block.place_above() {
1670                        WrapPoint::new(start.1.0, 0)
1671                    } else if bias == Bias::Left {
1672                        WrapPoint::new(start.1.0, 0)
1673                    } else {
1674                        let wrap_row = end.1.0 - 1;
1675                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1676                    }
1677                }
1678                None => {
1679                    let overshoot = block_point.row - start.0.0;
1680                    let wrap_row = start.1.0 + overshoot;
1681                    WrapPoint::new(wrap_row, block_point.column)
1682                }
1683            }
1684        } else {
1685            self.wrap_snapshot.max_point()
1686        }
1687    }
1688}
1689
1690impl BlockChunks<'_> {
1691    /// Go to the next transform
1692    fn advance(&mut self) {
1693        self.input_chunk = Chunk::default();
1694        self.transforms.next();
1695        while let Some(transform) = self.transforms.item() {
1696            if transform
1697                .block
1698                .as_ref()
1699                .is_some_and(|block| block.height() == 0)
1700            {
1701                self.transforms.next();
1702            } else {
1703                break;
1704            }
1705        }
1706
1707        if self
1708            .transforms
1709            .item()
1710            .is_some_and(|transform| transform.block.is_none())
1711        {
1712            let start_input_row = self.transforms.start().1.0;
1713            let start_output_row = self.transforms.start().0.0;
1714            if start_output_row < self.max_output_row {
1715                let end_input_row = cmp::min(
1716                    self.transforms.end().1.0,
1717                    start_input_row + (self.max_output_row - start_output_row),
1718                );
1719                self.input_chunks.seek(start_input_row..end_input_row);
1720            }
1721        }
1722    }
1723}
1724
1725pub struct StickyHeaderExcerpt<'a> {
1726    pub excerpt: &'a ExcerptInfo,
1727}
1728
1729impl<'a> Iterator for BlockChunks<'a> {
1730    type Item = Chunk<'a>;
1731
1732    fn next(&mut self) -> Option<Self::Item> {
1733        if self.output_row >= self.max_output_row {
1734            return None;
1735        }
1736
1737        let transform = self.transforms.item()?;
1738        if transform.block.is_some() {
1739            let block_start = self.transforms.start().0.0;
1740            let mut block_end = self.transforms.end().0.0;
1741            self.advance();
1742            if self.transforms.item().is_none() {
1743                block_end -= 1;
1744            }
1745
1746            let start_in_block = self.output_row - block_start;
1747            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1748            // todo: We need to split the chunk here?
1749            let line_count = cmp::min(end_in_block - start_in_block, u128::BITS);
1750            self.output_row += line_count;
1751
1752            return Some(Chunk {
1753                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1754                chars: 1u128.unbounded_shl(line_count) - 1,
1755                ..Default::default()
1756            });
1757        }
1758
1759        if self.input_chunk.text.is_empty() {
1760            if let Some(input_chunk) = self.input_chunks.next() {
1761                self.input_chunk = input_chunk;
1762            } else {
1763                if self.output_row < self.max_output_row {
1764                    self.output_row += 1;
1765                    self.advance();
1766                    if self.transforms.item().is_some() {
1767                        return Some(Chunk {
1768                            text: "\n",
1769                            chars: 1,
1770                            ..Default::default()
1771                        });
1772                    }
1773                }
1774                return None;
1775            }
1776        }
1777
1778        let transform_end = self.transforms.end().0.0;
1779        let (prefix_rows, prefix_bytes) =
1780            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1781        self.output_row += prefix_rows;
1782
1783        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1784        self.input_chunk.text = suffix;
1785        self.input_chunk.tabs >>= prefix_bytes.saturating_sub(1);
1786        self.input_chunk.chars >>= prefix_bytes.saturating_sub(1);
1787
1788        let mut tabs = self.input_chunk.tabs;
1789        let mut chars = self.input_chunk.chars;
1790
1791        if self.masked {
1792            // Not great for multibyte text because to keep cursor math correct we
1793            // need to have the same number of chars in the input as output.
1794            let chars_count = prefix.chars().count();
1795            let bullet_len = chars_count;
1796            prefix = unsafe { std::str::from_utf8_unchecked(&BULLETS[..bullet_len]) };
1797            chars = 1u128.unbounded_shl(bullet_len as u32).wrapping_sub(1);
1798            tabs = 0;
1799        }
1800
1801        let chunk = Chunk {
1802            text: prefix,
1803            tabs,
1804            chars,
1805            ..self.input_chunk.clone()
1806        };
1807
1808        if self.output_row == transform_end {
1809            self.advance();
1810        }
1811
1812        Some(chunk)
1813    }
1814}
1815
1816impl Iterator for BlockRows<'_> {
1817    type Item = RowInfo;
1818
1819    fn next(&mut self) -> Option<Self::Item> {
1820        if self.started {
1821            self.output_row.0 += 1;
1822        } else {
1823            self.started = true;
1824        }
1825
1826        if self.output_row.0 >= self.transforms.end().0.0 {
1827            self.transforms.next();
1828            while let Some(transform) = self.transforms.item() {
1829                if transform
1830                    .block
1831                    .as_ref()
1832                    .is_some_and(|block| block.height() == 0)
1833                {
1834                    self.transforms.next();
1835                } else {
1836                    break;
1837                }
1838            }
1839
1840            let transform = self.transforms.item()?;
1841            if transform
1842                .block
1843                .as_ref()
1844                .is_none_or(|block| block.is_replacement())
1845            {
1846                self.input_rows.seek(self.transforms.start().1.0);
1847            }
1848        }
1849
1850        let transform = self.transforms.item()?;
1851        if let Some(block) = transform.block.as_ref() {
1852            if block.is_replacement() && self.transforms.start().0 == self.output_row {
1853                if matches!(block, Block::FoldedBuffer { .. }) {
1854                    Some(RowInfo::default())
1855                } else {
1856                    Some(self.input_rows.next().unwrap())
1857                }
1858            } else {
1859                Some(RowInfo::default())
1860            }
1861        } else {
1862            Some(self.input_rows.next().unwrap())
1863        }
1864    }
1865}
1866
1867impl sum_tree::Item for Transform {
1868    type Summary = TransformSummary;
1869
1870    fn summary(&self, _cx: ()) -> Self::Summary {
1871        self.summary.clone()
1872    }
1873}
1874
1875impl sum_tree::ContextLessSummary for TransformSummary {
1876    fn zero() -> Self {
1877        Default::default()
1878    }
1879
1880    fn add_summary(&mut self, summary: &Self) {
1881        if summary.longest_row_chars > self.longest_row_chars {
1882            self.longest_row = self.output_rows + summary.longest_row;
1883            self.longest_row_chars = summary.longest_row_chars;
1884        }
1885        self.input_rows += summary.input_rows;
1886        self.output_rows += summary.output_rows;
1887    }
1888}
1889
1890impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1891    fn zero(_cx: ()) -> Self {
1892        Default::default()
1893    }
1894
1895    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1896        self.0 += summary.input_rows;
1897    }
1898}
1899
1900impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1901    fn zero(_cx: ()) -> Self {
1902        Default::default()
1903    }
1904
1905    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1906        self.0 += summary.output_rows;
1907    }
1908}
1909
1910impl Deref for BlockContext<'_, '_> {
1911    type Target = App;
1912
1913    fn deref(&self) -> &Self::Target {
1914        self.app
1915    }
1916}
1917
1918impl DerefMut for BlockContext<'_, '_> {
1919    fn deref_mut(&mut self) -> &mut Self::Target {
1920        self.app
1921    }
1922}
1923
1924impl CustomBlock {
1925    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1926        self.render.lock()(cx)
1927    }
1928
1929    pub fn start(&self) -> Anchor {
1930        *self.placement.start()
1931    }
1932
1933    pub fn end(&self) -> Anchor {
1934        *self.placement.end()
1935    }
1936
1937    pub fn style(&self) -> BlockStyle {
1938        self.style
1939    }
1940}
1941
1942impl Debug for CustomBlock {
1943    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1944        f.debug_struct("Block")
1945            .field("id", &self.id)
1946            .field("placement", &self.placement)
1947            .field("height", &self.height)
1948            .field("style", &self.style)
1949            .field("priority", &self.priority)
1950            .finish_non_exhaustive()
1951    }
1952}
1953
1954// Count the number of bytes prior to a target point. If the string doesn't contain the target
1955// point, return its total extent. Otherwise return the target point itself.
1956fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1957    let mut row = 0;
1958    let mut offset = 0;
1959    for (ix, line) in s.split('\n').enumerate() {
1960        if ix > 0 {
1961            row += 1;
1962            offset += 1;
1963        }
1964        if row >= target {
1965            break;
1966        }
1967        offset += line.len();
1968    }
1969    (row, offset)
1970}
1971
1972#[cfg(test)]
1973mod tests {
1974    use super::*;
1975    use crate::{
1976        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap},
1977        test::test_font,
1978    };
1979    use gpui::{App, AppContext as _, Element, div, font, px};
1980    use itertools::Itertools;
1981    use language::{Buffer, Capability};
1982    use multi_buffer::{ExcerptRange, MultiBuffer};
1983    use rand::prelude::*;
1984    use settings::SettingsStore;
1985    use std::env;
1986    use util::RandomCharIter;
1987
1988    #[gpui::test]
1989    fn test_offset_for_row() {
1990        assert_eq!(offset_for_row("", 0), (0, 0));
1991        assert_eq!(offset_for_row("", 1), (0, 0));
1992        assert_eq!(offset_for_row("abcd", 0), (0, 0));
1993        assert_eq!(offset_for_row("abcd", 1), (0, 4));
1994        assert_eq!(offset_for_row("\n", 0), (0, 0));
1995        assert_eq!(offset_for_row("\n", 1), (1, 1));
1996        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1997        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1998        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1999        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
2000    }
2001
2002    #[gpui::test]
2003    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2004        cx.update(init_test);
2005
2006        let text = "aaa\nbbb\nccc\nddd";
2007
2008        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2009        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2010        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2011        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2012        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2013        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2014        let (wrap_map, wraps_snapshot) =
2015            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2016        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2017
2018        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2019        let block_ids = writer.insert(vec![
2020            BlockProperties {
2021                style: BlockStyle::Fixed,
2022                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2023                height: Some(1),
2024                render: Arc::new(|_| div().into_any()),
2025                priority: 0,
2026            },
2027            BlockProperties {
2028                style: BlockStyle::Fixed,
2029                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2030                height: Some(2),
2031                render: Arc::new(|_| div().into_any()),
2032                priority: 0,
2033            },
2034            BlockProperties {
2035                style: BlockStyle::Fixed,
2036                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2037                height: Some(3),
2038                render: Arc::new(|_| div().into_any()),
2039                priority: 0,
2040            },
2041        ]);
2042
2043        let snapshot = block_map.read(wraps_snapshot, Default::default());
2044        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2045
2046        let blocks = snapshot
2047            .blocks_in_range(0..8)
2048            .map(|(start_row, block)| {
2049                let block = block.as_custom().unwrap();
2050                (start_row..start_row + block.height.unwrap(), block.id)
2051            })
2052            .collect::<Vec<_>>();
2053
2054        // When multiple blocks are on the same line, the newer blocks appear first.
2055        assert_eq!(
2056            blocks,
2057            &[
2058                (1..2, block_ids[0]),
2059                (2..4, block_ids[1]),
2060                (7..10, block_ids[2]),
2061            ]
2062        );
2063
2064        assert_eq!(
2065            snapshot.to_block_point(WrapPoint::new(0, 3)),
2066            BlockPoint::new(0, 3)
2067        );
2068        assert_eq!(
2069            snapshot.to_block_point(WrapPoint::new(1, 0)),
2070            BlockPoint::new(4, 0)
2071        );
2072        assert_eq!(
2073            snapshot.to_block_point(WrapPoint::new(3, 3)),
2074            BlockPoint::new(6, 3)
2075        );
2076
2077        assert_eq!(
2078            snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
2079            WrapPoint::new(0, 3)
2080        );
2081        assert_eq!(
2082            snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
2083            WrapPoint::new(1, 0)
2084        );
2085        assert_eq!(
2086            snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
2087            WrapPoint::new(1, 0)
2088        );
2089        assert_eq!(
2090            snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
2091            WrapPoint::new(3, 3)
2092        );
2093
2094        assert_eq!(
2095            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
2096            BlockPoint::new(0, 3)
2097        );
2098        assert_eq!(
2099            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
2100            BlockPoint::new(4, 0)
2101        );
2102        assert_eq!(
2103            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
2104            BlockPoint::new(0, 3)
2105        );
2106        assert_eq!(
2107            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
2108            BlockPoint::new(4, 0)
2109        );
2110        assert_eq!(
2111            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
2112            BlockPoint::new(4, 0)
2113        );
2114        assert_eq!(
2115            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
2116            BlockPoint::new(4, 0)
2117        );
2118        assert_eq!(
2119            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
2120            BlockPoint::new(6, 3)
2121        );
2122        assert_eq!(
2123            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
2124            BlockPoint::new(6, 3)
2125        );
2126        assert_eq!(
2127            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
2128            BlockPoint::new(6, 3)
2129        );
2130        assert_eq!(
2131            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
2132            BlockPoint::new(6, 3)
2133        );
2134
2135        assert_eq!(
2136            snapshot
2137                .row_infos(BlockRow(0))
2138                .map(|row_info| row_info.buffer_row)
2139                .collect::<Vec<_>>(),
2140            &[
2141                Some(0),
2142                None,
2143                None,
2144                None,
2145                Some(1),
2146                Some(2),
2147                Some(3),
2148                None,
2149                None,
2150                None
2151            ]
2152        );
2153
2154        // Insert a line break, separating two block decorations into separate lines.
2155        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2156            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2157            buffer.snapshot(cx)
2158        });
2159
2160        let (inlay_snapshot, inlay_edits) =
2161            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2162        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2163        let (tab_snapshot, tab_edits) =
2164            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2165        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2166            wrap_map.sync(tab_snapshot, tab_edits, cx)
2167        });
2168        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
2169        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2170    }
2171
2172    #[gpui::test]
2173    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2174        init_test(cx);
2175
2176        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2177        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2178        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2179
2180        let mut excerpt_ids = Vec::new();
2181        let multi_buffer = cx.new(|cx| {
2182            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2183            excerpt_ids.extend(multi_buffer.push_excerpts(
2184                buffer1.clone(),
2185                [ExcerptRange::new(0..buffer1.read(cx).len())],
2186                cx,
2187            ));
2188            excerpt_ids.extend(multi_buffer.push_excerpts(
2189                buffer2.clone(),
2190                [ExcerptRange::new(0..buffer2.read(cx).len())],
2191                cx,
2192            ));
2193            excerpt_ids.extend(multi_buffer.push_excerpts(
2194                buffer3.clone(),
2195                [ExcerptRange::new(0..buffer3.read(cx).len())],
2196                cx,
2197            ));
2198
2199            multi_buffer
2200        });
2201
2202        let font = test_font();
2203        let font_size = px(14.);
2204        let font_id = cx.text_system().resolve_font(&font);
2205        let mut wrap_width = px(0.);
2206        for c in "Buff".chars() {
2207            wrap_width += cx
2208                .text_system()
2209                .advance(font_id, font_size, c)
2210                .unwrap()
2211                .width;
2212        }
2213
2214        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2215        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
2216        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2217        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2218        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2219
2220        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2221        let snapshot = block_map.read(wraps_snapshot, Default::default());
2222
2223        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2224        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
2225
2226        let blocks: Vec<_> = snapshot
2227            .blocks_in_range(0..u32::MAX)
2228            .map(|(row, block)| (row..row + block.height(), block.id()))
2229            .collect();
2230        assert_eq!(
2231            blocks,
2232            vec![
2233                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
2234                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
2235                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
2236            ]
2237        );
2238    }
2239
2240    #[gpui::test]
2241    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2242        cx.update(init_test);
2243
2244        let text = "aaa\nbbb\nccc\nddd";
2245
2246        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2247        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2248        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2249        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2250        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2251        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2252        let (_wrap_map, wraps_snapshot) =
2253            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2254        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2255
2256        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2257        let block_ids = writer.insert(vec![
2258            BlockProperties {
2259                style: BlockStyle::Fixed,
2260                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2261                height: Some(1),
2262                render: Arc::new(|_| div().into_any()),
2263                priority: 0,
2264            },
2265            BlockProperties {
2266                style: BlockStyle::Fixed,
2267                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2268                height: Some(2),
2269                render: Arc::new(|_| div().into_any()),
2270                priority: 0,
2271            },
2272            BlockProperties {
2273                style: BlockStyle::Fixed,
2274                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2275                height: Some(3),
2276                render: Arc::new(|_| div().into_any()),
2277                priority: 0,
2278            },
2279        ]);
2280
2281        {
2282            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2283            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2284
2285            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2286
2287            let mut new_heights = HashMap::default();
2288            new_heights.insert(block_ids[0], 2);
2289            block_map_writer.resize(new_heights);
2290            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2291            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2292        }
2293
2294        {
2295            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2296
2297            let mut new_heights = HashMap::default();
2298            new_heights.insert(block_ids[0], 1);
2299            block_map_writer.resize(new_heights);
2300
2301            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2302            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2303        }
2304
2305        {
2306            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2307
2308            let mut new_heights = HashMap::default();
2309            new_heights.insert(block_ids[0], 0);
2310            block_map_writer.resize(new_heights);
2311
2312            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2313            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2314        }
2315
2316        {
2317            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2318
2319            let mut new_heights = HashMap::default();
2320            new_heights.insert(block_ids[0], 3);
2321            block_map_writer.resize(new_heights);
2322
2323            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2324            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2325        }
2326
2327        {
2328            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2329
2330            let mut new_heights = HashMap::default();
2331            new_heights.insert(block_ids[0], 3);
2332            block_map_writer.resize(new_heights);
2333
2334            let snapshot = block_map.read(wraps_snapshot, Default::default());
2335            // Same height as before, should remain the same
2336            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2337        }
2338    }
2339
2340    #[gpui::test]
2341    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2342        cx.update(init_test);
2343
2344        let text = "one two three\nfour five six\nseven eight";
2345
2346        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2347        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2348        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2349        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2350        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2351        let (_, wraps_snapshot) = cx.update(|cx| {
2352            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
2353        });
2354        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2355
2356        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2357        writer.insert(vec![
2358            BlockProperties {
2359                style: BlockStyle::Fixed,
2360                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2361                render: Arc::new(|_| div().into_any()),
2362                height: Some(1),
2363                priority: 0,
2364            },
2365            BlockProperties {
2366                style: BlockStyle::Fixed,
2367                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2368                render: Arc::new(|_| div().into_any()),
2369                height: Some(1),
2370                priority: 0,
2371            },
2372        ]);
2373
2374        // Blocks with an 'above' disposition go above their corresponding buffer line.
2375        // Blocks with a 'below' disposition go below their corresponding buffer line.
2376        let snapshot = block_map.read(wraps_snapshot, Default::default());
2377        assert_eq!(
2378            snapshot.text(),
2379            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2380        );
2381    }
2382
2383    #[gpui::test]
2384    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2385        cx.update(init_test);
2386
2387        let text = "line1\nline2\nline3\nline4\nline5";
2388
2389        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2390        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2391        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2392        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2393        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2394        let tab_size = 1.try_into().unwrap();
2395        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2396        let (wrap_map, wraps_snapshot) =
2397            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2398        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2399
2400        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2401        let replace_block_id = writer.insert(vec![BlockProperties {
2402            style: BlockStyle::Fixed,
2403            placement: BlockPlacement::Replace(
2404                buffer_snapshot.anchor_after(Point::new(1, 3))
2405                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
2406            ),
2407            height: Some(4),
2408            render: Arc::new(|_| div().into_any()),
2409            priority: 0,
2410        }])[0];
2411
2412        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2413        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2414
2415        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2416            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2417            buffer.snapshot(cx)
2418        });
2419        let (inlay_snapshot, inlay_edits) =
2420            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
2421        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2422        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2423        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2424            wrap_map.sync(tab_snapshot, tab_edits, cx)
2425        });
2426        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits);
2427        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2428
2429        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2430            buffer.edit(
2431                [(
2432                    Point::new(1, 5)..Point::new(1, 5),
2433                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2434                )],
2435                None,
2436                cx,
2437            );
2438            buffer.snapshot(cx)
2439        });
2440        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2441            buffer_snapshot.clone(),
2442            buffer_subscription.consume().into_inner(),
2443        );
2444        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2445        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2446        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2447            wrap_map.sync(tab_snapshot, tab_edits, cx)
2448        });
2449        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2450        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2451
2452        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2453        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2454        writer.insert(vec![
2455            BlockProperties {
2456                style: BlockStyle::Fixed,
2457                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2458                height: Some(1),
2459                render: Arc::new(|_| div().into_any()),
2460                priority: 0,
2461            },
2462            BlockProperties {
2463                style: BlockStyle::Fixed,
2464                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2465                height: Some(1),
2466                render: Arc::new(|_| div().into_any()),
2467                priority: 0,
2468            },
2469            BlockProperties {
2470                style: BlockStyle::Fixed,
2471                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2472                height: Some(1),
2473                render: Arc::new(|_| div().into_any()),
2474                priority: 0,
2475            },
2476        ]);
2477        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2478        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2479
2480        // Ensure blocks inserted *inside* replaced region are hidden.
2481        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2482        writer.insert(vec![
2483            BlockProperties {
2484                style: BlockStyle::Fixed,
2485                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2486                height: Some(1),
2487                render: Arc::new(|_| div().into_any()),
2488                priority: 0,
2489            },
2490            BlockProperties {
2491                style: BlockStyle::Fixed,
2492                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2493                height: Some(1),
2494                render: Arc::new(|_| div().into_any()),
2495                priority: 0,
2496            },
2497            BlockProperties {
2498                style: BlockStyle::Fixed,
2499                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2500                height: Some(1),
2501                render: Arc::new(|_| div().into_any()),
2502                priority: 0,
2503            },
2504        ]);
2505        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2506        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2507
2508        // Removing the replace block shows all the hidden blocks again.
2509        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2510        writer.remove(HashSet::from_iter([replace_block_id]));
2511        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2512        assert_eq!(
2513            blocks_snapshot.text(),
2514            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2515        );
2516    }
2517
2518    #[gpui::test]
2519    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2520        cx.update(init_test);
2521
2522        let text = "111\n222\n333\n444\n555\n666";
2523
2524        let buffer = cx.update(|cx| {
2525            MultiBuffer::build_multi(
2526                [
2527                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2528                    (
2529                        text,
2530                        vec![
2531                            Point::new(1, 0)..Point::new(1, 3),
2532                            Point::new(2, 0)..Point::new(2, 3),
2533                            Point::new(3, 0)..Point::new(3, 3),
2534                        ],
2535                    ),
2536                    (
2537                        text,
2538                        vec![
2539                            Point::new(4, 0)..Point::new(4, 3),
2540                            Point::new(5, 0)..Point::new(5, 3),
2541                        ],
2542                    ),
2543                ],
2544                cx,
2545            )
2546        });
2547        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2548        let buffer_ids = buffer_snapshot
2549            .excerpts()
2550            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2551            .dedup()
2552            .collect::<Vec<_>>();
2553        assert_eq!(buffer_ids.len(), 3);
2554        let buffer_id_1 = buffer_ids[0];
2555        let buffer_id_2 = buffer_ids[1];
2556        let buffer_id_3 = buffer_ids[2];
2557
2558        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2559        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2560        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2561        let (_, wrap_snapshot) =
2562            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2563        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2564        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2565
2566        assert_eq!(
2567            blocks_snapshot.text(),
2568            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
2569        );
2570        assert_eq!(
2571            blocks_snapshot
2572                .row_infos(BlockRow(0))
2573                .map(|i| i.buffer_row)
2574                .collect::<Vec<_>>(),
2575            vec![
2576                None,
2577                None,
2578                Some(0),
2579                None,
2580                None,
2581                Some(1),
2582                None,
2583                Some(2),
2584                None,
2585                Some(3),
2586                None,
2587                None,
2588                Some(4),
2589                None,
2590                Some(5),
2591            ]
2592        );
2593
2594        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2595        let excerpt_blocks_2 = writer.insert(vec![
2596            BlockProperties {
2597                style: BlockStyle::Fixed,
2598                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2599                height: Some(1),
2600                render: Arc::new(|_| div().into_any()),
2601                priority: 0,
2602            },
2603            BlockProperties {
2604                style: BlockStyle::Fixed,
2605                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2606                height: Some(1),
2607                render: Arc::new(|_| div().into_any()),
2608                priority: 0,
2609            },
2610            BlockProperties {
2611                style: BlockStyle::Fixed,
2612                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2613                height: Some(1),
2614                render: Arc::new(|_| div().into_any()),
2615                priority: 0,
2616            },
2617        ]);
2618        let excerpt_blocks_3 = writer.insert(vec![
2619            BlockProperties {
2620                style: BlockStyle::Fixed,
2621                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2622                height: Some(1),
2623                render: Arc::new(|_| div().into_any()),
2624                priority: 0,
2625            },
2626            BlockProperties {
2627                style: BlockStyle::Fixed,
2628                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2629                height: Some(1),
2630                render: Arc::new(|_| div().into_any()),
2631                priority: 0,
2632            },
2633        ]);
2634
2635        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2636        assert_eq!(
2637            blocks_snapshot.text(),
2638            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2639        );
2640        assert_eq!(
2641            blocks_snapshot
2642                .row_infos(BlockRow(0))
2643                .map(|i| i.buffer_row)
2644                .collect::<Vec<_>>(),
2645            vec![
2646                None,
2647                None,
2648                Some(0),
2649                None,
2650                None,
2651                None,
2652                Some(1),
2653                None,
2654                None,
2655                Some(2),
2656                None,
2657                Some(3),
2658                None,
2659                None,
2660                None,
2661                None,
2662                Some(4),
2663                None,
2664                Some(5),
2665                None,
2666            ]
2667        );
2668
2669        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2670        buffer.read_with(cx, |buffer, cx| {
2671            writer.fold_buffers([buffer_id_1], buffer, cx);
2672        });
2673        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2674            style: BlockStyle::Fixed,
2675            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2676            height: Some(1),
2677            render: Arc::new(|_| div().into_any()),
2678            priority: 0,
2679        }]);
2680        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2681        let blocks = blocks_snapshot
2682            .blocks_in_range(0..u32::MAX)
2683            .collect::<Vec<_>>();
2684        for (_, block) in &blocks {
2685            if let BlockId::Custom(custom_block_id) = block.id() {
2686                assert!(
2687                    !excerpt_blocks_1.contains(&custom_block_id),
2688                    "Should have no blocks from the folded buffer"
2689                );
2690                assert!(
2691                    excerpt_blocks_2.contains(&custom_block_id)
2692                        || excerpt_blocks_3.contains(&custom_block_id),
2693                    "Should have only blocks from unfolded buffers"
2694                );
2695            }
2696        }
2697        assert_eq!(
2698            1,
2699            blocks
2700                .iter()
2701                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2702                .count(),
2703            "Should have one folded block, producing a header of the second buffer"
2704        );
2705        assert_eq!(
2706            blocks_snapshot.text(),
2707            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2708        );
2709        assert_eq!(
2710            blocks_snapshot
2711                .row_infos(BlockRow(0))
2712                .map(|i| i.buffer_row)
2713                .collect::<Vec<_>>(),
2714            vec![
2715                None,
2716                None,
2717                None,
2718                None,
2719                None,
2720                Some(1),
2721                None,
2722                None,
2723                Some(2),
2724                None,
2725                Some(3),
2726                None,
2727                None,
2728                None,
2729                None,
2730                Some(4),
2731                None,
2732                Some(5),
2733                None,
2734            ]
2735        );
2736
2737        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2738        buffer.read_with(cx, |buffer, cx| {
2739            writer.fold_buffers([buffer_id_2], buffer, cx);
2740        });
2741        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2742        let blocks = blocks_snapshot
2743            .blocks_in_range(0..u32::MAX)
2744            .collect::<Vec<_>>();
2745        for (_, block) in &blocks {
2746            if let BlockId::Custom(custom_block_id) = block.id() {
2747                assert!(
2748                    !excerpt_blocks_1.contains(&custom_block_id),
2749                    "Should have no blocks from the folded buffer_1"
2750                );
2751                assert!(
2752                    !excerpt_blocks_2.contains(&custom_block_id),
2753                    "Should have no blocks from the folded buffer_2"
2754                );
2755                assert!(
2756                    excerpt_blocks_3.contains(&custom_block_id),
2757                    "Should have only blocks from unfolded buffers"
2758                );
2759            }
2760        }
2761        assert_eq!(
2762            2,
2763            blocks
2764                .iter()
2765                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2766                .count(),
2767            "Should have two folded blocks, producing headers"
2768        );
2769        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
2770        assert_eq!(
2771            blocks_snapshot
2772                .row_infos(BlockRow(0))
2773                .map(|i| i.buffer_row)
2774                .collect::<Vec<_>>(),
2775            vec![
2776                None,
2777                None,
2778                None,
2779                None,
2780                None,
2781                None,
2782                None,
2783                Some(4),
2784                None,
2785                Some(5),
2786                None,
2787            ]
2788        );
2789
2790        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2791        buffer.read_with(cx, |buffer, cx| {
2792            writer.unfold_buffers([buffer_id_1], buffer, cx);
2793        });
2794        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2795        let blocks = blocks_snapshot
2796            .blocks_in_range(0..u32::MAX)
2797            .collect::<Vec<_>>();
2798        for (_, block) in &blocks {
2799            if let BlockId::Custom(custom_block_id) = block.id() {
2800                assert!(
2801                    !excerpt_blocks_2.contains(&custom_block_id),
2802                    "Should have no blocks from the folded buffer_2"
2803                );
2804                assert!(
2805                    excerpt_blocks_1.contains(&custom_block_id)
2806                        || excerpt_blocks_3.contains(&custom_block_id),
2807                    "Should have only blocks from unfolded buffers"
2808                );
2809            }
2810        }
2811        assert_eq!(
2812            1,
2813            blocks
2814                .iter()
2815                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2816                .count(),
2817            "Should be back to a single folded buffer, producing a header for buffer_2"
2818        );
2819        assert_eq!(
2820            blocks_snapshot.text(),
2821            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
2822            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2823        );
2824        assert_eq!(
2825            blocks_snapshot
2826                .row_infos(BlockRow(0))
2827                .map(|i| i.buffer_row)
2828                .collect::<Vec<_>>(),
2829            vec![
2830                None,
2831                None,
2832                None,
2833                Some(0),
2834                None,
2835                None,
2836                None,
2837                None,
2838                None,
2839                Some(4),
2840                None,
2841                Some(5),
2842                None,
2843            ]
2844        );
2845
2846        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2847        buffer.read_with(cx, |buffer, cx| {
2848            writer.fold_buffers([buffer_id_3], buffer, cx);
2849        });
2850        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2851        let blocks = blocks_snapshot
2852            .blocks_in_range(0..u32::MAX)
2853            .collect::<Vec<_>>();
2854        for (_, block) in &blocks {
2855            if let BlockId::Custom(custom_block_id) = block.id() {
2856                assert!(
2857                    excerpt_blocks_1.contains(&custom_block_id),
2858                    "Should have no blocks from the folded buffer_1"
2859                );
2860                assert!(
2861                    !excerpt_blocks_2.contains(&custom_block_id),
2862                    "Should have only blocks from unfolded buffers"
2863                );
2864                assert!(
2865                    !excerpt_blocks_3.contains(&custom_block_id),
2866                    "Should have only blocks from unfolded buffers"
2867                );
2868            }
2869        }
2870
2871        assert_eq!(
2872            blocks_snapshot.text(),
2873            "\n\n\n111\n\n\n\n",
2874            "Should have a single, first buffer left after folding"
2875        );
2876        assert_eq!(
2877            blocks_snapshot
2878                .row_infos(BlockRow(0))
2879                .map(|i| i.buffer_row)
2880                .collect::<Vec<_>>(),
2881            vec![None, None, None, Some(0), None, None, None, None,]
2882        );
2883    }
2884
2885    #[gpui::test]
2886    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2887        cx.update(init_test);
2888
2889        let text = "111";
2890
2891        let buffer = cx.update(|cx| {
2892            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2893        });
2894        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2895        let buffer_ids = buffer_snapshot
2896            .excerpts()
2897            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2898            .dedup()
2899            .collect::<Vec<_>>();
2900        assert_eq!(buffer_ids.len(), 1);
2901        let buffer_id = buffer_ids[0];
2902
2903        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
2904        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2905        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2906        let (_, wrap_snapshot) =
2907            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2908        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2909        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2910
2911        assert_eq!(blocks_snapshot.text(), "\n\n111");
2912
2913        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2914        buffer.read_with(cx, |buffer, cx| {
2915            writer.fold_buffers([buffer_id], buffer, cx);
2916        });
2917        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2918        let blocks = blocks_snapshot
2919            .blocks_in_range(0..u32::MAX)
2920            .collect::<Vec<_>>();
2921        assert_eq!(
2922            1,
2923            blocks
2924                .iter()
2925                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
2926                .count(),
2927            "Should have one folded block, producing a header of the second buffer"
2928        );
2929        assert_eq!(blocks_snapshot.text(), "\n");
2930        assert_eq!(
2931            blocks_snapshot
2932                .row_infos(BlockRow(0))
2933                .map(|i| i.buffer_row)
2934                .collect::<Vec<_>>(),
2935            vec![None, None],
2936            "When fully folded, should be no buffer rows"
2937        );
2938    }
2939
2940    #[gpui::test(iterations = 100)]
2941    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2942        cx.update(init_test);
2943
2944        let operations = env::var("OPERATIONS")
2945            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2946            .unwrap_or(10);
2947
2948        let wrap_width = if rng.random_bool(0.2) {
2949            None
2950        } else {
2951            Some(px(rng.random_range(0.0..=100.0)))
2952        };
2953        let tab_size = 1.try_into().unwrap();
2954        let font_size = px(14.0);
2955        let buffer_start_header_height = rng.random_range(1..=5);
2956        let excerpt_header_height = rng.random_range(1..=5);
2957
2958        log::info!("Wrap width: {:?}", wrap_width);
2959        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2960        let is_singleton = rng.random();
2961        let buffer = if is_singleton {
2962            let len = rng.random_range(0..10);
2963            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2964            log::info!("initial singleton buffer text: {:?}", text);
2965            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2966        } else {
2967            cx.update(|cx| {
2968                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2969                log::info!(
2970                    "initial multi-buffer text: {:?}",
2971                    multibuffer.read(cx).read(cx).text()
2972                );
2973                multibuffer
2974            })
2975        };
2976
2977        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2978        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2979        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2980        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2981        let font = test_font();
2982        let (wrap_map, wraps_snapshot) =
2983            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
2984        let mut block_map = BlockMap::new(
2985            wraps_snapshot,
2986            buffer_start_header_height,
2987            excerpt_header_height,
2988        );
2989
2990        for _ in 0..operations {
2991            let mut buffer_edits = Vec::new();
2992            match rng.random_range(0..=100) {
2993                0..=19 => {
2994                    let wrap_width = if rng.random_bool(0.2) {
2995                        None
2996                    } else {
2997                        Some(px(rng.random_range(0.0..=100.0)))
2998                    };
2999                    log::info!("Setting wrap width to {:?}", wrap_width);
3000                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3001                }
3002                20..=39 => {
3003                    let block_count = rng.random_range(1..=5);
3004                    let block_properties = (0..block_count)
3005                        .map(|_| {
3006                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3007                            let offset =
3008                                buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
3009                            let mut min_height = 0;
3010                            let placement = match rng.random_range(0..3) {
3011                                0 => {
3012                                    min_height = 1;
3013                                    let start = buffer.anchor_after(offset);
3014                                    let end = buffer.anchor_after(buffer.clip_offset(
3015                                        rng.random_range(offset..=buffer.len()),
3016                                        Bias::Left,
3017                                    ));
3018                                    BlockPlacement::Replace(start..=end)
3019                                }
3020                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3021                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3022                            };
3023
3024                            let height = rng.random_range(min_height..5);
3025                            BlockProperties {
3026                                style: BlockStyle::Fixed,
3027                                placement,
3028                                height: Some(height),
3029                                render: Arc::new(|_| div().into_any()),
3030                                priority: 0,
3031                            }
3032                        })
3033                        .collect::<Vec<_>>();
3034
3035                    let (inlay_snapshot, inlay_edits) =
3036                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3037                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3038                    let (tab_snapshot, tab_edits) =
3039                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3040                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3041                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3042                    });
3043                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3044                    let block_ids =
3045                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3046                            placement: props.placement.clone(),
3047                            height: props.height,
3048                            style: props.style,
3049                            render: Arc::new(|_| div().into_any()),
3050                            priority: 0,
3051                        }));
3052
3053                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3054                        log::info!(
3055                            "inserted block {:?} with height {:?} and id {:?}",
3056                            block_properties
3057                                .placement
3058                                .as_ref()
3059                                .map(|p| p.to_point(&buffer_snapshot)),
3060                            block_properties.height,
3061                            block_id
3062                        );
3063                    }
3064                }
3065                40..=59 if !block_map.custom_blocks.is_empty() => {
3066                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3067                    let block_ids_to_remove = block_map
3068                        .custom_blocks
3069                        .choose_multiple(&mut rng, block_count)
3070                        .map(|block| block.id)
3071                        .collect::<HashSet<_>>();
3072
3073                    let (inlay_snapshot, inlay_edits) =
3074                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3075                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3076                    let (tab_snapshot, tab_edits) =
3077                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3078                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3079                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3080                    });
3081                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3082                    log::info!(
3083                        "removing {} blocks: {:?}",
3084                        block_ids_to_remove.len(),
3085                        block_ids_to_remove
3086                    );
3087                    block_map.remove(block_ids_to_remove);
3088                }
3089                60..=79 => {
3090                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3091                        log::info!("Noop fold/unfold operation on a singleton buffer");
3092                        continue;
3093                    }
3094                    let (inlay_snapshot, inlay_edits) =
3095                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3096                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3097                    let (tab_snapshot, tab_edits) =
3098                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3099                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3100                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3101                    });
3102                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3103                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3104                        let folded_buffers = block_map
3105                            .0
3106                            .folded_buffers
3107                            .iter()
3108                            .cloned()
3109                            .collect::<Vec<_>>();
3110                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3111                        unfolded_buffers.dedup();
3112                        log::debug!("All buffers {unfolded_buffers:?}");
3113                        log::debug!("Folded buffers {folded_buffers:?}");
3114                        unfolded_buffers
3115                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3116                        (unfolded_buffers, folded_buffers)
3117                    });
3118                    let mut folded_count = folded_buffers.len();
3119                    let mut unfolded_count = unfolded_buffers.len();
3120
3121                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
3122                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
3123                    if !fold && !unfold {
3124                        log::info!(
3125                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3126                        );
3127                        continue;
3128                    }
3129
3130                    buffer.update(cx, |buffer, cx| {
3131                        if fold {
3132                            let buffer_to_fold =
3133                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
3134                            log::info!("Folding {buffer_to_fold:?}");
3135                            let related_excerpts = buffer_snapshot
3136                                .excerpts()
3137                                .filter_map(|(excerpt_id, buffer, range)| {
3138                                    if buffer.remote_id() == buffer_to_fold {
3139                                        Some((
3140                                            excerpt_id,
3141                                            buffer
3142                                                .text_for_range(range.context)
3143                                                .collect::<String>(),
3144                                        ))
3145                                    } else {
3146                                        None
3147                                    }
3148                                })
3149                                .collect::<Vec<_>>();
3150                            log::info!(
3151                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3152                            );
3153                            folded_count += 1;
3154                            unfolded_count -= 1;
3155                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3156                        }
3157                        if unfold {
3158                            let buffer_to_unfold =
3159                                folded_buffers[rng.random_range(0..folded_buffers.len())];
3160                            log::info!("Unfolding {buffer_to_unfold:?}");
3161                            unfolded_count += 1;
3162                            folded_count -= 1;
3163                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3164                        }
3165                        log::info!(
3166                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3167                        );
3168                    });
3169                }
3170                _ => {
3171                    buffer.update(cx, |buffer, cx| {
3172                        let mutation_count = rng.random_range(1..=5);
3173                        let subscription = buffer.subscribe();
3174                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3175                        buffer_snapshot = buffer.snapshot(cx);
3176                        buffer_edits.extend(subscription.consume());
3177                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3178                    });
3179                }
3180            }
3181
3182            let (inlay_snapshot, inlay_edits) =
3183                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3184            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3185            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3186            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3187                wrap_map.sync(tab_snapshot, tab_edits, cx)
3188            });
3189            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3190            assert_eq!(
3191                blocks_snapshot.transforms.summary().input_rows,
3192                wraps_snapshot.max_point().row() + 1
3193            );
3194            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3195            log::info!("blocks text: {:?}", blocks_snapshot.text());
3196
3197            let mut expected_blocks = Vec::new();
3198            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3199                Some((
3200                    block.placement.to_wrap_row(&wraps_snapshot)?,
3201                    Block::Custom(block.clone()),
3202                ))
3203            }));
3204
3205            // Note that this needs to be synced with the related section in BlockMap::sync
3206            expected_blocks.extend(block_map.header_and_footer_blocks(
3207                &buffer_snapshot,
3208                0..,
3209                &wraps_snapshot,
3210            ));
3211
3212            BlockMap::sort_blocks(&mut expected_blocks);
3213
3214            for (placement, block) in &expected_blocks {
3215                log::info!(
3216                    "Block {:?} placement: {:?} Height: {:?}",
3217                    block.id(),
3218                    placement,
3219                    block.height()
3220                );
3221            }
3222
3223            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3224
3225            let input_buffer_rows = buffer_snapshot
3226                .row_infos(MultiBufferRow(0))
3227                .map(|row| row.buffer_row)
3228                .collect::<Vec<_>>();
3229            let mut expected_buffer_rows = Vec::new();
3230            let mut expected_text = String::new();
3231            let mut expected_block_positions = Vec::new();
3232            let mut expected_replaced_buffer_rows = HashSet::default();
3233            let input_text = wraps_snapshot.text();
3234
3235            // Loop over the input lines, creating (N - 1) empty lines for
3236            // blocks of height N.
3237            //
3238            // It's important to note that output *starts* as one empty line,
3239            // so we special case row 0 to assume a leading '\n'.
3240            //
3241            // Linehood is the birthright of strings.
3242            let input_text_lines = input_text.split('\n').enumerate().peekable();
3243            let mut block_row = 0;
3244            for (wrap_row, input_line) in input_text_lines {
3245                let wrap_row = wrap_row as u32;
3246                let multibuffer_row = wraps_snapshot
3247                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3248                    .row;
3249
3250                // Create empty lines for the above block
3251                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3252                    if placement.start().0 == wrap_row && block.place_above() {
3253                        let (_, block) = sorted_blocks_iter.next().unwrap();
3254                        expected_block_positions.push((block_row, block.id()));
3255                        if block.height() > 0 {
3256                            let text = "\n".repeat((block.height() - 1) as usize);
3257                            if block_row > 0 {
3258                                expected_text.push('\n')
3259                            }
3260                            expected_text.push_str(&text);
3261                            for _ in 0..block.height() {
3262                                expected_buffer_rows.push(None);
3263                            }
3264                            block_row += block.height();
3265                        }
3266                    } else {
3267                        break;
3268                    }
3269                }
3270
3271                // Skip lines within replace blocks, then create empty lines for the replace block's height
3272                let mut is_in_replace_block = false;
3273                if let Some((BlockPlacement::Replace(replace_range), block)) =
3274                    sorted_blocks_iter.peek()
3275                    && wrap_row >= replace_range.start().0
3276                {
3277                    is_in_replace_block = true;
3278
3279                    if wrap_row == replace_range.start().0 {
3280                        if matches!(block, Block::FoldedBuffer { .. }) {
3281                            expected_buffer_rows.push(None);
3282                        } else {
3283                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
3284                        }
3285                    }
3286
3287                    if wrap_row == replace_range.end().0 {
3288                        expected_block_positions.push((block_row, block.id()));
3289                        let text = "\n".repeat((block.height() - 1) as usize);
3290                        if block_row > 0 {
3291                            expected_text.push('\n');
3292                        }
3293                        expected_text.push_str(&text);
3294
3295                        for _ in 1..block.height() {
3296                            expected_buffer_rows.push(None);
3297                        }
3298                        block_row += block.height();
3299
3300                        sorted_blocks_iter.next();
3301                    }
3302                }
3303
3304                if is_in_replace_block {
3305                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3306                } else {
3307                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3308                    let soft_wrapped = wraps_snapshot
3309                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3310                        .column()
3311                        > 0;
3312                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3313                    if block_row > 0 {
3314                        expected_text.push('\n');
3315                    }
3316                    expected_text.push_str(input_line);
3317                    block_row += 1;
3318                }
3319
3320                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3321                    if placement.end().0 == wrap_row && block.place_below() {
3322                        let (_, block) = sorted_blocks_iter.next().unwrap();
3323                        expected_block_positions.push((block_row, block.id()));
3324                        if block.height() > 0 {
3325                            let text = "\n".repeat((block.height() - 1) as usize);
3326                            if block_row > 0 {
3327                                expected_text.push('\n')
3328                            }
3329                            expected_text.push_str(&text);
3330                            for _ in 0..block.height() {
3331                                expected_buffer_rows.push(None);
3332                            }
3333                            block_row += block.height();
3334                        }
3335                    } else {
3336                        break;
3337                    }
3338                }
3339            }
3340
3341            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3342            let expected_row_count = expected_lines.len();
3343            log::info!("expected text: {expected_text:?}");
3344
3345            assert_eq!(
3346                blocks_snapshot.max_point().row + 1,
3347                expected_row_count as u32,
3348                "actual row count != expected row count",
3349            );
3350            assert_eq!(
3351                blocks_snapshot.text(),
3352                expected_text,
3353                "actual text != expected text",
3354            );
3355
3356            for start_row in 0..expected_row_count {
3357                let end_row = rng.random_range(start_row + 1..=expected_row_count);
3358                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3359                if end_row < expected_row_count {
3360                    expected_text.push('\n');
3361                }
3362
3363                let actual_text = blocks_snapshot
3364                    .chunks(
3365                        start_row as u32..end_row as u32,
3366                        false,
3367                        false,
3368                        Highlights::default(),
3369                    )
3370                    .map(|chunk| chunk.text)
3371                    .collect::<String>();
3372                assert_eq!(
3373                    actual_text,
3374                    expected_text,
3375                    "incorrect text starting row row range {:?}",
3376                    start_row..end_row
3377                );
3378                assert_eq!(
3379                    blocks_snapshot
3380                        .row_infos(BlockRow(start_row as u32))
3381                        .map(|row_info| row_info.buffer_row)
3382                        .collect::<Vec<_>>(),
3383                    &expected_buffer_rows[start_row..],
3384                    "incorrect buffer_rows starting at row {:?}",
3385                    start_row
3386                );
3387            }
3388
3389            assert_eq!(
3390                blocks_snapshot
3391                    .blocks_in_range(0..(expected_row_count as u32))
3392                    .map(|(row, block)| (row, block.id()))
3393                    .collect::<Vec<_>>(),
3394                expected_block_positions,
3395                "invalid blocks_in_range({:?})",
3396                0..expected_row_count
3397            );
3398
3399            for (_, expected_block) in
3400                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3401            {
3402                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3403                assert_eq!(
3404                    actual_block.map(|block| block.id()),
3405                    Some(expected_block.id())
3406                );
3407            }
3408
3409            for (block_row, block_id) in expected_block_positions {
3410                if let BlockId::Custom(block_id) = block_id {
3411                    assert_eq!(
3412                        blocks_snapshot.row_for_block(block_id),
3413                        Some(BlockRow(block_row))
3414                    );
3415                }
3416            }
3417
3418            let mut expected_longest_rows = Vec::new();
3419            let mut longest_line_len = -1_isize;
3420            for (row, line) in expected_lines.iter().enumerate() {
3421                let row = row as u32;
3422
3423                assert_eq!(
3424                    blocks_snapshot.line_len(BlockRow(row)),
3425                    line.len() as u32,
3426                    "invalid line len for row {}",
3427                    row
3428                );
3429
3430                let line_char_count = line.chars().count() as isize;
3431                match line_char_count.cmp(&longest_line_len) {
3432                    Ordering::Less => {}
3433                    Ordering::Equal => expected_longest_rows.push(row),
3434                    Ordering::Greater => {
3435                        longest_line_len = line_char_count;
3436                        expected_longest_rows.clear();
3437                        expected_longest_rows.push(row);
3438                    }
3439                }
3440            }
3441
3442            let longest_row = blocks_snapshot.longest_row();
3443            assert!(
3444                expected_longest_rows.contains(&longest_row),
3445                "incorrect longest row {}. expected {:?} with length {}",
3446                longest_row,
3447                expected_longest_rows,
3448                longest_line_len,
3449            );
3450
3451            for _ in 0..10 {
3452                let end_row = rng.random_range(1..=expected_lines.len());
3453                let start_row = rng.random_range(0..end_row);
3454
3455                let mut expected_longest_rows_in_range = vec![];
3456                let mut longest_line_len_in_range = 0;
3457
3458                let mut row = start_row as u32;
3459                for line in &expected_lines[start_row..end_row] {
3460                    let line_char_count = line.chars().count() as isize;
3461                    match line_char_count.cmp(&longest_line_len_in_range) {
3462                        Ordering::Less => {}
3463                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3464                        Ordering::Greater => {
3465                            longest_line_len_in_range = line_char_count;
3466                            expected_longest_rows_in_range.clear();
3467                            expected_longest_rows_in_range.push(row);
3468                        }
3469                    }
3470                    row += 1;
3471                }
3472
3473                let longest_row_in_range = blocks_snapshot
3474                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3475                assert!(
3476                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3477                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3478                    longest_row,
3479                    start_row..end_row,
3480                    expected_longest_rows_in_range,
3481                    longest_line_len_in_range,
3482                );
3483            }
3484
3485            // Ensure that conversion between block points and wrap points is stable.
3486            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3487                let wrap_point = WrapPoint::new(row, 0);
3488                let block_point = blocks_snapshot.to_block_point(wrap_point);
3489                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3490                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3491                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3492                assert_eq!(
3493                    blocks_snapshot.to_block_point(right_wrap_point),
3494                    block_point
3495                );
3496            }
3497
3498            let mut block_point = BlockPoint::new(0, 0);
3499            for c in expected_text.chars() {
3500                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3501                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3502                assert_eq!(
3503                    blocks_snapshot
3504                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3505                    left_point,
3506                    "block point: {:?}, wrap point: {:?}",
3507                    block_point,
3508                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3509                );
3510                assert_eq!(
3511                    left_buffer_point,
3512                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3513                    "{:?} is not valid in buffer coordinates",
3514                    left_point
3515                );
3516
3517                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3518                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3519                assert_eq!(
3520                    blocks_snapshot
3521                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3522                    right_point,
3523                    "block point: {:?}, wrap point: {:?}",
3524                    block_point,
3525                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3526                );
3527                assert_eq!(
3528                    right_buffer_point,
3529                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3530                    "{:?} is not valid in buffer coordinates",
3531                    right_point
3532                );
3533
3534                if c == '\n' {
3535                    block_point.0 += Point::new(1, 0);
3536                } else {
3537                    block_point.column += c.len_utf8() as u32;
3538                }
3539            }
3540
3541            for buffer_row in 0..=buffer_snapshot.max_point().row {
3542                let buffer_row = MultiBufferRow(buffer_row);
3543                assert_eq!(
3544                    blocks_snapshot.is_line_replaced(buffer_row),
3545                    expected_replaced_buffer_rows.contains(&buffer_row),
3546                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3547                );
3548            }
3549        }
3550    }
3551
3552    #[gpui::test]
3553    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
3554        cx.update(init_test);
3555
3556        let text = "abc\ndef\nghi\njkl\nmno";
3557        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3558        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3559        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3560        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3561        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3562        let (_wrap_map, wraps_snapshot) =
3563            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3564        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3565
3566        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3567        let _block_id = writer.insert(vec![BlockProperties {
3568            style: BlockStyle::Fixed,
3569            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3570            height: Some(1),
3571            render: Arc::new(|_| div().into_any()),
3572            priority: 0,
3573        }])[0];
3574
3575        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
3576        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3577
3578        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3579        writer.remove_intersecting_replace_blocks(
3580            [buffer_snapshot.anchor_after(Point::new(1, 0))
3581                ..buffer_snapshot.anchor_after(Point::new(1, 0))],
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}