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