block_map.rs

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