block_map.rs

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