block_map.rs

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