block_map.rs

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