block_map.rs

   1use super::{
   2    wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
   3    Highlights,
   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        atomic::{AtomicUsize, Ordering::SeqCst},
  21        Arc,
  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::{div, font, px, App, AppContext as _, Element};
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.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!("Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}");
3081                        continue;
3082                    }
3083
3084                    buffer.update(cx, |buffer, cx| {
3085                        if fold {
3086                            let buffer_to_fold =
3087                                unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
3088                            log::info!("Folding {buffer_to_fold:?}");
3089                            let related_excerpts = buffer_snapshot
3090                                .excerpts()
3091                                .filter_map(|(excerpt_id, buffer, range)| {
3092                                    if buffer.remote_id() == buffer_to_fold {
3093                                        Some((
3094                                            excerpt_id,
3095                                            buffer
3096                                                .text_for_range(range.context)
3097                                                .collect::<String>(),
3098                                        ))
3099                                    } else {
3100                                        None
3101                                    }
3102                                })
3103                                .collect::<Vec<_>>();
3104                            log::info!(
3105                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3106                            );
3107                            folded_count += 1;
3108                            unfolded_count -= 1;
3109                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3110                        }
3111                        if unfold {
3112                            let buffer_to_unfold =
3113                                folded_buffers[rng.gen_range(0..folded_buffers.len())];
3114                            log::info!("Unfolding {buffer_to_unfold:?}");
3115                            unfolded_count += 1;
3116                            folded_count -= 1;
3117                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3118                        }
3119                        log::info!(
3120                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3121                        );
3122                    });
3123                }
3124                _ => {
3125                    buffer.update(cx, |buffer, cx| {
3126                        let mutation_count = rng.gen_range(1..=5);
3127                        let subscription = buffer.subscribe();
3128                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3129                        buffer_snapshot = buffer.snapshot(cx);
3130                        buffer_edits.extend(subscription.consume());
3131                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3132                    });
3133                }
3134            }
3135
3136            let (inlay_snapshot, inlay_edits) =
3137                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3138            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3139            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3140            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3141                wrap_map.sync(tab_snapshot, tab_edits, cx)
3142            });
3143            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3144            assert_eq!(
3145                blocks_snapshot.transforms.summary().input_rows,
3146                wraps_snapshot.max_point().row() + 1
3147            );
3148            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3149            log::info!("blocks text: {:?}", blocks_snapshot.text());
3150
3151            let mut expected_blocks = Vec::new();
3152            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3153                Some((
3154                    block.placement.to_wrap_row(&wraps_snapshot)?,
3155                    Block::Custom(block.clone()),
3156                ))
3157            }));
3158
3159            // Note that this needs to be synced with the related section in BlockMap::sync
3160            expected_blocks.extend(BlockMap::header_and_footer_blocks(
3161                buffer_start_header_height,
3162                excerpt_header_height,
3163                &buffer_snapshot,
3164                &block_map.folded_buffers,
3165                0..,
3166                &wraps_snapshot,
3167            ));
3168
3169            BlockMap::sort_blocks(&mut expected_blocks);
3170
3171            for (placement, block) in &expected_blocks {
3172                log::info!(
3173                    "Block {:?} placement: {:?} Height: {:?}",
3174                    block.id(),
3175                    placement,
3176                    block.height()
3177                );
3178            }
3179
3180            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3181
3182            let input_buffer_rows = buffer_snapshot
3183                .row_infos(MultiBufferRow(0))
3184                .map(|row| row.buffer_row)
3185                .collect::<Vec<_>>();
3186            let mut expected_buffer_rows = Vec::new();
3187            let mut expected_text = String::new();
3188            let mut expected_block_positions = Vec::new();
3189            let mut expected_replaced_buffer_rows = HashSet::default();
3190            let input_text = wraps_snapshot.text();
3191
3192            // Loop over the input lines, creating (N - 1) empty lines for
3193            // blocks of height N.
3194            //
3195            // It's important to note that output *starts* as one empty line,
3196            // so we special case row 0 to assume a leading '\n'.
3197            //
3198            // Linehood is the birthright of strings.
3199            let mut input_text_lines = input_text.split('\n').enumerate().peekable();
3200            let mut block_row = 0;
3201            while let Some((wrap_row, input_line)) = input_text_lines.next() {
3202                let wrap_row = wrap_row as u32;
3203                let multibuffer_row = wraps_snapshot
3204                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3205                    .row;
3206
3207                // Create empty lines for the above block
3208                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3209                    if placement.start().0 == wrap_row && block.place_above() {
3210                        let (_, block) = sorted_blocks_iter.next().unwrap();
3211                        expected_block_positions.push((block_row, block.id()));
3212                        if block.height() > 0 {
3213                            let text = "\n".repeat((block.height() - 1) as usize);
3214                            if block_row > 0 {
3215                                expected_text.push('\n')
3216                            }
3217                            expected_text.push_str(&text);
3218                            for _ in 0..block.height() {
3219                                expected_buffer_rows.push(None);
3220                            }
3221                            block_row += block.height();
3222                        }
3223                    } else {
3224                        break;
3225                    }
3226                }
3227
3228                // Skip lines within replace blocks, then create empty lines for the replace block's height
3229                let mut is_in_replace_block = false;
3230                if let Some((BlockPlacement::Replace(replace_range), block)) =
3231                    sorted_blocks_iter.peek()
3232                {
3233                    if wrap_row >= replace_range.start().0 {
3234                        is_in_replace_block = true;
3235
3236                        if wrap_row == replace_range.start().0 {
3237                            if matches!(block, Block::FoldedBuffer { .. }) {
3238                                expected_buffer_rows.push(None);
3239                            } else {
3240                                expected_buffer_rows
3241                                    .push(input_buffer_rows[multibuffer_row as usize]);
3242                            }
3243                        }
3244
3245                        if wrap_row == replace_range.end().0 {
3246                            expected_block_positions.push((block_row, block.id()));
3247                            let text = "\n".repeat((block.height() - 1) as usize);
3248                            if block_row > 0 {
3249                                expected_text.push('\n');
3250                            }
3251                            expected_text.push_str(&text);
3252
3253                            for _ in 1..block.height() {
3254                                expected_buffer_rows.push(None);
3255                            }
3256                            block_row += block.height();
3257
3258                            sorted_blocks_iter.next();
3259                        }
3260                    }
3261                }
3262
3263                if is_in_replace_block {
3264                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3265                } else {
3266                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3267                    let soft_wrapped = wraps_snapshot
3268                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3269                        .column()
3270                        > 0;
3271                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3272                    if block_row > 0 {
3273                        expected_text.push('\n');
3274                    }
3275                    expected_text.push_str(input_line);
3276                    block_row += 1;
3277                }
3278
3279                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3280                    if placement.end().0 == wrap_row && block.place_below() {
3281                        let (_, block) = sorted_blocks_iter.next().unwrap();
3282                        expected_block_positions.push((block_row, block.id()));
3283                        if block.height() > 0 {
3284                            let text = "\n".repeat((block.height() - 1) as usize);
3285                            if block_row > 0 {
3286                                expected_text.push('\n')
3287                            }
3288                            expected_text.push_str(&text);
3289                            for _ in 0..block.height() {
3290                                expected_buffer_rows.push(None);
3291                            }
3292                            block_row += block.height();
3293                        }
3294                    } else {
3295                        break;
3296                    }
3297                }
3298            }
3299
3300            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3301            let expected_row_count = expected_lines.len();
3302            log::info!("expected text: {expected_text:?}");
3303
3304            assert_eq!(
3305                blocks_snapshot.max_point().row + 1,
3306                expected_row_count as u32,
3307                "actual row count != expected row count",
3308            );
3309            assert_eq!(
3310                blocks_snapshot.text(),
3311                expected_text,
3312                "actual text != expected text",
3313            );
3314
3315            for start_row in 0..expected_row_count {
3316                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
3317                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3318                if end_row < expected_row_count {
3319                    expected_text.push('\n');
3320                }
3321
3322                let actual_text = blocks_snapshot
3323                    .chunks(
3324                        start_row as u32..end_row as u32,
3325                        false,
3326                        false,
3327                        Highlights::default(),
3328                    )
3329                    .map(|chunk| chunk.text)
3330                    .collect::<String>();
3331                assert_eq!(
3332                    actual_text,
3333                    expected_text,
3334                    "incorrect text starting row row range {:?}",
3335                    start_row..end_row
3336                );
3337                assert_eq!(
3338                    blocks_snapshot
3339                        .row_infos(BlockRow(start_row as u32))
3340                        .map(|row_info| row_info.buffer_row)
3341                        .collect::<Vec<_>>(),
3342                    &expected_buffer_rows[start_row..],
3343                    "incorrect buffer_rows starting at row {:?}",
3344                    start_row
3345                );
3346            }
3347
3348            assert_eq!(
3349                blocks_snapshot
3350                    .blocks_in_range(0..(expected_row_count as u32))
3351                    .map(|(row, block)| (row, block.id()))
3352                    .collect::<Vec<_>>(),
3353                expected_block_positions,
3354                "invalid blocks_in_range({:?})",
3355                0..expected_row_count
3356            );
3357
3358            for (_, expected_block) in
3359                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3360            {
3361                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3362                assert_eq!(
3363                    actual_block.map(|block| block.id()),
3364                    Some(expected_block.id())
3365                );
3366            }
3367
3368            for (block_row, block_id) in expected_block_positions {
3369                if let BlockId::Custom(block_id) = block_id {
3370                    assert_eq!(
3371                        blocks_snapshot.row_for_block(block_id),
3372                        Some(BlockRow(block_row))
3373                    );
3374                }
3375            }
3376
3377            let mut expected_longest_rows = Vec::new();
3378            let mut longest_line_len = -1_isize;
3379            for (row, line) in expected_lines.iter().enumerate() {
3380                let row = row as u32;
3381
3382                assert_eq!(
3383                    blocks_snapshot.line_len(BlockRow(row)),
3384                    line.len() as u32,
3385                    "invalid line len for row {}",
3386                    row
3387                );
3388
3389                let line_char_count = line.chars().count() as isize;
3390                match line_char_count.cmp(&longest_line_len) {
3391                    Ordering::Less => {}
3392                    Ordering::Equal => expected_longest_rows.push(row),
3393                    Ordering::Greater => {
3394                        longest_line_len = line_char_count;
3395                        expected_longest_rows.clear();
3396                        expected_longest_rows.push(row);
3397                    }
3398                }
3399            }
3400
3401            let longest_row = blocks_snapshot.longest_row();
3402            assert!(
3403                expected_longest_rows.contains(&longest_row),
3404                "incorrect longest row {}. expected {:?} with length {}",
3405                longest_row,
3406                expected_longest_rows,
3407                longest_line_len,
3408            );
3409
3410            for _ in 0..10 {
3411                let end_row = rng.gen_range(1..=expected_lines.len());
3412                let start_row = rng.gen_range(0..end_row);
3413
3414                let mut expected_longest_rows_in_range = vec![];
3415                let mut longest_line_len_in_range = 0;
3416
3417                let mut row = start_row as u32;
3418                for line in &expected_lines[start_row..end_row] {
3419                    let line_char_count = line.chars().count() as isize;
3420                    match line_char_count.cmp(&longest_line_len_in_range) {
3421                        Ordering::Less => {}
3422                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3423                        Ordering::Greater => {
3424                            longest_line_len_in_range = line_char_count;
3425                            expected_longest_rows_in_range.clear();
3426                            expected_longest_rows_in_range.push(row);
3427                        }
3428                    }
3429                    row += 1;
3430                }
3431
3432                let longest_row_in_range = blocks_snapshot
3433                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3434                assert!(
3435                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3436                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3437                    longest_row,
3438                    start_row..end_row,
3439                    expected_longest_rows_in_range,
3440                    longest_line_len_in_range,
3441                );
3442            }
3443
3444            // Ensure that conversion between block points and wrap points is stable.
3445            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3446                let wrap_point = WrapPoint::new(row, 0);
3447                let block_point = blocks_snapshot.to_block_point(wrap_point);
3448                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3449                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3450                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3451                assert_eq!(
3452                    blocks_snapshot.to_block_point(right_wrap_point),
3453                    block_point
3454                );
3455            }
3456
3457            let mut block_point = BlockPoint::new(0, 0);
3458            for c in expected_text.chars() {
3459                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3460                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3461                assert_eq!(
3462                    blocks_snapshot
3463                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3464                    left_point,
3465                    "block point: {:?}, wrap point: {:?}",
3466                    block_point,
3467                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3468                );
3469                assert_eq!(
3470                    left_buffer_point,
3471                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3472                    "{:?} is not valid in buffer coordinates",
3473                    left_point
3474                );
3475
3476                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3477                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3478                assert_eq!(
3479                    blocks_snapshot
3480                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3481                    right_point,
3482                    "block point: {:?}, wrap point: {:?}",
3483                    block_point,
3484                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3485                );
3486                assert_eq!(
3487                    right_buffer_point,
3488                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3489                    "{:?} is not valid in buffer coordinates",
3490                    right_point
3491                );
3492
3493                if c == '\n' {
3494                    block_point.0 += Point::new(1, 0);
3495                } else {
3496                    block_point.column += c.len_utf8() as u32;
3497                }
3498            }
3499
3500            for buffer_row in 0..=buffer_snapshot.max_point().row {
3501                let buffer_row = MultiBufferRow(buffer_row);
3502                assert_eq!(
3503                    blocks_snapshot.is_line_replaced(buffer_row),
3504                    expected_replaced_buffer_rows.contains(&buffer_row),
3505                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3506                );
3507            }
3508        }
3509    }
3510
3511    fn init_test(cx: &mut gpui::App) {
3512        let settings = SettingsStore::test(cx);
3513        cx.set_global(settings);
3514        theme::init(theme::LoadThemes::JustBase, cx);
3515        assets::Assets.load_test_fonts(cx);
3516    }
3517
3518    impl Block {
3519        fn as_custom(&self) -> Option<&CustomBlock> {
3520            match self {
3521                Block::Custom(block) => Some(block),
3522                _ => None,
3523            }
3524        }
3525    }
3526
3527    impl BlockSnapshot {
3528        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3529            self.wrap_snapshot
3530                .to_point(self.to_wrap_point(point, bias), bias)
3531        }
3532    }
3533}