block_map.rs

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