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