block_map.rs

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