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