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