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 sticky_header_excerpt(&self, top_row: u32) -> Option<StickyHeaderExcerpt<'_>> {
1415        let mut cursor = self.transforms.cursor::<BlockRow>(&());
1416        cursor.seek(&BlockRow(top_row), Bias::Left, &());
1417
1418        while let Some(transform) = cursor.item() {
1419            let start = cursor.start().0;
1420            let end = cursor.end(&()).0;
1421
1422            match &transform.block {
1423                Some(Block::ExcerptBoundary {
1424                    prev_excerpt,
1425                    next_excerpt,
1426                    starts_new_buffer,
1427                    show_excerpt_controls,
1428                    ..
1429                }) => {
1430                    let matches_start = if *show_excerpt_controls && prev_excerpt.is_some() {
1431                        start < top_row
1432                    } else {
1433                        start <= top_row
1434                    };
1435
1436                    if matches_start && top_row <= end {
1437                        return next_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
1438                            next_buffer_row: None,
1439                            next_excerpt_controls_present: *show_excerpt_controls,
1440                            excerpt,
1441                        });
1442                    }
1443
1444                    let next_buffer_row = if *starts_new_buffer { Some(end) } else { None };
1445
1446                    return prev_excerpt.as_ref().map(|excerpt| StickyHeaderExcerpt {
1447                        excerpt,
1448                        next_buffer_row,
1449                        next_excerpt_controls_present: *show_excerpt_controls,
1450                    });
1451                }
1452                Some(Block::FoldedBuffer {
1453                    prev_excerpt: Some(excerpt),
1454                    ..
1455                }) if top_row <= start => {
1456                    return Some(StickyHeaderExcerpt {
1457                        next_buffer_row: Some(end),
1458                        next_excerpt_controls_present: false,
1459                        excerpt,
1460                    });
1461                }
1462                Some(Block::FoldedBuffer { .. }) | Some(Block::Custom(_)) | None => {}
1463            }
1464
1465            // This is needed to iterate past None / FoldedBuffer / Custom blocks. For FoldedBuffer,
1466            // if scrolled slightly past the header of a folded block, the next block is needed for
1467            // the sticky header.
1468            cursor.next(&());
1469        }
1470
1471        None
1472    }
1473
1474    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1475        let buffer = self.wrap_snapshot.buffer_snapshot();
1476        let wrap_point = match block_id {
1477            BlockId::Custom(custom_block_id) => {
1478                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
1479                return Some(Block::Custom(custom_block.clone()));
1480            }
1481            BlockId::ExcerptBoundary(next_excerpt_id) => {
1482                if let Some(next_excerpt_id) = next_excerpt_id {
1483                    let excerpt_range = buffer.range_for_excerpt::<Point>(next_excerpt_id)?;
1484                    self.wrap_snapshot
1485                        .make_wrap_point(excerpt_range.start, Bias::Left)
1486                } else {
1487                    self.wrap_snapshot
1488                        .make_wrap_point(buffer.max_point(), Bias::Left)
1489                }
1490            }
1491            BlockId::FoldedBuffer(excerpt_id) => self.wrap_snapshot.make_wrap_point(
1492                buffer.range_for_excerpt::<Point>(excerpt_id)?.start,
1493                Bias::Left,
1494            ),
1495        };
1496        let wrap_row = WrapRow(wrap_point.row());
1497
1498        let mut cursor = self.transforms.cursor::<WrapRow>(&());
1499        cursor.seek(&wrap_row, Bias::Left, &());
1500
1501        while let Some(transform) = cursor.item() {
1502            if let Some(block) = transform.block.as_ref() {
1503                if block.id() == block_id {
1504                    return Some(block.clone());
1505                }
1506            } else if *cursor.start() > wrap_row {
1507                break;
1508            }
1509
1510            cursor.next(&());
1511        }
1512
1513        None
1514    }
1515
1516    pub fn max_point(&self) -> BlockPoint {
1517        let row = self.transforms.summary().output_rows.saturating_sub(1);
1518        BlockPoint::new(row, self.line_len(BlockRow(row)))
1519    }
1520
1521    pub fn longest_row(&self) -> u32 {
1522        self.transforms.summary().longest_row
1523    }
1524
1525    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
1526        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1527        cursor.seek(&range.start, Bias::Right, &());
1528
1529        let mut longest_row = range.start;
1530        let mut longest_row_chars = 0;
1531        if let Some(transform) = cursor.item() {
1532            if transform.block.is_none() {
1533                let (output_start, input_start) = cursor.start();
1534                let overshoot = range.start.0 - output_start.0;
1535                let wrap_start_row = input_start.0 + overshoot;
1536                let wrap_end_row = cmp::min(
1537                    input_start.0 + (range.end.0 - output_start.0),
1538                    cursor.end(&()).1 .0,
1539                );
1540                let summary = self
1541                    .wrap_snapshot
1542                    .text_summary_for_range(wrap_start_row..wrap_end_row);
1543                longest_row = BlockRow(range.start.0 + summary.longest_row);
1544                longest_row_chars = summary.longest_row_chars;
1545            }
1546            cursor.next(&());
1547        }
1548
1549        let cursor_start_row = cursor.start().0;
1550        if range.end > cursor_start_row {
1551            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right, &());
1552            if summary.longest_row_chars > longest_row_chars {
1553                longest_row = BlockRow(cursor_start_row.0 + summary.longest_row);
1554                longest_row_chars = summary.longest_row_chars;
1555            }
1556
1557            if let Some(transform) = cursor.item() {
1558                if transform.block.is_none() {
1559                    let (output_start, input_start) = cursor.start();
1560                    let overshoot = range.end.0 - output_start.0;
1561                    let wrap_start_row = input_start.0;
1562                    let wrap_end_row = input_start.0 + overshoot;
1563                    let summary = self
1564                        .wrap_snapshot
1565                        .text_summary_for_range(wrap_start_row..wrap_end_row);
1566                    if summary.longest_row_chars > longest_row_chars {
1567                        longest_row = BlockRow(output_start.0 + summary.longest_row);
1568                    }
1569                }
1570            }
1571        }
1572
1573        longest_row
1574    }
1575
1576    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1577        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1578        cursor.seek(&BlockRow(row.0), Bias::Right, &());
1579        if let Some(transform) = cursor.item() {
1580            let (output_start, input_start) = cursor.start();
1581            let overshoot = row.0 - output_start.0;
1582            if transform.block.is_some() {
1583                0
1584            } else {
1585                self.wrap_snapshot.line_len(input_start.0 + overshoot)
1586            }
1587        } else if row.0 == 0 {
1588            0
1589        } else {
1590            panic!("row out of range");
1591        }
1592    }
1593
1594    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1595        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1596        cursor.seek(&row, Bias::Right, &());
1597        cursor.item().map_or(false, |t| t.block.is_some())
1598    }
1599
1600    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
1601        let wrap_point = self
1602            .wrap_snapshot
1603            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
1604        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1605        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1606        cursor.item().map_or(false, |transform| {
1607            transform
1608                .block
1609                .as_ref()
1610                .map_or(false, |block| block.is_replacement())
1611        })
1612    }
1613
1614    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1615        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1616        cursor.seek(&BlockRow(point.row), Bias::Right, &());
1617
1618        let max_input_row = WrapRow(self.transforms.summary().input_rows);
1619        let mut search_left =
1620            (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
1621        let mut reversed = false;
1622
1623        loop {
1624            if let Some(transform) = cursor.item() {
1625                let (output_start_row, input_start_row) = cursor.start();
1626                let (output_end_row, input_end_row) = cursor.end(&());
1627                let output_start = Point::new(output_start_row.0, 0);
1628                let input_start = Point::new(input_start_row.0, 0);
1629                let input_end = Point::new(input_end_row.0, 0);
1630
1631                match transform.block.as_ref() {
1632                    Some(block) => {
1633                        if block.is_replacement() {
1634                            if ((bias == Bias::Left || search_left) && output_start <= point.0)
1635                                || (!search_left && output_start >= point.0)
1636                            {
1637                                return BlockPoint(output_start);
1638                            }
1639                        }
1640                    }
1641                    None => {
1642                        let input_point = if point.row >= output_end_row.0 {
1643                            let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1644                            self.wrap_snapshot
1645                                .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1646                        } else {
1647                            let output_overshoot = point.0.saturating_sub(output_start);
1648                            self.wrap_snapshot
1649                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
1650                        };
1651
1652                        if (input_start..input_end).contains(&input_point.0) {
1653                            let input_overshoot = input_point.0.saturating_sub(input_start);
1654                            return BlockPoint(output_start + input_overshoot);
1655                        }
1656                    }
1657                }
1658
1659                if search_left {
1660                    cursor.prev(&());
1661                } else {
1662                    cursor.next(&());
1663                }
1664            } else if reversed {
1665                return self.max_point();
1666            } else {
1667                reversed = true;
1668                search_left = !search_left;
1669                cursor.seek(&BlockRow(point.row), Bias::Right, &());
1670            }
1671        }
1672    }
1673
1674    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1675        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1676        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1677        if let Some(transform) = cursor.item() {
1678            if transform.block.is_some() {
1679                BlockPoint::new(cursor.start().1 .0, 0)
1680            } else {
1681                let (input_start_row, output_start_row) = cursor.start();
1682                let input_start = Point::new(input_start_row.0, 0);
1683                let output_start = Point::new(output_start_row.0, 0);
1684                let input_overshoot = wrap_point.0 - input_start;
1685                BlockPoint(output_start + input_overshoot)
1686            }
1687        } else {
1688            self.max_point()
1689        }
1690    }
1691
1692    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
1693        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1694        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
1695        if let Some(transform) = cursor.item() {
1696            match transform.block.as_ref() {
1697                Some(block) => {
1698                    if block.place_below() {
1699                        let wrap_row = cursor.start().1 .0 - 1;
1700                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1701                    } else if block.place_above() {
1702                        WrapPoint::new(cursor.start().1 .0, 0)
1703                    } else if bias == Bias::Left {
1704                        WrapPoint::new(cursor.start().1 .0, 0)
1705                    } else {
1706                        let wrap_row = cursor.end(&()).1 .0 - 1;
1707                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1708                    }
1709                }
1710                None => {
1711                    let overshoot = block_point.row - cursor.start().0 .0;
1712                    let wrap_row = cursor.start().1 .0 + overshoot;
1713                    WrapPoint::new(wrap_row, block_point.column)
1714                }
1715            }
1716        } else {
1717            self.wrap_snapshot.max_point()
1718        }
1719    }
1720}
1721
1722impl<'a> BlockChunks<'a> {
1723    /// Go to the next transform
1724    fn advance(&mut self) {
1725        self.transforms.next(&());
1726        while let Some(transform) = self.transforms.item() {
1727            if transform
1728                .block
1729                .as_ref()
1730                .map_or(false, |block| block.height() == 0)
1731            {
1732                self.transforms.next(&());
1733            } else {
1734                break;
1735            }
1736        }
1737
1738        if self
1739            .transforms
1740            .item()
1741            .map_or(false, |transform| transform.block.is_none())
1742        {
1743            let start_input_row = self.transforms.start().1 .0;
1744            let start_output_row = self.transforms.start().0 .0;
1745            if start_output_row < self.max_output_row {
1746                let end_input_row = cmp::min(
1747                    self.transforms.end(&()).1 .0,
1748                    start_input_row + (self.max_output_row - start_output_row),
1749                );
1750                self.input_chunks.seek(start_input_row..end_input_row);
1751            }
1752            self.input_chunk = Chunk::default();
1753        }
1754    }
1755}
1756
1757pub struct StickyHeaderExcerpt<'a> {
1758    pub excerpt: &'a ExcerptInfo,
1759    pub next_excerpt_controls_present: bool,
1760    pub next_buffer_row: Option<u32>,
1761}
1762
1763impl<'a> Iterator for BlockChunks<'a> {
1764    type Item = Chunk<'a>;
1765
1766    fn next(&mut self) -> Option<Self::Item> {
1767        if self.output_row >= self.max_output_row {
1768            return None;
1769        }
1770
1771        let transform = self.transforms.item()?;
1772        if transform.block.is_some() {
1773            let block_start = self.transforms.start().0 .0;
1774            let mut block_end = self.transforms.end(&()).0 .0;
1775            self.advance();
1776            if self.transforms.item().is_none() {
1777                block_end -= 1;
1778            }
1779
1780            let start_in_block = self.output_row - block_start;
1781            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1782            let line_count = end_in_block - start_in_block;
1783            self.output_row += line_count;
1784
1785            return Some(Chunk {
1786                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1787                ..Default::default()
1788            });
1789        }
1790
1791        if self.input_chunk.text.is_empty() {
1792            if let Some(input_chunk) = self.input_chunks.next() {
1793                self.input_chunk = input_chunk;
1794            } else {
1795                if self.output_row < self.max_output_row {
1796                    self.output_row += 1;
1797                    self.advance();
1798                    if self.transforms.item().is_some() {
1799                        return Some(Chunk {
1800                            text: "\n",
1801                            ..Default::default()
1802                        });
1803                    }
1804                }
1805                return None;
1806            }
1807        }
1808
1809        let transform_end = self.transforms.end(&()).0 .0;
1810        let (prefix_rows, prefix_bytes) =
1811            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1812        self.output_row += prefix_rows;
1813
1814        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1815        self.input_chunk.text = suffix;
1816        if self.output_row == transform_end {
1817            self.advance();
1818        }
1819
1820        if self.masked {
1821            // Not great for multibyte text because to keep cursor math correct we
1822            // need to have the same number of bytes in the input as output.
1823            let chars = prefix.chars().count();
1824            let bullet_len = chars;
1825            prefix = &BULLETS[..bullet_len];
1826        }
1827
1828        Some(Chunk {
1829            text: prefix,
1830            ..self.input_chunk.clone()
1831        })
1832    }
1833}
1834
1835impl<'a> Iterator for BlockBufferRows<'a> {
1836    type Item = Option<u32>;
1837
1838    fn next(&mut self) -> Option<Self::Item> {
1839        if self.started {
1840            self.output_row.0 += 1;
1841        } else {
1842            self.started = true;
1843        }
1844
1845        if self.output_row.0 >= self.transforms.end(&()).0 .0 {
1846            self.transforms.next(&());
1847            while let Some(transform) = self.transforms.item() {
1848                if transform
1849                    .block
1850                    .as_ref()
1851                    .map_or(false, |block| block.height() == 0)
1852                {
1853                    self.transforms.next(&());
1854                } else {
1855                    break;
1856                }
1857            }
1858
1859            let transform = self.transforms.item()?;
1860            if transform
1861                .block
1862                .as_ref()
1863                .map_or(true, |block| block.is_replacement())
1864            {
1865                self.input_buffer_rows.seek(self.transforms.start().1 .0);
1866            }
1867        }
1868
1869        let transform = self.transforms.item()?;
1870        if let Some(block) = transform.block.as_ref() {
1871            if block.is_replacement() && self.transforms.start().0 == self.output_row {
1872                if matches!(block, Block::FoldedBuffer { .. }) {
1873                    Some(None)
1874                } else {
1875                    Some(self.input_buffer_rows.next().unwrap())
1876                }
1877            } else {
1878                Some(None)
1879            }
1880        } else {
1881            Some(self.input_buffer_rows.next().unwrap())
1882        }
1883    }
1884}
1885
1886impl sum_tree::Item for Transform {
1887    type Summary = TransformSummary;
1888
1889    fn summary(&self, _cx: &()) -> Self::Summary {
1890        self.summary.clone()
1891    }
1892}
1893
1894impl sum_tree::Summary for TransformSummary {
1895    type Context = ();
1896
1897    fn zero(_cx: &()) -> Self {
1898        Default::default()
1899    }
1900
1901    fn add_summary(&mut self, summary: &Self, _: &()) {
1902        if summary.longest_row_chars > self.longest_row_chars {
1903            self.longest_row = self.output_rows + summary.longest_row;
1904            self.longest_row_chars = summary.longest_row_chars;
1905        }
1906        self.input_rows += summary.input_rows;
1907        self.output_rows += summary.output_rows;
1908    }
1909}
1910
1911impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1912    fn zero(_cx: &()) -> Self {
1913        Default::default()
1914    }
1915
1916    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1917        self.0 += summary.input_rows;
1918    }
1919}
1920
1921impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1922    fn zero(_cx: &()) -> Self {
1923        Default::default()
1924    }
1925
1926    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1927        self.0 += summary.output_rows;
1928    }
1929}
1930
1931impl<'a> Deref for BlockContext<'a, '_> {
1932    type Target = WindowContext<'a>;
1933
1934    fn deref(&self) -> &Self::Target {
1935        self.context
1936    }
1937}
1938
1939impl DerefMut for BlockContext<'_, '_> {
1940    fn deref_mut(&mut self) -> &mut Self::Target {
1941        self.context
1942    }
1943}
1944
1945impl CustomBlock {
1946    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1947        self.render.lock()(cx)
1948    }
1949
1950    pub fn start(&self) -> Anchor {
1951        *self.placement.start()
1952    }
1953
1954    pub fn end(&self) -> Anchor {
1955        *self.placement.end()
1956    }
1957
1958    pub fn style(&self) -> BlockStyle {
1959        self.style
1960    }
1961}
1962
1963impl Debug for CustomBlock {
1964    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1965        f.debug_struct("Block")
1966            .field("id", &self.id)
1967            .field("placement", &self.placement)
1968            .field("height", &self.height)
1969            .field("style", &self.style)
1970            .field("priority", &self.priority)
1971            .finish_non_exhaustive()
1972    }
1973}
1974
1975// Count the number of bytes prior to a target point. If the string doesn't contain the target
1976// point, return its total extent. Otherwise return the target point itself.
1977fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1978    let mut row = 0;
1979    let mut offset = 0;
1980    for (ix, line) in s.split('\n').enumerate() {
1981        if ix > 0 {
1982            row += 1;
1983            offset += 1;
1984        }
1985        if row >= target {
1986            break;
1987        }
1988        offset += line.len();
1989    }
1990    (row, offset)
1991}
1992
1993#[cfg(test)]
1994mod tests {
1995    use super::*;
1996    use crate::display_map::{
1997        fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
1998    };
1999    use gpui::{div, font, px, AppContext, Context as _, Element};
2000    use itertools::Itertools;
2001    use language::{Buffer, Capability};
2002    use multi_buffer::{ExcerptRange, MultiBuffer};
2003    use rand::prelude::*;
2004    use settings::SettingsStore;
2005    use std::env;
2006    use util::RandomCharIter;
2007
2008    #[gpui::test]
2009    fn test_offset_for_row() {
2010        assert_eq!(offset_for_row("", 0), (0, 0));
2011        assert_eq!(offset_for_row("", 1), (0, 0));
2012        assert_eq!(offset_for_row("abcd", 0), (0, 0));
2013        assert_eq!(offset_for_row("abcd", 1), (0, 4));
2014        assert_eq!(offset_for_row("\n", 0), (0, 0));
2015        assert_eq!(offset_for_row("\n", 1), (1, 1));
2016        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
2017        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
2018        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
2019        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
2020    }
2021
2022    #[gpui::test]
2023    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2024        cx.update(init_test);
2025
2026        let text = "aaa\nbbb\nccc\nddd";
2027
2028        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2029        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2030        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2031        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2032        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2033        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2034        let (wrap_map, wraps_snapshot) =
2035            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2036        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2037
2038        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2039        let block_ids = writer.insert(vec![
2040            BlockProperties {
2041                style: BlockStyle::Fixed,
2042                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2043                height: 1,
2044                render: Arc::new(|_| div().into_any()),
2045                priority: 0,
2046            },
2047            BlockProperties {
2048                style: BlockStyle::Fixed,
2049                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2050                height: 2,
2051                render: Arc::new(|_| div().into_any()),
2052                priority: 0,
2053            },
2054            BlockProperties {
2055                style: BlockStyle::Fixed,
2056                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2057                height: 3,
2058                render: Arc::new(|_| div().into_any()),
2059                priority: 0,
2060            },
2061        ]);
2062
2063        let snapshot = block_map.read(wraps_snapshot, Default::default());
2064        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2065
2066        let blocks = snapshot
2067            .blocks_in_range(0..8)
2068            .map(|(start_row, block)| {
2069                let block = block.as_custom().unwrap();
2070                (start_row..start_row + block.height, block.id)
2071            })
2072            .collect::<Vec<_>>();
2073
2074        // When multiple blocks are on the same line, the newer blocks appear first.
2075        assert_eq!(
2076            blocks,
2077            &[
2078                (1..2, block_ids[0]),
2079                (2..4, block_ids[1]),
2080                (7..10, block_ids[2]),
2081            ]
2082        );
2083
2084        assert_eq!(
2085            snapshot.to_block_point(WrapPoint::new(0, 3)),
2086            BlockPoint::new(0, 3)
2087        );
2088        assert_eq!(
2089            snapshot.to_block_point(WrapPoint::new(1, 0)),
2090            BlockPoint::new(4, 0)
2091        );
2092        assert_eq!(
2093            snapshot.to_block_point(WrapPoint::new(3, 3)),
2094            BlockPoint::new(6, 3)
2095        );
2096
2097        assert_eq!(
2098            snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
2099            WrapPoint::new(0, 3)
2100        );
2101        assert_eq!(
2102            snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
2103            WrapPoint::new(1, 0)
2104        );
2105        assert_eq!(
2106            snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
2107            WrapPoint::new(1, 0)
2108        );
2109        assert_eq!(
2110            snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
2111            WrapPoint::new(3, 3)
2112        );
2113
2114        assert_eq!(
2115            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
2116            BlockPoint::new(0, 3)
2117        );
2118        assert_eq!(
2119            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
2120            BlockPoint::new(4, 0)
2121        );
2122        assert_eq!(
2123            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
2124            BlockPoint::new(0, 3)
2125        );
2126        assert_eq!(
2127            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
2128            BlockPoint::new(4, 0)
2129        );
2130        assert_eq!(
2131            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
2132            BlockPoint::new(4, 0)
2133        );
2134        assert_eq!(
2135            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
2136            BlockPoint::new(4, 0)
2137        );
2138        assert_eq!(
2139            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
2140            BlockPoint::new(6, 3)
2141        );
2142        assert_eq!(
2143            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
2144            BlockPoint::new(6, 3)
2145        );
2146        assert_eq!(
2147            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
2148            BlockPoint::new(6, 3)
2149        );
2150        assert_eq!(
2151            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
2152            BlockPoint::new(6, 3)
2153        );
2154
2155        assert_eq!(
2156            snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2157            &[
2158                Some(0),
2159                None,
2160                None,
2161                None,
2162                Some(1),
2163                Some(2),
2164                Some(3),
2165                None,
2166                None,
2167                None
2168            ]
2169        );
2170
2171        // Insert a line break, separating two block decorations into separate lines.
2172        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2173            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2174            buffer.snapshot(cx)
2175        });
2176
2177        let (inlay_snapshot, inlay_edits) =
2178            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2179        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2180        let (tab_snapshot, tab_edits) =
2181            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2182        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2183            wrap_map.sync(tab_snapshot, tab_edits, cx)
2184        });
2185        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
2186        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2187    }
2188
2189    #[gpui::test]
2190    fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
2191        init_test(cx);
2192
2193        let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
2194        let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
2195        let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
2196
2197        let mut excerpt_ids = Vec::new();
2198        let multi_buffer = cx.new_model(|cx| {
2199            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2200            excerpt_ids.extend(multi_buffer.push_excerpts(
2201                buffer1.clone(),
2202                [ExcerptRange {
2203                    context: 0..buffer1.read(cx).len(),
2204                    primary: None,
2205                }],
2206                cx,
2207            ));
2208            excerpt_ids.extend(multi_buffer.push_excerpts(
2209                buffer2.clone(),
2210                [ExcerptRange {
2211                    context: 0..buffer2.read(cx).len(),
2212                    primary: None,
2213                }],
2214                cx,
2215            ));
2216            excerpt_ids.extend(multi_buffer.push_excerpts(
2217                buffer3.clone(),
2218                [ExcerptRange {
2219                    context: 0..buffer3.read(cx).len(),
2220                    primary: None,
2221                }],
2222                cx,
2223            ));
2224
2225            multi_buffer
2226        });
2227
2228        let font = font("Helvetica");
2229        let font_size = px(14.);
2230        let font_id = cx.text_system().resolve_font(&font);
2231        let mut wrap_width = px(0.);
2232        for c in "Buff".chars() {
2233            wrap_width += cx
2234                .text_system()
2235                .advance(font_id, font_size, c)
2236                .unwrap()
2237                .width;
2238        }
2239
2240        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2241        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
2242        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2243        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2244        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2245
2246        let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2247        let snapshot = block_map.read(wraps_snapshot, Default::default());
2248
2249        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2250        assert_eq!(
2251            snapshot.text(),
2252            "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
2253        );
2254
2255        let blocks: Vec<_> = snapshot
2256            .blocks_in_range(0..u32::MAX)
2257            .map(|(row, block)| (row..row + block.height(), block.id()))
2258            .collect();
2259        assert_eq!(
2260            blocks,
2261            vec![
2262                (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
2263                (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
2264                (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
2265                (14..15, BlockId::ExcerptBoundary(None)),               // footer
2266            ]
2267        );
2268    }
2269
2270    #[gpui::test]
2271    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2272        cx.update(init_test);
2273
2274        let text = "aaa\nbbb\nccc\nddd";
2275
2276        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2277        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2278        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2279        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2280        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2281        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2282        let (_wrap_map, wraps_snapshot) =
2283            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2284        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2285
2286        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2287        let block_ids = writer.insert(vec![
2288            BlockProperties {
2289                style: BlockStyle::Fixed,
2290                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2291                height: 1,
2292                render: Arc::new(|_| div().into_any()),
2293                priority: 0,
2294            },
2295            BlockProperties {
2296                style: BlockStyle::Fixed,
2297                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2298                height: 2,
2299                render: Arc::new(|_| div().into_any()),
2300                priority: 0,
2301            },
2302            BlockProperties {
2303                style: BlockStyle::Fixed,
2304                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2305                height: 3,
2306                render: Arc::new(|_| div().into_any()),
2307                priority: 0,
2308            },
2309        ]);
2310
2311        {
2312            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2313            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2314
2315            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2316
2317            let mut new_heights = HashMap::default();
2318            new_heights.insert(block_ids[0], 2);
2319            block_map_writer.resize(new_heights);
2320            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2321            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2322        }
2323
2324        {
2325            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2326
2327            let mut new_heights = HashMap::default();
2328            new_heights.insert(block_ids[0], 1);
2329            block_map_writer.resize(new_heights);
2330
2331            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2332            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2333        }
2334
2335        {
2336            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2337
2338            let mut new_heights = HashMap::default();
2339            new_heights.insert(block_ids[0], 0);
2340            block_map_writer.resize(new_heights);
2341
2342            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2343            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2344        }
2345
2346        {
2347            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2348
2349            let mut new_heights = HashMap::default();
2350            new_heights.insert(block_ids[0], 3);
2351            block_map_writer.resize(new_heights);
2352
2353            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2354            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2355        }
2356
2357        {
2358            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2359
2360            let mut new_heights = HashMap::default();
2361            new_heights.insert(block_ids[0], 3);
2362            block_map_writer.resize(new_heights);
2363
2364            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2365            // Same height as before, should remain the same
2366            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2367        }
2368    }
2369
2370    #[cfg(target_os = "macos")]
2371    #[gpui::test]
2372    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2373        cx.update(init_test);
2374
2375        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
2376
2377        let text = "one two three\nfour five six\nseven eight";
2378
2379        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2380        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2381        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2382        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2383        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2384        let (_, wraps_snapshot) = cx.update(|cx| {
2385            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
2386        });
2387        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
2388
2389        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2390        writer.insert(vec![
2391            BlockProperties {
2392                style: BlockStyle::Fixed,
2393                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2394                render: Arc::new(|_| div().into_any()),
2395                height: 1,
2396                priority: 0,
2397            },
2398            BlockProperties {
2399                style: BlockStyle::Fixed,
2400                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2401                render: Arc::new(|_| div().into_any()),
2402                height: 1,
2403                priority: 0,
2404            },
2405        ]);
2406
2407        // Blocks with an 'above' disposition go above their corresponding buffer line.
2408        // Blocks with a 'below' disposition go below their corresponding buffer line.
2409        let snapshot = block_map.read(wraps_snapshot, Default::default());
2410        assert_eq!(
2411            snapshot.text(),
2412            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2413        );
2414    }
2415
2416    #[gpui::test]
2417    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2418        cx.update(init_test);
2419
2420        let text = "line1\nline2\nline3\nline4\nline5";
2421
2422        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2423        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2424        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2425        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2426        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2427        let tab_size = 1.try_into().unwrap();
2428        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2429        let (wrap_map, wraps_snapshot) =
2430            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2431        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2432
2433        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2434        let replace_block_id = writer.insert(vec![BlockProperties {
2435            style: BlockStyle::Fixed,
2436            placement: BlockPlacement::Replace(
2437                buffer_snapshot.anchor_after(Point::new(1, 3))
2438                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
2439            ),
2440            height: 4,
2441            render: Arc::new(|_| div().into_any()),
2442            priority: 0,
2443        }])[0];
2444
2445        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2446        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2447
2448        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2449            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2450            buffer.snapshot(cx)
2451        });
2452        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2453            buffer_snapshot.clone(),
2454            buffer_subscription.consume().into_inner(),
2455        );
2456        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2457        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2458        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2459            wrap_map.sync(tab_snapshot, tab_edits, cx)
2460        });
2461        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2462        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2463
2464        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2465            buffer.edit(
2466                [(
2467                    Point::new(1, 5)..Point::new(1, 5),
2468                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2469                )],
2470                None,
2471                cx,
2472            );
2473            buffer.snapshot(cx)
2474        });
2475        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2476            buffer_snapshot.clone(),
2477            buffer_subscription.consume().into_inner(),
2478        );
2479        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2480        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2481        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2482            wrap_map.sync(tab_snapshot, tab_edits, cx)
2483        });
2484        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2485        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2486
2487        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2488        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2489        writer.insert(vec![
2490            BlockProperties {
2491                style: BlockStyle::Fixed,
2492                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2493                height: 1,
2494                render: Arc::new(|_| div().into_any()),
2495                priority: 0,
2496            },
2497            BlockProperties {
2498                style: BlockStyle::Fixed,
2499                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2500                height: 1,
2501                render: Arc::new(|_| div().into_any()),
2502                priority: 0,
2503            },
2504            BlockProperties {
2505                style: BlockStyle::Fixed,
2506                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2507                height: 1,
2508                render: Arc::new(|_| div().into_any()),
2509                priority: 0,
2510            },
2511        ]);
2512        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2513        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2514
2515        // Ensure blocks inserted *inside* replaced region are hidden.
2516        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2517        writer.insert(vec![
2518            BlockProperties {
2519                style: BlockStyle::Fixed,
2520                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2521                height: 1,
2522                render: Arc::new(|_| div().into_any()),
2523                priority: 0,
2524            },
2525            BlockProperties {
2526                style: BlockStyle::Fixed,
2527                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2528                height: 1,
2529                render: Arc::new(|_| div().into_any()),
2530                priority: 0,
2531            },
2532            BlockProperties {
2533                style: BlockStyle::Fixed,
2534                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2535                height: 1,
2536                render: Arc::new(|_| div().into_any()),
2537                priority: 0,
2538            },
2539        ]);
2540        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2541        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2542
2543        // Removing the replace block shows all the hidden blocks again.
2544        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2545        writer.remove(HashSet::from_iter([replace_block_id]));
2546        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2547        assert_eq!(
2548            blocks_snapshot.text(),
2549            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2550        );
2551    }
2552
2553    #[gpui::test]
2554    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2555        cx.update(init_test);
2556
2557        let text = "111\n222\n333\n444\n555\n666";
2558
2559        let buffer = cx.update(|cx| {
2560            MultiBuffer::build_multi(
2561                [
2562                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2563                    (
2564                        text,
2565                        vec![
2566                            Point::new(1, 0)..Point::new(1, 3),
2567                            Point::new(2, 0)..Point::new(2, 3),
2568                            Point::new(3, 0)..Point::new(3, 3),
2569                        ],
2570                    ),
2571                    (
2572                        text,
2573                        vec![
2574                            Point::new(4, 0)..Point::new(4, 3),
2575                            Point::new(5, 0)..Point::new(5, 3),
2576                        ],
2577                    ),
2578                ],
2579                cx,
2580            )
2581        });
2582        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2583        let buffer_ids = buffer_snapshot
2584            .excerpts()
2585            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2586            .dedup()
2587            .collect::<Vec<_>>();
2588        assert_eq!(buffer_ids.len(), 3);
2589        let buffer_id_1 = buffer_ids[0];
2590        let buffer_id_2 = buffer_ids[1];
2591        let buffer_id_3 = buffer_ids[2];
2592
2593        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2594        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2595        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2596        let (_, wrap_snapshot) =
2597            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2598        let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1);
2599        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2600
2601        assert_eq!(
2602            blocks_snapshot.text(),
2603            "\n\n\n111\n\n\n\n\n222\n\n\n333\n\n\n444\n\n\n\n\n555\n\n\n666\n"
2604        );
2605        assert_eq!(
2606            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2607            vec![
2608                None,
2609                None,
2610                None,
2611                Some(0),
2612                None,
2613                None,
2614                None,
2615                None,
2616                Some(1),
2617                None,
2618                None,
2619                Some(2),
2620                None,
2621                None,
2622                Some(3),
2623                None,
2624                None,
2625                None,
2626                None,
2627                Some(4),
2628                None,
2629                None,
2630                Some(5),
2631                None,
2632            ]
2633        );
2634
2635        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2636        let excerpt_blocks_2 = writer.insert(vec![
2637            BlockProperties {
2638                style: BlockStyle::Fixed,
2639                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2640                height: 1,
2641                render: Arc::new(|_| div().into_any()),
2642                priority: 0,
2643            },
2644            BlockProperties {
2645                style: BlockStyle::Fixed,
2646                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2647                height: 1,
2648                render: Arc::new(|_| div().into_any()),
2649                priority: 0,
2650            },
2651            BlockProperties {
2652                style: BlockStyle::Fixed,
2653                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2654                height: 1,
2655                render: Arc::new(|_| div().into_any()),
2656                priority: 0,
2657            },
2658        ]);
2659        let excerpt_blocks_3 = writer.insert(vec![
2660            BlockProperties {
2661                style: BlockStyle::Fixed,
2662                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2663                height: 1,
2664                render: Arc::new(|_| div().into_any()),
2665                priority: 0,
2666            },
2667            BlockProperties {
2668                style: BlockStyle::Fixed,
2669                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2670                height: 1,
2671                render: Arc::new(|_| div().into_any()),
2672                priority: 0,
2673            },
2674        ]);
2675
2676        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2677        assert_eq!(
2678            blocks_snapshot.text(),
2679            "\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"
2680        );
2681        assert_eq!(
2682            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2683            vec![
2684                None,
2685                None,
2686                None,
2687                Some(0),
2688                None,
2689                None,
2690                None,
2691                None,
2692                None,
2693                Some(1),
2694                None,
2695                None,
2696                None,
2697                Some(2),
2698                None,
2699                None,
2700                Some(3),
2701                None,
2702                None,
2703                None,
2704                None,
2705                None,
2706                None,
2707                Some(4),
2708                None,
2709                None,
2710                Some(5),
2711                None,
2712                None,
2713            ]
2714        );
2715
2716        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2717        buffer.read_with(cx, |buffer, cx| {
2718            writer.fold_buffer(buffer_id_1, buffer, cx);
2719        });
2720        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2721            style: BlockStyle::Fixed,
2722            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2723            height: 1,
2724            render: Arc::new(|_| div().into_any()),
2725            priority: 0,
2726        }]);
2727        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2728        let blocks = blocks_snapshot
2729            .blocks_in_range(0..u32::MAX)
2730            .collect::<Vec<_>>();
2731        for (_, block) in &blocks {
2732            if let BlockId::Custom(custom_block_id) = block.id() {
2733                assert!(
2734                    !excerpt_blocks_1.contains(&custom_block_id),
2735                    "Should have no blocks from the folded buffer"
2736                );
2737                assert!(
2738                    excerpt_blocks_2.contains(&custom_block_id)
2739                        || excerpt_blocks_3.contains(&custom_block_id),
2740                    "Should have only blocks from unfolded buffers"
2741                );
2742            }
2743        }
2744        assert_eq!(
2745            1,
2746            blocks
2747                .iter()
2748                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2749                .count(),
2750            "Should have one folded block, producing a header of the second buffer"
2751        );
2752        assert_eq!(
2753            blocks_snapshot.text(),
2754            "\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"
2755        );
2756        assert_eq!(
2757            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2758            vec![
2759                None,
2760                None,
2761                None,
2762                None,
2763                None,
2764                None,
2765                Some(1),
2766                None,
2767                None,
2768                None,
2769                Some(2),
2770                None,
2771                None,
2772                Some(3),
2773                None,
2774                None,
2775                None,
2776                None,
2777                None,
2778                None,
2779                Some(4),
2780                None,
2781                None,
2782                Some(5),
2783                None,
2784                None,
2785            ]
2786        );
2787
2788        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2789        buffer.read_with(cx, |buffer, cx| {
2790            writer.fold_buffer(buffer_id_2, buffer, cx);
2791        });
2792        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2793        let blocks = blocks_snapshot
2794            .blocks_in_range(0..u32::MAX)
2795            .collect::<Vec<_>>();
2796        for (_, block) in &blocks {
2797            if let BlockId::Custom(custom_block_id) = block.id() {
2798                assert!(
2799                    !excerpt_blocks_1.contains(&custom_block_id),
2800                    "Should have no blocks from the folded buffer_1"
2801                );
2802                assert!(
2803                    !excerpt_blocks_2.contains(&custom_block_id),
2804                    "Should have no blocks from the folded buffer_2"
2805                );
2806                assert!(
2807                    excerpt_blocks_3.contains(&custom_block_id),
2808                    "Should have only blocks from unfolded buffers"
2809                );
2810            }
2811        }
2812        assert_eq!(
2813            2,
2814            blocks
2815                .iter()
2816                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2817                .count(),
2818            "Should have two folded blocks, producing headers"
2819        );
2820        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n\n555\n\n\n666\n\n");
2821        assert_eq!(
2822            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2823            vec![
2824                None,
2825                None,
2826                None,
2827                None,
2828                None,
2829                None,
2830                None,
2831                None,
2832                Some(4),
2833                None,
2834                None,
2835                Some(5),
2836                None,
2837                None,
2838            ]
2839        );
2840
2841        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2842        buffer.read_with(cx, |buffer, cx| {
2843            writer.unfold_buffer(buffer_id_1, buffer, cx);
2844        });
2845        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2846        let blocks = blocks_snapshot
2847            .blocks_in_range(0..u32::MAX)
2848            .collect::<Vec<_>>();
2849        for (_, block) in &blocks {
2850            if let BlockId::Custom(custom_block_id) = block.id() {
2851                assert!(
2852                    !excerpt_blocks_2.contains(&custom_block_id),
2853                    "Should have no blocks from the folded buffer_2"
2854                );
2855                assert!(
2856                    excerpt_blocks_1.contains(&custom_block_id)
2857                        || excerpt_blocks_3.contains(&custom_block_id),
2858                    "Should have only blocks from unfolded buffers"
2859                );
2860            }
2861        }
2862        assert_eq!(
2863            1,
2864            blocks
2865                .iter()
2866                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2867                .count(),
2868            "Should be back to a single folded buffer, producing a header for buffer_2"
2869        );
2870        assert_eq!(
2871            blocks_snapshot.text(),
2872            "\n\n\n\n111\n\n\n\n\n\n\n\n555\n\n\n666\n\n",
2873            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2874        );
2875        assert_eq!(
2876            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2877            vec![
2878                None,
2879                None,
2880                None,
2881                None,
2882                Some(0),
2883                None,
2884                None,
2885                None,
2886                None,
2887                None,
2888                None,
2889                None,
2890                Some(4),
2891                None,
2892                None,
2893                Some(5),
2894                None,
2895                None,
2896            ]
2897        );
2898
2899        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2900        buffer.read_with(cx, |buffer, cx| {
2901            writer.fold_buffer(buffer_id_3, buffer, cx);
2902        });
2903        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2904        let blocks = blocks_snapshot
2905            .blocks_in_range(0..u32::MAX)
2906            .collect::<Vec<_>>();
2907        for (_, block) in &blocks {
2908            if let BlockId::Custom(custom_block_id) = block.id() {
2909                assert!(
2910                    excerpt_blocks_1.contains(&custom_block_id),
2911                    "Should have no blocks from the folded buffer_1"
2912                );
2913                assert!(
2914                    !excerpt_blocks_2.contains(&custom_block_id),
2915                    "Should have only blocks from unfolded buffers"
2916                );
2917                assert!(
2918                    !excerpt_blocks_3.contains(&custom_block_id),
2919                    "Should have only blocks from unfolded buffers"
2920                );
2921            }
2922        }
2923
2924        assert_eq!(
2925            blocks_snapshot.text(),
2926            "\n\n\n\n111\n\n\n\n\n",
2927            "Should have a single, first buffer left after folding"
2928        );
2929        assert_eq!(
2930            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
2931            vec![
2932                None,
2933                None,
2934                None,
2935                None,
2936                Some(0),
2937                None,
2938                None,
2939                None,
2940                None,
2941                None,
2942            ]
2943        );
2944    }
2945
2946    #[gpui::test]
2947    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2948        cx.update(init_test);
2949
2950        let text = "111";
2951
2952        let buffer = cx.update(|cx| {
2953            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2954        });
2955        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2956        let buffer_ids = buffer_snapshot
2957            .excerpts()
2958            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2959            .dedup()
2960            .collect::<Vec<_>>();
2961        assert_eq!(buffer_ids.len(), 1);
2962        let buffer_id = buffer_ids[0];
2963
2964        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2965        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2966        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2967        let (_, wrap_snapshot) =
2968            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2969        let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1);
2970        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2971
2972        assert_eq!(blocks_snapshot.text(), "\n\n\n111\n");
2973
2974        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2975        buffer.read_with(cx, |buffer, cx| {
2976            writer.fold_buffer(buffer_id, buffer, cx);
2977        });
2978        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2979        let blocks = blocks_snapshot
2980            .blocks_in_range(0..u32::MAX)
2981            .collect::<Vec<_>>();
2982        assert_eq!(
2983            1,
2984            blocks
2985                .iter()
2986                .filter(|(_, block)| {
2987                    match block {
2988                        Block::FoldedBuffer { prev_excerpt, .. } => {
2989                            assert!(prev_excerpt.is_none());
2990                            true
2991                        }
2992                        _ => false,
2993                    }
2994                })
2995                .count(),
2996            "Should have one folded block, producing a header of the second buffer"
2997        );
2998        assert_eq!(blocks_snapshot.text(), "\n");
2999        assert_eq!(
3000            blocks_snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
3001            vec![None, None],
3002            "When fully folded, should be no buffer rows"
3003        );
3004    }
3005
3006    #[gpui::test(iterations = 100)]
3007    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3008        cx.update(init_test);
3009
3010        let operations = env::var("OPERATIONS")
3011            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3012            .unwrap_or(10);
3013
3014        let wrap_width = if rng.gen_bool(0.2) {
3015            None
3016        } else {
3017            Some(px(rng.gen_range(0.0..=100.0)))
3018        };
3019        let tab_size = 1.try_into().unwrap();
3020        let font_size = px(14.0);
3021        let buffer_start_header_height = rng.gen_range(1..=5);
3022        let excerpt_header_height = rng.gen_range(1..=5);
3023        let excerpt_footer_height = rng.gen_range(1..=5);
3024
3025        log::info!("Wrap width: {:?}", wrap_width);
3026        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3027        log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
3028        let is_singleton = rng.gen();
3029        let buffer = if is_singleton {
3030            let len = rng.gen_range(0..10);
3031            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3032            log::info!("initial singleton buffer text: {:?}", text);
3033            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3034        } else {
3035            cx.update(|cx| {
3036                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3037                log::info!(
3038                    "initial multi-buffer text: {:?}",
3039                    multibuffer.read(cx).read(cx).text()
3040                );
3041                multibuffer
3042            })
3043        };
3044
3045        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3046        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3047        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3048        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3049        let (wrap_map, wraps_snapshot) = cx
3050            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
3051        let mut block_map = BlockMap::new(
3052            wraps_snapshot,
3053            true,
3054            buffer_start_header_height,
3055            excerpt_header_height,
3056            excerpt_footer_height,
3057        );
3058
3059        for _ in 0..operations {
3060            let mut buffer_edits = Vec::new();
3061            match rng.gen_range(0..=100) {
3062                0..=19 => {
3063                    let wrap_width = if rng.gen_bool(0.2) {
3064                        None
3065                    } else {
3066                        Some(px(rng.gen_range(0.0..=100.0)))
3067                    };
3068                    log::info!("Setting wrap width to {:?}", wrap_width);
3069                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3070                }
3071                20..=39 => {
3072                    let block_count = rng.gen_range(1..=5);
3073                    let block_properties = (0..block_count)
3074                        .map(|_| {
3075                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3076                            let offset =
3077                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
3078                            let mut min_height = 0;
3079                            let placement = match rng.gen_range(0..3) {
3080                                0 => {
3081                                    min_height = 1;
3082                                    let start = buffer.anchor_after(offset);
3083                                    let end = buffer.anchor_after(buffer.clip_offset(
3084                                        rng.gen_range(offset..=buffer.len()),
3085                                        Bias::Left,
3086                                    ));
3087                                    BlockPlacement::Replace(start..=end)
3088                                }
3089                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3090                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3091                            };
3092
3093                            let height = rng.gen_range(min_height..5);
3094                            BlockProperties {
3095                                style: BlockStyle::Fixed,
3096                                placement,
3097                                height,
3098                                render: Arc::new(|_| div().into_any()),
3099                                priority: 0,
3100                            }
3101                        })
3102                        .collect::<Vec<_>>();
3103
3104                    let (inlay_snapshot, inlay_edits) =
3105                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3106                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3107                    let (tab_snapshot, tab_edits) =
3108                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3109                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3110                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3111                    });
3112                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3113                    let block_ids =
3114                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3115                            placement: props.placement.clone(),
3116                            height: props.height,
3117                            style: props.style,
3118                            render: Arc::new(|_| div().into_any()),
3119                            priority: 0,
3120                        }));
3121
3122                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3123                        log::info!(
3124                            "inserted block {:?} with height {} and id {:?}",
3125                            block_properties
3126                                .placement
3127                                .as_ref()
3128                                .map(|p| p.to_point(&buffer_snapshot)),
3129                            block_properties.height,
3130                            block_id
3131                        );
3132                    }
3133                }
3134                40..=59 if !block_map.custom_blocks.is_empty() => {
3135                    let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
3136                    let block_ids_to_remove = block_map
3137                        .custom_blocks
3138                        .choose_multiple(&mut rng, block_count)
3139                        .map(|block| block.id)
3140                        .collect::<HashSet<_>>();
3141
3142                    let (inlay_snapshot, inlay_edits) =
3143                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3144                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3145                    let (tab_snapshot, tab_edits) =
3146                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3147                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3148                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3149                    });
3150                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3151                    log::info!(
3152                        "removing {} blocks: {:?}",
3153                        block_ids_to_remove.len(),
3154                        block_ids_to_remove
3155                    );
3156                    block_map.remove(block_ids_to_remove);
3157                }
3158                60..=79 => {
3159                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3160                        log::info!("Noop fold/unfold operation on a singleton buffer");
3161                        continue;
3162                    }
3163                    let (inlay_snapshot, inlay_edits) =
3164                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3165                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3166                    let (tab_snapshot, tab_edits) =
3167                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3168                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3169                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3170                    });
3171                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3172                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3173                        let folded_buffers = block_map
3174                            .0
3175                            .folded_buffers
3176                            .iter()
3177                            .cloned()
3178                            .collect::<Vec<_>>();
3179                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3180                        unfolded_buffers.dedup();
3181                        log::debug!("All buffers {unfolded_buffers:?}");
3182                        log::debug!("Folded buffers {folded_buffers:?}");
3183                        unfolded_buffers
3184                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3185                        (unfolded_buffers, folded_buffers)
3186                    });
3187                    let mut folded_count = folded_buffers.len();
3188                    let mut unfolded_count = unfolded_buffers.len();
3189
3190                    let fold = !unfolded_buffers.is_empty() && rng.gen_bool(0.5);
3191                    let unfold = !folded_buffers.is_empty() && rng.gen_bool(0.5);
3192                    if !fold && !unfold {
3193                        log::info!("Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}");
3194                        continue;
3195                    }
3196
3197                    buffer.update(cx, |buffer, cx| {
3198                        if fold {
3199                            let buffer_to_fold =
3200                                unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
3201                            log::info!("Folding {buffer_to_fold:?}");
3202                            let related_excerpts = buffer_snapshot
3203                                .excerpts()
3204                                .filter_map(|(excerpt_id, buffer, range)| {
3205                                    if buffer.remote_id() == buffer_to_fold {
3206                                        Some((
3207                                            excerpt_id,
3208                                            buffer
3209                                                .text_for_range(range.context)
3210                                                .collect::<String>(),
3211                                        ))
3212                                    } else {
3213                                        None
3214                                    }
3215                                })
3216                                .collect::<Vec<_>>();
3217                            log::info!(
3218                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3219                            );
3220                            folded_count += 1;
3221                            unfolded_count -= 1;
3222                            block_map.fold_buffer(buffer_to_fold, buffer, cx);
3223                        }
3224                        if unfold {
3225                            let buffer_to_unfold =
3226                                folded_buffers[rng.gen_range(0..folded_buffers.len())];
3227                            log::info!("Unfolding {buffer_to_unfold:?}");
3228                            unfolded_count += 1;
3229                            folded_count -= 1;
3230                            block_map.unfold_buffer(buffer_to_unfold, buffer, cx);
3231                        }
3232                        log::info!(
3233                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3234                        );
3235                    });
3236                }
3237                _ => {
3238                    buffer.update(cx, |buffer, cx| {
3239                        let mutation_count = rng.gen_range(1..=5);
3240                        let subscription = buffer.subscribe();
3241                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3242                        buffer_snapshot = buffer.snapshot(cx);
3243                        buffer_edits.extend(subscription.consume());
3244                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3245                    });
3246                }
3247            }
3248
3249            let (inlay_snapshot, inlay_edits) =
3250                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3251            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3252            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3253            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3254                wrap_map.sync(tab_snapshot, tab_edits, cx)
3255            });
3256            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3257            assert_eq!(
3258                blocks_snapshot.transforms.summary().input_rows,
3259                wraps_snapshot.max_point().row() + 1
3260            );
3261            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3262            log::info!("blocks text: {:?}", blocks_snapshot.text());
3263
3264            let mut expected_blocks = Vec::new();
3265            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3266                Some((
3267                    block.placement.to_wrap_row(&wraps_snapshot)?,
3268                    Block::Custom(block.clone()),
3269                ))
3270            }));
3271
3272            // Note that this needs to be synced with the related section in BlockMap::sync
3273            expected_blocks.extend(BlockMap::header_and_footer_blocks(
3274                true,
3275                excerpt_footer_height,
3276                buffer_start_header_height,
3277                excerpt_header_height,
3278                &buffer_snapshot,
3279                &block_map.folded_buffers,
3280                0..,
3281                &wraps_snapshot,
3282            ));
3283
3284            BlockMap::sort_blocks(&mut expected_blocks);
3285
3286            for (placement, block) in &expected_blocks {
3287                log::info!(
3288                    "Block {:?} placement: {:?} Height: {:?}",
3289                    block.id(),
3290                    placement,
3291                    block.height()
3292                );
3293            }
3294
3295            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3296
3297            let input_buffer_rows = buffer_snapshot
3298                .buffer_rows(MultiBufferRow(0))
3299                .collect::<Vec<_>>();
3300            let mut expected_buffer_rows = Vec::new();
3301            let mut expected_text = String::new();
3302            let mut expected_block_positions = Vec::new();
3303            let mut expected_replaced_buffer_rows = HashSet::default();
3304            let input_text = wraps_snapshot.text();
3305
3306            // Loop over the input lines, creating (N - 1) empty lines for
3307            // blocks of height N.
3308            //
3309            // It's important to note that output *starts* as one empty line,
3310            // so we special case row 0 to assume a leading '\n'.
3311            //
3312            // Linehood is the birthright of strings.
3313            let mut input_text_lines = input_text.split('\n').enumerate().peekable();
3314            let mut block_row = 0;
3315            while let Some((wrap_row, input_line)) = input_text_lines.next() {
3316                let wrap_row = wrap_row as u32;
3317                let multibuffer_row = wraps_snapshot
3318                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3319                    .row;
3320
3321                // Create empty lines for the above block
3322                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3323                    if placement.start().0 == wrap_row && block.place_above() {
3324                        let (_, block) = sorted_blocks_iter.next().unwrap();
3325                        expected_block_positions.push((block_row, block.id()));
3326                        if block.height() > 0 {
3327                            let text = "\n".repeat((block.height() - 1) as usize);
3328                            if block_row > 0 {
3329                                expected_text.push('\n')
3330                            }
3331                            expected_text.push_str(&text);
3332                            for _ in 0..block.height() {
3333                                expected_buffer_rows.push(None);
3334                            }
3335                            block_row += block.height();
3336                        }
3337                    } else {
3338                        break;
3339                    }
3340                }
3341
3342                // Skip lines within replace blocks, then create empty lines for the replace block's height
3343                let mut is_in_replace_block = false;
3344                if let Some((BlockPlacement::Replace(replace_range), block)) =
3345                    sorted_blocks_iter.peek()
3346                {
3347                    if wrap_row >= replace_range.start().0 {
3348                        is_in_replace_block = true;
3349
3350                        if wrap_row == replace_range.start().0 {
3351                            if matches!(block, Block::FoldedBuffer { .. }) {
3352                                expected_buffer_rows.push(None);
3353                            } else {
3354                                expected_buffer_rows
3355                                    .push(input_buffer_rows[multibuffer_row as usize]);
3356                            }
3357                        }
3358
3359                        if wrap_row == replace_range.end().0 {
3360                            expected_block_positions.push((block_row, block.id()));
3361                            let text = "\n".repeat((block.height() - 1) as usize);
3362                            if block_row > 0 {
3363                                expected_text.push('\n');
3364                            }
3365                            expected_text.push_str(&text);
3366
3367                            for _ in 1..block.height() {
3368                                expected_buffer_rows.push(None);
3369                            }
3370                            block_row += block.height();
3371
3372                            sorted_blocks_iter.next();
3373                        }
3374                    }
3375                }
3376
3377                if is_in_replace_block {
3378                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3379                } else {
3380                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3381                    let soft_wrapped = wraps_snapshot
3382                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3383                        .column()
3384                        > 0;
3385                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3386                    if block_row > 0 {
3387                        expected_text.push('\n');
3388                    }
3389                    expected_text.push_str(input_line);
3390                    block_row += 1;
3391                }
3392
3393                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3394                    if placement.end().0 == wrap_row && block.place_below() {
3395                        let (_, block) = sorted_blocks_iter.next().unwrap();
3396                        expected_block_positions.push((block_row, block.id()));
3397                        if block.height() > 0 {
3398                            let text = "\n".repeat((block.height() - 1) as usize);
3399                            if block_row > 0 {
3400                                expected_text.push('\n')
3401                            }
3402                            expected_text.push_str(&text);
3403                            for _ in 0..block.height() {
3404                                expected_buffer_rows.push(None);
3405                            }
3406                            block_row += block.height();
3407                        }
3408                    } else {
3409                        break;
3410                    }
3411                }
3412            }
3413
3414            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3415            let expected_row_count = expected_lines.len();
3416            log::info!("expected text: {expected_text:?}");
3417
3418            assert_eq!(
3419                blocks_snapshot.max_point().row + 1,
3420                expected_row_count as u32,
3421                "actual row count != expected row count",
3422            );
3423            assert_eq!(
3424                blocks_snapshot.text(),
3425                expected_text,
3426                "actual text != expected text",
3427            );
3428
3429            for start_row in 0..expected_row_count {
3430                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
3431                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3432                if end_row < expected_row_count {
3433                    expected_text.push('\n');
3434                }
3435
3436                let actual_text = blocks_snapshot
3437                    .chunks(
3438                        start_row as u32..end_row as u32,
3439                        false,
3440                        false,
3441                        Highlights::default(),
3442                    )
3443                    .map(|chunk| chunk.text)
3444                    .collect::<String>();
3445                assert_eq!(
3446                    actual_text,
3447                    expected_text,
3448                    "incorrect text starting row row range {:?}",
3449                    start_row..end_row
3450                );
3451                assert_eq!(
3452                    blocks_snapshot
3453                        .buffer_rows(BlockRow(start_row as u32))
3454                        .collect::<Vec<_>>(),
3455                    &expected_buffer_rows[start_row..],
3456                    "incorrect buffer_rows starting at row {:?}",
3457                    start_row
3458                );
3459            }
3460
3461            assert_eq!(
3462                blocks_snapshot
3463                    .blocks_in_range(0..(expected_row_count as u32))
3464                    .map(|(row, block)| (row, block.id()))
3465                    .collect::<Vec<_>>(),
3466                expected_block_positions,
3467                "invalid blocks_in_range({:?})",
3468                0..expected_row_count
3469            );
3470
3471            for (_, expected_block) in
3472                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3473            {
3474                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3475                assert_eq!(
3476                    actual_block.map(|block| block.id()),
3477                    Some(expected_block.id())
3478                );
3479            }
3480
3481            for (block_row, block_id) in expected_block_positions {
3482                if let BlockId::Custom(block_id) = block_id {
3483                    assert_eq!(
3484                        blocks_snapshot.row_for_block(block_id),
3485                        Some(BlockRow(block_row))
3486                    );
3487                }
3488            }
3489
3490            let mut expected_longest_rows = Vec::new();
3491            let mut longest_line_len = -1_isize;
3492            for (row, line) in expected_lines.iter().enumerate() {
3493                let row = row as u32;
3494
3495                assert_eq!(
3496                    blocks_snapshot.line_len(BlockRow(row)),
3497                    line.len() as u32,
3498                    "invalid line len for row {}",
3499                    row
3500                );
3501
3502                let line_char_count = line.chars().count() as isize;
3503                match line_char_count.cmp(&longest_line_len) {
3504                    Ordering::Less => {}
3505                    Ordering::Equal => expected_longest_rows.push(row),
3506                    Ordering::Greater => {
3507                        longest_line_len = line_char_count;
3508                        expected_longest_rows.clear();
3509                        expected_longest_rows.push(row);
3510                    }
3511                }
3512            }
3513
3514            let longest_row = blocks_snapshot.longest_row();
3515            assert!(
3516                expected_longest_rows.contains(&longest_row),
3517                "incorrect longest row {}. expected {:?} with length {}",
3518                longest_row,
3519                expected_longest_rows,
3520                longest_line_len,
3521            );
3522
3523            for _ in 0..10 {
3524                let end_row = rng.gen_range(1..=expected_lines.len());
3525                let start_row = rng.gen_range(0..end_row);
3526
3527                let mut expected_longest_rows_in_range = vec![];
3528                let mut longest_line_len_in_range = 0;
3529
3530                let mut row = start_row as u32;
3531                for line in &expected_lines[start_row..end_row] {
3532                    let line_char_count = line.chars().count() as isize;
3533                    match line_char_count.cmp(&longest_line_len_in_range) {
3534                        Ordering::Less => {}
3535                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3536                        Ordering::Greater => {
3537                            longest_line_len_in_range = line_char_count;
3538                            expected_longest_rows_in_range.clear();
3539                            expected_longest_rows_in_range.push(row);
3540                        }
3541                    }
3542                    row += 1;
3543                }
3544
3545                let longest_row_in_range = blocks_snapshot
3546                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3547                assert!(
3548                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3549                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3550                    longest_row,
3551                    start_row..end_row,
3552                    expected_longest_rows_in_range,
3553                    longest_line_len_in_range,
3554                );
3555            }
3556
3557            // Ensure that conversion between block points and wrap points is stable.
3558            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3559                let wrap_point = WrapPoint::new(row, 0);
3560                let block_point = blocks_snapshot.to_block_point(wrap_point);
3561                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3562                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3563                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3564                assert_eq!(
3565                    blocks_snapshot.to_block_point(right_wrap_point),
3566                    block_point
3567                );
3568            }
3569
3570            let mut block_point = BlockPoint::new(0, 0);
3571            for c in expected_text.chars() {
3572                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3573                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3574                assert_eq!(
3575                    blocks_snapshot
3576                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3577                    left_point,
3578                    "block point: {:?}, wrap point: {:?}",
3579                    block_point,
3580                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3581                );
3582                assert_eq!(
3583                    left_buffer_point,
3584                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3585                    "{:?} is not valid in buffer coordinates",
3586                    left_point
3587                );
3588
3589                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3590                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3591                assert_eq!(
3592                    blocks_snapshot
3593                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3594                    right_point,
3595                    "block point: {:?}, wrap point: {:?}",
3596                    block_point,
3597                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3598                );
3599                assert_eq!(
3600                    right_buffer_point,
3601                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3602                    "{:?} is not valid in buffer coordinates",
3603                    right_point
3604                );
3605
3606                if c == '\n' {
3607                    block_point.0 += Point::new(1, 0);
3608                } else {
3609                    block_point.column += c.len_utf8() as u32;
3610                }
3611            }
3612
3613            for buffer_row in 0..=buffer_snapshot.max_point().row {
3614                let buffer_row = MultiBufferRow(buffer_row);
3615                assert_eq!(
3616                    blocks_snapshot.is_line_replaced(buffer_row),
3617                    expected_replaced_buffer_rows.contains(&buffer_row),
3618                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3619                );
3620            }
3621        }
3622    }
3623
3624    fn init_test(cx: &mut gpui::AppContext) {
3625        let settings = SettingsStore::test(cx);
3626        cx.set_global(settings);
3627        theme::init(theme::LoadThemes::JustBase, cx);
3628        assets::Assets.load_test_fonts(cx);
3629    }
3630
3631    impl Block {
3632        fn as_custom(&self) -> Option<&CustomBlock> {
3633            match self {
3634                Block::Custom(block) => Some(block),
3635                _ => None,
3636            }
3637        }
3638    }
3639
3640    impl BlockSnapshot {
3641        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3642            self.wrap_snapshot
3643                .to_point(self.to_wrap_point(point, bias), bias)
3644        }
3645    }
3646}