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