block_map.rs

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