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::{
1996        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap},
1997        test::test_font,
1998    };
1999    use gpui::{div, font, px, App, AppContext as _, Element};
2000    use itertools::Itertools;
2001    use language::{Buffer, Capability};
2002    use multi_buffer::{ExcerptRange, MultiBuffer};
2003    use rand::prelude::*;
2004    use settings::SettingsStore;
2005    use std::env;
2006    use util::RandomCharIter;
2007
2008    #[gpui::test]
2009    fn test_offset_for_row() {
2010        assert_eq!(offset_for_row("", 0), (0, 0));
2011        assert_eq!(offset_for_row("", 1), (0, 0));
2012        assert_eq!(offset_for_row("abcd", 0), (0, 0));
2013        assert_eq!(offset_for_row("abcd", 1), (0, 4));
2014        assert_eq!(offset_for_row("\n", 0), (0, 0));
2015        assert_eq!(offset_for_row("\n", 1), (1, 1));
2016        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
2017        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
2018        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
2019        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
2020    }
2021
2022    #[gpui::test]
2023    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
2024        cx.update(init_test);
2025
2026        let text = "aaa\nbbb\nccc\nddd";
2027
2028        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2029        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2030        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2031        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2032        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2033        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2034        let (wrap_map, wraps_snapshot) =
2035            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2036        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2037
2038        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2039        let block_ids = writer.insert(vec![
2040            BlockProperties {
2041                style: BlockStyle::Fixed,
2042                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2043                height: 1,
2044                render: Arc::new(|_| div().into_any()),
2045                priority: 0,
2046            },
2047            BlockProperties {
2048                style: BlockStyle::Fixed,
2049                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2050                height: 2,
2051                render: Arc::new(|_| div().into_any()),
2052                priority: 0,
2053            },
2054            BlockProperties {
2055                style: BlockStyle::Fixed,
2056                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2057                height: 3,
2058                render: Arc::new(|_| div().into_any()),
2059                priority: 0,
2060            },
2061        ]);
2062
2063        let snapshot = block_map.read(wraps_snapshot, Default::default());
2064        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2065
2066        let blocks = snapshot
2067            .blocks_in_range(0..8)
2068            .map(|(start_row, block)| {
2069                let block = block.as_custom().unwrap();
2070                (start_row..start_row + block.height, block.id)
2071            })
2072            .collect::<Vec<_>>();
2073
2074        // When multiple blocks are on the same line, the newer blocks appear first.
2075        assert_eq!(
2076            blocks,
2077            &[
2078                (1..2, block_ids[0]),
2079                (2..4, block_ids[1]),
2080                (7..10, block_ids[2]),
2081            ]
2082        );
2083
2084        assert_eq!(
2085            snapshot.to_block_point(WrapPoint::new(0, 3)),
2086            BlockPoint::new(0, 3)
2087        );
2088        assert_eq!(
2089            snapshot.to_block_point(WrapPoint::new(1, 0)),
2090            BlockPoint::new(4, 0)
2091        );
2092        assert_eq!(
2093            snapshot.to_block_point(WrapPoint::new(3, 3)),
2094            BlockPoint::new(6, 3)
2095        );
2096
2097        assert_eq!(
2098            snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
2099            WrapPoint::new(0, 3)
2100        );
2101        assert_eq!(
2102            snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
2103            WrapPoint::new(1, 0)
2104        );
2105        assert_eq!(
2106            snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
2107            WrapPoint::new(1, 0)
2108        );
2109        assert_eq!(
2110            snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
2111            WrapPoint::new(3, 3)
2112        );
2113
2114        assert_eq!(
2115            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
2116            BlockPoint::new(0, 3)
2117        );
2118        assert_eq!(
2119            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
2120            BlockPoint::new(4, 0)
2121        );
2122        assert_eq!(
2123            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
2124            BlockPoint::new(0, 3)
2125        );
2126        assert_eq!(
2127            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
2128            BlockPoint::new(4, 0)
2129        );
2130        assert_eq!(
2131            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
2132            BlockPoint::new(4, 0)
2133        );
2134        assert_eq!(
2135            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
2136            BlockPoint::new(4, 0)
2137        );
2138        assert_eq!(
2139            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
2140            BlockPoint::new(6, 3)
2141        );
2142        assert_eq!(
2143            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
2144            BlockPoint::new(6, 3)
2145        );
2146        assert_eq!(
2147            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
2148            BlockPoint::new(6, 3)
2149        );
2150        assert_eq!(
2151            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
2152            BlockPoint::new(6, 3)
2153        );
2154
2155        assert_eq!(
2156            snapshot
2157                .row_infos(BlockRow(0))
2158                .map(|row_info| row_info.buffer_row)
2159                .collect::<Vec<_>>(),
2160            &[
2161                Some(0),
2162                None,
2163                None,
2164                None,
2165                Some(1),
2166                Some(2),
2167                Some(3),
2168                None,
2169                None,
2170                None
2171            ]
2172        );
2173
2174        // Insert a line break, separating two block decorations into separate lines.
2175        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2176            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2177            buffer.snapshot(cx)
2178        });
2179
2180        let (inlay_snapshot, inlay_edits) =
2181            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2182        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2183        let (tab_snapshot, tab_edits) =
2184            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2185        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2186            wrap_map.sync(tab_snapshot, tab_edits, cx)
2187        });
2188        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
2189        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2190    }
2191
2192    #[gpui::test]
2193    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2194        init_test(cx);
2195
2196        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2197        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2198        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2199
2200        let mut excerpt_ids = Vec::new();
2201        let multi_buffer = cx.new(|cx| {
2202            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2203            excerpt_ids.extend(multi_buffer.push_excerpts(
2204                buffer1.clone(),
2205                [ExcerptRange {
2206                    context: 0..buffer1.read(cx).len(),
2207                    primary: None,
2208                }],
2209                cx,
2210            ));
2211            excerpt_ids.extend(multi_buffer.push_excerpts(
2212                buffer2.clone(),
2213                [ExcerptRange {
2214                    context: 0..buffer2.read(cx).len(),
2215                    primary: None,
2216                }],
2217                cx,
2218            ));
2219            excerpt_ids.extend(multi_buffer.push_excerpts(
2220                buffer3.clone(),
2221                [ExcerptRange {
2222                    context: 0..buffer3.read(cx).len(),
2223                    primary: None,
2224                }],
2225                cx,
2226            ));
2227
2228            multi_buffer
2229        });
2230
2231        let font = test_font();
2232        let font_size = px(14.);
2233        let font_id = cx.text_system().resolve_font(&font);
2234        let mut wrap_width = px(0.);
2235        for c in "Buff".chars() {
2236            wrap_width += cx
2237                .text_system()
2238                .advance(font_id, font_size, c)
2239                .unwrap()
2240                .width;
2241        }
2242
2243        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2244        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
2245        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2246        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2247        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2248
2249        let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2250        let snapshot = block_map.read(wraps_snapshot, Default::default());
2251
2252        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2253        assert_eq!(
2254            snapshot.text(),
2255            "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
2256        );
2257
2258        let blocks: Vec<_> = snapshot
2259            .blocks_in_range(0..u32::MAX)
2260            .map(|(row, block)| (row..row + block.height(), block.id()))
2261            .collect();
2262        assert_eq!(
2263            blocks,
2264            vec![
2265                (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
2266                (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
2267                (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
2268                (14..15, BlockId::ExcerptBoundary(None)),               // footer
2269            ]
2270        );
2271    }
2272
2273    #[gpui::test]
2274    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2275        cx.update(init_test);
2276
2277        let text = "aaa\nbbb\nccc\nddd";
2278
2279        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2280        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2281        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2282        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2283        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2284        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2285        let (_wrap_map, wraps_snapshot) =
2286            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2287        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2288
2289        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2290        let block_ids = writer.insert(vec![
2291            BlockProperties {
2292                style: BlockStyle::Fixed,
2293                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2294                height: 1,
2295                render: Arc::new(|_| div().into_any()),
2296                priority: 0,
2297            },
2298            BlockProperties {
2299                style: BlockStyle::Fixed,
2300                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2301                height: 2,
2302                render: Arc::new(|_| div().into_any()),
2303                priority: 0,
2304            },
2305            BlockProperties {
2306                style: BlockStyle::Fixed,
2307                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2308                height: 3,
2309                render: Arc::new(|_| div().into_any()),
2310                priority: 0,
2311            },
2312        ]);
2313
2314        {
2315            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2316            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2317
2318            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2319
2320            let mut new_heights = HashMap::default();
2321            new_heights.insert(block_ids[0], 2);
2322            block_map_writer.resize(new_heights);
2323            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2324            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2325        }
2326
2327        {
2328            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2329
2330            let mut new_heights = HashMap::default();
2331            new_heights.insert(block_ids[0], 1);
2332            block_map_writer.resize(new_heights);
2333
2334            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2335            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2336        }
2337
2338        {
2339            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2340
2341            let mut new_heights = HashMap::default();
2342            new_heights.insert(block_ids[0], 0);
2343            block_map_writer.resize(new_heights);
2344
2345            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2346            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2347        }
2348
2349        {
2350            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2351
2352            let mut new_heights = HashMap::default();
2353            new_heights.insert(block_ids[0], 3);
2354            block_map_writer.resize(new_heights);
2355
2356            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2357            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2358        }
2359
2360        {
2361            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2362
2363            let mut new_heights = HashMap::default();
2364            new_heights.insert(block_ids[0], 3);
2365            block_map_writer.resize(new_heights);
2366
2367            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2368            // Same height as before, should remain the same
2369            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2370        }
2371    }
2372
2373    #[cfg(target_os = "macos")]
2374    #[gpui::test]
2375    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2376        cx.update(init_test);
2377
2378        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
2379
2380        let text = "one two three\nfour five six\nseven eight";
2381
2382        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2383        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2384        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2385        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2386        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2387        let (_, wraps_snapshot) = cx.update(|cx| {
2388            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
2389        });
2390        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
2391
2392        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2393        writer.insert(vec![
2394            BlockProperties {
2395                style: BlockStyle::Fixed,
2396                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2397                render: Arc::new(|_| div().into_any()),
2398                height: 1,
2399                priority: 0,
2400            },
2401            BlockProperties {
2402                style: BlockStyle::Fixed,
2403                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2404                render: Arc::new(|_| div().into_any()),
2405                height: 1,
2406                priority: 0,
2407            },
2408        ]);
2409
2410        // Blocks with an 'above' disposition go above their corresponding buffer line.
2411        // Blocks with a 'below' disposition go below their corresponding buffer line.
2412        let snapshot = block_map.read(wraps_snapshot, Default::default());
2413        assert_eq!(
2414            snapshot.text(),
2415            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2416        );
2417    }
2418
2419    #[gpui::test]
2420    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2421        cx.update(init_test);
2422
2423        let text = "line1\nline2\nline3\nline4\nline5";
2424
2425        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2426        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2427        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2428        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2429        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2430        let tab_size = 1.try_into().unwrap();
2431        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2432        let (wrap_map, wraps_snapshot) =
2433            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2434        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2435
2436        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2437        let replace_block_id = writer.insert(vec![BlockProperties {
2438            style: BlockStyle::Fixed,
2439            placement: BlockPlacement::Replace(
2440                buffer_snapshot.anchor_after(Point::new(1, 3))
2441                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
2442            ),
2443            height: 4,
2444            render: Arc::new(|_| div().into_any()),
2445            priority: 0,
2446        }])[0];
2447
2448        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2449        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2450
2451        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2452            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2453            buffer.snapshot(cx)
2454        });
2455        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2456            buffer_snapshot.clone(),
2457            buffer_subscription.consume().into_inner(),
2458        );
2459        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2460        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2461        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2462            wrap_map.sync(tab_snapshot, tab_edits, cx)
2463        });
2464        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2465        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2466
2467        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2468            buffer.edit(
2469                [(
2470                    Point::new(1, 5)..Point::new(1, 5),
2471                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2472                )],
2473                None,
2474                cx,
2475            );
2476            buffer.snapshot(cx)
2477        });
2478        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2479            buffer_snapshot.clone(),
2480            buffer_subscription.consume().into_inner(),
2481        );
2482        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2483        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2484        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2485            wrap_map.sync(tab_snapshot, tab_edits, cx)
2486        });
2487        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2488        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2489
2490        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2491        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2492        writer.insert(vec![
2493            BlockProperties {
2494                style: BlockStyle::Fixed,
2495                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2496                height: 1,
2497                render: Arc::new(|_| div().into_any()),
2498                priority: 0,
2499            },
2500            BlockProperties {
2501                style: BlockStyle::Fixed,
2502                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2503                height: 1,
2504                render: Arc::new(|_| div().into_any()),
2505                priority: 0,
2506            },
2507            BlockProperties {
2508                style: BlockStyle::Fixed,
2509                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2510                height: 1,
2511                render: Arc::new(|_| div().into_any()),
2512                priority: 0,
2513            },
2514        ]);
2515        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2516        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2517
2518        // Ensure blocks inserted *inside* replaced region are hidden.
2519        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2520        writer.insert(vec![
2521            BlockProperties {
2522                style: BlockStyle::Fixed,
2523                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2524                height: 1,
2525                render: Arc::new(|_| div().into_any()),
2526                priority: 0,
2527            },
2528            BlockProperties {
2529                style: BlockStyle::Fixed,
2530                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2531                height: 1,
2532                render: Arc::new(|_| div().into_any()),
2533                priority: 0,
2534            },
2535            BlockProperties {
2536                style: BlockStyle::Fixed,
2537                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2538                height: 1,
2539                render: Arc::new(|_| div().into_any()),
2540                priority: 0,
2541            },
2542        ]);
2543        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2544        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2545
2546        // Removing the replace block shows all the hidden blocks again.
2547        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2548        writer.remove(HashSet::from_iter([replace_block_id]));
2549        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2550        assert_eq!(
2551            blocks_snapshot.text(),
2552            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2553        );
2554    }
2555
2556    #[gpui::test]
2557    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2558        cx.update(init_test);
2559
2560        let text = "111\n222\n333\n444\n555\n666";
2561
2562        let buffer = cx.update(|cx| {
2563            MultiBuffer::build_multi(
2564                [
2565                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2566                    (
2567                        text,
2568                        vec![
2569                            Point::new(1, 0)..Point::new(1, 3),
2570                            Point::new(2, 0)..Point::new(2, 3),
2571                            Point::new(3, 0)..Point::new(3, 3),
2572                        ],
2573                    ),
2574                    (
2575                        text,
2576                        vec![
2577                            Point::new(4, 0)..Point::new(4, 3),
2578                            Point::new(5, 0)..Point::new(5, 3),
2579                        ],
2580                    ),
2581                ],
2582                cx,
2583            )
2584        });
2585        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2586        let buffer_ids = buffer_snapshot
2587            .excerpts()
2588            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2589            .dedup()
2590            .collect::<Vec<_>>();
2591        assert_eq!(buffer_ids.len(), 3);
2592        let buffer_id_1 = buffer_ids[0];
2593        let buffer_id_2 = buffer_ids[1];
2594        let buffer_id_3 = buffer_ids[2];
2595
2596        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2597        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2598        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2599        let (_, wrap_snapshot) =
2600            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2601        let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1);
2602        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2603
2604        assert_eq!(
2605            blocks_snapshot.text(),
2606            "\n\n\n111\n\n\n\n\n222\n\n\n333\n\n\n444\n\n\n\n\n555\n\n\n666\n"
2607        );
2608        assert_eq!(
2609            blocks_snapshot
2610                .row_infos(BlockRow(0))
2611                .map(|i| i.buffer_row)
2612                .collect::<Vec<_>>(),
2613            vec![
2614                None,
2615                None,
2616                None,
2617                Some(0),
2618                None,
2619                None,
2620                None,
2621                None,
2622                Some(1),
2623                None,
2624                None,
2625                Some(2),
2626                None,
2627                None,
2628                Some(3),
2629                None,
2630                None,
2631                None,
2632                None,
2633                Some(4),
2634                None,
2635                None,
2636                Some(5),
2637                None,
2638            ]
2639        );
2640
2641        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2642        let excerpt_blocks_2 = writer.insert(vec![
2643            BlockProperties {
2644                style: BlockStyle::Fixed,
2645                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2646                height: 1,
2647                render: Arc::new(|_| div().into_any()),
2648                priority: 0,
2649            },
2650            BlockProperties {
2651                style: BlockStyle::Fixed,
2652                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2653                height: 1,
2654                render: Arc::new(|_| div().into_any()),
2655                priority: 0,
2656            },
2657            BlockProperties {
2658                style: BlockStyle::Fixed,
2659                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2660                height: 1,
2661                render: Arc::new(|_| div().into_any()),
2662                priority: 0,
2663            },
2664        ]);
2665        let excerpt_blocks_3 = writer.insert(vec![
2666            BlockProperties {
2667                style: BlockStyle::Fixed,
2668                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2669                height: 1,
2670                render: Arc::new(|_| div().into_any()),
2671                priority: 0,
2672            },
2673            BlockProperties {
2674                style: BlockStyle::Fixed,
2675                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2676                height: 1,
2677                render: Arc::new(|_| div().into_any()),
2678                priority: 0,
2679            },
2680        ]);
2681
2682        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2683        assert_eq!(
2684            blocks_snapshot.text(),
2685            "\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"
2686        );
2687        assert_eq!(
2688            blocks_snapshot
2689                .row_infos(BlockRow(0))
2690                .map(|i| i.buffer_row)
2691                .collect::<Vec<_>>(),
2692            vec![
2693                None,
2694                None,
2695                None,
2696                Some(0),
2697                None,
2698                None,
2699                None,
2700                None,
2701                None,
2702                Some(1),
2703                None,
2704                None,
2705                None,
2706                Some(2),
2707                None,
2708                None,
2709                Some(3),
2710                None,
2711                None,
2712                None,
2713                None,
2714                None,
2715                None,
2716                Some(4),
2717                None,
2718                None,
2719                Some(5),
2720                None,
2721                None,
2722            ]
2723        );
2724
2725        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2726        buffer.read_with(cx, |buffer, cx| {
2727            writer.fold_buffer(buffer_id_1, buffer, cx);
2728        });
2729        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2730            style: BlockStyle::Fixed,
2731            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2732            height: 1,
2733            render: Arc::new(|_| div().into_any()),
2734            priority: 0,
2735        }]);
2736        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2737        let blocks = blocks_snapshot
2738            .blocks_in_range(0..u32::MAX)
2739            .collect::<Vec<_>>();
2740        for (_, block) in &blocks {
2741            if let BlockId::Custom(custom_block_id) = block.id() {
2742                assert!(
2743                    !excerpt_blocks_1.contains(&custom_block_id),
2744                    "Should have no blocks from the folded buffer"
2745                );
2746                assert!(
2747                    excerpt_blocks_2.contains(&custom_block_id)
2748                        || excerpt_blocks_3.contains(&custom_block_id),
2749                    "Should have only blocks from unfolded buffers"
2750                );
2751            }
2752        }
2753        assert_eq!(
2754            1,
2755            blocks
2756                .iter()
2757                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2758                .count(),
2759            "Should have one folded block, producing a header of the second buffer"
2760        );
2761        assert_eq!(
2762            blocks_snapshot.text(),
2763            "\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"
2764        );
2765        assert_eq!(
2766            blocks_snapshot
2767                .row_infos(BlockRow(0))
2768                .map(|i| i.buffer_row)
2769                .collect::<Vec<_>>(),
2770            vec![
2771                None,
2772                None,
2773                None,
2774                None,
2775                None,
2776                None,
2777                Some(1),
2778                None,
2779                None,
2780                None,
2781                Some(2),
2782                None,
2783                None,
2784                Some(3),
2785                None,
2786                None,
2787                None,
2788                None,
2789                None,
2790                None,
2791                Some(4),
2792                None,
2793                None,
2794                Some(5),
2795                None,
2796                None,
2797            ]
2798        );
2799
2800        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2801        buffer.read_with(cx, |buffer, cx| {
2802            writer.fold_buffer(buffer_id_2, buffer, cx);
2803        });
2804        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2805        let blocks = blocks_snapshot
2806            .blocks_in_range(0..u32::MAX)
2807            .collect::<Vec<_>>();
2808        for (_, block) in &blocks {
2809            if let BlockId::Custom(custom_block_id) = block.id() {
2810                assert!(
2811                    !excerpt_blocks_1.contains(&custom_block_id),
2812                    "Should have no blocks from the folded buffer_1"
2813                );
2814                assert!(
2815                    !excerpt_blocks_2.contains(&custom_block_id),
2816                    "Should have no blocks from the folded buffer_2"
2817                );
2818                assert!(
2819                    excerpt_blocks_3.contains(&custom_block_id),
2820                    "Should have only blocks from unfolded buffers"
2821                );
2822            }
2823        }
2824        assert_eq!(
2825            2,
2826            blocks
2827                .iter()
2828                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2829                .count(),
2830            "Should have two folded blocks, producing headers"
2831        );
2832        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n\n555\n\n\n666\n\n");
2833        assert_eq!(
2834            blocks_snapshot
2835                .row_infos(BlockRow(0))
2836                .map(|i| i.buffer_row)
2837                .collect::<Vec<_>>(),
2838            vec![
2839                None,
2840                None,
2841                None,
2842                None,
2843                None,
2844                None,
2845                None,
2846                None,
2847                Some(4),
2848                None,
2849                None,
2850                Some(5),
2851                None,
2852                None,
2853            ]
2854        );
2855
2856        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2857        buffer.read_with(cx, |buffer, cx| {
2858            writer.unfold_buffer(buffer_id_1, buffer, cx);
2859        });
2860        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2861        let blocks = blocks_snapshot
2862            .blocks_in_range(0..u32::MAX)
2863            .collect::<Vec<_>>();
2864        for (_, block) in &blocks {
2865            if let BlockId::Custom(custom_block_id) = block.id() {
2866                assert!(
2867                    !excerpt_blocks_2.contains(&custom_block_id),
2868                    "Should have no blocks from the folded buffer_2"
2869                );
2870                assert!(
2871                    excerpt_blocks_1.contains(&custom_block_id)
2872                        || excerpt_blocks_3.contains(&custom_block_id),
2873                    "Should have only blocks from unfolded buffers"
2874                );
2875            }
2876        }
2877        assert_eq!(
2878            1,
2879            blocks
2880                .iter()
2881                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2882                .count(),
2883            "Should be back to a single folded buffer, producing a header for buffer_2"
2884        );
2885        assert_eq!(
2886            blocks_snapshot.text(),
2887            "\n\n\n\n111\n\n\n\n\n\n\n\n555\n\n\n666\n\n",
2888            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2889        );
2890        assert_eq!(
2891            blocks_snapshot
2892                .row_infos(BlockRow(0))
2893                .map(|i| i.buffer_row)
2894                .collect::<Vec<_>>(),
2895            vec![
2896                None,
2897                None,
2898                None,
2899                None,
2900                Some(0),
2901                None,
2902                None,
2903                None,
2904                None,
2905                None,
2906                None,
2907                None,
2908                Some(4),
2909                None,
2910                None,
2911                Some(5),
2912                None,
2913                None,
2914            ]
2915        );
2916
2917        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2918        buffer.read_with(cx, |buffer, cx| {
2919            writer.fold_buffer(buffer_id_3, buffer, cx);
2920        });
2921        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2922        let blocks = blocks_snapshot
2923            .blocks_in_range(0..u32::MAX)
2924            .collect::<Vec<_>>();
2925        for (_, block) in &blocks {
2926            if let BlockId::Custom(custom_block_id) = block.id() {
2927                assert!(
2928                    excerpt_blocks_1.contains(&custom_block_id),
2929                    "Should have no blocks from the folded buffer_1"
2930                );
2931                assert!(
2932                    !excerpt_blocks_2.contains(&custom_block_id),
2933                    "Should have only blocks from unfolded buffers"
2934                );
2935                assert!(
2936                    !excerpt_blocks_3.contains(&custom_block_id),
2937                    "Should have only blocks from unfolded buffers"
2938                );
2939            }
2940        }
2941
2942        assert_eq!(
2943            blocks_snapshot.text(),
2944            "\n\n\n\n111\n\n\n\n\n",
2945            "Should have a single, first buffer left after folding"
2946        );
2947        assert_eq!(
2948            blocks_snapshot
2949                .row_infos(BlockRow(0))
2950                .map(|i| i.buffer_row)
2951                .collect::<Vec<_>>(),
2952            vec![
2953                None,
2954                None,
2955                None,
2956                None,
2957                Some(0),
2958                None,
2959                None,
2960                None,
2961                None,
2962                None,
2963            ]
2964        );
2965    }
2966
2967    #[gpui::test]
2968    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2969        cx.update(init_test);
2970
2971        let text = "111";
2972
2973        let buffer = cx.update(|cx| {
2974            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2975        });
2976        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2977        let buffer_ids = buffer_snapshot
2978            .excerpts()
2979            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2980            .dedup()
2981            .collect::<Vec<_>>();
2982        assert_eq!(buffer_ids.len(), 1);
2983        let buffer_id = buffer_ids[0];
2984
2985        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2986        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2987        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2988        let (_, wrap_snapshot) =
2989            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2990        let mut block_map = BlockMap::new(wrap_snapshot.clone(), true, 2, 1, 1);
2991        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2992
2993        assert_eq!(blocks_snapshot.text(), "\n\n\n111\n");
2994
2995        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2996        buffer.read_with(cx, |buffer, cx| {
2997            writer.fold_buffer(buffer_id, buffer, cx);
2998        });
2999        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
3000        let blocks = blocks_snapshot
3001            .blocks_in_range(0..u32::MAX)
3002            .collect::<Vec<_>>();
3003        assert_eq!(
3004            1,
3005            blocks
3006                .iter()
3007                .filter(|(_, block)| {
3008                    match block {
3009                        Block::FoldedBuffer { prev_excerpt, .. } => {
3010                            assert!(prev_excerpt.is_none());
3011                            true
3012                        }
3013                        _ => false,
3014                    }
3015                })
3016                .count(),
3017            "Should have one folded block, producing a header of the second buffer"
3018        );
3019        assert_eq!(blocks_snapshot.text(), "\n");
3020        assert_eq!(
3021            blocks_snapshot
3022                .row_infos(BlockRow(0))
3023                .map(|i| i.buffer_row)
3024                .collect::<Vec<_>>(),
3025            vec![None, None],
3026            "When fully folded, should be no buffer rows"
3027        );
3028    }
3029
3030    #[gpui::test(iterations = 100)]
3031    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
3032        cx.update(init_test);
3033
3034        let operations = env::var("OPERATIONS")
3035            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
3036            .unwrap_or(10);
3037
3038        let wrap_width = if rng.gen_bool(0.2) {
3039            None
3040        } else {
3041            Some(px(rng.gen_range(0.0..=100.0)))
3042        };
3043        let tab_size = 1.try_into().unwrap();
3044        let font_size = px(14.0);
3045        let buffer_start_header_height = rng.gen_range(1..=5);
3046        let excerpt_header_height = rng.gen_range(1..=5);
3047        let excerpt_footer_height = rng.gen_range(1..=5);
3048
3049        log::info!("Wrap width: {:?}", wrap_width);
3050        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
3051        log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
3052        let is_singleton = rng.gen();
3053        let buffer = if is_singleton {
3054            let len = rng.gen_range(0..10);
3055            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
3056            log::info!("initial singleton buffer text: {:?}", text);
3057            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
3058        } else {
3059            cx.update(|cx| {
3060                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
3061                log::info!(
3062                    "initial multi-buffer text: {:?}",
3063                    multibuffer.read(cx).read(cx).text()
3064                );
3065                multibuffer
3066            })
3067        };
3068
3069        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3070        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3071        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3072        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3073        let font = test_font();
3074        let (wrap_map, wraps_snapshot) =
3075            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
3076        let mut block_map = BlockMap::new(
3077            wraps_snapshot,
3078            true,
3079            buffer_start_header_height,
3080            excerpt_header_height,
3081            excerpt_footer_height,
3082        );
3083
3084        for _ in 0..operations {
3085            let mut buffer_edits = Vec::new();
3086            match rng.gen_range(0..=100) {
3087                0..=19 => {
3088                    let wrap_width = if rng.gen_bool(0.2) {
3089                        None
3090                    } else {
3091                        Some(px(rng.gen_range(0.0..=100.0)))
3092                    };
3093                    log::info!("Setting wrap width to {:?}", wrap_width);
3094                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3095                }
3096                20..=39 => {
3097                    let block_count = rng.gen_range(1..=5);
3098                    let block_properties = (0..block_count)
3099                        .map(|_| {
3100                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
3101                            let offset =
3102                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
3103                            let mut min_height = 0;
3104                            let placement = match rng.gen_range(0..3) {
3105                                0 => {
3106                                    min_height = 1;
3107                                    let start = buffer.anchor_after(offset);
3108                                    let end = buffer.anchor_after(buffer.clip_offset(
3109                                        rng.gen_range(offset..=buffer.len()),
3110                                        Bias::Left,
3111                                    ));
3112                                    BlockPlacement::Replace(start..=end)
3113                                }
3114                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3115                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3116                            };
3117
3118                            let height = rng.gen_range(min_height..5);
3119                            BlockProperties {
3120                                style: BlockStyle::Fixed,
3121                                placement,
3122                                height,
3123                                render: Arc::new(|_| div().into_any()),
3124                                priority: 0,
3125                            }
3126                        })
3127                        .collect::<Vec<_>>();
3128
3129                    let (inlay_snapshot, inlay_edits) =
3130                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3131                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3132                    let (tab_snapshot, tab_edits) =
3133                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3134                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3135                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3136                    });
3137                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3138                    let block_ids =
3139                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3140                            placement: props.placement.clone(),
3141                            height: props.height,
3142                            style: props.style,
3143                            render: Arc::new(|_| div().into_any()),
3144                            priority: 0,
3145                        }));
3146
3147                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3148                        log::info!(
3149                            "inserted block {:?} with height {} and id {:?}",
3150                            block_properties
3151                                .placement
3152                                .as_ref()
3153                                .map(|p| p.to_point(&buffer_snapshot)),
3154                            block_properties.height,
3155                            block_id
3156                        );
3157                    }
3158                }
3159                40..=59 if !block_map.custom_blocks.is_empty() => {
3160                    let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
3161                    let block_ids_to_remove = block_map
3162                        .custom_blocks
3163                        .choose_multiple(&mut rng, block_count)
3164                        .map(|block| block.id)
3165                        .collect::<HashSet<_>>();
3166
3167                    let (inlay_snapshot, inlay_edits) =
3168                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3169                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3170                    let (tab_snapshot, tab_edits) =
3171                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3172                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3173                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3174                    });
3175                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3176                    log::info!(
3177                        "removing {} blocks: {:?}",
3178                        block_ids_to_remove.len(),
3179                        block_ids_to_remove
3180                    );
3181                    block_map.remove(block_ids_to_remove);
3182                }
3183                60..=79 => {
3184                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3185                        log::info!("Noop fold/unfold operation on a singleton buffer");
3186                        continue;
3187                    }
3188                    let (inlay_snapshot, inlay_edits) =
3189                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3190                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3191                    let (tab_snapshot, tab_edits) =
3192                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3193                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3194                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3195                    });
3196                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3197                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3198                        let folded_buffers = block_map
3199                            .0
3200                            .folded_buffers
3201                            .iter()
3202                            .cloned()
3203                            .collect::<Vec<_>>();
3204                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3205                        unfolded_buffers.dedup();
3206                        log::debug!("All buffers {unfolded_buffers:?}");
3207                        log::debug!("Folded buffers {folded_buffers:?}");
3208                        unfolded_buffers
3209                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3210                        (unfolded_buffers, folded_buffers)
3211                    });
3212                    let mut folded_count = folded_buffers.len();
3213                    let mut unfolded_count = unfolded_buffers.len();
3214
3215                    let fold = !unfolded_buffers.is_empty() && rng.gen_bool(0.5);
3216                    let unfold = !folded_buffers.is_empty() && rng.gen_bool(0.5);
3217                    if !fold && !unfold {
3218                        log::info!("Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}");
3219                        continue;
3220                    }
3221
3222                    buffer.update(cx, |buffer, cx| {
3223                        if fold {
3224                            let buffer_to_fold =
3225                                unfolded_buffers[rng.gen_range(0..unfolded_buffers.len())];
3226                            log::info!("Folding {buffer_to_fold:?}");
3227                            let related_excerpts = buffer_snapshot
3228                                .excerpts()
3229                                .filter_map(|(excerpt_id, buffer, range)| {
3230                                    if buffer.remote_id() == buffer_to_fold {
3231                                        Some((
3232                                            excerpt_id,
3233                                            buffer
3234                                                .text_for_range(range.context)
3235                                                .collect::<String>(),
3236                                        ))
3237                                    } else {
3238                                        None
3239                                    }
3240                                })
3241                                .collect::<Vec<_>>();
3242                            log::info!(
3243                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3244                            );
3245                            folded_count += 1;
3246                            unfolded_count -= 1;
3247                            block_map.fold_buffer(buffer_to_fold, buffer, cx);
3248                        }
3249                        if unfold {
3250                            let buffer_to_unfold =
3251                                folded_buffers[rng.gen_range(0..folded_buffers.len())];
3252                            log::info!("Unfolding {buffer_to_unfold:?}");
3253                            unfolded_count += 1;
3254                            folded_count -= 1;
3255                            block_map.unfold_buffer(buffer_to_unfold, buffer, cx);
3256                        }
3257                        log::info!(
3258                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3259                        );
3260                    });
3261                }
3262                _ => {
3263                    buffer.update(cx, |buffer, cx| {
3264                        let mutation_count = rng.gen_range(1..=5);
3265                        let subscription = buffer.subscribe();
3266                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3267                        buffer_snapshot = buffer.snapshot(cx);
3268                        buffer_edits.extend(subscription.consume());
3269                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3270                    });
3271                }
3272            }
3273
3274            let (inlay_snapshot, inlay_edits) =
3275                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3276            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3277            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3278            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3279                wrap_map.sync(tab_snapshot, tab_edits, cx)
3280            });
3281            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3282            assert_eq!(
3283                blocks_snapshot.transforms.summary().input_rows,
3284                wraps_snapshot.max_point().row() + 1
3285            );
3286            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3287            log::info!("blocks text: {:?}", blocks_snapshot.text());
3288
3289            let mut expected_blocks = Vec::new();
3290            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3291                Some((
3292                    block.placement.to_wrap_row(&wraps_snapshot)?,
3293                    Block::Custom(block.clone()),
3294                ))
3295            }));
3296
3297            // Note that this needs to be synced with the related section in BlockMap::sync
3298            expected_blocks.extend(BlockMap::header_and_footer_blocks(
3299                true,
3300                excerpt_footer_height,
3301                buffer_start_header_height,
3302                excerpt_header_height,
3303                &buffer_snapshot,
3304                &block_map.folded_buffers,
3305                0..,
3306                &wraps_snapshot,
3307            ));
3308
3309            BlockMap::sort_blocks(&mut expected_blocks);
3310
3311            for (placement, block) in &expected_blocks {
3312                log::info!(
3313                    "Block {:?} placement: {:?} Height: {:?}",
3314                    block.id(),
3315                    placement,
3316                    block.height()
3317                );
3318            }
3319
3320            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3321
3322            let input_buffer_rows = buffer_snapshot
3323                .row_infos(MultiBufferRow(0))
3324                .map(|row| row.buffer_row)
3325                .collect::<Vec<_>>();
3326            let mut expected_buffer_rows = Vec::new();
3327            let mut expected_text = String::new();
3328            let mut expected_block_positions = Vec::new();
3329            let mut expected_replaced_buffer_rows = HashSet::default();
3330            let input_text = wraps_snapshot.text();
3331
3332            // Loop over the input lines, creating (N - 1) empty lines for
3333            // blocks of height N.
3334            //
3335            // It's important to note that output *starts* as one empty line,
3336            // so we special case row 0 to assume a leading '\n'.
3337            //
3338            // Linehood is the birthright of strings.
3339            let mut input_text_lines = input_text.split('\n').enumerate().peekable();
3340            let mut block_row = 0;
3341            while let Some((wrap_row, input_line)) = input_text_lines.next() {
3342                let wrap_row = wrap_row as u32;
3343                let multibuffer_row = wraps_snapshot
3344                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3345                    .row;
3346
3347                // Create empty lines for the above block
3348                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3349                    if placement.start().0 == wrap_row && block.place_above() {
3350                        let (_, block) = sorted_blocks_iter.next().unwrap();
3351                        expected_block_positions.push((block_row, block.id()));
3352                        if block.height() > 0 {
3353                            let text = "\n".repeat((block.height() - 1) as usize);
3354                            if block_row > 0 {
3355                                expected_text.push('\n')
3356                            }
3357                            expected_text.push_str(&text);
3358                            for _ in 0..block.height() {
3359                                expected_buffer_rows.push(None);
3360                            }
3361                            block_row += block.height();
3362                        }
3363                    } else {
3364                        break;
3365                    }
3366                }
3367
3368                // Skip lines within replace blocks, then create empty lines for the replace block's height
3369                let mut is_in_replace_block = false;
3370                if let Some((BlockPlacement::Replace(replace_range), block)) =
3371                    sorted_blocks_iter.peek()
3372                {
3373                    if wrap_row >= replace_range.start().0 {
3374                        is_in_replace_block = true;
3375
3376                        if wrap_row == replace_range.start().0 {
3377                            if matches!(block, Block::FoldedBuffer { .. }) {
3378                                expected_buffer_rows.push(None);
3379                            } else {
3380                                expected_buffer_rows
3381                                    .push(input_buffer_rows[multibuffer_row as usize]);
3382                            }
3383                        }
3384
3385                        if wrap_row == replace_range.end().0 {
3386                            expected_block_positions.push((block_row, block.id()));
3387                            let text = "\n".repeat((block.height() - 1) as usize);
3388                            if block_row > 0 {
3389                                expected_text.push('\n');
3390                            }
3391                            expected_text.push_str(&text);
3392
3393                            for _ in 1..block.height() {
3394                                expected_buffer_rows.push(None);
3395                            }
3396                            block_row += block.height();
3397
3398                            sorted_blocks_iter.next();
3399                        }
3400                    }
3401                }
3402
3403                if is_in_replace_block {
3404                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3405                } else {
3406                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3407                    let soft_wrapped = wraps_snapshot
3408                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3409                        .column()
3410                        > 0;
3411                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3412                    if block_row > 0 {
3413                        expected_text.push('\n');
3414                    }
3415                    expected_text.push_str(input_line);
3416                    block_row += 1;
3417                }
3418
3419                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3420                    if placement.end().0 == wrap_row && block.place_below() {
3421                        let (_, block) = sorted_blocks_iter.next().unwrap();
3422                        expected_block_positions.push((block_row, block.id()));
3423                        if block.height() > 0 {
3424                            let text = "\n".repeat((block.height() - 1) as usize);
3425                            if block_row > 0 {
3426                                expected_text.push('\n')
3427                            }
3428                            expected_text.push_str(&text);
3429                            for _ in 0..block.height() {
3430                                expected_buffer_rows.push(None);
3431                            }
3432                            block_row += block.height();
3433                        }
3434                    } else {
3435                        break;
3436                    }
3437                }
3438            }
3439
3440            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3441            let expected_row_count = expected_lines.len();
3442            log::info!("expected text: {expected_text:?}");
3443
3444            assert_eq!(
3445                blocks_snapshot.max_point().row + 1,
3446                expected_row_count as u32,
3447                "actual row count != expected row count",
3448            );
3449            assert_eq!(
3450                blocks_snapshot.text(),
3451                expected_text,
3452                "actual text != expected text",
3453            );
3454
3455            for start_row in 0..expected_row_count {
3456                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
3457                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3458                if end_row < expected_row_count {
3459                    expected_text.push('\n');
3460                }
3461
3462                let actual_text = blocks_snapshot
3463                    .chunks(
3464                        start_row as u32..end_row as u32,
3465                        false,
3466                        false,
3467                        Highlights::default(),
3468                    )
3469                    .map(|chunk| chunk.text)
3470                    .collect::<String>();
3471                assert_eq!(
3472                    actual_text,
3473                    expected_text,
3474                    "incorrect text starting row row range {:?}",
3475                    start_row..end_row
3476                );
3477                assert_eq!(
3478                    blocks_snapshot
3479                        .row_infos(BlockRow(start_row as u32))
3480                        .map(|row_info| row_info.buffer_row)
3481                        .collect::<Vec<_>>(),
3482                    &expected_buffer_rows[start_row..],
3483                    "incorrect buffer_rows starting at row {:?}",
3484                    start_row
3485                );
3486            }
3487
3488            assert_eq!(
3489                blocks_snapshot
3490                    .blocks_in_range(0..(expected_row_count as u32))
3491                    .map(|(row, block)| (row, block.id()))
3492                    .collect::<Vec<_>>(),
3493                expected_block_positions,
3494                "invalid blocks_in_range({:?})",
3495                0..expected_row_count
3496            );
3497
3498            for (_, expected_block) in
3499                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3500            {
3501                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3502                assert_eq!(
3503                    actual_block.map(|block| block.id()),
3504                    Some(expected_block.id())
3505                );
3506            }
3507
3508            for (block_row, block_id) in expected_block_positions {
3509                if let BlockId::Custom(block_id) = block_id {
3510                    assert_eq!(
3511                        blocks_snapshot.row_for_block(block_id),
3512                        Some(BlockRow(block_row))
3513                    );
3514                }
3515            }
3516
3517            let mut expected_longest_rows = Vec::new();
3518            let mut longest_line_len = -1_isize;
3519            for (row, line) in expected_lines.iter().enumerate() {
3520                let row = row as u32;
3521
3522                assert_eq!(
3523                    blocks_snapshot.line_len(BlockRow(row)),
3524                    line.len() as u32,
3525                    "invalid line len for row {}",
3526                    row
3527                );
3528
3529                let line_char_count = line.chars().count() as isize;
3530                match line_char_count.cmp(&longest_line_len) {
3531                    Ordering::Less => {}
3532                    Ordering::Equal => expected_longest_rows.push(row),
3533                    Ordering::Greater => {
3534                        longest_line_len = line_char_count;
3535                        expected_longest_rows.clear();
3536                        expected_longest_rows.push(row);
3537                    }
3538                }
3539            }
3540
3541            let longest_row = blocks_snapshot.longest_row();
3542            assert!(
3543                expected_longest_rows.contains(&longest_row),
3544                "incorrect longest row {}. expected {:?} with length {}",
3545                longest_row,
3546                expected_longest_rows,
3547                longest_line_len,
3548            );
3549
3550            for _ in 0..10 {
3551                let end_row = rng.gen_range(1..=expected_lines.len());
3552                let start_row = rng.gen_range(0..end_row);
3553
3554                let mut expected_longest_rows_in_range = vec![];
3555                let mut longest_line_len_in_range = 0;
3556
3557                let mut row = start_row as u32;
3558                for line in &expected_lines[start_row..end_row] {
3559                    let line_char_count = line.chars().count() as isize;
3560                    match line_char_count.cmp(&longest_line_len_in_range) {
3561                        Ordering::Less => {}
3562                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3563                        Ordering::Greater => {
3564                            longest_line_len_in_range = line_char_count;
3565                            expected_longest_rows_in_range.clear();
3566                            expected_longest_rows_in_range.push(row);
3567                        }
3568                    }
3569                    row += 1;
3570                }
3571
3572                let longest_row_in_range = blocks_snapshot
3573                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3574                assert!(
3575                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3576                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3577                    longest_row,
3578                    start_row..end_row,
3579                    expected_longest_rows_in_range,
3580                    longest_line_len_in_range,
3581                );
3582            }
3583
3584            // Ensure that conversion between block points and wrap points is stable.
3585            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3586                let wrap_point = WrapPoint::new(row, 0);
3587                let block_point = blocks_snapshot.to_block_point(wrap_point);
3588                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3589                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3590                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3591                assert_eq!(
3592                    blocks_snapshot.to_block_point(right_wrap_point),
3593                    block_point
3594                );
3595            }
3596
3597            let mut block_point = BlockPoint::new(0, 0);
3598            for c in expected_text.chars() {
3599                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3600                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3601                assert_eq!(
3602                    blocks_snapshot
3603                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3604                    left_point,
3605                    "block point: {:?}, wrap point: {:?}",
3606                    block_point,
3607                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3608                );
3609                assert_eq!(
3610                    left_buffer_point,
3611                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3612                    "{:?} is not valid in buffer coordinates",
3613                    left_point
3614                );
3615
3616                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3617                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3618                assert_eq!(
3619                    blocks_snapshot
3620                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3621                    right_point,
3622                    "block point: {:?}, wrap point: {:?}",
3623                    block_point,
3624                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3625                );
3626                assert_eq!(
3627                    right_buffer_point,
3628                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3629                    "{:?} is not valid in buffer coordinates",
3630                    right_point
3631                );
3632
3633                if c == '\n' {
3634                    block_point.0 += Point::new(1, 0);
3635                } else {
3636                    block_point.column += c.len_utf8() as u32;
3637                }
3638            }
3639
3640            for buffer_row in 0..=buffer_snapshot.max_point().row {
3641                let buffer_row = MultiBufferRow(buffer_row);
3642                assert_eq!(
3643                    blocks_snapshot.is_line_replaced(buffer_row),
3644                    expected_replaced_buffer_rows.contains(&buffer_row),
3645                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3646                );
3647            }
3648        }
3649    }
3650
3651    fn init_test(cx: &mut gpui::App) {
3652        let settings = SettingsStore::test(cx);
3653        cx.set_global(settings);
3654        theme::init(theme::LoadThemes::JustBase, cx);
3655        assets::Assets.load_test_fonts(cx);
3656    }
3657
3658    impl Block {
3659        fn as_custom(&self) -> Option<&CustomBlock> {
3660            match self {
3661                Block::Custom(block) => Some(block),
3662                _ => None,
3663            }
3664        }
3665    }
3666
3667    impl BlockSnapshot {
3668        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3669            self.wrap_snapshot
3670                .to_point(self.to_wrap_point(point, bias), bias)
3671        }
3672    }
3673}