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