block_map.rs

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