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