block_map.rs

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