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