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