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