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