block_map.rs

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