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