block_map.rs

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