block_map.rs

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