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