block_map.rs

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