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