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, EntityId, Pixels, WindowContext};
   8use language::{Chunk, Patch, Point};
   9use multi_buffer::{
  10    Anchor, ExcerptId, ExcerptInfo, MultiBufferRow, MultiBufferSnapshot, ToOffset, ToPoint as _,
  11};
  12use parking_lot::Mutex;
  13use std::{
  14    cell::RefCell,
  15    cmp::{self, Ordering},
  16    fmt::Debug,
  17    ops::{Deref, DerefMut, Range, RangeBounds},
  18    sync::{
  19        atomic::{AtomicUsize, Ordering::SeqCst},
  20        Arc,
  21    },
  22};
  23use sum_tree::{Bias, SumTree, Summary, TreeMap};
  24use text::Edit;
  25use ui::ElementId;
  26
  27const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
  28const BULLETS: &str = "********************************************************************************************************************************";
  29
  30/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
  31///
  32/// See the [`display_map` module documentation](crate::display_map) for more information.
  33pub struct BlockMap {
  34    next_block_id: AtomicUsize,
  35    wrap_snapshot: RefCell<WrapSnapshot>,
  36    custom_blocks: Vec<Arc<CustomBlock>>,
  37    custom_blocks_by_id: TreeMap<CustomBlockId, Arc<CustomBlock>>,
  38    transforms: RefCell<SumTree<Transform>>,
  39    show_excerpt_controls: bool,
  40    buffer_header_height: u32,
  41    excerpt_header_height: u32,
  42    excerpt_footer_height: u32,
  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    pub(super) excerpt_footer_height: u32,
  60}
  61
  62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
  63pub struct CustomBlockId(usize);
  64
  65impl From<CustomBlockId> for ElementId {
  66    fn from(val: CustomBlockId) -> Self {
  67        ElementId::Integer(val.0)
  68    }
  69}
  70
  71#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  72pub struct BlockPoint(pub Point);
  73
  74#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  75pub struct BlockRow(pub(super) u32);
  76
  77#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  78struct WrapRow(u32);
  79
  80pub type RenderBlock = Arc<dyn Send + Sync + Fn(&mut BlockContext) -> AnyElement>;
  81
  82#[derive(Clone, Debug, Eq, PartialEq)]
  83pub enum BlockPlacement<T> {
  84    Above(T),
  85    Below(T),
  86    Replace(Range<T>),
  87}
  88
  89impl<T> BlockPlacement<T> {
  90    fn start(&self) -> &T {
  91        match self {
  92            BlockPlacement::Above(position) => position,
  93            BlockPlacement::Below(position) => position,
  94            BlockPlacement::Replace(range) => &range.start,
  95        }
  96    }
  97
  98    fn end(&self) -> &T {
  99        match self {
 100            BlockPlacement::Above(position) => position,
 101            BlockPlacement::Below(position) => position,
 102            BlockPlacement::Replace(range) => &range.end,
 103        }
 104    }
 105
 106    pub fn as_ref(&self) -> BlockPlacement<&T> {
 107        match self {
 108            BlockPlacement::Above(position) => BlockPlacement::Above(position),
 109            BlockPlacement::Below(position) => BlockPlacement::Below(position),
 110            BlockPlacement::Replace(range) => BlockPlacement::Replace(&range.start..&range.end),
 111        }
 112    }
 113
 114    pub fn map<R>(self, mut f: impl FnMut(T) -> R) -> BlockPlacement<R> {
 115        match self {
 116            BlockPlacement::Above(position) => BlockPlacement::Above(f(position)),
 117            BlockPlacement::Below(position) => BlockPlacement::Below(f(position)),
 118            BlockPlacement::Replace(range) => BlockPlacement::Replace(f(range.start)..f(range.end)),
 119        }
 120    }
 121}
 122
 123impl BlockPlacement<Anchor> {
 124    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
 125        match (self, other) {
 126            (BlockPlacement::Above(anchor_a), BlockPlacement::Above(anchor_b))
 127            | (BlockPlacement::Below(anchor_a), BlockPlacement::Below(anchor_b)) => {
 128                anchor_a.cmp(anchor_b, buffer)
 129            }
 130            (BlockPlacement::Above(anchor_a), BlockPlacement::Below(anchor_b)) => {
 131                anchor_a.cmp(anchor_b, buffer).then(Ordering::Less)
 132            }
 133            (BlockPlacement::Below(anchor_a), BlockPlacement::Above(anchor_b)) => {
 134                anchor_a.cmp(anchor_b, buffer).then(Ordering::Greater)
 135            }
 136            (BlockPlacement::Above(anchor), BlockPlacement::Replace(range)) => {
 137                anchor.cmp(&range.start, buffer).then(Ordering::Less)
 138            }
 139            (BlockPlacement::Replace(range), BlockPlacement::Above(anchor)) => {
 140                range.start.cmp(anchor, buffer).then(Ordering::Greater)
 141            }
 142            (BlockPlacement::Below(anchor), BlockPlacement::Replace(range)) => {
 143                anchor.cmp(&range.start, buffer).then(Ordering::Greater)
 144            }
 145            (BlockPlacement::Replace(range), BlockPlacement::Below(anchor)) => {
 146                range.start.cmp(anchor, buffer).then(Ordering::Less)
 147            }
 148            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
 149                .start
 150                .cmp(&range_b.start, buffer)
 151                .then_with(|| range_b.end.cmp(&range_a.end, buffer)),
 152        }
 153    }
 154
 155    fn to_wrap_row(&self, wrap_snapshot: &WrapSnapshot) -> Option<BlockPlacement<WrapRow>> {
 156        let buffer_snapshot = wrap_snapshot.buffer_snapshot();
 157        match self {
 158            BlockPlacement::Above(position) => {
 159                let mut position = position.to_point(buffer_snapshot);
 160                position.column = 0;
 161                let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
 162                Some(BlockPlacement::Above(wrap_row))
 163            }
 164            BlockPlacement::Below(position) => {
 165                let mut position = position.to_point(buffer_snapshot);
 166                position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
 167                let wrap_row = WrapRow(wrap_snapshot.make_wrap_point(position, Bias::Left).row());
 168                Some(BlockPlacement::Below(wrap_row))
 169            }
 170            BlockPlacement::Replace(range) => {
 171                let mut start = range.start.to_point(buffer_snapshot);
 172                let mut end = range.end.to_point(buffer_snapshot);
 173                if start == end {
 174                    None
 175                } else {
 176                    start.column = 0;
 177                    let start_wrap_row =
 178                        WrapRow(wrap_snapshot.make_wrap_point(start, Bias::Left).row());
 179                    end.column = buffer_snapshot.line_len(MultiBufferRow(end.row));
 180                    let end_wrap_row =
 181                        WrapRow(wrap_snapshot.make_wrap_point(end, Bias::Left).row());
 182                    Some(BlockPlacement::Replace(start_wrap_row..end_wrap_row))
 183                }
 184            }
 185        }
 186    }
 187}
 188
 189impl Ord for BlockPlacement<WrapRow> {
 190    fn cmp(&self, other: &Self) -> Ordering {
 191        match (self, other) {
 192            (BlockPlacement::Above(row_a), BlockPlacement::Above(row_b))
 193            | (BlockPlacement::Below(row_a), BlockPlacement::Below(row_b)) => row_a.cmp(row_b),
 194            (BlockPlacement::Above(row_a), BlockPlacement::Below(row_b)) => {
 195                row_a.cmp(row_b).then(Ordering::Less)
 196            }
 197            (BlockPlacement::Below(row_a), BlockPlacement::Above(row_b)) => {
 198                row_a.cmp(row_b).then(Ordering::Greater)
 199            }
 200            (BlockPlacement::Above(row), BlockPlacement::Replace(range)) => {
 201                row.cmp(&range.start).then(Ordering::Less)
 202            }
 203            (BlockPlacement::Replace(range), BlockPlacement::Above(row)) => {
 204                range.start.cmp(row).then(Ordering::Greater)
 205            }
 206            (BlockPlacement::Below(row), BlockPlacement::Replace(range)) => {
 207                row.cmp(&range.start).then(Ordering::Greater)
 208            }
 209            (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => {
 210                range.start.cmp(row).then(Ordering::Less)
 211            }
 212            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => range_a
 213                .start
 214                .cmp(&range_b.start)
 215                .then_with(|| range_b.end.cmp(&range_a.end)),
 216        }
 217    }
 218}
 219
 220impl PartialOrd for BlockPlacement<WrapRow> {
 221    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 222        Some(self.cmp(other))
 223    }
 224}
 225
 226pub struct CustomBlock {
 227    id: CustomBlockId,
 228    placement: BlockPlacement<Anchor>,
 229    height: u32,
 230    style: BlockStyle,
 231    render: Arc<Mutex<RenderBlock>>,
 232    priority: usize,
 233}
 234
 235pub struct BlockProperties<P> {
 236    pub placement: BlockPlacement<P>,
 237    pub height: u32,
 238    pub style: BlockStyle,
 239    pub render: RenderBlock,
 240    pub priority: usize,
 241}
 242
 243impl<P: Debug> Debug for BlockProperties<P> {
 244    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 245        f.debug_struct("BlockProperties")
 246            .field("placement", &self.placement)
 247            .field("height", &self.height)
 248            .field("style", &self.style)
 249            .finish()
 250    }
 251}
 252
 253#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
 254pub enum BlockStyle {
 255    Fixed,
 256    Flex,
 257    Sticky,
 258}
 259
 260pub struct BlockContext<'a, 'b> {
 261    pub context: &'b mut WindowContext<'a>,
 262    pub anchor_x: Pixels,
 263    pub max_width: Pixels,
 264    pub gutter_dimensions: &'b GutterDimensions,
 265    pub em_width: Pixels,
 266    pub line_height: Pixels,
 267    pub block_id: BlockId,
 268    pub selected: bool,
 269    pub editor_style: &'b EditorStyle,
 270}
 271
 272#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
 273pub enum BlockId {
 274    ExcerptBoundary(Option<ExcerptId>),
 275    Custom(CustomBlockId),
 276}
 277
 278impl From<BlockId> for ElementId {
 279    fn from(value: BlockId) -> Self {
 280        match value {
 281            BlockId::Custom(CustomBlockId(id)) => ("Block", id).into(),
 282            BlockId::ExcerptBoundary(next_excerpt) => match next_excerpt {
 283                Some(id) => ("ExcerptBoundary", EntityId::from(id)).into(),
 284                None => "LastExcerptBoundary".into(),
 285            },
 286        }
 287    }
 288}
 289
 290impl std::fmt::Display for BlockId {
 291    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 292        match self {
 293            Self::Custom(id) => write!(f, "Block({id:?})"),
 294            Self::ExcerptBoundary(id) => write!(f, "ExcerptHeader({id:?})"),
 295        }
 296    }
 297}
 298
 299#[derive(Clone, Debug)]
 300struct Transform {
 301    summary: TransformSummary,
 302    block: Option<Block>,
 303}
 304
 305#[allow(clippy::large_enum_variant)]
 306#[derive(Clone)]
 307pub enum Block {
 308    Custom(Arc<CustomBlock>),
 309    ExcerptBoundary {
 310        prev_excerpt: Option<ExcerptInfo>,
 311        next_excerpt: Option<ExcerptInfo>,
 312        height: u32,
 313        starts_new_buffer: bool,
 314        show_excerpt_controls: bool,
 315    },
 316}
 317
 318impl Block {
 319    pub fn id(&self) -> BlockId {
 320        match self {
 321            Block::Custom(block) => BlockId::Custom(block.id),
 322            Block::ExcerptBoundary { next_excerpt, .. } => {
 323                BlockId::ExcerptBoundary(next_excerpt.as_ref().map(|info| info.id))
 324            }
 325        }
 326    }
 327
 328    pub fn height(&self) -> u32 {
 329        match self {
 330            Block::Custom(block) => block.height,
 331            Block::ExcerptBoundary { height, .. } => *height,
 332        }
 333    }
 334
 335    pub fn style(&self) -> BlockStyle {
 336        match self {
 337            Block::Custom(block) => block.style,
 338            Block::ExcerptBoundary { .. } => BlockStyle::Sticky,
 339        }
 340    }
 341
 342    fn place_above(&self) -> bool {
 343        match self {
 344            Block::Custom(block) => matches!(block.placement, BlockPlacement::Above(_)),
 345            Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_some(),
 346        }
 347    }
 348
 349    fn place_below(&self) -> bool {
 350        match self {
 351            Block::Custom(block) => matches!(block.placement, BlockPlacement::Below(_)),
 352            Block::ExcerptBoundary { next_excerpt, .. } => next_excerpt.is_none(),
 353        }
 354    }
 355
 356    fn is_replacement(&self) -> bool {
 357        match self {
 358            Block::Custom(block) => matches!(block.placement, BlockPlacement::Replace(_)),
 359            Block::ExcerptBoundary { .. } => false,
 360        }
 361    }
 362}
 363
 364impl Debug for Block {
 365    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 366        match self {
 367            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
 368            Self::ExcerptBoundary {
 369                starts_new_buffer,
 370                next_excerpt,
 371                prev_excerpt,
 372                ..
 373            } => f
 374                .debug_struct("ExcerptBoundary")
 375                .field("prev_excerpt", &prev_excerpt)
 376                .field("next_excerpt", &next_excerpt)
 377                .field("starts_new_buffer", &starts_new_buffer)
 378                .finish(),
 379        }
 380    }
 381}
 382
 383#[derive(Clone, Debug, Default)]
 384struct TransformSummary {
 385    input_rows: u32,
 386    output_rows: u32,
 387    longest_row: u32,
 388    longest_row_chars: u32,
 389}
 390
 391pub struct BlockChunks<'a> {
 392    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
 393    input_chunks: wrap_map::WrapChunks<'a>,
 394    input_chunk: Chunk<'a>,
 395    output_row: u32,
 396    max_output_row: u32,
 397    masked: bool,
 398}
 399
 400#[derive(Clone)]
 401pub struct BlockBufferRows<'a> {
 402    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
 403    input_buffer_rows: wrap_map::WrapBufferRows<'a>,
 404    output_row: BlockRow,
 405    started: bool,
 406}
 407
 408impl BlockMap {
 409    pub fn new(
 410        wrap_snapshot: WrapSnapshot,
 411        show_excerpt_controls: bool,
 412        buffer_header_height: u32,
 413        excerpt_header_height: u32,
 414        excerpt_footer_height: u32,
 415    ) -> Self {
 416        let row_count = wrap_snapshot.max_point().row() + 1;
 417        let mut transforms = SumTree::default();
 418        push_isomorphic(&mut transforms, row_count, &wrap_snapshot);
 419        let map = Self {
 420            next_block_id: AtomicUsize::new(0),
 421            custom_blocks: Vec::new(),
 422            custom_blocks_by_id: TreeMap::default(),
 423            transforms: RefCell::new(transforms),
 424            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
 425            show_excerpt_controls,
 426            buffer_header_height,
 427            excerpt_header_height,
 428            excerpt_footer_height,
 429        };
 430        map.sync(
 431            &wrap_snapshot,
 432            Patch::new(vec![Edit {
 433                old: 0..row_count,
 434                new: 0..row_count,
 435            }]),
 436        );
 437        map
 438    }
 439
 440    pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapReader {
 441        self.sync(&wrap_snapshot, edits);
 442        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
 443        BlockMapReader {
 444            blocks: &self.custom_blocks,
 445            snapshot: BlockSnapshot {
 446                wrap_snapshot,
 447                transforms: self.transforms.borrow().clone(),
 448                custom_blocks_by_id: self.custom_blocks_by_id.clone(),
 449                buffer_header_height: self.buffer_header_height,
 450                excerpt_header_height: self.excerpt_header_height,
 451                excerpt_footer_height: self.excerpt_footer_height,
 452            },
 453        }
 454    }
 455
 456    pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {
 457        self.sync(&wrap_snapshot, edits);
 458        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
 459        BlockMapWriter(self)
 460    }
 461
 462    fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
 463        let buffer = wrap_snapshot.buffer_snapshot();
 464
 465        // Handle changing the last excerpt if it is empty.
 466        if buffer.trailing_excerpt_update_count()
 467            != self
 468                .wrap_snapshot
 469                .borrow()
 470                .buffer_snapshot()
 471                .trailing_excerpt_update_count()
 472        {
 473            let max_point = wrap_snapshot.max_point();
 474            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
 475            let edit_end = max_point.row() + 1;
 476            edits = edits.compose([WrapEdit {
 477                old: edit_start..edit_end,
 478                new: edit_start..edit_end,
 479            }]);
 480        }
 481
 482        let edits = edits.into_inner();
 483        if edits.is_empty() {
 484            return;
 485        }
 486
 487        let mut transforms = self.transforms.borrow_mut();
 488        let mut new_transforms = SumTree::default();
 489        let mut cursor = transforms.cursor::<WrapRow>(&());
 490        let mut last_block_ix = 0;
 491        let mut blocks_in_edit = Vec::new();
 492        let mut edits = edits.into_iter().peekable();
 493
 494        while let Some(edit) = edits.next() {
 495            let mut old_start = WrapRow(edit.old.start);
 496            let mut new_start = WrapRow(edit.new.start);
 497
 498            // Preserve transforms that:
 499            // * strictly precedes this edit
 500            // * isomorphic or replace transforms that end *at* the start of the edit
 501            // * below blocks that end at the start of the edit
 502            new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
 503            if let Some(transform) = cursor.item() {
 504                if transform.summary.input_rows > 0 && cursor.end(&()) == old_start {
 505                    // Preserve the transform (push and next)
 506                    new_transforms.push(transform.clone(), &());
 507                    cursor.next(&());
 508
 509                    // Preserve below blocks at end of edit
 510                    while let Some(transform) = cursor.item() {
 511                        if transform.block.as_ref().map_or(false, |b| b.place_below()) {
 512                            new_transforms.push(transform.clone(), &());
 513                            cursor.next(&());
 514                        } else {
 515                            break;
 516                        }
 517                    }
 518                }
 519            }
 520
 521            // Ensure the edit starts at a transform boundary.
 522            // If the edit starts within an isomorphic transform, preserve its prefix
 523            // If the edit lands within a replacement block, expand the edit to include the start of the replaced input range
 524            let mut preserved_blocks_above_edit = false;
 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                    // The blocks *above* it are already in the new transforms, so
 543                    // we don't need to re-insert them when querying blocks.
 544                    preserved_blocks_above_edit = true;
 545                }
 546            }
 547
 548            // Decide where the edit ends
 549            // * It should end at a transform boundary
 550            // * Coalesce edits that intersect the same transform
 551            let mut old_end = WrapRow(edit.old.end);
 552            let mut new_end = WrapRow(edit.new.end);
 553            loop {
 554                // Seek to the transform starting at or after the end of the edit
 555                cursor.seek(&old_end, Bias::Left, &());
 556                cursor.next(&());
 557
 558                // Extend edit to the end of the discarded transform so it is reconstructed in full
 559                let transform_rows_after_edit = cursor.start().0 - old_end.0;
 560                old_end.0 += transform_rows_after_edit;
 561                new_end.0 += transform_rows_after_edit;
 562
 563                // Combine this edit with any subsequent edits that intersect the same transform.
 564                while let Some(next_edit) = edits.peek() {
 565                    if next_edit.old.start <= cursor.start().0 {
 566                        old_end = WrapRow(next_edit.old.end);
 567                        new_end = WrapRow(next_edit.new.end);
 568                        cursor.seek(&old_end, Bias::Left, &());
 569                        cursor.next(&());
 570                        edits.next();
 571                    } else {
 572                        break;
 573                    }
 574                }
 575
 576                if *cursor.start() == old_end {
 577                    break;
 578                }
 579            }
 580
 581            // Discard below blocks at the end of the edit. They'll be reconstructed.
 582            while let Some(transform) = cursor.item() {
 583                if transform.block.as_ref().map_or(false, |b| b.place_below()) {
 584                    cursor.next(&());
 585                } else {
 586                    break;
 587                }
 588            }
 589
 590            // Find the blocks within this edited region.
 591            let new_buffer_start =
 592                wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
 593            let start_bound = Bound::Included(new_buffer_start);
 594            let start_block_ix =
 595                match self.custom_blocks[last_block_ix..].binary_search_by(|probe| {
 596                    probe
 597                        .start()
 598                        .to_point(buffer)
 599                        .cmp(&new_buffer_start)
 600                        // Move left until we find the index of the first block starting within this edit
 601                        .then(Ordering::Greater)
 602                }) {
 603                    Ok(ix) | Err(ix) => last_block_ix + ix,
 604                };
 605
 606            let end_bound;
 607            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
 608                end_bound = Bound::Unbounded;
 609                self.custom_blocks.len()
 610            } else {
 611                let new_buffer_end =
 612                    wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
 613                end_bound = Bound::Excluded(new_buffer_end);
 614                match self.custom_blocks[start_block_ix..].binary_search_by(|probe| {
 615                    probe
 616                        .start()
 617                        .to_point(buffer)
 618                        .cmp(&new_buffer_end)
 619                        .then(Ordering::Greater)
 620                }) {
 621                    Ok(ix) | Err(ix) => start_block_ix + ix,
 622                }
 623            };
 624            last_block_ix = end_block_ix;
 625
 626            debug_assert!(blocks_in_edit.is_empty());
 627
 628            blocks_in_edit.extend(
 629                self.custom_blocks[start_block_ix..end_block_ix]
 630                    .iter()
 631                    .filter_map(|block| {
 632                        Some((
 633                            block.placement.to_wrap_row(wrap_snapshot)?,
 634                            Block::Custom(block.clone()),
 635                        ))
 636                    }),
 637            );
 638
 639            if buffer.show_headers() {
 640                blocks_in_edit.extend(BlockMap::header_and_footer_blocks(
 641                    self.show_excerpt_controls,
 642                    self.excerpt_footer_height,
 643                    self.buffer_header_height,
 644                    self.excerpt_header_height,
 645                    buffer,
 646                    (start_bound, end_bound),
 647                    wrap_snapshot,
 648                ));
 649            }
 650
 651            BlockMap::sort_blocks(&mut blocks_in_edit);
 652
 653            // For each of these blocks, insert a new isomorphic transform preceding the block,
 654            // and then insert the block itself.
 655            for (block_placement, block) in blocks_in_edit.drain(..) {
 656                if preserved_blocks_above_edit
 657                    && block_placement == BlockPlacement::Above(new_start)
 658                {
 659                    continue;
 660                }
 661
 662                let mut summary = TransformSummary {
 663                    input_rows: 0,
 664                    output_rows: block.height(),
 665                    longest_row: 0,
 666                    longest_row_chars: 0,
 667                };
 668
 669                let rows_before_block;
 670                match block_placement {
 671                    BlockPlacement::Above(position) => {
 672                        rows_before_block = position.0 - new_transforms.summary().input_rows;
 673                    }
 674                    BlockPlacement::Below(position) => {
 675                        rows_before_block = (position.0 + 1) - new_transforms.summary().input_rows;
 676                    }
 677                    BlockPlacement::Replace(range) => {
 678                        rows_before_block = range.start.0 - new_transforms.summary().input_rows;
 679                        summary.input_rows = range.end.0 - range.start.0 + 1;
 680                    }
 681                }
 682
 683                push_isomorphic(&mut new_transforms, rows_before_block, wrap_snapshot);
 684                new_transforms.push(
 685                    Transform {
 686                        summary,
 687                        block: Some(block),
 688                    },
 689                    &(),
 690                );
 691            }
 692
 693            // Insert an isomorphic transform after the final block.
 694            let rows_after_last_block = new_end
 695                .0
 696                .saturating_sub(new_transforms.summary().input_rows);
 697            push_isomorphic(&mut new_transforms, rows_after_last_block, wrap_snapshot);
 698        }
 699
 700        new_transforms.append(cursor.suffix(&()), &());
 701        debug_assert_eq!(
 702            new_transforms.summary().input_rows,
 703            wrap_snapshot.max_point().row() + 1
 704        );
 705
 706        drop(cursor);
 707        *transforms = new_transforms;
 708    }
 709
 710    pub fn replace_blocks(&mut self, mut renderers: HashMap<CustomBlockId, RenderBlock>) {
 711        for block in &mut self.custom_blocks {
 712            if let Some(render) = renderers.remove(&block.id) {
 713                *block.render.lock() = render;
 714            }
 715        }
 716    }
 717
 718    pub fn show_excerpt_controls(&self) -> bool {
 719        self.show_excerpt_controls
 720    }
 721
 722    fn header_and_footer_blocks<'a, 'b: 'a, 'c: 'a + 'b, R, T>(
 723        show_excerpt_controls: bool,
 724        excerpt_footer_height: u32,
 725        buffer_header_height: u32,
 726        excerpt_header_height: u32,
 727        buffer: &'b multi_buffer::MultiBufferSnapshot,
 728        range: R,
 729        wrap_snapshot: &'c WrapSnapshot,
 730    ) -> impl Iterator<Item = (BlockPlacement<WrapRow>, Block)> + 'b
 731    where
 732        R: RangeBounds<T>,
 733        T: multi_buffer::ToOffset,
 734    {
 735        buffer
 736            .excerpt_boundaries_in_range(range)
 737            .filter_map(move |excerpt_boundary| {
 738                let wrap_row;
 739                if excerpt_boundary.next.is_some() {
 740                    wrap_row = wrap_snapshot
 741                        .make_wrap_point(Point::new(excerpt_boundary.row.0, 0), Bias::Left)
 742                        .row();
 743                } else {
 744                    wrap_row = wrap_snapshot
 745                        .make_wrap_point(
 746                            Point::new(
 747                                excerpt_boundary.row.0,
 748                                buffer.line_len(excerpt_boundary.row),
 749                            ),
 750                            Bias::Left,
 751                        )
 752                        .row();
 753                }
 754
 755                let starts_new_buffer = match (&excerpt_boundary.prev, &excerpt_boundary.next) {
 756                    (_, None) => false,
 757                    (None, Some(_)) => true,
 758                    (Some(prev), Some(next)) => prev.buffer_id != next.buffer_id,
 759                };
 760
 761                let mut height = 0;
 762                if excerpt_boundary.prev.is_some() {
 763                    if show_excerpt_controls {
 764                        height += excerpt_footer_height;
 765                    }
 766                }
 767                if excerpt_boundary.next.is_some() {
 768                    if starts_new_buffer {
 769                        height += buffer_header_height;
 770                        if show_excerpt_controls {
 771                            height += excerpt_header_height;
 772                        }
 773                    } else {
 774                        height += excerpt_header_height;
 775                    }
 776                }
 777
 778                if height == 0 {
 779                    return None;
 780                }
 781
 782                Some((
 783                    if excerpt_boundary.next.is_some() {
 784                        BlockPlacement::Above(WrapRow(wrap_row))
 785                    } else {
 786                        BlockPlacement::Below(WrapRow(wrap_row))
 787                    },
 788                    Block::ExcerptBoundary {
 789                        prev_excerpt: excerpt_boundary.prev,
 790                        next_excerpt: excerpt_boundary.next,
 791                        height,
 792                        starts_new_buffer,
 793                        show_excerpt_controls,
 794                    },
 795                ))
 796            })
 797    }
 798
 799    fn sort_blocks(blocks: &mut Vec<(BlockPlacement<WrapRow>, Block)>) {
 800        blocks.sort_unstable_by(|(placement_a, block_a), (placement_b, block_b)| {
 801            placement_a
 802                .cmp(&placement_b)
 803                .then_with(|| match (block_a, block_b) {
 804                    (
 805                        Block::ExcerptBoundary {
 806                            next_excerpt: next_excerpt_a,
 807                            ..
 808                        },
 809                        Block::ExcerptBoundary {
 810                            next_excerpt: next_excerpt_b,
 811                            ..
 812                        },
 813                    ) => next_excerpt_a
 814                        .as_ref()
 815                        .map(|excerpt| excerpt.id)
 816                        .cmp(&next_excerpt_b.as_ref().map(|excerpt| excerpt.id)),
 817                    (Block::ExcerptBoundary { next_excerpt, .. }, Block::Custom(_)) => {
 818                        if next_excerpt.is_some() {
 819                            Ordering::Less
 820                        } else {
 821                            Ordering::Greater
 822                        }
 823                    }
 824                    (Block::Custom(_), Block::ExcerptBoundary { next_excerpt, .. }) => {
 825                        if next_excerpt.is_some() {
 826                            Ordering::Greater
 827                        } else {
 828                            Ordering::Less
 829                        }
 830                    }
 831                    (Block::Custom(block_a), Block::Custom(block_b)) => block_a
 832                        .priority
 833                        .cmp(&block_b.priority)
 834                        .then_with(|| block_a.id.cmp(&block_b.id)),
 835                })
 836        });
 837        blocks.dedup_by(|(right, _), (left, _)| match (left, right) {
 838            (BlockPlacement::Replace(range), BlockPlacement::Above(row)) => {
 839                range.start < *row && range.end >= *row
 840            }
 841            (BlockPlacement::Replace(range), BlockPlacement::Below(row)) => {
 842                range.start <= *row && range.end > *row
 843            }
 844            (BlockPlacement::Replace(range_a), BlockPlacement::Replace(range_b)) => {
 845                if range_a.end >= range_b.start && range_a.start <= range_b.end {
 846                    range_a.end = range_a.end.max(range_b.end);
 847                    true
 848                } else {
 849                    false
 850                }
 851            }
 852            _ => false,
 853        });
 854    }
 855}
 856
 857fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32, wrap_snapshot: &WrapSnapshot) {
 858    if rows == 0 {
 859        return;
 860    }
 861
 862    let wrap_row_start = tree.summary().input_rows;
 863    let wrap_row_end = wrap_row_start + rows;
 864    let wrap_summary = wrap_snapshot.text_summary_for_range(wrap_row_start..wrap_row_end);
 865    let summary = TransformSummary {
 866        input_rows: rows,
 867        output_rows: rows,
 868        longest_row: wrap_summary.longest_row,
 869        longest_row_chars: wrap_summary.longest_row_chars,
 870    };
 871    let mut merged = false;
 872    tree.update_last(
 873        |last_transform| {
 874            if last_transform.block.is_none() {
 875                last_transform.summary.add_summary(&summary, &());
 876                merged = true;
 877            }
 878        },
 879        &(),
 880    );
 881    if !merged {
 882        tree.push(
 883            Transform {
 884                summary,
 885                block: None,
 886            },
 887            &(),
 888        );
 889    }
 890}
 891
 892impl BlockPoint {
 893    pub fn new(row: u32, column: u32) -> Self {
 894        Self(Point::new(row, column))
 895    }
 896}
 897
 898impl Deref for BlockPoint {
 899    type Target = Point;
 900
 901    fn deref(&self) -> &Self::Target {
 902        &self.0
 903    }
 904}
 905
 906impl std::ops::DerefMut for BlockPoint {
 907    fn deref_mut(&mut self) -> &mut Self::Target {
 908        &mut self.0
 909    }
 910}
 911
 912impl<'a> Deref for BlockMapReader<'a> {
 913    type Target = BlockSnapshot;
 914
 915    fn deref(&self) -> &Self::Target {
 916        &self.snapshot
 917    }
 918}
 919
 920impl<'a> DerefMut for BlockMapReader<'a> {
 921    fn deref_mut(&mut self) -> &mut Self::Target {
 922        &mut self.snapshot
 923    }
 924}
 925
 926impl<'a> BlockMapReader<'a> {
 927    pub fn row_for_block(&self, block_id: CustomBlockId) -> Option<BlockRow> {
 928        let block = self.blocks.iter().find(|block| block.id == block_id)?;
 929        let buffer_row = block
 930            .start()
 931            .to_point(self.wrap_snapshot.buffer_snapshot())
 932            .row;
 933        let wrap_row = self
 934            .wrap_snapshot
 935            .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
 936            .row();
 937        let start_wrap_row = WrapRow(
 938            self.wrap_snapshot
 939                .prev_row_boundary(WrapPoint::new(wrap_row, 0)),
 940        );
 941        let end_wrap_row = WrapRow(
 942            self.wrap_snapshot
 943                .next_row_boundary(WrapPoint::new(wrap_row, 0))
 944                .unwrap_or(self.wrap_snapshot.max_point().row() + 1),
 945        );
 946
 947        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
 948        cursor.seek(&start_wrap_row, Bias::Left, &());
 949        while let Some(transform) = cursor.item() {
 950            if cursor.start().0 > end_wrap_row {
 951                break;
 952            }
 953
 954            if let Some(BlockId::Custom(id)) = transform.block.as_ref().map(|block| block.id()) {
 955                if id == block_id {
 956                    return Some(cursor.start().1);
 957                }
 958            }
 959            cursor.next(&());
 960        }
 961
 962        None
 963    }
 964}
 965
 966impl<'a> BlockMapWriter<'a> {
 967    pub fn insert(
 968        &mut self,
 969        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
 970    ) -> Vec<CustomBlockId> {
 971        let blocks = blocks.into_iter();
 972        let mut ids = Vec::with_capacity(blocks.size_hint().1.unwrap_or(0));
 973        let mut edits = Patch::default();
 974        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
 975        let buffer = wrap_snapshot.buffer_snapshot();
 976
 977        let mut previous_wrap_row_range: Option<Range<u32>> = None;
 978        for block in blocks {
 979            if let BlockPlacement::Replace(_) = &block.placement {
 980                debug_assert!(block.height > 0);
 981            }
 982
 983            let id = CustomBlockId(self.0.next_block_id.fetch_add(1, SeqCst));
 984            ids.push(id);
 985
 986            let start = block.placement.start().to_point(buffer);
 987            let end = block.placement.end().to_point(buffer);
 988            let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
 989            let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
 990
 991            let (start_row, end_row) = {
 992                previous_wrap_row_range.take_if(|range| {
 993                    !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
 994                });
 995                let range = previous_wrap_row_range.get_or_insert_with(|| {
 996                    let start_row =
 997                        wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
 998                    let end_row = wrap_snapshot
 999                        .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1000                        .unwrap_or(wrap_snapshot.max_point().row() + 1);
1001                    start_row..end_row
1002                });
1003                (range.start, range.end)
1004            };
1005            let block_ix = match self
1006                .0
1007                .custom_blocks
1008                .binary_search_by(|probe| probe.placement.cmp(&block.placement, buffer))
1009            {
1010                Ok(ix) | Err(ix) => ix,
1011            };
1012            let new_block = Arc::new(CustomBlock {
1013                id,
1014                placement: block.placement,
1015                height: block.height,
1016                render: Arc::new(Mutex::new(block.render)),
1017                style: block.style,
1018                priority: block.priority,
1019            });
1020            self.0.custom_blocks.insert(block_ix, new_block.clone());
1021            self.0.custom_blocks_by_id.insert(id, new_block);
1022
1023            edits = edits.compose([Edit {
1024                old: start_row..end_row,
1025                new: start_row..end_row,
1026            }]);
1027        }
1028
1029        self.0.sync(wrap_snapshot, edits);
1030        ids
1031    }
1032
1033    pub fn resize(&mut self, mut heights: HashMap<CustomBlockId, u32>) {
1034        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
1035        let buffer = wrap_snapshot.buffer_snapshot();
1036        let mut edits = Patch::default();
1037        let mut last_block_buffer_row = None;
1038
1039        for block in &mut self.0.custom_blocks {
1040            if let Some(new_height) = heights.remove(&block.id) {
1041                if let BlockPlacement::Replace(_) = &block.placement {
1042                    debug_assert!(new_height > 0);
1043                }
1044
1045                if block.height != new_height {
1046                    let new_block = CustomBlock {
1047                        id: block.id,
1048                        placement: block.placement.clone(),
1049                        height: new_height,
1050                        style: block.style,
1051                        render: block.render.clone(),
1052                        priority: block.priority,
1053                    };
1054                    let new_block = Arc::new(new_block);
1055                    *block = new_block.clone();
1056                    self.0.custom_blocks_by_id.insert(block.id, new_block);
1057
1058                    let start_row = block.placement.start().to_point(buffer).row;
1059                    let end_row = block.placement.end().to_point(buffer).row;
1060                    if last_block_buffer_row != Some(end_row) {
1061                        last_block_buffer_row = Some(end_row);
1062                        let start_wrap_row = wrap_snapshot
1063                            .make_wrap_point(Point::new(start_row, 0), Bias::Left)
1064                            .row();
1065                        let end_wrap_row = wrap_snapshot
1066                            .make_wrap_point(Point::new(end_row, 0), Bias::Left)
1067                            .row();
1068                        let start =
1069                            wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1070                        let end = wrap_snapshot
1071                            .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1072                            .unwrap_or(wrap_snapshot.max_point().row() + 1);
1073                        edits.push(Edit {
1074                            old: start..end,
1075                            new: start..end,
1076                        })
1077                    }
1078                }
1079            }
1080        }
1081
1082        self.0.sync(wrap_snapshot, edits);
1083    }
1084
1085    pub fn remove(&mut self, block_ids: HashSet<CustomBlockId>) {
1086        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
1087        let buffer = wrap_snapshot.buffer_snapshot();
1088        let mut edits = Patch::default();
1089        let mut last_block_buffer_row = None;
1090        let mut previous_wrap_row_range: Option<Range<u32>> = None;
1091        self.0.custom_blocks.retain(|block| {
1092            if block_ids.contains(&block.id) {
1093                let start = block.placement.start().to_point(buffer);
1094                let end = block.placement.end().to_point(buffer);
1095                if last_block_buffer_row != Some(end.row) {
1096                    last_block_buffer_row = Some(end.row);
1097                    let start_wrap_row = wrap_snapshot.make_wrap_point(start, Bias::Left).row();
1098                    let end_wrap_row = wrap_snapshot.make_wrap_point(end, Bias::Left).row();
1099                    let (start_row, end_row) = {
1100                        previous_wrap_row_range.take_if(|range| {
1101                            !range.contains(&start_wrap_row) || !range.contains(&end_wrap_row)
1102                        });
1103                        let range = previous_wrap_row_range.get_or_insert_with(|| {
1104                            let start_row =
1105                                wrap_snapshot.prev_row_boundary(WrapPoint::new(start_wrap_row, 0));
1106                            let end_row = wrap_snapshot
1107                                .next_row_boundary(WrapPoint::new(end_wrap_row, 0))
1108                                .unwrap_or(wrap_snapshot.max_point().row() + 1);
1109                            start_row..end_row
1110                        });
1111                        (range.start, range.end)
1112                    };
1113
1114                    edits.push(Edit {
1115                        old: start_row..end_row,
1116                        new: start_row..end_row,
1117                    })
1118                }
1119                false
1120            } else {
1121                true
1122            }
1123        });
1124        self.0
1125            .custom_blocks_by_id
1126            .retain(|id, _| !block_ids.contains(id));
1127        self.0.sync(wrap_snapshot, edits);
1128    }
1129
1130    pub fn remove_intersecting_replace_blocks<T>(
1131        &mut self,
1132        ranges: impl IntoIterator<Item = Range<T>>,
1133        inclusive: bool,
1134    ) where
1135        T: ToOffset,
1136    {
1137        let wrap_snapshot = self.0.wrap_snapshot.borrow();
1138        let mut blocks_to_remove = HashSet::default();
1139        for range in ranges {
1140            let range = range.start.to_offset(wrap_snapshot.buffer_snapshot())
1141                ..range.end.to_offset(wrap_snapshot.buffer_snapshot());
1142            for block in self.blocks_intersecting_buffer_range(range, inclusive) {
1143                if matches!(block.placement, BlockPlacement::Replace(_)) {
1144                    blocks_to_remove.insert(block.id);
1145                }
1146            }
1147        }
1148        drop(wrap_snapshot);
1149        self.remove(blocks_to_remove);
1150    }
1151
1152    fn blocks_intersecting_buffer_range(
1153        &self,
1154        range: Range<usize>,
1155        inclusive: bool,
1156    ) -> &[Arc<CustomBlock>] {
1157        let wrap_snapshot = self.0.wrap_snapshot.borrow();
1158        let buffer = wrap_snapshot.buffer_snapshot();
1159        let start_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
1160            probe
1161                .end()
1162                .to_offset(buffer)
1163                .cmp(&range.start)
1164                .then(if inclusive {
1165                    Ordering::Greater
1166                } else {
1167                    Ordering::Less
1168                })
1169        }) {
1170            Ok(ix) | Err(ix) => ix,
1171        };
1172        let end_block_ix = match self.0.custom_blocks.binary_search_by(|probe| {
1173            probe
1174                .start()
1175                .to_offset(buffer)
1176                .cmp(&range.end)
1177                .then(if inclusive {
1178                    Ordering::Less
1179                } else {
1180                    Ordering::Greater
1181                })
1182        }) {
1183            Ok(ix) | Err(ix) => ix,
1184        };
1185        &self.0.custom_blocks[start_block_ix..end_block_ix]
1186    }
1187}
1188
1189impl BlockSnapshot {
1190    #[cfg(test)]
1191    pub fn text(&self) -> String {
1192        self.chunks(
1193            0..self.transforms.summary().output_rows,
1194            false,
1195            false,
1196            Highlights::default(),
1197        )
1198        .map(|chunk| chunk.text)
1199        .collect()
1200    }
1201
1202    pub(crate) fn chunks<'a>(
1203        &'a self,
1204        rows: Range<u32>,
1205        language_aware: bool,
1206        masked: bool,
1207        highlights: Highlights<'a>,
1208    ) -> BlockChunks<'a> {
1209        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
1210
1211        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1212        cursor.seek(&BlockRow(rows.start), Bias::Right, &());
1213        let transform_output_start = cursor.start().0 .0;
1214        let transform_input_start = cursor.start().1 .0;
1215
1216        let mut input_start = transform_input_start;
1217        let mut input_end = transform_input_start;
1218        if let Some(transform) = cursor.item() {
1219            if transform.block.is_none() {
1220                input_start += rows.start - transform_output_start;
1221                input_end += cmp::min(
1222                    rows.end - transform_output_start,
1223                    transform.summary.input_rows,
1224                );
1225            }
1226        }
1227
1228        BlockChunks {
1229            input_chunks: self.wrap_snapshot.chunks(
1230                input_start..input_end,
1231                language_aware,
1232                highlights,
1233            ),
1234            input_chunk: Default::default(),
1235            transforms: cursor,
1236            output_row: rows.start,
1237            max_output_row,
1238            masked,
1239        }
1240    }
1241
1242    pub(super) fn buffer_rows(&self, start_row: BlockRow) -> BlockBufferRows {
1243        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1244        cursor.seek(&start_row, Bias::Right, &());
1245        let (output_start, input_start) = cursor.start();
1246        let overshoot = if cursor
1247            .item()
1248            .map_or(false, |transform| transform.block.is_none())
1249        {
1250            start_row.0 - output_start.0
1251        } else {
1252            0
1253        };
1254        let input_start_row = input_start.0 + overshoot;
1255        BlockBufferRows {
1256            transforms: cursor,
1257            input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
1258            output_row: start_row,
1259            started: false,
1260        }
1261    }
1262
1263    pub fn blocks_in_range(&self, rows: Range<u32>) -> impl Iterator<Item = (u32, &Block)> {
1264        let mut cursor = self.transforms.cursor::<BlockRow>(&());
1265        cursor.seek(&BlockRow(rows.start), Bias::Left, &());
1266        while cursor.start().0 < rows.start && cursor.end(&()).0 <= rows.start {
1267            cursor.next(&());
1268        }
1269
1270        std::iter::from_fn(move || {
1271            while let Some(transform) = cursor.item() {
1272                let start_row = cursor.start().0;
1273                if start_row > rows.end
1274                    || (start_row == rows.end
1275                        && transform
1276                            .block
1277                            .as_ref()
1278                            .map_or(false, |block| block.height() > 0))
1279                {
1280                    break;
1281                }
1282                if let Some(block) = &transform.block {
1283                    cursor.next(&());
1284                    return Some((start_row, block));
1285                } else {
1286                    cursor.next(&());
1287                }
1288            }
1289            None
1290        })
1291    }
1292
1293    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1294        let buffer = self.wrap_snapshot.buffer_snapshot();
1295
1296        match block_id {
1297            BlockId::Custom(custom_block_id) => {
1298                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
1299                Some(Block::Custom(custom_block.clone()))
1300            }
1301            BlockId::ExcerptBoundary(next_excerpt_id) => {
1302                let wrap_point;
1303                if let Some(next_excerpt_id) = next_excerpt_id {
1304                    let excerpt_range = buffer.range_for_excerpt::<Point>(next_excerpt_id)?;
1305                    wrap_point = self
1306                        .wrap_snapshot
1307                        .make_wrap_point(excerpt_range.start, Bias::Left);
1308                } else {
1309                    wrap_point = self
1310                        .wrap_snapshot
1311                        .make_wrap_point(buffer.max_point(), Bias::Left);
1312                }
1313
1314                let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1315                cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
1316                while let Some(transform) = cursor.item() {
1317                    if let Some(block) = transform.block.as_ref() {
1318                        if block.id() == block_id {
1319                            return Some(block.clone());
1320                        }
1321                    } else if cursor.start().0 > WrapRow(wrap_point.row()) {
1322                        break;
1323                    }
1324
1325                    cursor.next(&());
1326                }
1327
1328                None
1329            }
1330        }
1331    }
1332
1333    pub fn max_point(&self) -> BlockPoint {
1334        let row = self.transforms.summary().output_rows.saturating_sub(1);
1335        BlockPoint::new(row, self.line_len(BlockRow(row)))
1336    }
1337
1338    pub fn longest_row(&self) -> u32 {
1339        self.transforms.summary().longest_row
1340    }
1341
1342    pub fn longest_row_in_range(&self, range: Range<BlockRow>) -> BlockRow {
1343        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1344        cursor.seek(&range.start, Bias::Right, &());
1345
1346        let mut longest_row = range.start;
1347        let mut longest_row_chars = 0;
1348        if let Some(transform) = cursor.item() {
1349            if transform.block.is_none() {
1350                let (output_start, input_start) = cursor.start();
1351                let overshoot = range.start.0 - output_start.0;
1352                let wrap_start_row = input_start.0 + overshoot;
1353                let wrap_end_row = cmp::min(
1354                    input_start.0 + (range.end.0 - output_start.0),
1355                    cursor.end(&()).1 .0,
1356                );
1357                let summary = self
1358                    .wrap_snapshot
1359                    .text_summary_for_range(wrap_start_row..wrap_end_row);
1360                longest_row = BlockRow(range.start.0 + summary.longest_row);
1361                longest_row_chars = summary.longest_row_chars;
1362            }
1363            cursor.next(&());
1364        }
1365
1366        let cursor_start_row = cursor.start().0;
1367        if range.end > cursor_start_row {
1368            let summary = cursor.summary::<_, TransformSummary>(&range.end, Bias::Right, &());
1369            if summary.longest_row_chars > longest_row_chars {
1370                longest_row = BlockRow(cursor_start_row.0 + summary.longest_row);
1371                longest_row_chars = summary.longest_row_chars;
1372            }
1373
1374            if let Some(transform) = cursor.item() {
1375                if transform.block.is_none() {
1376                    let (output_start, input_start) = cursor.start();
1377                    let overshoot = range.end.0 - output_start.0;
1378                    let wrap_start_row = input_start.0;
1379                    let wrap_end_row = input_start.0 + overshoot;
1380                    let summary = self
1381                        .wrap_snapshot
1382                        .text_summary_for_range(wrap_start_row..wrap_end_row);
1383                    if summary.longest_row_chars > longest_row_chars {
1384                        longest_row = BlockRow(output_start.0 + summary.longest_row);
1385                    }
1386                }
1387            }
1388        }
1389
1390        longest_row
1391    }
1392
1393    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1394        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1395        cursor.seek(&BlockRow(row.0), Bias::Right, &());
1396        if let Some(transform) = cursor.item() {
1397            let (output_start, input_start) = cursor.start();
1398            let overshoot = row.0 - output_start.0;
1399            if transform.block.is_some() {
1400                0
1401            } else {
1402                self.wrap_snapshot.line_len(input_start.0 + overshoot)
1403            }
1404        } else if row.0 == 0 {
1405            0
1406        } else {
1407            panic!("row out of range");
1408        }
1409    }
1410
1411    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1412        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1413        cursor.seek(&row, Bias::Right, &());
1414        cursor.item().map_or(false, |t| t.block.is_some())
1415    }
1416
1417    pub(super) fn is_line_replaced(&self, row: MultiBufferRow) -> bool {
1418        let wrap_point = self
1419            .wrap_snapshot
1420            .make_wrap_point(Point::new(row.0, 0), Bias::Left);
1421        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1422        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1423        cursor.item().map_or(false, |transform| {
1424            if let Some(Block::Custom(block)) = transform.block.as_ref() {
1425                matches!(block.placement, BlockPlacement::Replace(_))
1426            } else {
1427                false
1428            }
1429        })
1430    }
1431
1432    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1433        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1434        cursor.seek(&BlockRow(point.row), Bias::Right, &());
1435
1436        let max_input_row = WrapRow(self.transforms.summary().input_rows);
1437        let mut search_left =
1438            (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
1439        let mut reversed = false;
1440
1441        loop {
1442            if let Some(transform) = cursor.item() {
1443                let (output_start_row, input_start_row) = cursor.start();
1444                let (output_end_row, input_end_row) = cursor.end(&());
1445                let output_start = Point::new(output_start_row.0, 0);
1446                let input_start = Point::new(input_start_row.0, 0);
1447                let input_end = Point::new(input_end_row.0, 0);
1448
1449                match transform.block.as_ref() {
1450                    Some(Block::Custom(block))
1451                        if matches!(block.placement, BlockPlacement::Replace(_)) =>
1452                    {
1453                        if ((bias == Bias::Left || search_left) && output_start <= point.0)
1454                            || (!search_left && output_start >= point.0)
1455                        {
1456                            return BlockPoint(output_start);
1457                        }
1458                    }
1459                    None => {
1460                        let input_point = if point.row >= output_end_row.0 {
1461                            let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1462                            self.wrap_snapshot
1463                                .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1464                        } else {
1465                            let output_overshoot = point.0.saturating_sub(output_start);
1466                            self.wrap_snapshot
1467                                .clip_point(WrapPoint(input_start + output_overshoot), bias)
1468                        };
1469
1470                        if (input_start..input_end).contains(&input_point.0) {
1471                            let input_overshoot = input_point.0.saturating_sub(input_start);
1472                            return BlockPoint(output_start + input_overshoot);
1473                        }
1474                    }
1475                    _ => {}
1476                }
1477
1478                if search_left {
1479                    cursor.prev(&());
1480                } else {
1481                    cursor.next(&());
1482                }
1483            } else if reversed {
1484                return self.max_point();
1485            } else {
1486                reversed = true;
1487                search_left = !search_left;
1488                cursor.seek(&BlockRow(point.row), Bias::Right, &());
1489            }
1490        }
1491    }
1492
1493    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1494        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>(&());
1495        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1496        if let Some(transform) = cursor.item() {
1497            if transform.block.is_some() {
1498                BlockPoint::new(cursor.start().1 .0, 0)
1499            } else {
1500                let (input_start_row, output_start_row) = cursor.start();
1501                let input_start = Point::new(input_start_row.0, 0);
1502                let output_start = Point::new(output_start_row.0, 0);
1503                let input_overshoot = wrap_point.0 - input_start;
1504                BlockPoint(output_start + input_overshoot)
1505            }
1506        } else {
1507            self.max_point()
1508        }
1509    }
1510
1511    pub fn to_wrap_point(&self, block_point: BlockPoint, bias: Bias) -> WrapPoint {
1512        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>(&());
1513        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
1514        if let Some(transform) = cursor.item() {
1515            match transform.block.as_ref() {
1516                Some(block) => {
1517                    if block.place_below() {
1518                        let wrap_row = cursor.start().1 .0 - 1;
1519                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1520                    } else if block.place_above() {
1521                        WrapPoint::new(cursor.start().1 .0, 0)
1522                    } else if bias == Bias::Left {
1523                        WrapPoint::new(cursor.start().1 .0, 0)
1524                    } else {
1525                        let wrap_row = cursor.end(&()).1 .0 - 1;
1526                        WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1527                    }
1528                }
1529                None => {
1530                    let overshoot = block_point.row - cursor.start().0 .0;
1531                    let wrap_row = cursor.start().1 .0 + overshoot;
1532                    WrapPoint::new(wrap_row, block_point.column)
1533                }
1534            }
1535        } else {
1536            self.wrap_snapshot.max_point()
1537        }
1538    }
1539}
1540
1541impl<'a> BlockChunks<'a> {
1542    /// Go to the next transform
1543    fn advance(&mut self) {
1544        self.transforms.next(&());
1545        while let Some(transform) = self.transforms.item() {
1546            if transform
1547                .block
1548                .as_ref()
1549                .map_or(false, |block| block.height() == 0)
1550            {
1551                self.transforms.next(&());
1552            } else {
1553                break;
1554            }
1555        }
1556
1557        if self
1558            .transforms
1559            .item()
1560            .map_or(false, |transform| transform.block.is_none())
1561        {
1562            let start_input_row = self.transforms.start().1 .0;
1563            let start_output_row = self.transforms.start().0 .0;
1564            if start_output_row < self.max_output_row {
1565                let end_input_row = cmp::min(
1566                    self.transforms.end(&()).1 .0,
1567                    start_input_row + (self.max_output_row - start_output_row),
1568                );
1569                self.input_chunks.seek(start_input_row..end_input_row);
1570            }
1571            self.input_chunk = Chunk::default();
1572        }
1573    }
1574}
1575
1576impl<'a> Iterator for BlockChunks<'a> {
1577    type Item = Chunk<'a>;
1578
1579    fn next(&mut self) -> Option<Self::Item> {
1580        if self.output_row >= self.max_output_row {
1581            return None;
1582        }
1583
1584        let transform = self.transforms.item()?;
1585        if transform.block.is_some() {
1586            let block_start = self.transforms.start().0 .0;
1587            let mut block_end = self.transforms.end(&()).0 .0;
1588            self.advance();
1589            if self.transforms.item().is_none() {
1590                block_end -= 1;
1591            }
1592
1593            let start_in_block = self.output_row - block_start;
1594            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1595            let line_count = end_in_block - start_in_block;
1596            self.output_row += line_count;
1597
1598            return Some(Chunk {
1599                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1600                ..Default::default()
1601            });
1602        }
1603
1604        if self.input_chunk.text.is_empty() {
1605            if let Some(input_chunk) = self.input_chunks.next() {
1606                self.input_chunk = input_chunk;
1607            } else {
1608                if self.output_row < self.max_output_row {
1609                    self.output_row += 1;
1610                    self.advance();
1611                    if self.transforms.item().is_some() {
1612                        return Some(Chunk {
1613                            text: "\n",
1614                            ..Default::default()
1615                        });
1616                    }
1617                }
1618                return None;
1619            }
1620        }
1621
1622        let transform_end = self.transforms.end(&()).0 .0;
1623        let (prefix_rows, prefix_bytes) =
1624            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1625        self.output_row += prefix_rows;
1626
1627        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1628        self.input_chunk.text = suffix;
1629        if self.output_row == transform_end {
1630            self.advance();
1631        }
1632
1633        if self.masked {
1634            // Not great for multibyte text because to keep cursor math correct we
1635            // need to have the same number of bytes in the input as output.
1636            let chars = prefix.chars().count();
1637            let bullet_len = chars;
1638            prefix = &BULLETS[..bullet_len];
1639        }
1640
1641        Some(Chunk {
1642            text: prefix,
1643            ..self.input_chunk.clone()
1644        })
1645    }
1646}
1647
1648impl<'a> Iterator for BlockBufferRows<'a> {
1649    type Item = Option<u32>;
1650
1651    fn next(&mut self) -> Option<Self::Item> {
1652        if self.started {
1653            self.output_row.0 += 1;
1654        } else {
1655            self.started = true;
1656        }
1657
1658        if self.output_row.0 >= self.transforms.end(&()).0 .0 {
1659            self.transforms.next(&());
1660            while let Some(transform) = self.transforms.item() {
1661                if transform
1662                    .block
1663                    .as_ref()
1664                    .map_or(false, |block| block.height() == 0)
1665                {
1666                    self.transforms.next(&());
1667                } else {
1668                    break;
1669                }
1670            }
1671
1672            let transform = self.transforms.item()?;
1673            if transform
1674                .block
1675                .as_ref()
1676                .map_or(true, |block| block.is_replacement())
1677            {
1678                self.input_buffer_rows.seek(self.transforms.start().1 .0);
1679            }
1680        }
1681
1682        let transform = self.transforms.item()?;
1683        if let Some(block) = transform.block.as_ref() {
1684            if block.is_replacement() && self.transforms.start().0 == self.output_row {
1685                Some(self.input_buffer_rows.next().unwrap())
1686            } else {
1687                Some(None)
1688            }
1689        } else {
1690            Some(self.input_buffer_rows.next().unwrap())
1691        }
1692    }
1693}
1694
1695impl sum_tree::Item for Transform {
1696    type Summary = TransformSummary;
1697
1698    fn summary(&self, _cx: &()) -> Self::Summary {
1699        self.summary.clone()
1700    }
1701}
1702
1703impl sum_tree::Summary for TransformSummary {
1704    type Context = ();
1705
1706    fn zero(_cx: &()) -> Self {
1707        Default::default()
1708    }
1709
1710    fn add_summary(&mut self, summary: &Self, _: &()) {
1711        if summary.longest_row_chars > self.longest_row_chars {
1712            self.longest_row = self.output_rows + summary.longest_row;
1713            self.longest_row_chars = summary.longest_row_chars;
1714        }
1715        self.input_rows += summary.input_rows;
1716        self.output_rows += summary.output_rows;
1717    }
1718}
1719
1720impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1721    fn zero(_cx: &()) -> Self {
1722        Default::default()
1723    }
1724
1725    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1726        self.0 += summary.input_rows;
1727    }
1728}
1729
1730impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1731    fn zero(_cx: &()) -> Self {
1732        Default::default()
1733    }
1734
1735    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1736        self.0 += summary.output_rows;
1737    }
1738}
1739
1740impl<'a> Deref for BlockContext<'a, '_> {
1741    type Target = WindowContext<'a>;
1742
1743    fn deref(&self) -> &Self::Target {
1744        self.context
1745    }
1746}
1747
1748impl DerefMut for BlockContext<'_, '_> {
1749    fn deref_mut(&mut self) -> &mut Self::Target {
1750        self.context
1751    }
1752}
1753
1754impl CustomBlock {
1755    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1756        self.render.lock()(cx)
1757    }
1758
1759    pub fn start(&self) -> Anchor {
1760        *self.placement.start()
1761    }
1762
1763    pub fn end(&self) -> Anchor {
1764        *self.placement.end()
1765    }
1766
1767    pub fn style(&self) -> BlockStyle {
1768        self.style
1769    }
1770}
1771
1772impl Debug for CustomBlock {
1773    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1774        f.debug_struct("Block")
1775            .field("id", &self.id)
1776            .field("placement", &self.placement)
1777            .field("height", &self.height)
1778            .field("style", &self.style)
1779            .field("priority", &self.priority)
1780            .finish_non_exhaustive()
1781    }
1782}
1783
1784// Count the number of bytes prior to a target point. If the string doesn't contain the target
1785// point, return its total extent. Otherwise return the target point itself.
1786fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1787    let mut row = 0;
1788    let mut offset = 0;
1789    for (ix, line) in s.split('\n').enumerate() {
1790        if ix > 0 {
1791            row += 1;
1792            offset += 1;
1793        }
1794        if row >= target {
1795            break;
1796        }
1797        offset += line.len();
1798    }
1799    (row, offset)
1800}
1801
1802#[cfg(test)]
1803mod tests {
1804    use super::*;
1805    use crate::display_map::{
1806        fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
1807    };
1808    use gpui::{div, font, px, AppContext, Context as _, Element};
1809    use language::{Buffer, Capability};
1810    use multi_buffer::{ExcerptRange, MultiBuffer};
1811    use rand::prelude::*;
1812    use settings::SettingsStore;
1813    use std::env;
1814    use util::RandomCharIter;
1815
1816    #[gpui::test]
1817    fn test_offset_for_row() {
1818        assert_eq!(offset_for_row("", 0), (0, 0));
1819        assert_eq!(offset_for_row("", 1), (0, 0));
1820        assert_eq!(offset_for_row("abcd", 0), (0, 0));
1821        assert_eq!(offset_for_row("abcd", 1), (0, 4));
1822        assert_eq!(offset_for_row("\n", 0), (0, 0));
1823        assert_eq!(offset_for_row("\n", 1), (1, 1));
1824        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1825        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1826        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1827        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1828    }
1829
1830    #[gpui::test]
1831    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1832        cx.update(init_test);
1833
1834        let text = "aaa\nbbb\nccc\nddd";
1835
1836        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1837        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1838        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1839        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1840        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1841        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1842        let (wrap_map, wraps_snapshot) =
1843            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1844        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1845
1846        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1847        let block_ids = writer.insert(vec![
1848            BlockProperties {
1849                style: BlockStyle::Fixed,
1850                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
1851                height: 1,
1852                render: Arc::new(|_| div().into_any()),
1853                priority: 0,
1854            },
1855            BlockProperties {
1856                style: BlockStyle::Fixed,
1857                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
1858                height: 2,
1859                render: Arc::new(|_| div().into_any()),
1860                priority: 0,
1861            },
1862            BlockProperties {
1863                style: BlockStyle::Fixed,
1864                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
1865                height: 3,
1866                render: Arc::new(|_| div().into_any()),
1867                priority: 0,
1868            },
1869        ]);
1870
1871        let snapshot = block_map.read(wraps_snapshot, Default::default());
1872        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1873
1874        let blocks = snapshot
1875            .blocks_in_range(0..8)
1876            .map(|(start_row, block)| {
1877                let block = block.as_custom().unwrap();
1878                (start_row..start_row + block.height, block.id)
1879            })
1880            .collect::<Vec<_>>();
1881
1882        // When multiple blocks are on the same line, the newer blocks appear first.
1883        assert_eq!(
1884            blocks,
1885            &[
1886                (1..2, block_ids[0]),
1887                (2..4, block_ids[1]),
1888                (7..10, block_ids[2]),
1889            ]
1890        );
1891
1892        assert_eq!(
1893            snapshot.to_block_point(WrapPoint::new(0, 3)),
1894            BlockPoint::new(0, 3)
1895        );
1896        assert_eq!(
1897            snapshot.to_block_point(WrapPoint::new(1, 0)),
1898            BlockPoint::new(4, 0)
1899        );
1900        assert_eq!(
1901            snapshot.to_block_point(WrapPoint::new(3, 3)),
1902            BlockPoint::new(6, 3)
1903        );
1904
1905        assert_eq!(
1906            snapshot.to_wrap_point(BlockPoint::new(0, 3), Bias::Left),
1907            WrapPoint::new(0, 3)
1908        );
1909        assert_eq!(
1910            snapshot.to_wrap_point(BlockPoint::new(1, 0), Bias::Left),
1911            WrapPoint::new(1, 0)
1912        );
1913        assert_eq!(
1914            snapshot.to_wrap_point(BlockPoint::new(3, 0), Bias::Left),
1915            WrapPoint::new(1, 0)
1916        );
1917        assert_eq!(
1918            snapshot.to_wrap_point(BlockPoint::new(7, 0), Bias::Left),
1919            WrapPoint::new(3, 3)
1920        );
1921
1922        assert_eq!(
1923            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1924            BlockPoint::new(0, 3)
1925        );
1926        assert_eq!(
1927            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1928            BlockPoint::new(4, 0)
1929        );
1930        assert_eq!(
1931            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1932            BlockPoint::new(0, 3)
1933        );
1934        assert_eq!(
1935            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1936            BlockPoint::new(4, 0)
1937        );
1938        assert_eq!(
1939            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1940            BlockPoint::new(4, 0)
1941        );
1942        assert_eq!(
1943            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1944            BlockPoint::new(4, 0)
1945        );
1946        assert_eq!(
1947            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1948            BlockPoint::new(6, 3)
1949        );
1950        assert_eq!(
1951            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1952            BlockPoint::new(6, 3)
1953        );
1954        assert_eq!(
1955            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1956            BlockPoint::new(6, 3)
1957        );
1958        assert_eq!(
1959            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1960            BlockPoint::new(6, 3)
1961        );
1962
1963        assert_eq!(
1964            snapshot.buffer_rows(BlockRow(0)).collect::<Vec<_>>(),
1965            &[
1966                Some(0),
1967                None,
1968                None,
1969                None,
1970                Some(1),
1971                Some(2),
1972                Some(3),
1973                None,
1974                None,
1975                None
1976            ]
1977        );
1978
1979        // Insert a line break, separating two block decorations into separate lines.
1980        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1981            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1982            buffer.snapshot(cx)
1983        });
1984
1985        let (inlay_snapshot, inlay_edits) =
1986            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1987        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1988        let (tab_snapshot, tab_edits) =
1989            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1990        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1991            wrap_map.sync(tab_snapshot, tab_edits, cx)
1992        });
1993        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1994        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1995    }
1996
1997    #[gpui::test]
1998    fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
1999        init_test(cx);
2000
2001        let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
2002        let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
2003        let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
2004
2005        let mut excerpt_ids = Vec::new();
2006        let multi_buffer = cx.new_model(|cx| {
2007            let mut multi_buffer = MultiBuffer::new(Capability::ReadWrite);
2008            excerpt_ids.extend(multi_buffer.push_excerpts(
2009                buffer1.clone(),
2010                [ExcerptRange {
2011                    context: 0..buffer1.read(cx).len(),
2012                    primary: None,
2013                }],
2014                cx,
2015            ));
2016            excerpt_ids.extend(multi_buffer.push_excerpts(
2017                buffer2.clone(),
2018                [ExcerptRange {
2019                    context: 0..buffer2.read(cx).len(),
2020                    primary: None,
2021                }],
2022                cx,
2023            ));
2024            excerpt_ids.extend(multi_buffer.push_excerpts(
2025                buffer3.clone(),
2026                [ExcerptRange {
2027                    context: 0..buffer3.read(cx).len(),
2028                    primary: None,
2029                }],
2030                cx,
2031            ));
2032
2033            multi_buffer
2034        });
2035
2036        let font = font("Helvetica");
2037        let font_size = px(14.);
2038        let font_id = cx.text_system().resolve_font(&font);
2039        let mut wrap_width = px(0.);
2040        for c in "Buff".chars() {
2041            wrap_width += cx
2042                .text_system()
2043                .advance(font_id, font_size, c)
2044                .unwrap()
2045                .width;
2046        }
2047
2048        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
2049        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
2050        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2051        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2052        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
2053
2054        let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
2055        let snapshot = block_map.read(wraps_snapshot, Default::default());
2056
2057        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
2058        assert_eq!(
2059            snapshot.text(),
2060            "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
2061        );
2062
2063        let blocks: Vec<_> = snapshot
2064            .blocks_in_range(0..u32::MAX)
2065            .map(|(row, block)| (row..row + block.height(), block.id()))
2066            .collect();
2067        assert_eq!(
2068            blocks,
2069            vec![
2070                (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
2071                (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
2072                (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
2073                (14..15, BlockId::ExcerptBoundary(None)),               // footer
2074            ]
2075        );
2076    }
2077
2078    #[gpui::test]
2079    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
2080        cx.update(init_test);
2081
2082        let text = "aaa\nbbb\nccc\nddd";
2083
2084        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2085        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2086        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
2087        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2088        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2089        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
2090        let (_wrap_map, wraps_snapshot) =
2091            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2092        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2093
2094        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2095        let block_ids = writer.insert(vec![
2096            BlockProperties {
2097                style: BlockStyle::Fixed,
2098                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 0))),
2099                height: 1,
2100                render: Arc::new(|_| div().into_any()),
2101                priority: 0,
2102            },
2103            BlockProperties {
2104                style: BlockStyle::Fixed,
2105                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 2))),
2106                height: 2,
2107                render: Arc::new(|_| div().into_any()),
2108                priority: 0,
2109            },
2110            BlockProperties {
2111                style: BlockStyle::Fixed,
2112                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(3, 3))),
2113                height: 3,
2114                render: Arc::new(|_| div().into_any()),
2115                priority: 0,
2116            },
2117        ]);
2118
2119        {
2120            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2121            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2122
2123            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2124
2125            let mut new_heights = HashMap::default();
2126            new_heights.insert(block_ids[0], 2);
2127            block_map_writer.resize(new_heights);
2128            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2129            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2130        }
2131
2132        {
2133            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2134
2135            let mut new_heights = HashMap::default();
2136            new_heights.insert(block_ids[0], 1);
2137            block_map_writer.resize(new_heights);
2138
2139            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2140            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
2141        }
2142
2143        {
2144            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2145
2146            let mut new_heights = HashMap::default();
2147            new_heights.insert(block_ids[0], 0);
2148            block_map_writer.resize(new_heights);
2149
2150            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2151            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
2152        }
2153
2154        {
2155            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2156
2157            let mut new_heights = HashMap::default();
2158            new_heights.insert(block_ids[0], 3);
2159            block_map_writer.resize(new_heights);
2160
2161            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2162            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2163        }
2164
2165        {
2166            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
2167
2168            let mut new_heights = HashMap::default();
2169            new_heights.insert(block_ids[0], 3);
2170            block_map_writer.resize(new_heights);
2171
2172            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2173            // Same height as before, should remain the same
2174            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
2175        }
2176    }
2177
2178    #[cfg(target_os = "macos")]
2179    #[gpui::test]
2180    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
2181        cx.update(init_test);
2182
2183        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
2184
2185        let text = "one two three\nfour five six\nseven eight";
2186
2187        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2188        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2189        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2190        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
2191        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2192        let (_, wraps_snapshot) = cx.update(|cx| {
2193            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
2194        });
2195        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
2196
2197        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2198        writer.insert(vec![
2199            BlockProperties {
2200                style: BlockStyle::Fixed,
2201                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 12))),
2202                render: Arc::new(|_| div().into_any()),
2203                height: 1,
2204                priority: 0,
2205            },
2206            BlockProperties {
2207                style: BlockStyle::Fixed,
2208                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 1))),
2209                render: Arc::new(|_| div().into_any()),
2210                height: 1,
2211                priority: 0,
2212            },
2213        ]);
2214
2215        // Blocks with an 'above' disposition go above their corresponding buffer line.
2216        // Blocks with a 'below' disposition go below their corresponding buffer line.
2217        let snapshot = block_map.read(wraps_snapshot, Default::default());
2218        assert_eq!(
2219            snapshot.text(),
2220            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
2221        );
2222    }
2223
2224    #[gpui::test]
2225    fn test_replace_lines(cx: &mut gpui::TestAppContext) {
2226        cx.update(init_test);
2227
2228        let text = "line1\nline2\nline3\nline4\nline5";
2229
2230        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
2231        let buffer_subscription = buffer.update(cx, |buffer, _cx| buffer.subscribe());
2232        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2233        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2234        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2235        let tab_size = 1.try_into().unwrap();
2236        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, tab_size);
2237        let (wrap_map, wraps_snapshot) =
2238            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
2239        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
2240
2241        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2242        writer.insert(vec![BlockProperties {
2243            style: BlockStyle::Fixed,
2244            placement: BlockPlacement::Replace(
2245                buffer_snapshot.anchor_after(Point::new(1, 3))
2246                    ..buffer_snapshot.anchor_before(Point::new(3, 1)),
2247            ),
2248            height: 4,
2249            render: Arc::new(|_| div().into_any()),
2250            priority: 0,
2251        }]);
2252
2253        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2254        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2255
2256        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2257            buffer.edit([(Point::new(2, 0)..Point::new(3, 0), "")], None, cx);
2258            buffer.snapshot(cx)
2259        });
2260        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2261            buffer_snapshot.clone(),
2262            buffer_subscription.consume().into_inner(),
2263        );
2264        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2265        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2266        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2267            wrap_map.sync(tab_snapshot, tab_edits, cx)
2268        });
2269        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2270        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2271
2272        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
2273            buffer.edit(
2274                [(
2275                    Point::new(1, 5)..Point::new(1, 5),
2276                    "\nline 6\nline7\nline 8\nline 9",
2277                )],
2278                None,
2279                cx,
2280            );
2281            buffer.snapshot(cx)
2282        });
2283        let (inlay_snapshot, inlay_edits) = inlay_map.sync(
2284            buffer_snapshot.clone(),
2285            buffer_subscription.consume().into_inner(),
2286        );
2287        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2288        let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2289        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2290            wrap_map.sync(tab_snapshot, tab_edits, cx)
2291        });
2292        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2293        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\nline5");
2294
2295        // Ensure blocks inserted above the start or below the end of the replaced region are shown.
2296        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2297        writer.insert(vec![
2298            BlockProperties {
2299                style: BlockStyle::Fixed,
2300                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(1, 3))),
2301                height: 1,
2302                render: Arc::new(|_| div().into_any()),
2303                priority: 0,
2304            },
2305            BlockProperties {
2306                style: BlockStyle::Fixed,
2307                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(6, 2))),
2308                height: 1,
2309                render: Arc::new(|_| div().into_any()),
2310                priority: 0,
2311            },
2312        ]);
2313        let blocks_snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
2314        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2315
2316        // Ensure blocks inserted *inside* replaced region are hidden.
2317        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
2318        writer.insert(vec![
2319            BlockProperties {
2320                style: BlockStyle::Fixed,
2321                placement: BlockPlacement::Below(buffer_snapshot.anchor_after(Point::new(1, 3))),
2322                height: 1,
2323                render: Arc::new(|_| div().into_any()),
2324                priority: 0,
2325            },
2326            BlockProperties {
2327                style: BlockStyle::Fixed,
2328                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(2, 1))),
2329                height: 1,
2330                render: Arc::new(|_| div().into_any()),
2331                priority: 0,
2332            },
2333            BlockProperties {
2334                style: BlockStyle::Fixed,
2335                placement: BlockPlacement::Above(buffer_snapshot.anchor_after(Point::new(6, 1))),
2336                height: 1,
2337                render: Arc::new(|_| div().into_any()),
2338                priority: 0,
2339            },
2340        ]);
2341        let blocks_snapshot = block_map.read(wraps_snapshot, Default::default());
2342        assert_eq!(blocks_snapshot.text(), "line1\n\n\n\n\n\n\nline5");
2343    }
2344
2345    #[gpui::test(iterations = 100)]
2346    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2347        cx.update(init_test);
2348
2349        let operations = env::var("OPERATIONS")
2350            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2351            .unwrap_or(10);
2352
2353        let wrap_width = if rng.gen_bool(0.2) {
2354            None
2355        } else {
2356            Some(px(rng.gen_range(0.0..=100.0)))
2357        };
2358        let tab_size = 1.try_into().unwrap();
2359        let font_size = px(14.0);
2360        let buffer_start_header_height = rng.gen_range(1..=5);
2361        let excerpt_header_height = rng.gen_range(1..=5);
2362        let excerpt_footer_height = rng.gen_range(1..=5);
2363
2364        log::info!("Wrap width: {:?}", wrap_width);
2365        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
2366        log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
2367        let is_singleton = rng.gen();
2368        let buffer = if is_singleton {
2369            let len = rng.gen_range(0..10);
2370            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
2371            log::info!("initial singleton buffer text: {:?}", text);
2372            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
2373        } else {
2374            cx.update(|cx| {
2375                let multibuffer = MultiBuffer::build_random(&mut rng, cx);
2376                log::info!(
2377                    "initial multi-buffer text: {:?}",
2378                    multibuffer.read(cx).read(cx).text()
2379                );
2380                multibuffer
2381            })
2382        };
2383
2384        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
2385        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
2386        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
2387        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
2388        let (wrap_map, wraps_snapshot) = cx
2389            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
2390        let mut block_map = BlockMap::new(
2391            wraps_snapshot,
2392            true,
2393            buffer_start_header_height,
2394            excerpt_header_height,
2395            excerpt_footer_height,
2396        );
2397
2398        for _ in 0..operations {
2399            let mut buffer_edits = Vec::new();
2400            match rng.gen_range(0..=100) {
2401                0..=19 => {
2402                    let wrap_width = if rng.gen_bool(0.2) {
2403                        None
2404                    } else {
2405                        Some(px(rng.gen_range(0.0..=100.0)))
2406                    };
2407                    log::info!("Setting wrap width to {:?}", wrap_width);
2408                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2409                }
2410                20..=39 => {
2411                    let block_count = rng.gen_range(1..=5);
2412                    let block_properties = (0..block_count)
2413                        .map(|_| {
2414                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
2415                            let offset =
2416                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left);
2417                            let mut min_height = 0;
2418                            let placement = match rng.gen_range(0..3) {
2419                                0 => {
2420                                    min_height = 1;
2421                                    let start = buffer.anchor_after(offset);
2422                                    let end = buffer.anchor_after(buffer.clip_offset(
2423                                        rng.gen_range(offset..=buffer.len()),
2424                                        Bias::Left,
2425                                    ));
2426                                    BlockPlacement::Replace(start..end)
2427                                }
2428                                1 => BlockPlacement::Above(buffer.anchor_after(offset)),
2429                                _ => BlockPlacement::Below(buffer.anchor_after(offset)),
2430                            };
2431
2432                            let height = rng.gen_range(min_height..5);
2433                            log::info!(
2434                                "inserting block {:?} with height {}",
2435                                placement.as_ref().map(|p| p.to_point(&buffer)),
2436                                height
2437                            );
2438                            BlockProperties {
2439                                style: BlockStyle::Fixed,
2440                                placement,
2441                                height,
2442                                render: Arc::new(|_| div().into_any()),
2443                                priority: 0,
2444                            }
2445                        })
2446                        .collect::<Vec<_>>();
2447
2448                    let (inlay_snapshot, inlay_edits) =
2449                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
2450                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2451                    let (tab_snapshot, tab_edits) =
2452                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
2453                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2454                        wrap_map.sync(tab_snapshot, tab_edits, cx)
2455                    });
2456                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2457                    block_map.insert(block_properties.iter().map(|props| BlockProperties {
2458                        placement: props.placement.clone(),
2459                        height: props.height,
2460                        style: props.style,
2461                        render: Arc::new(|_| div().into_any()),
2462                        priority: 0,
2463                    }));
2464                }
2465                40..=59 if !block_map.custom_blocks.is_empty() => {
2466                    let block_count = rng.gen_range(1..=4.min(block_map.custom_blocks.len()));
2467                    let block_ids_to_remove = block_map
2468                        .custom_blocks
2469                        .choose_multiple(&mut rng, block_count)
2470                        .map(|block| block.id)
2471                        .collect::<HashSet<_>>();
2472
2473                    let (inlay_snapshot, inlay_edits) =
2474                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
2475                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2476                    let (tab_snapshot, tab_edits) =
2477                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
2478                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2479                        wrap_map.sync(tab_snapshot, tab_edits, cx)
2480                    });
2481                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
2482                    block_map.remove(block_ids_to_remove);
2483                }
2484                _ => {
2485                    buffer.update(cx, |buffer, cx| {
2486                        let mutation_count = rng.gen_range(1..=5);
2487                        let subscription = buffer.subscribe();
2488                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
2489                        buffer_snapshot = buffer.snapshot(cx);
2490                        buffer_edits.extend(subscription.consume());
2491                        log::info!("buffer text: {:?}", buffer_snapshot.text());
2492                    });
2493                }
2494            }
2495
2496            let (inlay_snapshot, inlay_edits) =
2497                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
2498            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2499            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2500            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2501                wrap_map.sync(tab_snapshot, tab_edits, cx)
2502            });
2503            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2504            assert_eq!(
2505                blocks_snapshot.transforms.summary().input_rows,
2506                wraps_snapshot.max_point().row() + 1
2507            );
2508            log::info!("wrapped text: {:?}", wraps_snapshot.text());
2509            log::info!("blocks text: {:?}", blocks_snapshot.text());
2510
2511            let mut expected_blocks = Vec::new();
2512            expected_blocks.extend(block_map.custom_blocks.iter().filter_map(|block| {
2513                Some((
2514                    block.placement.to_wrap_row(&wraps_snapshot)?,
2515                    Block::Custom(block.clone()),
2516                ))
2517            }));
2518
2519            // Note that this needs to be synced with the related section in BlockMap::sync
2520            expected_blocks.extend(BlockMap::header_and_footer_blocks(
2521                true,
2522                excerpt_footer_height,
2523                buffer_start_header_height,
2524                excerpt_header_height,
2525                &buffer_snapshot,
2526                0..,
2527                &wraps_snapshot,
2528            ));
2529
2530            BlockMap::sort_blocks(&mut expected_blocks);
2531
2532            for (placement, block) in &expected_blocks {
2533                log::info!(
2534                    "Block {:?} placement: {:?} Height: {:?}",
2535                    block.id(),
2536                    placement,
2537                    block.height()
2538                );
2539            }
2540
2541            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
2542
2543            let input_buffer_rows = buffer_snapshot
2544                .buffer_rows(MultiBufferRow(0))
2545                .collect::<Vec<_>>();
2546            let mut expected_buffer_rows = Vec::new();
2547            let mut expected_text = String::new();
2548            let mut expected_block_positions = Vec::new();
2549            let mut expected_replaced_buffer_rows = HashSet::default();
2550            let input_text = wraps_snapshot.text();
2551
2552            // Loop over the input lines, creating (N - 1) empty lines for
2553            // blocks of height N.
2554            //
2555            // It's important to note that output *starts* as one empty line,
2556            // so we special case row 0 to assume a leading '\n'.
2557            //
2558            // Linehood is the birthright of strings.
2559            let mut input_text_lines = input_text.split('\n').enumerate().peekable();
2560            let mut block_row = 0;
2561            while let Some((wrap_row, input_line)) = input_text_lines.next() {
2562                let wrap_row = wrap_row as u32;
2563                let multibuffer_row = wraps_snapshot
2564                    .to_point(WrapPoint::new(wrap_row, 0), Bias::Left)
2565                    .row;
2566
2567                // Create empty lines for the above block
2568                while let Some((placement, block)) = sorted_blocks_iter.peek() {
2569                    if placement.start().0 == wrap_row && block.place_above() {
2570                        let (_, block) = sorted_blocks_iter.next().unwrap();
2571                        expected_block_positions.push((block_row, block.id()));
2572                        if block.height() > 0 {
2573                            let text = "\n".repeat((block.height() - 1) as usize);
2574                            if block_row > 0 {
2575                                expected_text.push('\n')
2576                            }
2577                            expected_text.push_str(&text);
2578                            for _ in 0..block.height() {
2579                                expected_buffer_rows.push(None);
2580                            }
2581                            block_row += block.height();
2582                        }
2583                    } else {
2584                        break;
2585                    }
2586                }
2587
2588                // Skip lines within replace blocks, then create empty lines for the replace block's height
2589                let mut is_in_replace_block = false;
2590                if let Some((BlockPlacement::Replace(replace_range), block)) =
2591                    sorted_blocks_iter.peek()
2592                {
2593                    if wrap_row >= replace_range.start.0 {
2594                        is_in_replace_block = true;
2595
2596                        if wrap_row == replace_range.start.0 {
2597                            expected_buffer_rows.push(input_buffer_rows[multibuffer_row as usize]);
2598                        }
2599
2600                        if wrap_row == replace_range.end.0 {
2601                            expected_block_positions.push((block_row, block.id()));
2602                            let text = "\n".repeat((block.height() - 1) as usize);
2603                            if block_row > 0 {
2604                                expected_text.push('\n');
2605                            }
2606                            expected_text.push_str(&text);
2607
2608                            for _ in 1..block.height() {
2609                                expected_buffer_rows.push(None);
2610                            }
2611                            block_row += block.height();
2612
2613                            sorted_blocks_iter.next();
2614                        }
2615                    }
2616                }
2617
2618                if is_in_replace_block {
2619                    expected_replaced_buffer_rows.insert(MultiBufferRow(multibuffer_row));
2620                } else {
2621                    let buffer_row = input_buffer_rows[multibuffer_row as usize];
2622                    let soft_wrapped = wraps_snapshot
2623                        .to_tab_point(WrapPoint::new(wrap_row, 0))
2624                        .column()
2625                        > 0;
2626                    expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
2627                    if block_row > 0 {
2628                        expected_text.push('\n');
2629                    }
2630                    expected_text.push_str(input_line);
2631                    block_row += 1;
2632                }
2633
2634                while let Some((placement, block)) = sorted_blocks_iter.peek() {
2635                    if placement.end().0 == wrap_row && block.place_below() {
2636                        let (_, block) = sorted_blocks_iter.next().unwrap();
2637                        expected_block_positions.push((block_row, block.id()));
2638                        if block.height() > 0 {
2639                            let text = "\n".repeat((block.height() - 1) as usize);
2640                            if block_row > 0 {
2641                                expected_text.push('\n')
2642                            }
2643                            expected_text.push_str(&text);
2644                            for _ in 0..block.height() {
2645                                expected_buffer_rows.push(None);
2646                            }
2647                            block_row += block.height();
2648                        }
2649                    } else {
2650                        break;
2651                    }
2652                }
2653            }
2654
2655            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
2656            let expected_row_count = expected_lines.len();
2657
2658            assert_eq!(
2659                blocks_snapshot.max_point().row + 1,
2660                expected_row_count as u32
2661            );
2662
2663            log::info!("expected text: {:?}", expected_text);
2664
2665            for start_row in 0..expected_row_count {
2666                let end_row = rng.gen_range(start_row + 1..=expected_row_count);
2667                let mut expected_text = expected_lines[start_row..end_row].join("\n");
2668                if end_row < expected_row_count {
2669                    expected_text.push('\n');
2670                }
2671
2672                let actual_text = blocks_snapshot
2673                    .chunks(
2674                        start_row as u32..end_row as u32,
2675                        false,
2676                        false,
2677                        Highlights::default(),
2678                    )
2679                    .map(|chunk| chunk.text)
2680                    .collect::<String>();
2681                assert_eq!(
2682                    actual_text,
2683                    expected_text,
2684                    "incorrect text starting row row range {:?}",
2685                    start_row..end_row
2686                );
2687                assert_eq!(
2688                    blocks_snapshot
2689                        .buffer_rows(BlockRow(start_row as u32))
2690                        .collect::<Vec<_>>(),
2691                    &expected_buffer_rows[start_row..],
2692                    "incorrect buffer_rows starting at row {:?}",
2693                    start_row
2694                );
2695            }
2696
2697            assert_eq!(
2698                blocks_snapshot
2699                    .blocks_in_range(0..(expected_row_count as u32))
2700                    .map(|(row, block)| (row, block.id()))
2701                    .collect::<Vec<_>>(),
2702                expected_block_positions,
2703                "invalid blocks_in_range({:?})",
2704                0..expected_row_count
2705            );
2706
2707            for (_, expected_block) in
2708                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
2709            {
2710                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
2711                assert_eq!(
2712                    actual_block.map(|block| block.id()),
2713                    Some(expected_block.id())
2714                );
2715            }
2716
2717            for (block_row, block_id) in expected_block_positions {
2718                if let BlockId::Custom(block_id) = block_id {
2719                    assert_eq!(
2720                        blocks_snapshot.row_for_block(block_id),
2721                        Some(BlockRow(block_row))
2722                    );
2723                }
2724            }
2725
2726            let mut expected_longest_rows = Vec::new();
2727            let mut longest_line_len = -1_isize;
2728            for (row, line) in expected_lines.iter().enumerate() {
2729                let row = row as u32;
2730
2731                assert_eq!(
2732                    blocks_snapshot.line_len(BlockRow(row)),
2733                    line.len() as u32,
2734                    "invalid line len for row {}",
2735                    row
2736                );
2737
2738                let line_char_count = line.chars().count() as isize;
2739                match line_char_count.cmp(&longest_line_len) {
2740                    Ordering::Less => {}
2741                    Ordering::Equal => expected_longest_rows.push(row),
2742                    Ordering::Greater => {
2743                        longest_line_len = line_char_count;
2744                        expected_longest_rows.clear();
2745                        expected_longest_rows.push(row);
2746                    }
2747                }
2748            }
2749
2750            let longest_row = blocks_snapshot.longest_row();
2751            assert!(
2752                expected_longest_rows.contains(&longest_row),
2753                "incorrect longest row {}. expected {:?} with length {}",
2754                longest_row,
2755                expected_longest_rows,
2756                longest_line_len,
2757            );
2758
2759            for _ in 0..10 {
2760                let end_row = rng.gen_range(1..=expected_lines.len());
2761                let start_row = rng.gen_range(0..end_row);
2762
2763                let mut expected_longest_rows_in_range = vec![];
2764                let mut longest_line_len_in_range = 0;
2765
2766                let mut row = start_row as u32;
2767                for line in &expected_lines[start_row..end_row] {
2768                    let line_char_count = line.chars().count() as isize;
2769                    match line_char_count.cmp(&longest_line_len_in_range) {
2770                        Ordering::Less => {}
2771                        Ordering::Equal => expected_longest_rows_in_range.push(row),
2772                        Ordering::Greater => {
2773                            longest_line_len_in_range = line_char_count;
2774                            expected_longest_rows_in_range.clear();
2775                            expected_longest_rows_in_range.push(row);
2776                        }
2777                    }
2778                    row += 1;
2779                }
2780
2781                let longest_row_in_range = blocks_snapshot
2782                    .longest_row_in_range(BlockRow(start_row as u32)..BlockRow(end_row as u32));
2783                assert!(
2784                    expected_longest_rows_in_range.contains(&longest_row_in_range.0),
2785                    "incorrect longest row {} in range {:?}. expected {:?} with length {}",
2786                    longest_row,
2787                    start_row..end_row,
2788                    expected_longest_rows_in_range,
2789                    longest_line_len_in_range,
2790                );
2791            }
2792
2793            // Ensure that conversion between block points and wrap points is stable.
2794            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
2795                let wrap_point = WrapPoint::new(row, 0);
2796                let block_point = blocks_snapshot.to_block_point(wrap_point);
2797                let left_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Left);
2798                let right_wrap_point = blocks_snapshot.to_wrap_point(block_point, Bias::Right);
2799                assert_eq!(blocks_snapshot.to_block_point(left_wrap_point), block_point);
2800                assert_eq!(
2801                    blocks_snapshot.to_block_point(right_wrap_point),
2802                    block_point
2803                );
2804            }
2805
2806            let mut block_point = BlockPoint::new(0, 0);
2807            for c in expected_text.chars() {
2808                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
2809                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
2810                assert_eq!(
2811                    blocks_snapshot
2812                        .to_block_point(blocks_snapshot.to_wrap_point(left_point, Bias::Left)),
2813                    left_point,
2814                    "block point: {:?}, wrap point: {:?}",
2815                    block_point,
2816                    blocks_snapshot.to_wrap_point(left_point, Bias::Left)
2817                );
2818                assert_eq!(
2819                    left_buffer_point,
2820                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
2821                    "{:?} is not valid in buffer coordinates",
2822                    left_point
2823                );
2824
2825                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
2826                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
2827                assert_eq!(
2828                    blocks_snapshot
2829                        .to_block_point(blocks_snapshot.to_wrap_point(right_point, Bias::Right)),
2830                    right_point,
2831                    "block point: {:?}, wrap point: {:?}",
2832                    block_point,
2833                    blocks_snapshot.to_wrap_point(right_point, Bias::Right)
2834                );
2835                assert_eq!(
2836                    right_buffer_point,
2837                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
2838                    "{:?} is not valid in buffer coordinates",
2839                    right_point
2840                );
2841
2842                if c == '\n' {
2843                    block_point.0 += Point::new(1, 0);
2844                } else {
2845                    block_point.column += c.len_utf8() as u32;
2846                }
2847            }
2848
2849            for buffer_row in 0..=buffer_snapshot.max_point().row {
2850                let buffer_row = MultiBufferRow(buffer_row);
2851                assert_eq!(
2852                    blocks_snapshot.is_line_replaced(buffer_row),
2853                    expected_replaced_buffer_rows.contains(&buffer_row),
2854                    "incorrect is_line_replaced({:?})",
2855                    buffer_row
2856                );
2857            }
2858        }
2859    }
2860
2861    fn init_test(cx: &mut gpui::AppContext) {
2862        let settings = SettingsStore::test(cx);
2863        cx.set_global(settings);
2864        theme::init(theme::LoadThemes::JustBase, cx);
2865        assets::Assets.load_test_fonts(cx);
2866    }
2867
2868    impl Block {
2869        fn as_custom(&self) -> Option<&CustomBlock> {
2870            match self {
2871                Block::Custom(block) => Some(block),
2872                Block::ExcerptBoundary { .. } => None,
2873            }
2874        }
2875    }
2876
2877    impl BlockSnapshot {
2878        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
2879            self.wrap_snapshot
2880                .to_point(self.to_wrap_point(point, bias), bias)
2881        }
2882    }
2883}