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