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.clone());
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.clone(), 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) = inlay_map.sync(
2364            buffer_snapshot.clone(),
2365            buffer_subscription.consume().into_inner(),
2366        );
2367        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2368        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2369        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2370            wrap_map.sync(tab_snapshot, tab_edits, cx)
2371        });
2372        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2373        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2374
2375        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2376            buffer.edit(
2377                [(
2378                    Point::new(1, 5)..Point::new(1, 5),
2379                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2380                )],
2381                None,
2382                cx,
2383            );
2384            buffer.snapshot(cx)
2385        });
2386        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2387            buffer_snapshot.clone(),
2388            buffer_subscription.consume().into_inner(),
2389        );
2390        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2391        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2392        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2393            wrap_map.sync(tab_snapshot, tab_edits, cx)
2394        });
2395        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2396        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2397
2398        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2399        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2400        writer.insert(vec![
2401            BlockProperties {
2402                style: BlockStyle::Fixed,
2403                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2404                height: Some(1),
2405                render: Arc::new(|_| div().into_any()),
2406                priority: 0,
2407            },
2408            BlockProperties {
2409                style: BlockStyle::Fixed,
2410                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2411                height: Some(1),
2412                render: Arc::new(|_| div().into_any()),
2413                priority: 0,
2414            },
2415            BlockProperties {
2416                style: BlockStyle::Fixed,
2417                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2418                height: Some(1),
2419                render: Arc::new(|_| div().into_any()),
2420                priority: 0,
2421            },
2422        ]);
2423        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2424        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2425
2426        // Ensure blocks inserted *inside* replaced region are hidden.
2427        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2428        writer.insert(vec![
2429            BlockProperties {
2430                style: BlockStyle::Fixed,
2431                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2432                height: Some(1),
2433                render: Arc::new(|_| div().into_any()),
2434                priority: 0,
2435            },
2436            BlockProperties {
2437                style: BlockStyle::Fixed,
2438                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2439                height: Some(1),
2440                render: Arc::new(|_| div().into_any()),
2441                priority: 0,
2442            },
2443            BlockProperties {
2444                style: BlockStyle::Fixed,
2445                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2446                height: Some(1),
2447                render: Arc::new(|_| div().into_any()),
2448                priority: 0,
2449            },
2450        ]);
2451        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2452        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2453
2454        // Removing the replace block shows all the hidden blocks again.
2455        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2456        writer.remove(HashSet::from_iter([replace_block_id]));
2457        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2458        assert_eq!(
2459            blocks_snapshot.text(),
2460            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2461        );
2462    }
2463
2464    #[gpui::test]
2465    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2466        cx.update(init_test);
2467
2468        let text = "111\n222\n333\n444\n555\n666";
2469
2470        let buffer = cx.update(|cx| {
2471            MultiBuffer::build_multi(
2472                [
2473                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2474                    (
2475                        text,
2476                        vec![
2477                            Point::new(1, 0)..Point::new(1, 3),
2478                            Point::new(2, 0)..Point::new(2, 3),
2479                            Point::new(3, 0)..Point::new(3, 3),
2480                        ],
2481                    ),
2482                    (
2483                        text,
2484                        vec![
2485                            Point::new(4, 0)..Point::new(4, 3),
2486                            Point::new(5, 0)..Point::new(5, 3),
2487                        ],
2488                    ),
2489                ],
2490                cx,
2491            )
2492        });
2493        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2494        let buffer_ids = buffer_snapshot
2495            .excerpts()
2496            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2497            .dedup()
2498            .collect::<Vec<_>>();
2499        assert_eq!(buffer_ids.len(), 3);
2500        let buffer_id_1 = buffer_ids[0];
2501        let buffer_id_2 = buffer_ids[1];
2502        let buffer_id_3 = buffer_ids[2];
2503
2504        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2505        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2506        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2507        let (_, wrap_snapshot) =
2508            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2509        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2510        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2511
2512        assert_eq!(
2513            blocks_snapshot.text(),
2514            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
2515        );
2516        assert_eq!(
2517            blocks_snapshot
2518                .row_infos(BlockRow(0))
2519                .map(|i| i.buffer_row)
2520                .collect::<Vec<_>>(),
2521            vec![
2522                None,
2523                None,
2524                Some(0),
2525                None,
2526                None,
2527                Some(1),
2528                None,
2529                Some(2),
2530                None,
2531                Some(3),
2532                None,
2533                None,
2534                Some(4),
2535                None,
2536                Some(5),
2537            ]
2538        );
2539
2540        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2541        let excerpt_blocks_2 = writer.insert(vec![
2542            BlockProperties {
2543                style: BlockStyle::Fixed,
2544                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2545                height: Some(1),
2546                render: Arc::new(|_| div().into_any()),
2547                priority: 0,
2548            },
2549            BlockProperties {
2550                style: BlockStyle::Fixed,
2551                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2552                height: Some(1),
2553                render: Arc::new(|_| div().into_any()),
2554                priority: 0,
2555            },
2556            BlockProperties {
2557                style: BlockStyle::Fixed,
2558                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2559                height: Some(1),
2560                render: Arc::new(|_| div().into_any()),
2561                priority: 0,
2562            },
2563        ]);
2564        let excerpt_blocks_3 = writer.insert(vec![
2565            BlockProperties {
2566                style: BlockStyle::Fixed,
2567                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2568                height: Some(1),
2569                render: Arc::new(|_| div().into_any()),
2570                priority: 0,
2571            },
2572            BlockProperties {
2573                style: BlockStyle::Fixed,
2574                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2575                height: Some(1),
2576                render: Arc::new(|_| div().into_any()),
2577                priority: 0,
2578            },
2579        ]);
2580
2581        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2582        assert_eq!(
2583            blocks_snapshot.text(),
2584            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2585        );
2586        assert_eq!(
2587            blocks_snapshot
2588                .row_infos(BlockRow(0))
2589                .map(|i| i.buffer_row)
2590                .collect::<Vec<_>>(),
2591            vec![
2592                None,
2593                None,
2594                Some(0),
2595                None,
2596                None,
2597                None,
2598                Some(1),
2599                None,
2600                None,
2601                Some(2),
2602                None,
2603                Some(3),
2604                None,
2605                None,
2606                None,
2607                None,
2608                Some(4),
2609                None,
2610                Some(5),
2611                None,
2612            ]
2613        );
2614
2615        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2616        buffer.read_with(cx, |buffer, cx| {
2617            writer.fold_buffers([buffer_id_1], buffer, cx);
2618        });
2619        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2620            style: BlockStyle::Fixed,
2621            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2622            height: Some(1),
2623            render: Arc::new(|_| div().into_any()),
2624            priority: 0,
2625        }]);
2626        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2627        let blocks = blocks_snapshot
2628            .blocks_in_range(0..u32::MAX)
2629            .collect::<Vec<_>>();
2630        for (_, block) in &blocks {
2631            if let BlockId::Custom(custom_block_id) = block.id() {
2632                assert!(
2633                    !excerpt_blocks_1.contains(&custom_block_id),
2634                    "Should have no blocks from the folded buffer"
2635                );
2636                assert!(
2637                    excerpt_blocks_2.contains(&custom_block_id)
2638                        || excerpt_blocks_3.contains(&custom_block_id),
2639                    "Should have only blocks from unfolded buffers"
2640                );
2641            }
2642        }
2643        assert_eq!(
2644            1,
2645            blocks
2646                .iter()
2647                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2648                .count(),
2649            "Should have one folded block, producing a header of the second buffer"
2650        );
2651        assert_eq!(
2652            blocks_snapshot.text(),
2653            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2654        );
2655        assert_eq!(
2656            blocks_snapshot
2657                .row_infos(BlockRow(0))
2658                .map(|i| i.buffer_row)
2659                .collect::<Vec<_>>(),
2660            vec![
2661                None,
2662                None,
2663                None,
2664                None,
2665                None,
2666                Some(1),
2667                None,
2668                None,
2669                Some(2),
2670                None,
2671                Some(3),
2672                None,
2673                None,
2674                None,
2675                None,
2676                Some(4),
2677                None,
2678                Some(5),
2679                None,
2680            ]
2681        );
2682
2683        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2684        buffer.read_with(cx, |buffer, cx| {
2685            writer.fold_buffers([buffer_id_2], buffer, cx);
2686        });
2687        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2688        let blocks = blocks_snapshot
2689            .blocks_in_range(0..u32::MAX)
2690            .collect::<Vec<_>>();
2691        for (_, block) in &blocks {
2692            if let BlockId::Custom(custom_block_id) = block.id() {
2693                assert!(
2694                    !excerpt_blocks_1.contains(&custom_block_id),
2695                    "Should have no blocks from the folded buffer_1"
2696                );
2697                assert!(
2698                    !excerpt_blocks_2.contains(&custom_block_id),
2699                    "Should have no blocks from the folded buffer_2"
2700                );
2701                assert!(
2702                    excerpt_blocks_3.contains(&custom_block_id),
2703                    "Should have only blocks from unfolded buffers"
2704                );
2705            }
2706        }
2707        assert_eq!(
2708            2,
2709            blocks
2710                .iter()
2711                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2712                .count(),
2713            "Should have two folded blocks, producing headers"
2714        );
2715        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
2716        assert_eq!(
2717            blocks_snapshot
2718                .row_infos(BlockRow(0))
2719                .map(|i| i.buffer_row)
2720                .collect::<Vec<_>>(),
2721            vec![
2722                None,
2723                None,
2724                None,
2725                None,
2726                None,
2727                None,
2728                None,
2729                Some(4),
2730                None,
2731                Some(5),
2732                None,
2733            ]
2734        );
2735
2736        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2737        buffer.read_with(cx, |buffer, cx| {
2738            writer.unfold_buffers([buffer_id_1], buffer, cx);
2739        });
2740        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2741        let blocks = blocks_snapshot
2742            .blocks_in_range(0..u32::MAX)
2743            .collect::<Vec<_>>();
2744        for (_, block) in &blocks {
2745            if let BlockId::Custom(custom_block_id) = block.id() {
2746                assert!(
2747                    !excerpt_blocks_2.contains(&custom_block_id),
2748                    "Should have no blocks from the folded buffer_2"
2749                );
2750                assert!(
2751                    excerpt_blocks_1.contains(&custom_block_id)
2752                        || excerpt_blocks_3.contains(&custom_block_id),
2753                    "Should have only blocks from unfolded buffers"
2754                );
2755            }
2756        }
2757        assert_eq!(
2758            1,
2759            blocks
2760                .iter()
2761                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2762                .count(),
2763            "Should be back to a single folded buffer, producing a header for buffer_2"
2764        );
2765        assert_eq!(
2766            blocks_snapshot.text(),
2767            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
2768            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2769        );
2770        assert_eq!(
2771            blocks_snapshot
2772                .row_infos(BlockRow(0))
2773                .map(|i| i.buffer_row)
2774                .collect::<Vec<_>>(),
2775            vec![
2776                None,
2777                None,
2778                None,
2779                Some(0),
2780                None,
2781                None,
2782                None,
2783                None,
2784                None,
2785                Some(4),
2786                None,
2787                Some(5),
2788                None,
2789            ]
2790        );
2791
2792        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2793        buffer.read_with(cx, |buffer, cx| {
2794            writer.fold_buffers([buffer_id_3], buffer, cx);
2795        });
2796        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2797        let blocks = blocks_snapshot
2798            .blocks_in_range(0..u32::MAX)
2799            .collect::<Vec<_>>();
2800        for (_, block) in &blocks {
2801            if let BlockId::Custom(custom_block_id) = block.id() {
2802                assert!(
2803                    excerpt_blocks_1.contains(&custom_block_id),
2804                    "Should have no blocks from the folded buffer_1"
2805                );
2806                assert!(
2807                    !excerpt_blocks_2.contains(&custom_block_id),
2808                    "Should have only blocks from unfolded buffers"
2809                );
2810                assert!(
2811                    !excerpt_blocks_3.contains(&custom_block_id),
2812                    "Should have only blocks from unfolded buffers"
2813                );
2814            }
2815        }
2816
2817        assert_eq!(
2818            blocks_snapshot.text(),
2819            "\n\n\n111\n\n\n\n",
2820            "Should have a single, first buffer left after folding"
2821        );
2822        assert_eq!(
2823            blocks_snapshot
2824                .row_infos(BlockRow(0))
2825                .map(|i| i.buffer_row)
2826                .collect::<Vec<_>>(),
2827            vec![None, None, None, Some(0), None, None, None, None,]
2828        );
2829    }
2830
2831    #[gpui::test]
2832    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2833        cx.update(init_test);
2834
2835        let text = "111";
2836
2837        let buffer = cx.update(|cx| {
2838            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2839        });
2840        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2841        let buffer_ids = buffer_snapshot
2842            .excerpts()
2843            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2844            .dedup()
2845            .collect::<Vec<_>>();
2846        assert_eq!(buffer_ids.len(), 1);
2847        let buffer_id = buffer_ids[0];
2848
2849        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2850        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2851        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2852        let (_, wrap_snapshot) =
2853            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2854        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2855        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2856
2857        assert_eq!(blocks_snapshot.text(), "\n\n111");
2858
2859        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2860        buffer.read_with(cx, |buffer, cx| {
2861            writer.fold_buffers([buffer_id], buffer, cx);
2862        });
2863        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2864        let blocks = blocks_snapshot
2865            .blocks_in_range(0..u32::MAX)
2866            .collect::<Vec<_>>();
2867        assert_eq!(
2868            1,
2869            blocks
2870                .iter()
2871                .filter(|(_, block)| {
2872                    match block {
2873                        Block::FoldedBuffer { .. } => true,
2874                        _ => false,
2875                    }
2876                })
2877                .count(),
2878            "Should have one folded block, producing a header of the second buffer"
2879        );
2880        assert_eq!(blocks_snapshot.text(), "\n");
2881        assert_eq!(
2882            blocks_snapshot
2883                .row_infos(BlockRow(0))
2884                .map(|i| i.buffer_row)
2885                .collect::<Vec<_>>(),
2886            vec![None, None],
2887            "When fully folded, should be no buffer rows"
2888        );
2889    }
2890
2891    #[gpui::test(iterations = 100)]
2892    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2893        cx.update(init_test);
2894
2895        let operations = env::var("OPERATIONS")
2896            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2897            .unwrap_or(10);
2898
2899        let wrap_width = if rng.gen_bool(0.2) {
2900            None
2901        } else {
2902            Some(px(rng.gen_range(0.0..=100.0)))
2903        };
2904        let tab_size = 1.try_into().unwrap();
2905        let font_size = px(14.0);
2906        let buffer_start_header_height = rng.gen_range(1..=5);
2907        let excerpt_header_height = rng.gen_range(1..=5);
2908
2909        log::info!("Wrap width: {:?}", wrap_width);
2910        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2911        let is_singleton = rng.r#gen();
2912        let buffer = if is_singleton {
2913            let len = rng.gen_range(0..10);
2914            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2915            log::info!("initial singleton buffer text: {:?}", text);
2916            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2917        } else {
2918            cx.update(|cx| {
2919                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2920                log::info!(
2921                    "initial multi-buffer text: {:?}",
2922                    multibuffer.read(cx).read(cx).text()
2923                );
2924                multibuffer
2925            })
2926        };
2927
2928        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2929        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2930        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2931        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2932        let font = test_font();
2933        let (wrap_map, wraps_snapshot) =
2934            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
2935        let mut block_map = BlockMap::new(
2936            wraps_snapshot,
2937            buffer_start_header_height,
2938            excerpt_header_height,
2939        );
2940
2941        for _ in 0..operations {
2942            let mut buffer_edits = Vec::new();
2943            match rng.gen_range(0..=100) {
2944                0..=19 => {
2945                    let wrap_width = if rng.gen_bool(0.2) {
2946                        None
2947                    } else {
2948                        Some(px(rng.gen_range(0.0..=100.0)))
2949                    };
2950                    log::info!("Setting wrap width to {:?}", wrap_width);
2951                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2952                }
2953                20..=39 => {
2954                    let block_count = rng.gen_range(1..=5);
2955                    let block_properties = (0..block_count)
2956                        .map(|_| {
2957                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
2958                            let offset =
2959                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
2960                            let mut min_height = 0;
2961                            let placement = match rng.gen_range(0..3) {
2962                                0 => {
2963                                    min_height = 1;
2964                                    let start = buffer.anchor_after(offset);
2965                                    let end = buffer.anchor_after(buffer.clip_offset(
2966                                        rng.gen_range(offset..=buffer.len()),
2967                                        Bias::Left,
2968                                    ));
2969                                    BlockPlacement::Replace(start..=end)
2970                                }
2971                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
2972                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
2973                            };
2974
2975                            let height = rng.gen_range(min_height..5);
2976                            BlockProperties {
2977                                style: BlockStyle::Fixed,
2978                                placement,
2979                                height: Some(height),
2980                                render: Arc::new(|_| div().into_any()),
2981                                priority: 0,
2982                            }
2983                        })
2984                        .collect::<Vec<_>>();
2985
2986                    let (inlay_snapshot, inlay_edits) =
2987                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
2988                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2989                    let (tab_snapshot, tab_edits) =
2990                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
2991                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2992                        wrap_map.sync(tab_snapshot, tab_edits, cx)
2993                    });
2994                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2995                    let block_ids =
2996                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
2997                            placement: props.placement.clone(),
2998                            height: props.height,
2999                            style: props.style,
3000                            render: Arc::new(|_| div().into_any()),
3001                            priority: 0,
3002                        }));
3003
3004                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3005                        log::info!(
3006                            "inserted block {:?} with height {:?} and id {:?}",
3007                            block_properties
3008                                .placement
3009                                .as_ref()
3010                                .map(|p| p.to_point(&buffer_snapshot)),
3011                            block_properties.height,
3012                            block_id
3013                        );
3014                    }
3015                }
3016                40..=59 if !block_map.custom_blocks.is_empty() => {
3017                    let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
3018                    let block_ids_to_remove = block_map
3019                        .custom_blocks
3020                        .choose_multiple(&mut rng, block_count)
3021                        .map(|block| block.id)
3022                        .collect::<HashSet<_>>();
3023
3024                    let (inlay_snapshot, inlay_edits) =
3025                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3026                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3027                    let (tab_snapshot, tab_edits) =
3028                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3029                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3030                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3031                    });
3032                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3033                    log::info!(
3034                        "removing {} blocks: {:?}",
3035                        block_ids_to_remove.len(),
3036                        block_ids_to_remove
3037                    );
3038                    block_map.remove(block_ids_to_remove);
3039                }
3040                60..=79 => {
3041                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3042                        log::info!("Noop fold/unfold operation on a singleton buffer");
3043                        continue;
3044                    }
3045                    let (inlay_snapshot, inlay_edits) =
3046                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3047                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3048                    let (tab_snapshot, tab_edits) =
3049                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3050                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3051                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3052                    });
3053                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3054                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3055                        let folded_buffers = block_map
3056                            .0
3057                            .folded_buffers
3058                            .iter()
3059                            .cloned()
3060                            .collect::<Vec<_>>();
3061                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3062                        unfolded_buffers.dedup();
3063                        log::debug!("All buffers {unfolded_buffers:?}");
3064                        log::debug!("Folded buffers {folded_buffers:?}");
3065                        unfolded_buffers
3066                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3067                        (unfolded_buffers, folded_buffers)
3068                    });
3069                    let mut folded_count = folded_buffers.len();
3070                    let mut unfolded_count = unfolded_buffers.len();
3071
3072                    let fold = !unfolded_buffers.is_empty() && rng.gen_bool(0.5);
3073                    let unfold = !folded_buffers.is_empty() && rng.gen_bool(0.5);
3074                    if !fold && !unfold {
3075                        log::info!(
3076                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3077                        );
3078                        continue;
3079                    }
3080
3081                    buffer.update(cx, |buffer, cx| {
3082                        if fold {
3083                            let buffer_to_fold =
3084                                unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
3085                            log::info!("Folding {buffer_to_fold:?}");
3086                            let related_excerpts = buffer_snapshot
3087                                .excerpts()
3088                                .filter_map(|(excerpt_id, buffer, range)| {
3089                                    if buffer.remote_id() == buffer_to_fold {
3090                                        Some((
3091                                            excerpt_id,
3092                                            buffer
3093                                                .text_for_range(range.context)
3094                                                .collect::<String>(),
3095                                        ))
3096                                    } else {
3097                                        None
3098                                    }
3099                                })
3100                                .collect::<Vec<_>>();
3101                            log::info!(
3102                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3103                            );
3104                            folded_count += 1;
3105                            unfolded_count -= 1;
3106                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3107                        }
3108                        if unfold {
3109                            let buffer_to_unfold =
3110                                folded_buffers[rng.gen_range(0..folded_buffers.len())];
3111                            log::info!("Unfolding {buffer_to_unfold:?}");
3112                            unfolded_count += 1;
3113                            folded_count -= 1;
3114                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3115                        }
3116                        log::info!(
3117                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3118                        );
3119                    });
3120                }
3121                _ => {
3122                    buffer.update(cx, |buffer, cx| {
3123                        let mutation_count = rng.gen_range(1..=5);
3124                        let subscription = buffer.subscribe();
3125                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3126                        buffer_snapshot = buffer.snapshot(cx);
3127                        buffer_edits.extend(subscription.consume());
3128                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3129                    });
3130                }
3131            }
3132
3133            let (inlay_snapshot, inlay_edits) =
3134                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3135            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3136            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3137            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3138                wrap_map.sync(tab_snapshot, tab_edits, cx)
3139            });
3140            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3141            assert_eq!(
3142                blocks_snapshot.transforms.summary().input_rows,
3143                wraps_snapshot.max_point().row() + 1
3144            );
3145            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3146            log::info!("blocks text: {:?}", blocks_snapshot.text());
3147
3148            let mut expected_blocks = Vec::new();
3149            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3150                Some((
3151                    block.placement.to_wrap_row(&wraps_snapshot)?,
3152                    Block::Custom(block.clone()),
3153                ))
3154            }));
3155
3156            // Note that this needs to be synced with the related section in BlockMap::sync
3157            expected_blocks.extend(block_map.header_and_footer_blocks(
3158                &buffer_snapshot,
3159                0..,
3160                &wraps_snapshot,
3161            ));
3162
3163            BlockMap::sort_blocks(&mut expected_blocks);
3164
3165            for (placement, block) in &expected_blocks {
3166                log::info!(
3167                    "Block {:?} placement: {:?} Height: {:?}",
3168                    block.id(),
3169                    placement,
3170                    block.height()
3171                );
3172            }
3173
3174            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3175
3176            let input_buffer_rows = buffer_snapshot
3177                .row_infos(MultiBufferRow(0))
3178                .map(|row| row.buffer_row)
3179                .collect::<Vec<_>>();
3180            let mut expected_buffer_rows = Vec::new();
3181            let mut expected_text = String::new();
3182            let mut expected_block_positions = Vec::new();
3183            let mut expected_replaced_buffer_rows = HashSet::default();
3184            let input_text = wraps_snapshot.text();
3185
3186            // Loop over the input lines, creating (N - 1) empty lines for
3187            // blocks of height N.
3188            //
3189            // It's important to note that output *starts* as one empty line,
3190            // so we special case row 0 to assume a leading '\n'.
3191            //
3192            // Linehood is the birthright of strings.
3193            let mut input_text_lines = input_text.split('\n').enumerate().peekable();
3194            let mut block_row = 0;
3195            while let Some((wrap_row, input_line)) = input_text_lines.next() {
3196                let wrap_row = wrap_row as u32;
3197                let multibuffer_row = wraps_snapshot
3198                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3199                    .row;
3200
3201                // Create empty lines for the above block
3202                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3203                    if placement.start().0 == wrap_row && block.place_above() {
3204                        let (_, block) = sorted_blocks_iter.next().unwrap();
3205                        expected_block_positions.push((block_row, block.id()));
3206                        if block.height() > 0 {
3207                            let text = "\n".repeat((block.height() - 1) as usize);
3208                            if block_row > 0 {
3209                                expected_text.push('\n')
3210                            }
3211                            expected_text.push_str(&text);
3212                            for _ in 0..block.height() {
3213                                expected_buffer_rows.push(None);
3214                            }
3215                            block_row += block.height();
3216                        }
3217                    } else {
3218                        break;
3219                    }
3220                }
3221
3222                // Skip lines within replace blocks, then create empty lines for the replace block's height
3223                let mut is_in_replace_block = false;
3224                if let Some((BlockPlacement::Replace(replace_range), block)) =
3225                    sorted_blocks_iter.peek()
3226                    && wrap_row >= replace_range.start().0
3227                {
3228                    is_in_replace_block = true;
3229
3230                    if wrap_row == replace_range.start().0 {
3231                        if matches!(block, Block::FoldedBuffer { .. }) {
3232                            expected_buffer_rows.push(None);
3233                        } else {
3234                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
3235                        }
3236                    }
3237
3238                    if wrap_row == replace_range.end().0 {
3239                        expected_block_positions.push((block_row, block.id()));
3240                        let text = "\n".repeat((block.height() - 1) as usize);
3241                        if block_row > 0 {
3242                            expected_text.push('\n');
3243                        }
3244                        expected_text.push_str(&text);
3245
3246                        for _ in 1..block.height() {
3247                            expected_buffer_rows.push(None);
3248                        }
3249                        block_row += block.height();
3250
3251                        sorted_blocks_iter.next();
3252                    }
3253                }
3254
3255                if is_in_replace_block {
3256                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3257                } else {
3258                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3259                    let soft_wrapped = wraps_snapshot
3260                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3261                        .column()
3262                        > 0;
3263                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3264                    if block_row > 0 {
3265                        expected_text.push('\n');
3266                    }
3267                    expected_text.push_str(input_line);
3268                    block_row += 1;
3269                }
3270
3271                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3272                    if placement.end().0 == wrap_row && block.place_below() {
3273                        let (_, block) = sorted_blocks_iter.next().unwrap();
3274                        expected_block_positions.push((block_row, block.id()));
3275                        if block.height() > 0 {
3276                            let text = "\n".repeat((block.height() - 1) as usize);
3277                            if block_row > 0 {
3278                                expected_text.push('\n')
3279                            }
3280                            expected_text.push_str(&text);
3281                            for _ in 0..block.height() {
3282                                expected_buffer_rows.push(None);
3283                            }
3284                            block_row += block.height();
3285                        }
3286                    } else {
3287                        break;
3288                    }
3289                }
3290            }
3291
3292            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3293            let expected_row_count = expected_lines.len();
3294            log::info!("expected text: {expected_text:?}");
3295
3296            assert_eq!(
3297                blocks_snapshot.max_point().row + 1,
3298                expected_row_count as u32,
3299                "actual row count != expected row count",
3300            );
3301            assert_eq!(
3302                blocks_snapshot.text(),
3303                expected_text,
3304                "actual text != expected text",
3305            );
3306
3307            for start_row in 0..expected_row_count {
3308                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
3309                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3310                if end_row < expected_row_count {
3311                    expected_text.push('\n');
3312                }
3313
3314                let actual_text = blocks_snapshot
3315                    .chunks(
3316                        start_row as u32..end_row as u32,
3317                        false,
3318                        false,
3319                        Highlights::default(),
3320                    )
3321                    .map(|chunk| chunk.text)
3322                    .collect::<String>();
3323                assert_eq!(
3324                    actual_text,
3325                    expected_text,
3326                    "incorrect text starting row row range {:?}",
3327                    start_row..end_row
3328                );
3329                assert_eq!(
3330                    blocks_snapshot
3331                        .row_infos(BlockRow(start_row as u32))
3332                        .map(|row_info| row_info.buffer_row)
3333                        .collect::<Vec<_>>(),
3334                    &expected_buffer_rows[start_row..],
3335                    "incorrect buffer_rows starting at row {:?}",
3336                    start_row
3337                );
3338            }
3339
3340            assert_eq!(
3341                blocks_snapshot
3342                    .blocks_in_range(0..(expected_row_count as u32))
3343                    .map(|(row, block)| (row, block.id()))
3344                    .collect::<Vec<_>>(),
3345                expected_block_positions,
3346                "invalid blocks_in_range({:?})",
3347                0..expected_row_count
3348            );
3349
3350            for (_, expected_block) in
3351                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3352            {
3353                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3354                assert_eq!(
3355                    actual_block.map(|block| block.id()),
3356                    Some(expected_block.id())
3357                );
3358            }
3359
3360            for (block_row, block_id) in expected_block_positions {
3361                if let BlockId::Custom(block_id) = block_id {
3362                    assert_eq!(
3363                        blocks_snapshot.row_for_block(block_id),
3364                        Some(BlockRow(block_row))
3365                    );
3366                }
3367            }
3368
3369            let mut expected_longest_rows = Vec::new();
3370            let mut longest_line_len = -1_isize;
3371            for (row, line) in expected_lines.iter().enumerate() {
3372                let row = row as u32;
3373
3374                assert_eq!(
3375                    blocks_snapshot.line_len(BlockRow(row)),
3376                    line.len() as u32,
3377                    "invalid line len for row {}",
3378                    row
3379                );
3380
3381                let line_char_count = line.chars().count() as isize;
3382                match line_char_count.cmp(&longest_line_len) {
3383                    Ordering::Less => {}
3384                    Ordering::Equal => expected_longest_rows.push(row),
3385                    Ordering::Greater => {
3386                        longest_line_len = line_char_count;
3387                        expected_longest_rows.clear();
3388                        expected_longest_rows.push(row);
3389                    }
3390                }
3391            }
3392
3393            let longest_row = blocks_snapshot.longest_row();
3394            assert!(
3395                expected_longest_rows.contains(&longest_row),
3396                "incorrect longest row {}. expected {:?} with length {}",
3397                longest_row,
3398                expected_longest_rows,
3399                longest_line_len,
3400            );
3401
3402            for _ in 0..10 {
3403                let end_row = rng.gen_range(1..=expected_lines.len());
3404                let start_row = rng.gen_range(0..end_row);
3405
3406                let mut expected_longest_rows_in_range = vec![];
3407                let mut longest_line_len_in_range = 0;
3408
3409                let mut row = start_row as u32;
3410                for line in &expected_lines[start_row..end_row] {
3411                    let line_char_count = line.chars().count() as isize;
3412                    match line_char_count.cmp(&longest_line_len_in_range) {
3413                        Ordering::Less => {}
3414                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3415                        Ordering::Greater => {
3416                            longest_line_len_in_range = line_char_count;
3417                            expected_longest_rows_in_range.clear();
3418                            expected_longest_rows_in_range.push(row);
3419                        }
3420                    }
3421                    row += 1;
3422                }
3423
3424                let longest_row_in_range = blocks_snapshot
3425                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3426                assert!(
3427                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3428                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3429                    longest_row,
3430                    start_row..end_row,
3431                    expected_longest_rows_in_range,
3432                    longest_line_len_in_range,
3433                );
3434            }
3435
3436            // Ensure that conversion between block points and wrap points is stable.
3437            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3438                let wrap_point = WrapPoint::new(row, 0);
3439                let block_point = blocks_snapshot.to_block_point(wrap_point);
3440                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3441                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3442                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3443                assert_eq!(
3444                    blocks_snapshot.to_block_point(right_wrap_point),
3445                    block_point
3446                );
3447            }
3448
3449            let mut block_point = BlockPoint::new(0, 0);
3450            for c in expected_text.chars() {
3451                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3452                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3453                assert_eq!(
3454                    blocks_snapshot
3455                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3456                    left_point,
3457                    "block point: {:?}, wrap point: {:?}",
3458                    block_point,
3459                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3460                );
3461                assert_eq!(
3462                    left_buffer_point,
3463                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3464                    "{:?} is not valid in buffer coordinates",
3465                    left_point
3466                );
3467
3468                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3469                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3470                assert_eq!(
3471                    blocks_snapshot
3472                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3473                    right_point,
3474                    "block point: {:?}, wrap point: {:?}",
3475                    block_point,
3476                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3477                );
3478                assert_eq!(
3479                    right_buffer_point,
3480                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3481                    "{:?} is not valid in buffer coordinates",
3482                    right_point
3483                );
3484
3485                if c == '\n' {
3486                    block_point.0 += Point::new(1, 0);
3487                } else {
3488                    block_point.column += c.len_utf8() as u32;
3489                }
3490            }
3491
3492            for buffer_row in 0..=buffer_snapshot.max_point().row {
3493                let buffer_row = MultiBufferRow(buffer_row);
3494                assert_eq!(
3495                    blocks_snapshot.is_line_replaced(buffer_row),
3496                    expected_replaced_buffer_rows.contains(&buffer_row),
3497                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3498                );
3499            }
3500        }
3501    }
3502
3503    #[gpui::test]
3504    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
3505        cx.update(init_test);
3506
3507        let text = "abc\ndef\nghi\njkl\nmno";
3508        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3509        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3510        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3511        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3512        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3513        let (_wrap_map, wraps_snapshot) =
3514            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3515        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3516
3517        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3518        let _block_id = writer.insert(vec![BlockProperties {
3519            style: BlockStyle::Fixed,
3520            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3521            height: Some(1),
3522            render: Arc::new(|_| div().into_any()),
3523            priority: 0,
3524        }])[0];
3525
3526        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
3527        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3528
3529        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3530        writer.remove_intersecting_replace_blocks(
3531            [buffer_snapshot.anchor_after(Point::new(1, 0))
3532                ..buffer_snapshot.anchor_after(Point::new(1, 0))],
3533            false,
3534        );
3535        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
3536        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3537    }
3538
3539    fn init_test(cx: &mut gpui::App) {
3540        let settings = SettingsStore::test(cx);
3541        cx.set_global(settings);
3542        theme::init(theme::LoadThemes::JustBase, cx);
3543        assets::Assets.load_test_fonts(cx);
3544    }
3545
3546    impl Block {
3547        fn as_custom(&self) -> Option<&CustomBlock> {
3548            match self {
3549                Block::Custom(block) => Some(block),
3550                _ => None,
3551            }
3552        }
3553    }
3554
3555    impl BlockSnapshot {
3556        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3557            self.wrap_snapshot
3558                .to_point(self.to_wrap_point(point, bias), bias)
3559        }
3560    }
3561}