block_map.rs

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