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