block_map.rs

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