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