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