block_map.rs

   1use super::{
   2    Highlights,
   3    fold_map::Chunk,
   4    wrap_map::{self, WrapEdit, WrapPoint, WrapSnapshot},
   5};
   6use crate::{EditorStyle, GutterDimensions};
   7use collections::{Bound, HashMap, HashSet};
   8use gpui::{AnyElement, App, EntityId, Pixels, Window};
   9use language::{Patch, Point};
  10use multi_buffer::{
  11    Anchor, ExcerptId, ExcerptInfo, MultiBuffer, MultiBufferRow, MultiBufferSnapshot, RowInfo,
  12    ToOffset, ToPoint as _,
  13};
  14use parking_lot::Mutex;
  15use std::{
  16    cell::RefCell,
  17    cmp::{self, Ordering},
  18    fmt::Debug,
  19    ops::{Deref, DerefMut, Range, RangeBounds, RangeInclusive},
  20    sync::{
  21        Arc,
  22        atomic::{AtomicUsize, Ordering::SeqCst},
  23    },
  24};
  25use sum_tree::{Bias, ContextLessSummary, Dimensions, SumTree, TreeMap};
  26use text::{BufferId, Edit};
  27use ui::ElementId;
  28
  29const NEWLINES: &[u8; u128::BITS as usize] = &[b'\n'; _];
  30const BULLETS: &[u8; u128::BITS as usize] = &[b'*'; _];
  31
  32/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
  33///
  34/// See the [`display_map` module documentation](crate::display_map) for more information.
  35pub struct BlockMap {
  36    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, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
 437    input_chunks: wrap_map::WrapChunks<'a>,
 438    input_chunk: Chunk<'a>,
 439    output_row: u32,
 440    max_output_row: u32,
 441    masked: bool,
 442}
 443
 444#[derive(Clone)]
 445pub struct BlockRows<'a> {
 446    transforms: sum_tree::Cursor<'a, 'static, Transform, Dimensions<BlockRow, WrapRow>>,
 447    input_rows: wrap_map::WrapRows<'a>,
 448    output_row: BlockRow,
 449    started: bool,
 450}
 451
 452impl BlockMap {
 453    pub fn new(
 454        wrap_snapshot: WrapSnapshot,
 455        buffer_header_height: u32,
 456        excerpt_header_height: u32,
 457    ) -> Self {
 458        let row_count = wrap_snapshot.max_point().row() + 1;
 459        let mut transforms = SumTree::default();
 460        push_isomorphic(&mut transforms, row_count, &wrap_snapshot);
 461        let map = Self {
 462            next_block_id: AtomicUsize::new(0),
 463            custom_blocks: Vec::new(),
 464            custom_blocks_by_id: TreeMap::default(),
 465            folded_buffers: HashSet::default(),
 466            buffers_with_disabled_headers: HashSet::default(),
 467            transforms: RefCell::new(transforms),
 468            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
 469            buffer_header_height,
 470            excerpt_header_height,
 471        };
 472        map.sync(
 473            &wrap_snapshot,
 474            Patch::new(vec![Edit {
 475                old: 0..row_count,
 476                new: 0..row_count,
 477            }]),
 478        );
 479        map
 480    }
 481
 482    pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapReader<'_> {
 483        self.sync(&wrap_snapshot, edits);
 484        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
 485        BlockMapReader {
 486            blocks: &self.custom_blocks,
 487            snapshot: BlockSnapshot {
 488                wrap_snapshot,
 489                transforms: self.transforms.borrow().clone(),
 490                custom_blocks_by_id: self.custom_blocks_by_id.clone(),
 491                buffer_header_height: self.buffer_header_height,
 492                excerpt_header_height: self.excerpt_header_height,
 493            },
 494        }
 495    }
 496
 497    pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter<'_> {
 498        self.sync(&wrap_snapshot, edits);
 499        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
 500        BlockMapWriter(self)
 501    }
 502
 503    fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
 504        let buffer = wrap_snapshot.buffer_snapshot();
 505
 506        // Handle changing the last excerpt if it is empty.
 507        if buffer.trailing_excerpt_update_count()
 508            != self
 509                .wrap_snapshot
 510                .borrow()
 511                .buffer_snapshot()
 512                .trailing_excerpt_update_count()
 513        {
 514            let max_point = wrap_snapshot.max_point();
 515            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
 516            let edit_end = max_point.row() + 1;
 517            edits = edits.compose([WrapEdit {
 518                old: edit_start..edit_end,
 519                new: edit_start..edit_end,
 520            }]);
 521        }
 522
 523        let edits = edits.into_inner();
 524        if edits.is_empty() {
 525            return;
 526        }
 527
 528        let mut transforms = self.transforms.borrow_mut();
 529        let mut new_transforms = SumTree::default();
 530        let mut cursor = transforms.cursor::<WrapRow>(());
 531        let mut last_block_ix = 0;
 532        let mut blocks_in_edit = Vec::new();
 533        let mut edits = edits.into_iter().peekable();
 534
 535        while let Some(edit) = edits.next() {
 536            let mut old_start = WrapRow(edit.old.start);
 537            let mut new_start = WrapRow(edit.new.start);
 538
 539            // Only preserve transforms that:
 540            // * Strictly precedes this edit
 541            // * Isomorphic transforms that end *at* the start of the edit
 542            // * Below blocks that end at the start of the edit
 543            // However, if we hit a replace block that ends at the start of the edit we want to reconstruct it.
 544            new_transforms.append(cursor.slice(&old_start, Bias::Left), ());
 545            if let Some(transform) = cursor.item()
 546                && transform.summary.input_rows > 0
 547                && cursor.end() == old_start
 548                && transform.block.as_ref().is_none_or(|b| !b.is_replacement())
 549            {
 550                // Preserve the transform (push and next)
 551                new_transforms.push(transform.clone(), ());
 552                cursor.next();
 553
 554                // Preserve below blocks at end of edit
 555                while let Some(transform) = cursor.item() {
 556                    if transform.block.as_ref().is_some_and(|b| b.place_below()) {
 557                        new_transforms.push(transform.clone(), ());
 558                        cursor.next();
 559                    } else {
 560                        break;
 561                    }
 562                }
 563            }
 564
 565            // Ensure the edit starts at a transform boundary.
 566            // If the edit starts within an isomorphic transform, preserve its prefix
 567            // If the edit lands within a replacement block, expand the edit to include the start of the replaced input range
 568            let transform = cursor.item().unwrap();
 569            let transform_rows_before_edit = old_start.0 - cursor.start().0;
 570            if transform_rows_before_edit > 0 {
 571                if transform.block.is_none() {
 572                    // Preserve any portion of the old isomorphic transform that precedes this edit.
 573                    push_isomorphic(
 574                        &mut new_transforms,
 575                        transform_rows_before_edit,
 576                        wrap_snapshot,
 577                    );
 578                } else {
 579                    // We landed within a block that replaces some lines, so we
 580                    // extend the edit to start at the beginning of the
 581                    // replacement.
 582                    debug_assert!(transform.summary.input_rows > 0);
 583                    old_start.0 -= transform_rows_before_edit;
 584                    new_start.0 -= transform_rows_before_edit;
 585                }
 586            }
 587
 588            // Decide where the edit ends
 589            // * It should end at a transform boundary
 590            // * Coalesce edits that intersect the same transform
 591            let mut old_end = WrapRow(edit.old.end);
 592            let mut new_end = WrapRow(edit.new.end);
 593            loop {
 594                // Seek to the transform starting at or after the end of the edit
 595                cursor.seek(&old_end, Bias::Left);
 596                cursor.next();
 597
 598                // Extend edit to the end of the discarded transform so it is reconstructed in full
 599                let transform_rows_after_edit = cursor.start().0 - old_end.0;
 600                old_end.0 += transform_rows_after_edit;
 601                new_end.0 += transform_rows_after_edit;
 602
 603                // Combine this edit with any subsequent edits that intersect the same transform.
 604                while let Some(next_edit) = edits.peek() {
 605                    if next_edit.old.start <= cursor.start().0 {
 606                        old_end = WrapRow(next_edit.old.end);
 607                        new_end = WrapRow(next_edit.new.end);
 608                        cursor.seek(&old_end, Bias::Left);
 609                        cursor.next();
 610                        edits.next();
 611                    } else {
 612                        break;
 613                    }
 614                }
 615
 616                if *cursor.start() == old_end {
 617                    break;
 618                }
 619            }
 620
 621            // Discard below blocks at the end of the edit. They'll be reconstructed.
 622            while let Some(transform) = cursor.item() {
 623                if transform.block.as_ref().is_some_and(|b| b.place_below()) {
 624                    cursor.next();
 625                } else {
 626                    break;
 627                }
 628            }
 629
 630            // Find the blocks within this edited region.
 631            let new_buffer_start =
 632                wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
 633            let start_bound = Bound::Included(new_buffer_start);
 634            let start_block_ix =
 635                match self.custom_blocks[last_block_ix..].binary_search_by(|probe| {
 636                    probe
 637                        .start()
 638                        .to_point(buffer)
 639                        .cmp(&new_buffer_start)
 640                        // Move left until we find the index of the first block starting within this edit
 641                        .then(Ordering::Greater)
 642                }) {
 643                    Ok(ix) | Err(ix) => last_block_ix + ix,
 644                };
 645
 646            let end_bound;
 647            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
 648                end_bound = Bound::Unbounded;
 649                self.custom_blocks.len()
 650            } else {
 651                let new_buffer_end =
 652                    wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
 653                end_bound = Bound::Excluded(new_buffer_end);
 654                match self.custom_blocks[start_block_ix..].binary_search_by(|probe| {
 655                    probe
 656                        .start()
 657                        .to_point(buffer)
 658                        .cmp(&new_buffer_end)
 659                        .then(Ordering::Greater)
 660                }) {
 661                    Ok(ix) | Err(ix) => start_block_ix + ix,
 662                }
 663            };
 664            last_block_ix = end_block_ix;
 665
 666            debug_assert!(blocks_in_edit.is_empty());
 667
 668            blocks_in_edit.extend(
 669                self.custom_blocks[start_block_ix..end_block_ix]
 670                    .iter()
 671                    .filter_map(|block| {
 672                        let placement = block.placement.to_wrap_row(wrap_snapshot)?;
 673                        if let BlockPlacement::Above(row) = placement
 674                            && row < new_start
 675                        {
 676                            return None;
 677                        }
 678                        Some((placement, Block::Custom(block.clone())))
 679                    }),
 680            );
 681
 682            blocks_in_edit.extend(self.header_and_footer_blocks(
 683                buffer,
 684                (start_bound, end_bound),
 685                wrap_snapshot,
 686            ));
 687
 688            BlockMap::sort_blocks(&mut blocks_in_edit);
 689
 690            // For each of these blocks, insert a new isomorphic transform preceding the block,
 691            // and then insert the block itself.
 692            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 = unsafe { std::str::from_utf8_unchecked(&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::ContextLessSummary for TransformSummary {
1857    fn zero() -> Self {
1858        Default::default()
1859    }
1860
1861    fn add_summary(&mut self, summary: &Self) {
1862        if summary.longest_row_chars > self.longest_row_chars {
1863            self.longest_row = self.output_rows + summary.longest_row;
1864            self.longest_row_chars = summary.longest_row_chars;
1865        }
1866        self.input_rows += summary.input_rows;
1867        self.output_rows += summary.output_rows;
1868    }
1869}
1870
1871impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1872    fn zero(_cx: ()) -> Self {
1873        Default::default()
1874    }
1875
1876    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1877        self.0 += summary.input_rows;
1878    }
1879}
1880
1881impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1882    fn zero(_cx: ()) -> Self {
1883        Default::default()
1884    }
1885
1886    fn add_summary(&mut self, summary: &'a TransformSummary, _: ()) {
1887        self.0 += summary.output_rows;
1888    }
1889}
1890
1891impl Deref for BlockContext<'_, '_> {
1892    type Target = App;
1893
1894    fn deref(&self) -> &Self::Target {
1895        self.app
1896    }
1897}
1898
1899impl DerefMut for BlockContext<'_, '_> {
1900    fn deref_mut(&mut self) -> &mut Self::Target {
1901        self.app
1902    }
1903}
1904
1905impl CustomBlock {
1906    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1907        self.render.lock()(cx)
1908    }
1909
1910    pub fn start(&self) -> Anchor {
1911        *self.placement.start()
1912    }
1913
1914    pub fn end(&self) -> Anchor {
1915        *self.placement.end()
1916    }
1917
1918    pub fn style(&self) -> BlockStyle {
1919        self.style
1920    }
1921}
1922
1923impl Debug for CustomBlock {
1924    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1925        f.debug_struct("Block")
1926            .field("id", &self.id)
1927            .field("placement", &self.placement)
1928            .field("height", &self.height)
1929            .field("style", &self.style)
1930            .field("priority", &self.priority)
1931            .finish_non_exhaustive()
1932    }
1933}
1934
1935// Count the number of bytes prior to a target point. If the string doesn't contain the target
1936// point, return its total extent. Otherwise return the target point itself.
1937fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1938    let mut row = 0;
1939    let mut offset = 0;
1940    for (ix, line) in s.split('\n').enumerate() {
1941        if ix > 0 {
1942            row += 1;
1943            offset += 1;
1944        }
1945        if row >= target {
1946            break;
1947        }
1948        offset += line.len();
1949    }
1950    (row, offset)
1951}
1952
1953#[cfg(test)]
1954mod tests {
1955    use super::*;
1956    use crate::{
1957        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap},
1958        test::test_font,
1959    };
1960    use gpui::{App, AppContext as _, Element, div, font, px};
1961    use itertools::Itertools;
1962    use language::{Buffer, Capability};
1963    use multi_buffer::{ExcerptRange, MultiBuffer};
1964    use rand::prelude::*;
1965    use settings::SettingsStore;
1966    use std::env;
1967    use util::RandomCharIter;
1968
1969    #[gpui::test]
1970    fn test_offset_for_row() {
1971        assert_eq!(offset_for_row("", 0), (0, 0));
1972        assert_eq!(offset_for_row("", 1), (0, 0));
1973        assert_eq!(offset_for_row("abcd", 0), (0, 0));
1974        assert_eq!(offset_for_row("abcd", 1), (0, 4));
1975        assert_eq!(offset_for_row("\n", 0), (0, 0));
1976        assert_eq!(offset_for_row("\n", 1), (1, 1));
1977        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1978        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1979        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1980        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1981    }
1982
1983    #[gpui::test]
1984    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1985        cx.update(init_test);
1986
1987        let text = "aaa\nbbb\nccc\nddd";
1988
1989        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1990        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1991        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1992        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1993        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1994        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1995        let (wrap_map, wraps_snapshot) =
1996            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1997        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
1998
1999        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2000        let block_ids = writer.insert(vec![
2001            BlockProperties {
2002                style: BlockStyle::Fixed,
2003                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2004                height: Some(1),
2005                render: Arc::new(|_| div().into_any()),
2006                priority: 0,
2007            },
2008            BlockProperties {
2009                style: BlockStyle::Fixed,
2010                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2011                height: Some(2),
2012                render: Arc::new(|_| div().into_any()),
2013                priority: 0,
2014            },
2015            BlockProperties {
2016                style: BlockStyle::Fixed,
2017                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2018                height: Some(3),
2019                render: Arc::new(|_| div().into_any()),
2020                priority: 0,
2021            },
2022        ]);
2023
2024        let snapshot = block_map.read(wraps_snapshot, Default::default());
2025        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2026
2027        let blocks = snapshot
2028            .blocks_in_range(0..8)
2029            .map(|(start_row, block)| {
2030                let block = block.as_custom().unwrap();
2031                (start_row..start_row + block.height.unwrap(), block.id)
2032            })
2033            .collect::<Vec<_>>();
2034
2035        // When multiple blocks are on the same line, the newer blocks appear first.
2036        assert_eq!(
2037            blocks,
2038            &[
2039                (1..2, block_ids[0]),
2040                (2..4, block_ids[1]),
2041                (7..10, block_ids[2]),
2042            ]
2043        );
2044
2045        assert_eq!(
2046            snapshot.to_block_point(WrapPoint::new(0, 3)),
2047            BlockPoint::new(0, 3)
2048        );
2049        assert_eq!(
2050            snapshot.to_block_point(WrapPoint::new(1, 0)),
2051            BlockPoint::new(4, 0)
2052        );
2053        assert_eq!(
2054            snapshot.to_block_point(WrapPoint::new(3, 3)),
2055            BlockPoint::new(6, 3)
2056        );
2057
2058        assert_eq!(
2059            snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
2060            WrapPoint::new(0, 3)
2061        );
2062        assert_eq!(
2063            snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
2064            WrapPoint::new(1, 0)
2065        );
2066        assert_eq!(
2067            snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
2068            WrapPoint::new(1, 0)
2069        );
2070        assert_eq!(
2071            snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
2072            WrapPoint::new(3, 3)
2073        );
2074
2075        assert_eq!(
2076            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
2077            BlockPoint::new(0, 3)
2078        );
2079        assert_eq!(
2080            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
2081            BlockPoint::new(4, 0)
2082        );
2083        assert_eq!(
2084            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
2085            BlockPoint::new(0, 3)
2086        );
2087        assert_eq!(
2088            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
2089            BlockPoint::new(4, 0)
2090        );
2091        assert_eq!(
2092            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
2093            BlockPoint::new(4, 0)
2094        );
2095        assert_eq!(
2096            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
2097            BlockPoint::new(4, 0)
2098        );
2099        assert_eq!(
2100            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
2101            BlockPoint::new(6, 3)
2102        );
2103        assert_eq!(
2104            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
2105            BlockPoint::new(6, 3)
2106        );
2107        assert_eq!(
2108            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
2109            BlockPoint::new(6, 3)
2110        );
2111        assert_eq!(
2112            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
2113            BlockPoint::new(6, 3)
2114        );
2115
2116        assert_eq!(
2117            snapshot
2118                .row_infos(BlockRow(0))
2119                .map(|row_info| row_info.buffer_row)
2120                .collect::<Vec<_>>(),
2121            &[
2122                Some(0),
2123                None,
2124                None,
2125                None,
2126                Some(1),
2127                Some(2),
2128                Some(3),
2129                None,
2130                None,
2131                None
2132            ]
2133        );
2134
2135        // Insert a line break, separating two block decorations into separate lines.
2136        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2137            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
2138            buffer.snapshot(cx)
2139        });
2140
2141        let (inlay_snapshot, inlay_edits) =
2142            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
2143        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2144        let (tab_snapshot, tab_edits) =
2145            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
2146        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2147            wrap_map.sync(tab_snapshot, tab_edits, cx)
2148        });
2149        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
2150        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
2151    }
2152
2153    #[gpui::test]
2154    fn test_multibuffer_headers_and_footers(cx: &mut App) {
2155        init_test(cx);
2156
2157        let buffer1 = cx.new(|cx| Buffer::local("Buffer 1", cx));
2158        let buffer2 = cx.new(|cx| Buffer::local("Buffer 2", cx));
2159        let buffer3 = cx.new(|cx| Buffer::local("Buffer 3", cx));
2160
2161        let mut excerpt_ids = Vec::new();
2162        let multi_buffer = cx.new(|cx| {
2163            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2164            excerpt_ids.extend(multi_buffer.push_excerpts(
2165                buffer1.clone(),
2166                [ExcerptRange::new(0..buffer1.read(cx).len())],
2167                cx,
2168            ));
2169            excerpt_ids.extend(multi_buffer.push_excerpts(
2170                buffer2.clone(),
2171                [ExcerptRange::new(0..buffer2.read(cx).len())],
2172                cx,
2173            ));
2174            excerpt_ids.extend(multi_buffer.push_excerpts(
2175                buffer3.clone(),
2176                [ExcerptRange::new(0..buffer3.read(cx).len())],
2177                cx,
2178            ));
2179
2180            multi_buffer
2181        });
2182
2183        let font = test_font();
2184        let font_size = px(14.);
2185        let font_id = cx.text_system().resolve_font(&font);
2186        let mut wrap_width = px(0.);
2187        for c in "Buff".chars() {
2188            wrap_width += cx
2189                .text_system()
2190                .advance(font_id, font_size, c)
2191                .unwrap()
2192                .width;
2193        }
2194
2195        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2196        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot);
2197        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2198        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2199        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2200
2201        let block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2202        let snapshot = block_map.read(wraps_snapshot, Default::default());
2203
2204        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2205        assert_eq!(snapshot.text(), "\nBuff\ner 1\n\nBuff\ner 2\n\nBuff\ner 3");
2206
2207        let blocks: Vec<_> = snapshot
2208            .blocks_in_range(0..u32::MAX)
2209            .map(|(row, block)| (row..row + block.height(), block.id()))
2210            .collect();
2211        assert_eq!(
2212            blocks,
2213            vec![
2214                (0..1, BlockId::ExcerptBoundary(excerpt_ids[0])), // path, header
2215                (3..4, BlockId::ExcerptBoundary(excerpt_ids[1])), // path, header
2216                (6..7, BlockId::ExcerptBoundary(excerpt_ids[2])), // path, header
2217            ]
2218        );
2219    }
2220
2221    #[gpui::test]
2222    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2223        cx.update(init_test);
2224
2225        let text = "aaa\nbbb\nccc\nddd";
2226
2227        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2228        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2229        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2230        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2231        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2232        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2233        let (_wrap_map, wraps_snapshot) =
2234            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2235        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2236
2237        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2238        let block_ids = writer.insert(vec![
2239            BlockProperties {
2240                style: BlockStyle::Fixed,
2241                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2242                height: Some(1),
2243                render: Arc::new(|_| div().into_any()),
2244                priority: 0,
2245            },
2246            BlockProperties {
2247                style: BlockStyle::Fixed,
2248                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2249                height: Some(2),
2250                render: Arc::new(|_| div().into_any()),
2251                priority: 0,
2252            },
2253            BlockProperties {
2254                style: BlockStyle::Fixed,
2255                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2256                height: Some(3),
2257                render: Arc::new(|_| div().into_any()),
2258                priority: 0,
2259            },
2260        ]);
2261
2262        {
2263            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2264            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2265
2266            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2267
2268            let mut new_heights = HashMap::default();
2269            new_heights.insert(block_ids[0], 2);
2270            block_map_writer.resize(new_heights);
2271            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2272            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2273        }
2274
2275        {
2276            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2277
2278            let mut new_heights = HashMap::default();
2279            new_heights.insert(block_ids[0], 1);
2280            block_map_writer.resize(new_heights);
2281
2282            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2283            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2284        }
2285
2286        {
2287            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2288
2289            let mut new_heights = HashMap::default();
2290            new_heights.insert(block_ids[0], 0);
2291            block_map_writer.resize(new_heights);
2292
2293            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2294            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2295        }
2296
2297        {
2298            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2299
2300            let mut new_heights = HashMap::default();
2301            new_heights.insert(block_ids[0], 3);
2302            block_map_writer.resize(new_heights);
2303
2304            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2305            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2306        }
2307
2308        {
2309            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2310
2311            let mut new_heights = HashMap::default();
2312            new_heights.insert(block_ids[0], 3);
2313            block_map_writer.resize(new_heights);
2314
2315            let snapshot = block_map.read(wraps_snapshot, Default::default());
2316            // Same height as before, should remain the same
2317            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2318        }
2319    }
2320
2321    #[gpui::test]
2322    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2323        cx.update(init_test);
2324
2325        let text = "one two three\nfour five six\nseven eight";
2326
2327        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2328        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2329        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2330        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2331        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2332        let (_, wraps_snapshot) = cx.update(|cx| {
2333            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(90.)), cx)
2334        });
2335        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2336
2337        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2338        writer.insert(vec![
2339            BlockProperties {
2340                style: BlockStyle::Fixed,
2341                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2342                render: Arc::new(|_| div().into_any()),
2343                height: Some(1),
2344                priority: 0,
2345            },
2346            BlockProperties {
2347                style: BlockStyle::Fixed,
2348                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2349                render: Arc::new(|_| div().into_any()),
2350                height: Some(1),
2351                priority: 0,
2352            },
2353        ]);
2354
2355        // Blocks with an 'above' disposition go above their corresponding buffer line.
2356        // Blocks with a 'below' disposition go below their corresponding buffer line.
2357        let snapshot = block_map.read(wraps_snapshot, Default::default());
2358        assert_eq!(
2359            snapshot.text(),
2360            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2361        );
2362    }
2363
2364    #[gpui::test]
2365    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2366        cx.update(init_test);
2367
2368        let text = "line1\nline2\nline3\nline4\nline5";
2369
2370        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2371        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2372        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2373        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2374        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2375        let tab_size = 1.try_into().unwrap();
2376        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2377        let (wrap_map, wraps_snapshot) =
2378            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2379        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
2380
2381        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2382        let replace_block_id = writer.insert(vec![BlockProperties {
2383            style: BlockStyle::Fixed,
2384            placement: BlockPlacement::Replace(
2385                buffer_snapshot.anchor_after(Point::new(1, 3))
2386                    ..=buffer_snapshot.anchor_before(Point::new(3, 1)),
2387            ),
2388            height: Some(4),
2389            render: Arc::new(|_| div().into_any()),
2390            priority: 0,
2391        }])[0];
2392
2393        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2394        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2395
2396        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2397            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2398            buffer.snapshot(cx)
2399        });
2400        let (inlay_snapshot, inlay_edits) =
2401            inlay_map.sync(buffer_snapshot, buffer_subscription.consume().into_inner());
2402        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2403        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2404        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2405            wrap_map.sync(tab_snapshot, tab_edits, cx)
2406        });
2407        let blocks_snapshot = block_map.read(wraps_snapshot, wrap_edits);
2408        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2409
2410        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2411            buffer.edit(
2412                [(
2413                    Point::new(1, 5)..Point::new(1, 5),
2414                    "\nline 2.1\nline2.2\nline 2.3\nline 2.4",
2415                )],
2416                None,
2417                cx,
2418            );
2419            buffer.snapshot(cx)
2420        });
2421        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2422            buffer_snapshot.clone(),
2423            buffer_subscription.consume().into_inner(),
2424        );
2425        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2426        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2427        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2428            wrap_map.sync(tab_snapshot, tab_edits, cx)
2429        });
2430        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2431        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2432
2433        // Blocks inserted right above the start or right below the end of the replaced region are hidden.
2434        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2435        writer.insert(vec![
2436            BlockProperties {
2437                style: BlockStyle::Fixed,
2438                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 3))),
2439                height: Some(1),
2440                render: Arc::new(|_| div().into_any()),
2441                priority: 0,
2442            },
2443            BlockProperties {
2444                style: BlockStyle::Fixed,
2445                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2446                height: Some(1),
2447                render: Arc::new(|_| div().into_any()),
2448                priority: 0,
2449            },
2450            BlockProperties {
2451                style: BlockStyle::Fixed,
2452                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2453                height: Some(1),
2454                render: Arc::new(|_| div().into_any()),
2455                priority: 0,
2456            },
2457        ]);
2458        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2459        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2460
2461        // Ensure blocks inserted *inside* replaced region are hidden.
2462        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2463        writer.insert(vec![
2464            BlockProperties {
2465                style: BlockStyle::Fixed,
2466                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2467                height: Some(1),
2468                render: Arc::new(|_| div().into_any()),
2469                priority: 0,
2470            },
2471            BlockProperties {
2472                style: BlockStyle::Fixed,
2473                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2474                height: Some(1),
2475                render: Arc::new(|_| div().into_any()),
2476                priority: 0,
2477            },
2478            BlockProperties {
2479                style: BlockStyle::Fixed,
2480                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2481                height: Some(1),
2482                render: Arc::new(|_| div().into_any()),
2483                priority: 0,
2484            },
2485        ]);
2486        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2487        assert_eq!(blocks_snapshot.text(), "\nline1\n\n\n\n\nline5");
2488
2489        // Removing the replace block shows all the hidden blocks again.
2490        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2491        writer.remove(HashSet::from_iter([replace_block_id]));
2492        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2493        assert_eq!(
2494            blocks_snapshot.text(),
2495            "\nline1\n\nline2\n\n\nline 2.1\nline2.2\nline 2.3\nline 2.4\n\nline4\n\nline5"
2496        );
2497    }
2498
2499    #[gpui::test]
2500    fn test_custom_blocks_inside_buffer_folds(cx: &mut gpui::TestAppContext) {
2501        cx.update(init_test);
2502
2503        let text = "111\n222\n333\n444\n555\n666";
2504
2505        let buffer = cx.update(|cx| {
2506            MultiBuffer::build_multi(
2507                [
2508                    (text, vec![Point::new(0, 0)..Point::new(0, 3)]),
2509                    (
2510                        text,
2511                        vec![
2512                            Point::new(1, 0)..Point::new(1, 3),
2513                            Point::new(2, 0)..Point::new(2, 3),
2514                            Point::new(3, 0)..Point::new(3, 3),
2515                        ],
2516                    ),
2517                    (
2518                        text,
2519                        vec![
2520                            Point::new(4, 0)..Point::new(4, 3),
2521                            Point::new(5, 0)..Point::new(5, 3),
2522                        ],
2523                    ),
2524                ],
2525                cx,
2526            )
2527        });
2528        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2529        let buffer_ids = buffer_snapshot
2530            .excerpts()
2531            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2532            .dedup()
2533            .collect::<Vec<_>>();
2534        assert_eq!(buffer_ids.len(), 3);
2535        let buffer_id_1 = buffer_ids[0];
2536        let buffer_id_2 = buffer_ids[1];
2537        let buffer_id_3 = buffer_ids[2];
2538
2539        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2540        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2541        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2542        let (_, wrap_snapshot) =
2543            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2544        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2545        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2546
2547        assert_eq!(
2548            blocks_snapshot.text(),
2549            "\n\n111\n\n\n222\n\n333\n\n444\n\n\n555\n\n666"
2550        );
2551        assert_eq!(
2552            blocks_snapshot
2553                .row_infos(BlockRow(0))
2554                .map(|i| i.buffer_row)
2555                .collect::<Vec<_>>(),
2556            vec![
2557                None,
2558                None,
2559                Some(0),
2560                None,
2561                None,
2562                Some(1),
2563                None,
2564                Some(2),
2565                None,
2566                Some(3),
2567                None,
2568                None,
2569                Some(4),
2570                None,
2571                Some(5),
2572            ]
2573        );
2574
2575        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2576        let excerpt_blocks_2 = writer.insert(vec![
2577            BlockProperties {
2578                style: BlockStyle::Fixed,
2579                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2580                height: Some(1),
2581                render: Arc::new(|_| div().into_any()),
2582                priority: 0,
2583            },
2584            BlockProperties {
2585                style: BlockStyle::Fixed,
2586                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 0))),
2587                height: Some(1),
2588                render: Arc::new(|_| div().into_any()),
2589                priority: 0,
2590            },
2591            BlockProperties {
2592                style: BlockStyle::Fixed,
2593                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 0))),
2594                height: Some(1),
2595                render: Arc::new(|_| div().into_any()),
2596                priority: 0,
2597            },
2598        ]);
2599        let excerpt_blocks_3 = writer.insert(vec![
2600            BlockProperties {
2601                style: BlockStyle::Fixed,
2602                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(4, 0))),
2603                height: Some(1),
2604                render: Arc::new(|_| div().into_any()),
2605                priority: 0,
2606            },
2607            BlockProperties {
2608                style: BlockStyle::Fixed,
2609                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(5, 0))),
2610                height: Some(1),
2611                render: Arc::new(|_| div().into_any()),
2612                priority: 0,
2613            },
2614        ]);
2615
2616        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2617        assert_eq!(
2618            blocks_snapshot.text(),
2619            "\n\n111\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2620        );
2621        assert_eq!(
2622            blocks_snapshot
2623                .row_infos(BlockRow(0))
2624                .map(|i| i.buffer_row)
2625                .collect::<Vec<_>>(),
2626            vec![
2627                None,
2628                None,
2629                Some(0),
2630                None,
2631                None,
2632                None,
2633                Some(1),
2634                None,
2635                None,
2636                Some(2),
2637                None,
2638                Some(3),
2639                None,
2640                None,
2641                None,
2642                None,
2643                Some(4),
2644                None,
2645                Some(5),
2646                None,
2647            ]
2648        );
2649
2650        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2651        buffer.read_with(cx, |buffer, cx| {
2652            writer.fold_buffers([buffer_id_1], buffer, cx);
2653        });
2654        let excerpt_blocks_1 = writer.insert(vec![BlockProperties {
2655            style: BlockStyle::Fixed,
2656            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(0, 0))),
2657            height: Some(1),
2658            render: Arc::new(|_| div().into_any()),
2659            priority: 0,
2660        }]);
2661        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2662        let blocks = blocks_snapshot
2663            .blocks_in_range(0..u32::MAX)
2664            .collect::<Vec<_>>();
2665        for (_, block) in &blocks {
2666            if let BlockId::Custom(custom_block_id) = block.id() {
2667                assert!(
2668                    !excerpt_blocks_1.contains(&custom_block_id),
2669                    "Should have no blocks from the folded buffer"
2670                );
2671                assert!(
2672                    excerpt_blocks_2.contains(&custom_block_id)
2673                        || excerpt_blocks_3.contains(&custom_block_id),
2674                    "Should have only blocks from unfolded buffers"
2675                );
2676            }
2677        }
2678        assert_eq!(
2679            1,
2680            blocks
2681                .iter()
2682                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2683                .count(),
2684            "Should have one folded block, producing a header of the second buffer"
2685        );
2686        assert_eq!(
2687            blocks_snapshot.text(),
2688            "\n\n\n\n\n222\n\n\n333\n\n444\n\n\n\n\n555\n\n666\n"
2689        );
2690        assert_eq!(
2691            blocks_snapshot
2692                .row_infos(BlockRow(0))
2693                .map(|i| i.buffer_row)
2694                .collect::<Vec<_>>(),
2695            vec![
2696                None,
2697                None,
2698                None,
2699                None,
2700                None,
2701                Some(1),
2702                None,
2703                None,
2704                Some(2),
2705                None,
2706                Some(3),
2707                None,
2708                None,
2709                None,
2710                None,
2711                Some(4),
2712                None,
2713                Some(5),
2714                None,
2715            ]
2716        );
2717
2718        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2719        buffer.read_with(cx, |buffer, cx| {
2720            writer.fold_buffers([buffer_id_2], buffer, cx);
2721        });
2722        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2723        let blocks = blocks_snapshot
2724            .blocks_in_range(0..u32::MAX)
2725            .collect::<Vec<_>>();
2726        for (_, block) in &blocks {
2727            if let BlockId::Custom(custom_block_id) = block.id() {
2728                assert!(
2729                    !excerpt_blocks_1.contains(&custom_block_id),
2730                    "Should have no blocks from the folded buffer_1"
2731                );
2732                assert!(
2733                    !excerpt_blocks_2.contains(&custom_block_id),
2734                    "Should have no blocks from the folded buffer_2"
2735                );
2736                assert!(
2737                    excerpt_blocks_3.contains(&custom_block_id),
2738                    "Should have only blocks from unfolded buffers"
2739                );
2740            }
2741        }
2742        assert_eq!(
2743            2,
2744            blocks
2745                .iter()
2746                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2747                .count(),
2748            "Should have two folded blocks, producing headers"
2749        );
2750        assert_eq!(blocks_snapshot.text(), "\n\n\n\n\n\n\n555\n\n666\n");
2751        assert_eq!(
2752            blocks_snapshot
2753                .row_infos(BlockRow(0))
2754                .map(|i| i.buffer_row)
2755                .collect::<Vec<_>>(),
2756            vec![
2757                None,
2758                None,
2759                None,
2760                None,
2761                None,
2762                None,
2763                None,
2764                Some(4),
2765                None,
2766                Some(5),
2767                None,
2768            ]
2769        );
2770
2771        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2772        buffer.read_with(cx, |buffer, cx| {
2773            writer.unfold_buffers([buffer_id_1], buffer, cx);
2774        });
2775        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2776        let blocks = blocks_snapshot
2777            .blocks_in_range(0..u32::MAX)
2778            .collect::<Vec<_>>();
2779        for (_, block) in &blocks {
2780            if let BlockId::Custom(custom_block_id) = block.id() {
2781                assert!(
2782                    !excerpt_blocks_2.contains(&custom_block_id),
2783                    "Should have no blocks from the folded buffer_2"
2784                );
2785                assert!(
2786                    excerpt_blocks_1.contains(&custom_block_id)
2787                        || excerpt_blocks_3.contains(&custom_block_id),
2788                    "Should have only blocks from unfolded buffers"
2789                );
2790            }
2791        }
2792        assert_eq!(
2793            1,
2794            blocks
2795                .iter()
2796                .filter(|(_, block)| matches!(block, Block::FoldedBuffer { .. }))
2797                .count(),
2798            "Should be back to a single folded buffer, producing a header for buffer_2"
2799        );
2800        assert_eq!(
2801            blocks_snapshot.text(),
2802            "\n\n\n111\n\n\n\n\n\n555\n\n666\n",
2803            "Should have extra newline for 111 buffer, due to a new block added when it was folded"
2804        );
2805        assert_eq!(
2806            blocks_snapshot
2807                .row_infos(BlockRow(0))
2808                .map(|i| i.buffer_row)
2809                .collect::<Vec<_>>(),
2810            vec![
2811                None,
2812                None,
2813                None,
2814                Some(0),
2815                None,
2816                None,
2817                None,
2818                None,
2819                None,
2820                Some(4),
2821                None,
2822                Some(5),
2823                None,
2824            ]
2825        );
2826
2827        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2828        buffer.read_with(cx, |buffer, cx| {
2829            writer.fold_buffers([buffer_id_3], buffer, cx);
2830        });
2831        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2832        let blocks = blocks_snapshot
2833            .blocks_in_range(0..u32::MAX)
2834            .collect::<Vec<_>>();
2835        for (_, block) in &blocks {
2836            if let BlockId::Custom(custom_block_id) = block.id() {
2837                assert!(
2838                    excerpt_blocks_1.contains(&custom_block_id),
2839                    "Should have no blocks from the folded buffer_1"
2840                );
2841                assert!(
2842                    !excerpt_blocks_2.contains(&custom_block_id),
2843                    "Should have only blocks from unfolded buffers"
2844                );
2845                assert!(
2846                    !excerpt_blocks_3.contains(&custom_block_id),
2847                    "Should have only blocks from unfolded buffers"
2848                );
2849            }
2850        }
2851
2852        assert_eq!(
2853            blocks_snapshot.text(),
2854            "\n\n\n111\n\n\n\n",
2855            "Should have a single, first buffer left after folding"
2856        );
2857        assert_eq!(
2858            blocks_snapshot
2859                .row_infos(BlockRow(0))
2860                .map(|i| i.buffer_row)
2861                .collect::<Vec<_>>(),
2862            vec![None, None, None, Some(0), None, None, None, None,]
2863        );
2864    }
2865
2866    #[gpui::test]
2867    fn test_basic_buffer_fold(cx: &mut gpui::TestAppContext) {
2868        cx.update(init_test);
2869
2870        let text = "111";
2871
2872        let buffer = cx.update(|cx| {
2873            MultiBuffer::build_multi([(text, vec![Point::new(0, 0)..Point::new(0, 3)])], cx)
2874        });
2875        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2876        let buffer_ids = buffer_snapshot
2877            .excerpts()
2878            .map(|(_, buffer_snapshot, _)| buffer_snapshot.remote_id())
2879            .dedup()
2880            .collect::<Vec<_>>();
2881        assert_eq!(buffer_ids.len(), 1);
2882        let buffer_id = buffer_ids[0];
2883
2884        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
2885        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2886        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2887        let (_, wrap_snapshot) =
2888            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2889        let mut block_map = BlockMap::new(wrap_snapshot.clone(), 2, 1);
2890        let blocks_snapshot = block_map.read(wrap_snapshot.clone(), Patch::default());
2891
2892        assert_eq!(blocks_snapshot.text(), "\n\n111");
2893
2894        let mut writer = block_map.write(wrap_snapshot.clone(), Patch::default());
2895        buffer.read_with(cx, |buffer, cx| {
2896            writer.fold_buffers([buffer_id], buffer, cx);
2897        });
2898        let blocks_snapshot = block_map.read(wrap_snapshot, Patch::default());
2899        let blocks = blocks_snapshot
2900            .blocks_in_range(0..u32::MAX)
2901            .collect::<Vec<_>>();
2902        assert_eq!(
2903            1,
2904            blocks
2905                .iter()
2906                .filter(|(_, block)| { matches!(block, Block::FoldedBuffer { .. }) })
2907                .count(),
2908            "Should have one folded block, producing a header of the second buffer"
2909        );
2910        assert_eq!(blocks_snapshot.text(), "\n");
2911        assert_eq!(
2912            blocks_snapshot
2913                .row_infos(BlockRow(0))
2914                .map(|i| i.buffer_row)
2915                .collect::<Vec<_>>(),
2916            vec![None, None],
2917            "When fully folded, should be no buffer rows"
2918        );
2919    }
2920
2921    #[gpui::test(iterations = 100)]
2922    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2923        cx.update(init_test);
2924
2925        let operations = env::var("OPERATIONS")
2926            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2927            .unwrap_or(10);
2928
2929        let wrap_width = if rng.random_bool(0.2) {
2930            None
2931        } else {
2932            Some(px(rng.random_range(0.0..=100.0)))
2933        };
2934        let tab_size = 1.try_into().unwrap();
2935        let font_size = px(14.0);
2936        let buffer_start_header_height = rng.random_range(1..=5);
2937        let excerpt_header_height = rng.random_range(1..=5);
2938
2939        log::info!("Wrap width: {:?}", wrap_width);
2940        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2941        let is_singleton = rng.random();
2942        let buffer = if is_singleton {
2943            let len = rng.random_range(0..10);
2944            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2945            log::info!("initial singleton buffer text: {:?}", text);
2946            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2947        } else {
2948            cx.update(|cx| {
2949                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2950                log::info!(
2951                    "initial multi-buffer text: {:?}",
2952                    multibuffer.read(cx).read(cx).text()
2953                );
2954                multibuffer
2955            })
2956        };
2957
2958        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2959        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2960        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2961        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2962        let font = test_font();
2963        let (wrap_map, wraps_snapshot) =
2964            cx.update(|cx| WrapMap::new(tab_snapshot, font, font_size, wrap_width, cx));
2965        let mut block_map = BlockMap::new(
2966            wraps_snapshot,
2967            buffer_start_header_height,
2968            excerpt_header_height,
2969        );
2970
2971        for _ in 0..operations {
2972            let mut buffer_edits = Vec::new();
2973            match rng.random_range(0..=100) {
2974                0..=19 => {
2975                    let wrap_width = if rng.random_bool(0.2) {
2976                        None
2977                    } else {
2978                        Some(px(rng.random_range(0.0..=100.0)))
2979                    };
2980                    log::info!("Setting wrap width to {:?}", wrap_width);
2981                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2982                }
2983                20..=39 => {
2984                    let block_count = rng.random_range(1..=5);
2985                    let block_properties = (0..block_count)
2986                        .map(|_| {
2987                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
2988                            let offset =
2989                                buffer.clip_offset(rng.random_range(0..=buffer.len()), Bias::Left);
2990                            let mut min_height = 0;
2991                            let placement = match rng.random_range(0..3) {
2992                                0 => {
2993                                    min_height = 1;
2994                                    let start = buffer.anchor_after(offset);
2995                                    let end = buffer.anchor_after(buffer.clip_offset(
2996                                        rng.random_range(offset..=buffer.len()),
2997                                        Bias::Left,
2998                                    ));
2999                                    BlockPlacement::Replace(start..=end)
3000                                }
3001                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
3002                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
3003                            };
3004
3005                            let height = rng.random_range(min_height..5);
3006                            BlockProperties {
3007                                style: BlockStyle::Fixed,
3008                                placement,
3009                                height: Some(height),
3010                                render: Arc::new(|_| div().into_any()),
3011                                priority: 0,
3012                            }
3013                        })
3014                        .collect::<Vec<_>>();
3015
3016                    let (inlay_snapshot, inlay_edits) =
3017                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3018                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3019                    let (tab_snapshot, tab_edits) =
3020                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3021                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3022                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3023                    });
3024                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3025                    let block_ids =
3026                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
3027                            placement: props.placement.clone(),
3028                            height: props.height,
3029                            style: props.style,
3030                            render: Arc::new(|_| div().into_any()),
3031                            priority: 0,
3032                        }));
3033
3034                    for (block_properties, block_id) in block_properties.iter().zip(block_ids) {
3035                        log::info!(
3036                            "inserted block {:?} with height {:?} and id {:?}",
3037                            block_properties
3038                                .placement
3039                                .as_ref()
3040                                .map(|p| p.to_point(&buffer_snapshot)),
3041                            block_properties.height,
3042                            block_id
3043                        );
3044                    }
3045                }
3046                40..=59 if !block_map.custom_blocks.is_empty() => {
3047                    let block_count = rng.random_range(1..=4.min(block_map.custom_blocks.len()));
3048                    let block_ids_to_remove = block_map
3049                        .custom_blocks
3050                        .choose_multiple(&mut rng, block_count)
3051                        .map(|block| block.id)
3052                        .collect::<HashSet<_>>();
3053
3054                    let (inlay_snapshot, inlay_edits) =
3055                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3056                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3057                    let (tab_snapshot, tab_edits) =
3058                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3059                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3060                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3061                    });
3062                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3063                    log::info!(
3064                        "removing {} blocks: {:?}",
3065                        block_ids_to_remove.len(),
3066                        block_ids_to_remove
3067                    );
3068                    block_map.remove(block_ids_to_remove);
3069                }
3070                60..=79 => {
3071                    if buffer.read_with(cx, |buffer, _| buffer.is_singleton()) {
3072                        log::info!("Noop fold/unfold operation on a singleton buffer");
3073                        continue;
3074                    }
3075                    let (inlay_snapshot, inlay_edits) =
3076                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
3077                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3078                    let (tab_snapshot, tab_edits) =
3079                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
3080                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3081                        wrap_map.sync(tab_snapshot, tab_edits, cx)
3082                    });
3083                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
3084                    let (unfolded_buffers, folded_buffers) = buffer.read_with(cx, |buffer, _| {
3085                        let folded_buffers = block_map
3086                            .0
3087                            .folded_buffers
3088                            .iter()
3089                            .cloned()
3090                            .collect::<Vec<_>>();
3091                        let mut unfolded_buffers = buffer.excerpt_buffer_ids();
3092                        unfolded_buffers.dedup();
3093                        log::debug!("All buffers {unfolded_buffers:?}");
3094                        log::debug!("Folded buffers {folded_buffers:?}");
3095                        unfolded_buffers
3096                            .retain(|buffer_id| !block_map.0.folded_buffers.contains(buffer_id));
3097                        (unfolded_buffers, folded_buffers)
3098                    });
3099                    let mut folded_count = folded_buffers.len();
3100                    let mut unfolded_count = unfolded_buffers.len();
3101
3102                    let fold = !unfolded_buffers.is_empty() && rng.random_bool(0.5);
3103                    let unfold = !folded_buffers.is_empty() && rng.random_bool(0.5);
3104                    if !fold && !unfold {
3105                        log::info!(
3106                            "Noop fold/unfold operation. Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3107                        );
3108                        continue;
3109                    }
3110
3111                    buffer.update(cx, |buffer, cx| {
3112                        if fold {
3113                            let buffer_to_fold =
3114                                unfolded_buffers[rng.random_range(0..unfolded_buffers.len())];
3115                            log::info!("Folding {buffer_to_fold:?}");
3116                            let related_excerpts = buffer_snapshot
3117                                .excerpts()
3118                                .filter_map(|(excerpt_id, buffer, range)| {
3119                                    if buffer.remote_id() == buffer_to_fold {
3120                                        Some((
3121                                            excerpt_id,
3122                                            buffer
3123                                                .text_for_range(range.context)
3124                                                .collect::<String>(),
3125                                        ))
3126                                    } else {
3127                                        None
3128                                    }
3129                                })
3130                                .collect::<Vec<_>>();
3131                            log::info!(
3132                                "Folding {buffer_to_fold:?}, related excerpts: {related_excerpts:?}"
3133                            );
3134                            folded_count += 1;
3135                            unfolded_count -= 1;
3136                            block_map.fold_buffers([buffer_to_fold], buffer, cx);
3137                        }
3138                        if unfold {
3139                            let buffer_to_unfold =
3140                                folded_buffers[rng.random_range(0..folded_buffers.len())];
3141                            log::info!("Unfolding {buffer_to_unfold:?}");
3142                            unfolded_count += 1;
3143                            folded_count -= 1;
3144                            block_map.unfold_buffers([buffer_to_unfold], buffer, cx);
3145                        }
3146                        log::info!(
3147                            "Unfolded buffers: {unfolded_count}, folded buffers: {folded_count}"
3148                        );
3149                    });
3150                }
3151                _ => {
3152                    buffer.update(cx, |buffer, cx| {
3153                        let mutation_count = rng.random_range(1..=5);
3154                        let subscription = buffer.subscribe();
3155                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
3156                        buffer_snapshot = buffer.snapshot(cx);
3157                        buffer_edits.extend(subscription.consume());
3158                        log::info!("buffer text: {:?}", buffer_snapshot.text());
3159                    });
3160                }
3161            }
3162
3163            let (inlay_snapshot, inlay_edits) =
3164                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
3165            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
3166            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
3167            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
3168                wrap_map.sync(tab_snapshot, tab_edits, cx)
3169            });
3170            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
3171            assert_eq!(
3172                blocks_snapshot.transforms.summary().input_rows,
3173                wraps_snapshot.max_point().row() + 1
3174            );
3175            log::info!("wrapped text: {:?}", wraps_snapshot.text());
3176            log::info!("blocks text: {:?}", blocks_snapshot.text());
3177
3178            let mut expected_blocks = Vec::new();
3179            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
3180                Some((
3181                    block.placement.to_wrap_row(&wraps_snapshot)?,
3182                    Block::Custom(block.clone()),
3183                ))
3184            }));
3185
3186            // Note that this needs to be synced with the related section in BlockMap::sync
3187            expected_blocks.extend(block_map.header_and_footer_blocks(
3188                &buffer_snapshot,
3189                0..,
3190                &wraps_snapshot,
3191            ));
3192
3193            BlockMap::sort_blocks(&mut expected_blocks);
3194
3195            for (placement, block) in &expected_blocks {
3196                log::info!(
3197                    "Block {:?} placement: {:?} Height: {:?}",
3198                    block.id(),
3199                    placement,
3200                    block.height()
3201                );
3202            }
3203
3204            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
3205
3206            let input_buffer_rows = buffer_snapshot
3207                .row_infos(MultiBufferRow(0))
3208                .map(|row| row.buffer_row)
3209                .collect::<Vec<_>>();
3210            let mut expected_buffer_rows = Vec::new();
3211            let mut expected_text = String::new();
3212            let mut expected_block_positions = Vec::new();
3213            let mut expected_replaced_buffer_rows = HashSet::default();
3214            let input_text = wraps_snapshot.text();
3215
3216            // Loop over the input lines, creating (N - 1) empty lines for
3217            // blocks of height N.
3218            //
3219            // It's important to note that output *starts* as one empty line,
3220            // so we special case row 0 to assume a leading '\n'.
3221            //
3222            // Linehood is the birthright of strings.
3223            let input_text_lines = input_text.split('\n').enumerate().peekable();
3224            let mut block_row = 0;
3225            for (wrap_row, input_line) in input_text_lines {
3226                let wrap_row = wrap_row as u32;
3227                let multibuffer_row = wraps_snapshot
3228                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
3229                    .row;
3230
3231                // Create empty lines for the above block
3232                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3233                    if placement.start().0 == wrap_row && block.place_above() {
3234                        let (_, block) = sorted_blocks_iter.next().unwrap();
3235                        expected_block_positions.push((block_row, block.id()));
3236                        if block.height() > 0 {
3237                            let text = "\n".repeat((block.height() - 1) as usize);
3238                            if block_row > 0 {
3239                                expected_text.push('\n')
3240                            }
3241                            expected_text.push_str(&text);
3242                            for _ in 0..block.height() {
3243                                expected_buffer_rows.push(None);
3244                            }
3245                            block_row += block.height();
3246                        }
3247                    } else {
3248                        break;
3249                    }
3250                }
3251
3252                // Skip lines within replace blocks, then create empty lines for the replace block's height
3253                let mut is_in_replace_block = false;
3254                if let Some((BlockPlacement::Replace(replace_range), block)) =
3255                    sorted_blocks_iter.peek()
3256                    && wrap_row >= replace_range.start().0
3257                {
3258                    is_in_replace_block = true;
3259
3260                    if wrap_row == replace_range.start().0 {
3261                        if matches!(block, Block::FoldedBuffer { .. }) {
3262                            expected_buffer_rows.push(None);
3263                        } else {
3264                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
3265                        }
3266                    }
3267
3268                    if wrap_row == replace_range.end().0 {
3269                        expected_block_positions.push((block_row, block.id()));
3270                        let text = "\n".repeat((block.height() - 1) as usize);
3271                        if block_row > 0 {
3272                            expected_text.push('\n');
3273                        }
3274                        expected_text.push_str(&text);
3275
3276                        for _ in 1..block.height() {
3277                            expected_buffer_rows.push(None);
3278                        }
3279                        block_row += block.height();
3280
3281                        sorted_blocks_iter.next();
3282                    }
3283                }
3284
3285                if is_in_replace_block {
3286                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
3287                } else {
3288                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
3289                    let soft_wrapped = wraps_snapshot
3290                        .to_tab_point(WrapPoint::new(wrap_row, 0))
3291                        .column()
3292                        > 0;
3293                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
3294                    if block_row > 0 {
3295                        expected_text.push('\n');
3296                    }
3297                    expected_text.push_str(input_line);
3298                    block_row += 1;
3299                }
3300
3301                while let Some((placement, block)) = sorted_blocks_iter.peek() {
3302                    if placement.end().0 == wrap_row && block.place_below() {
3303                        let (_, block) = sorted_blocks_iter.next().unwrap();
3304                        expected_block_positions.push((block_row, block.id()));
3305                        if block.height() > 0 {
3306                            let text = "\n".repeat((block.height() - 1) as usize);
3307                            if block_row > 0 {
3308                                expected_text.push('\n')
3309                            }
3310                            expected_text.push_str(&text);
3311                            for _ in 0..block.height() {
3312                                expected_buffer_rows.push(None);
3313                            }
3314                            block_row += block.height();
3315                        }
3316                    } else {
3317                        break;
3318                    }
3319                }
3320            }
3321
3322            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
3323            let expected_row_count = expected_lines.len();
3324            log::info!("expected text: {expected_text:?}");
3325
3326            assert_eq!(
3327                blocks_snapshot.max_point().row + 1,
3328                expected_row_count as u32,
3329                "actual row count != expected row count",
3330            );
3331            assert_eq!(
3332                blocks_snapshot.text(),
3333                expected_text,
3334                "actual text != expected text",
3335            );
3336
3337            for start_row in 0..expected_row_count {
3338                let end_row = rng.random_range(start_row + 1..=expected_row_count);
3339                let mut expected_text = expected_lines[start_row..end_row].join("\n");
3340                if end_row < expected_row_count {
3341                    expected_text.push('\n');
3342                }
3343
3344                let actual_text = blocks_snapshot
3345                    .chunks(
3346                        start_row as u32..end_row as u32,
3347                        false,
3348                        false,
3349                        Highlights::default(),
3350                    )
3351                    .map(|chunk| chunk.text)
3352                    .collect::<String>();
3353                assert_eq!(
3354                    actual_text,
3355                    expected_text,
3356                    "incorrect text starting row row range {:?}",
3357                    start_row..end_row
3358                );
3359                assert_eq!(
3360                    blocks_snapshot
3361                        .row_infos(BlockRow(start_row as u32))
3362                        .map(|row_info| row_info.buffer_row)
3363                        .collect::<Vec<_>>(),
3364                    &expected_buffer_rows[start_row..],
3365                    "incorrect buffer_rows starting at row {:?}",
3366                    start_row
3367                );
3368            }
3369
3370            assert_eq!(
3371                blocks_snapshot
3372                    .blocks_in_range(0..(expected_row_count as u32))
3373                    .map(|(row, block)| (row, block.id()))
3374                    .collect::<Vec<_>>(),
3375                expected_block_positions,
3376                "invalid blocks_in_range({:?})",
3377                0..expected_row_count
3378            );
3379
3380            for (_, expected_block) in
3381                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
3382            {
3383                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
3384                assert_eq!(
3385                    actual_block.map(|block| block.id()),
3386                    Some(expected_block.id())
3387                );
3388            }
3389
3390            for (block_row, block_id) in expected_block_positions {
3391                if let BlockId::Custom(block_id) = block_id {
3392                    assert_eq!(
3393                        blocks_snapshot.row_for_block(block_id),
3394                        Some(BlockRow(block_row))
3395                    );
3396                }
3397            }
3398
3399            let mut expected_longest_rows = Vec::new();
3400            let mut longest_line_len = -1_isize;
3401            for (row, line) in expected_lines.iter().enumerate() {
3402                let row = row as u32;
3403
3404                assert_eq!(
3405                    blocks_snapshot.line_len(BlockRow(row)),
3406                    line.len() as u32,
3407                    "invalid line len for row {}",
3408                    row
3409                );
3410
3411                let line_char_count = line.chars().count() as isize;
3412                match line_char_count.cmp(&longest_line_len) {
3413                    Ordering::Less => {}
3414                    Ordering::Equal => expected_longest_rows.push(row),
3415                    Ordering::Greater => {
3416                        longest_line_len = line_char_count;
3417                        expected_longest_rows.clear();
3418                        expected_longest_rows.push(row);
3419                    }
3420                }
3421            }
3422
3423            let longest_row = blocks_snapshot.longest_row();
3424            assert!(
3425                expected_longest_rows.contains(&longest_row),
3426                "incorrect longest row {}. expected {:?} with length {}",
3427                longest_row,
3428                expected_longest_rows,
3429                longest_line_len,
3430            );
3431
3432            for _ in 0..10 {
3433                let end_row = rng.random_range(1..=expected_lines.len());
3434                let start_row = rng.random_range(0..end_row);
3435
3436                let mut expected_longest_rows_in_range = vec![];
3437                let mut longest_line_len_in_range = 0;
3438
3439                let mut row = start_row as u32;
3440                for line in &expected_lines[start_row..end_row] {
3441                    let line_char_count = line.chars().count() as isize;
3442                    match line_char_count.cmp(&longest_line_len_in_range) {
3443                        Ordering::Less => {}
3444                        Ordering::Equal => expected_longest_rows_in_range.push(row),
3445                        Ordering::Greater => {
3446                            longest_line_len_in_range = line_char_count;
3447                            expected_longest_rows_in_range.clear();
3448                            expected_longest_rows_in_range.push(row);
3449                        }
3450                    }
3451                    row += 1;
3452                }
3453
3454                let longest_row_in_range = blocks_snapshot
3455                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
3456                assert!(
3457                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
3458                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
3459                    longest_row,
3460                    start_row..end_row,
3461                    expected_longest_rows_in_range,
3462                    longest_line_len_in_range,
3463                );
3464            }
3465
3466            // Ensure that conversion between block points and wrap points is stable.
3467            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
3468                let wrap_point = WrapPoint::new(row, 0);
3469                let block_point = blocks_snapshot.to_block_point(wrap_point);
3470                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
3471                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
3472                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
3473                assert_eq!(
3474                    blocks_snapshot.to_block_point(right_wrap_point),
3475                    block_point
3476                );
3477            }
3478
3479            let mut block_point = BlockPoint::new(0, 0);
3480            for c in expected_text.chars() {
3481                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
3482                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
3483                assert_eq!(
3484                    blocks_snapshot
3485                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
3486                    left_point,
3487                    "block point: {:?}, wrap point: {:?}",
3488                    block_point,
3489                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
3490                );
3491                assert_eq!(
3492                    left_buffer_point,
3493                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
3494                    "{:?} is not valid in buffer coordinates",
3495                    left_point
3496                );
3497
3498                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
3499                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
3500                assert_eq!(
3501                    blocks_snapshot
3502                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
3503                    right_point,
3504                    "block point: {:?}, wrap point: {:?}",
3505                    block_point,
3506                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
3507                );
3508                assert_eq!(
3509                    right_buffer_point,
3510                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
3511                    "{:?} is not valid in buffer coordinates",
3512                    right_point
3513                );
3514
3515                if c == '\n' {
3516                    block_point.0 += Point::new(1, 0);
3517                } else {
3518                    block_point.column += c.len_utf8() as u32;
3519                }
3520            }
3521
3522            for buffer_row in 0..=buffer_snapshot.max_point().row {
3523                let buffer_row = MultiBufferRow(buffer_row);
3524                assert_eq!(
3525                    blocks_snapshot.is_line_replaced(buffer_row),
3526                    expected_replaced_buffer_rows.contains(&buffer_row),
3527                    "incorrect is_line_replaced({buffer_row:?}), expected replaced rows: {expected_replaced_buffer_rows:?}",
3528                );
3529            }
3530        }
3531    }
3532
3533    #[gpui::test]
3534    fn test_remove_intersecting_replace_blocks_edge_case(cx: &mut gpui::TestAppContext) {
3535        cx.update(init_test);
3536
3537        let text = "abc\ndef\nghi\njkl\nmno";
3538        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
3539        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
3540        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
3541        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
3542        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
3543        let (_wrap_map, wraps_snapshot) =
3544            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
3545        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
3546
3547        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3548        let _block_id = writer.insert(vec![BlockProperties {
3549            style: BlockStyle::Fixed,
3550            placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
3551            height: Some(1),
3552            render: Arc::new(|_| div().into_any()),
3553            priority: 0,
3554        }])[0];
3555
3556        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
3557        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3558
3559        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
3560        writer.remove_intersecting_replace_blocks(
3561            [buffer_snapshot.anchor_after(Point::new(1, 0))
3562                ..buffer_snapshot.anchor_after(Point::new(1, 0))],
3563            false,
3564        );
3565        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
3566        assert_eq!(blocks_snapshot.text(), "abc\n\ndef\nghi\njkl\nmno");
3567    }
3568
3569    fn init_test(cx: &mut gpui::App) {
3570        let settings = SettingsStore::test(cx);
3571        cx.set_global(settings);
3572        theme::init(theme::LoadThemes::JustBase, cx);
3573        assets::Assets.load_test_fonts(cx);
3574    }
3575
3576    impl Block {
3577        fn as_custom(&self) -> Option<&CustomBlock> {
3578            match self {
3579                Block::Custom(block) => Some(block),
3580                _ => None,
3581            }
3582        }
3583    }
3584
3585    impl BlockSnapshot {
3586        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
3587            self.wrap_snapshot
3588                .to_point(self.to_wrap_point(point, bias), bias)
3589        }
3590    }
3591}