block_map.rs

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