block_map.rs

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