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        fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, 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 tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1460        let (wrap_map, wraps_snapshot) =
1461            cx.update(|cx| WrapMap::new(tab_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 (tab_snapshot, tab_edits) =
1613            tab_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(tab_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 (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1676        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
1677
1678        let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1679        let snapshot = block_map.read(wraps_snapshot, Default::default());
1680
1681        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
1682        assert_eq!(
1683            snapshot.text(),
1684            "\n\nBuff\ner 1\n\n\n\nBuff\ner 2\n\n\n\nBuff\ner 3\n"
1685        );
1686
1687        let blocks: Vec<_> = snapshot
1688            .blocks_in_range(0..u32::MAX)
1689            .map(|(row, block)| (row..row + block.height(), block.id()))
1690            .collect();
1691        assert_eq!(
1692            blocks,
1693            vec![
1694                (0..2, BlockId::ExcerptBoundary(Some(excerpt_ids[0]))), // path, header
1695                (4..7, BlockId::ExcerptBoundary(Some(excerpt_ids[1]))), // footer, path, header
1696                (9..12, BlockId::ExcerptBoundary(Some(excerpt_ids[2]))), // footer, path, header
1697                (14..15, BlockId::ExcerptBoundary(None)),               // footer
1698            ]
1699        );
1700    }
1701
1702    #[gpui::test]
1703    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
1704        cx.update(init_test);
1705
1706        let text = "aaa\nbbb\nccc\nddd";
1707
1708        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1709        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1710        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1711        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1712        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1713        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1714        let (_wrap_map, wraps_snapshot) =
1715            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1716        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
1717
1718        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1719        let block_ids = writer.insert(vec![
1720            BlockProperties {
1721                style: BlockStyle::Fixed,
1722                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
1723                height: 1,
1724                disposition: BlockDisposition::Above,
1725                render: Box::new(|_| div().into_any()),
1726                priority: 0,
1727            },
1728            BlockProperties {
1729                style: BlockStyle::Fixed,
1730                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
1731                height: 2,
1732                disposition: BlockDisposition::Above,
1733                render: Box::new(|_| div().into_any()),
1734                priority: 0,
1735            },
1736            BlockProperties {
1737                style: BlockStyle::Fixed,
1738                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
1739                height: 3,
1740                disposition: BlockDisposition::Below,
1741                render: Box::new(|_| div().into_any()),
1742                priority: 0,
1743            },
1744        ]);
1745
1746        {
1747            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1748            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1749
1750            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1751
1752            let mut new_heights = HashMap::default();
1753            new_heights.insert(block_ids[0], 2);
1754            block_map_writer.resize(new_heights);
1755            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1756            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1757        }
1758
1759        {
1760            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1761
1762            let mut new_heights = HashMap::default();
1763            new_heights.insert(block_ids[0], 1);
1764            block_map_writer.resize(new_heights);
1765
1766            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1767            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1768        }
1769
1770        {
1771            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1772
1773            let mut new_heights = HashMap::default();
1774            new_heights.insert(block_ids[0], 0);
1775            block_map_writer.resize(new_heights);
1776
1777            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1778            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
1779        }
1780
1781        {
1782            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1783
1784            let mut new_heights = HashMap::default();
1785            new_heights.insert(block_ids[0], 3);
1786            block_map_writer.resize(new_heights);
1787
1788            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1789            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1790        }
1791
1792        {
1793            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1794
1795            let mut new_heights = HashMap::default();
1796            new_heights.insert(block_ids[0], 3);
1797            block_map_writer.resize(new_heights);
1798
1799            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1800            // Same height as before, should remain the same
1801            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1802        }
1803    }
1804
1805    #[cfg(target_os = "macos")]
1806    #[gpui::test]
1807    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
1808        cx.update(init_test);
1809
1810        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
1811
1812        let text = "one two three\nfour five six\nseven eight";
1813
1814        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1815        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1816        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1817        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
1818        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1819        let (_, wraps_snapshot) = cx.update(|cx| {
1820            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
1821        });
1822        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
1823
1824        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1825        writer.insert(vec![
1826            BlockProperties {
1827                style: BlockStyle::Fixed,
1828                position: buffer_snapshot.anchor_after(Point::new(1, 12)),
1829                disposition: BlockDisposition::Above,
1830                render: Box::new(|_| div().into_any()),
1831                height: 1,
1832                priority: 0,
1833            },
1834            BlockProperties {
1835                style: BlockStyle::Fixed,
1836                position: buffer_snapshot.anchor_after(Point::new(1, 1)),
1837                disposition: BlockDisposition::Below,
1838                render: Box::new(|_| div().into_any()),
1839                height: 1,
1840                priority: 0,
1841            },
1842        ]);
1843
1844        // Blocks with an 'above' disposition go above their corresponding buffer line.
1845        // Blocks with a 'below' disposition go below their corresponding buffer line.
1846        let snapshot = block_map.read(wraps_snapshot, Default::default());
1847        assert_eq!(
1848            snapshot.text(),
1849            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
1850        );
1851    }
1852
1853    #[gpui::test(iterations = 100)]
1854    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1855        cx.update(init_test);
1856
1857        let operations = env::var("OPERATIONS")
1858            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1859            .unwrap_or(10);
1860
1861        let wrap_width = if rng.gen_bool(0.2) {
1862            None
1863        } else {
1864            Some(px(rng.gen_range(0.0..=100.0)))
1865        };
1866        let tab_size = 1.try_into().unwrap();
1867        let font_size = px(14.0);
1868        let buffer_start_header_height = rng.gen_range(1..=5);
1869        let excerpt_header_height = rng.gen_range(1..=5);
1870        let excerpt_footer_height = rng.gen_range(1..=5);
1871
1872        log::info!("Wrap width: {:?}", wrap_width);
1873        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
1874        log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
1875
1876        let buffer = if rng.gen() {
1877            let len = rng.gen_range(0..10);
1878            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1879            log::info!("initial buffer text: {:?}", text);
1880            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
1881        } else {
1882            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
1883        };
1884
1885        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1886        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1887        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1888        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1889        let (wrap_map, wraps_snapshot) = cx
1890            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
1891        let mut block_map = BlockMap::new(
1892            wraps_snapshot,
1893            true,
1894            buffer_start_header_height,
1895            excerpt_header_height,
1896            excerpt_footer_height,
1897        );
1898        let mut custom_blocks = Vec::new();
1899
1900        for _ in 0..operations {
1901            let mut buffer_edits = Vec::new();
1902            match rng.gen_range(0..=100) {
1903                0..=19 => {
1904                    let wrap_width = if rng.gen_bool(0.2) {
1905                        None
1906                    } else {
1907                        Some(px(rng.gen_range(0.0..=100.0)))
1908                    };
1909                    log::info!("Setting wrap width to {:?}", wrap_width);
1910                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1911                }
1912                20..=39 => {
1913                    let block_count = rng.gen_range(1..=5);
1914                    let block_properties = (0..block_count)
1915                        .map(|_| {
1916                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
1917                            let position = buffer.anchor_after(
1918                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1919                            );
1920
1921                            let disposition = if rng.gen() {
1922                                BlockDisposition::Above
1923                            } else {
1924                                BlockDisposition::Below
1925                            };
1926                            let height = rng.gen_range(0..5);
1927                            log::info!(
1928                                "inserting block {:?} {:?} with height {}",
1929                                disposition,
1930                                position.to_point(&buffer),
1931                                height
1932                            );
1933                            BlockProperties {
1934                                style: BlockStyle::Fixed,
1935                                position,
1936                                height,
1937                                disposition,
1938                                render: Box::new(|_| div().into_any()),
1939                                priority: 0,
1940                            }
1941                        })
1942                        .collect::<Vec<_>>();
1943
1944                    let (inlay_snapshot, inlay_edits) =
1945                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1946                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1947                    let (tab_snapshot, tab_edits) =
1948                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1949                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1950                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1951                    });
1952                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1953                    let block_ids =
1954                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
1955                            position: props.position,
1956                            height: props.height,
1957                            style: props.style,
1958                            render: Box::new(|_| div().into_any()),
1959                            disposition: props.disposition,
1960                            priority: 0,
1961                        }));
1962                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1963                        custom_blocks.push((block_id, props));
1964                    }
1965                }
1966                40..=59 if !custom_blocks.is_empty() => {
1967                    let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
1968                    let block_ids_to_remove = (0..block_count)
1969                        .map(|_| {
1970                            custom_blocks
1971                                .remove(rng.gen_range(0..custom_blocks.len()))
1972                                .0
1973                        })
1974                        .collect();
1975
1976                    let (inlay_snapshot, inlay_edits) =
1977                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1978                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1979                    let (tab_snapshot, tab_edits) =
1980                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1981                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1982                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1983                    });
1984                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1985                    block_map.remove(block_ids_to_remove);
1986                }
1987                _ => {
1988                    buffer.update(cx, |buffer, cx| {
1989                        let mutation_count = rng.gen_range(1..=5);
1990                        let subscription = buffer.subscribe();
1991                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
1992                        buffer_snapshot = buffer.snapshot(cx);
1993                        buffer_edits.extend(subscription.consume());
1994                        log::info!("buffer text: {:?}", buffer_snapshot.text());
1995                    });
1996                }
1997            }
1998
1999            let (inlay_snapshot, inlay_edits) =
2000                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
2001            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
2002            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
2003            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
2004                wrap_map.sync(tab_snapshot, tab_edits, cx)
2005            });
2006            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
2007            assert_eq!(
2008                blocks_snapshot.transforms.summary().input_rows,
2009                wraps_snapshot.max_point().row() + 1
2010            );
2011            log::info!("blocks text: {:?}", blocks_snapshot.text());
2012
2013            let mut expected_blocks = Vec::new();
2014            expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
2015                let mut position = block.position.to_point(&buffer_snapshot);
2016                match block.disposition {
2017                    BlockDisposition::Above => {
2018                        position.column = 0;
2019                    }
2020                    BlockDisposition::Below => {
2021                        position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
2022                    }
2023                };
2024                let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
2025                (
2026                    row,
2027                    ExpectedBlock::Custom {
2028                        disposition: block.disposition,
2029                        id: *id,
2030                        height: block.height,
2031                        priority: block.priority,
2032                    },
2033                )
2034            }));
2035
2036            // Note that this needs to be synced with the related section in BlockMap::sync
2037            expected_blocks.extend(
2038                BlockMap::header_and_footer_blocks(
2039                    true,
2040                    excerpt_footer_height,
2041                    buffer_start_header_height,
2042                    excerpt_header_height,
2043                    &buffer_snapshot,
2044                    0..,
2045                    &wraps_snapshot,
2046                )
2047                .map(|(row, block)| (row, block.into())),
2048            );
2049
2050            BlockMap::sort_blocks(&mut expected_blocks);
2051
2052            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
2053
2054            let input_buffer_rows = buffer_snapshot
2055                .buffer_rows(MultiBufferRow(0))
2056                .collect::<Vec<_>>();
2057            let mut expected_buffer_rows = Vec::new();
2058            let mut expected_text = String::new();
2059            let mut expected_block_positions = Vec::new();
2060            let input_text = wraps_snapshot.text();
2061            for (row, input_line) in input_text.split('\n').enumerate() {
2062                let row = row as u32;
2063                if row > 0 {
2064                    expected_text.push('\n');
2065                }
2066
2067                let buffer_row = input_buffer_rows[wraps_snapshot
2068                    .to_point(WrapPoint::new(row, 0), Bias::Left)
2069                    .row as usize];
2070
2071                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
2072                    if *block_row == row && block.disposition() == BlockDisposition::Above {
2073                        let (_, block) = sorted_blocks_iter.next().unwrap();
2074                        let height = block.height() as usize;
2075                        expected_block_positions
2076                            .push((expected_text.matches('\n').count() as u32, block));
2077                        let text = "\n".repeat(height);
2078                        expected_text.push_str(&text);
2079                        for _ in 0..height {
2080                            expected_buffer_rows.push(None);
2081                        }
2082                    } else {
2083                        break;
2084                    }
2085                }
2086
2087                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
2088                expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
2089                expected_text.push_str(input_line);
2090
2091                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
2092                    if *block_row == row && block.disposition() == BlockDisposition::Below {
2093                        let (_, block) = sorted_blocks_iter.next().unwrap();
2094                        let height = block.height() as usize;
2095                        expected_block_positions
2096                            .push((expected_text.matches('\n').count() as u32 + 1, block));
2097                        let text = "\n".repeat(height);
2098                        expected_text.push_str(&text);
2099                        for _ in 0..height {
2100                            expected_buffer_rows.push(None);
2101                        }
2102                    } else {
2103                        break;
2104                    }
2105                }
2106            }
2107
2108            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
2109            let expected_row_count = expected_lines.len();
2110            for start_row in 0..expected_row_count {
2111                let expected_text = expected_lines[start_row..].join("\n");
2112                let actual_text = blocks_snapshot
2113                    .chunks(
2114                        start_row as u32..blocks_snapshot.max_point().row + 1,
2115                        false,
2116                        false,
2117                        Highlights::default(),
2118                    )
2119                    .map(|chunk| chunk.text)
2120                    .collect::<String>();
2121                assert_eq!(
2122                    actual_text, expected_text,
2123                    "incorrect text starting from row {}",
2124                    start_row
2125                );
2126                assert_eq!(
2127                    blocks_snapshot
2128                        .buffer_rows(BlockRow(start_row as u32))
2129                        .map(|row| row.map(|r| r.0))
2130                        .collect::<Vec<_>>(),
2131                    &expected_buffer_rows[start_row..]
2132                );
2133            }
2134
2135            assert_eq!(
2136                blocks_snapshot
2137                    .blocks_in_range(0..(expected_row_count as u32))
2138                    .map(|(row, block)| (row, block.clone().into()))
2139                    .collect::<Vec<_>>(),
2140                expected_block_positions,
2141                "invalid blocks_in_range({:?})",
2142                0..expected_row_count
2143            );
2144
2145            for (_, expected_block) in
2146                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
2147            {
2148                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
2149                assert_eq!(
2150                    actual_block.map(|block| block.id()),
2151                    Some(expected_block.id())
2152                );
2153            }
2154
2155            for (block_row, block) in expected_block_positions {
2156                if let BlockType::Custom(block_id) = block.block_type() {
2157                    assert_eq!(
2158                        blocks_snapshot.row_for_block(block_id),
2159                        Some(BlockRow(block_row))
2160                    );
2161                }
2162            }
2163
2164            let mut expected_longest_rows = Vec::new();
2165            let mut longest_line_len = -1_isize;
2166            for (row, line) in expected_lines.iter().enumerate() {
2167                let row = row as u32;
2168
2169                assert_eq!(
2170                    blocks_snapshot.line_len(BlockRow(row)),
2171                    line.len() as u32,
2172                    "invalid line len for row {}",
2173                    row
2174                );
2175
2176                let line_char_count = line.chars().count() as isize;
2177                match line_char_count.cmp(&longest_line_len) {
2178                    Ordering::Less => {}
2179                    Ordering::Equal => expected_longest_rows.push(row),
2180                    Ordering::Greater => {
2181                        longest_line_len = line_char_count;
2182                        expected_longest_rows.clear();
2183                        expected_longest_rows.push(row);
2184                    }
2185                }
2186            }
2187
2188            let longest_row = blocks_snapshot.longest_row();
2189            assert!(
2190                expected_longest_rows.contains(&longest_row),
2191                "incorrect longest row {}. expected {:?} with length {}",
2192                longest_row,
2193                expected_longest_rows,
2194                longest_line_len,
2195            );
2196
2197            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
2198                let wrap_point = WrapPoint::new(row, 0);
2199                let block_point = blocks_snapshot.to_block_point(wrap_point);
2200                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
2201            }
2202
2203            let mut block_point = BlockPoint::new(0, 0);
2204            for c in expected_text.chars() {
2205                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
2206                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
2207                assert_eq!(
2208                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
2209                    left_point
2210                );
2211                assert_eq!(
2212                    left_buffer_point,
2213                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
2214                    "{:?} is not valid in buffer coordinates",
2215                    left_point
2216                );
2217
2218                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
2219                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
2220                assert_eq!(
2221                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
2222                    right_point
2223                );
2224                assert_eq!(
2225                    right_buffer_point,
2226                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
2227                    "{:?} is not valid in buffer coordinates",
2228                    right_point
2229                );
2230
2231                if c == '\n' {
2232                    block_point.0 += Point::new(1, 0);
2233                } else {
2234                    block_point.column += c.len_utf8() as u32;
2235                }
2236            }
2237        }
2238
2239        #[derive(Debug, Eq, PartialEq)]
2240        enum ExpectedBlock {
2241            ExcerptBoundary {
2242                height: u32,
2243                starts_new_buffer: bool,
2244                is_last: bool,
2245            },
2246            Custom {
2247                disposition: BlockDisposition,
2248                id: CustomBlockId,
2249                height: u32,
2250                priority: usize,
2251            },
2252        }
2253
2254        impl BlockLike for ExpectedBlock {
2255            fn block_type(&self) -> BlockType {
2256                match self {
2257                    ExpectedBlock::Custom { id, .. } => BlockType::Custom(*id),
2258                    ExpectedBlock::ExcerptBoundary { .. } => BlockType::ExcerptBoundary,
2259                }
2260            }
2261
2262            fn disposition(&self) -> BlockDisposition {
2263                self.disposition()
2264            }
2265
2266            fn priority(&self) -> usize {
2267                match self {
2268                    ExpectedBlock::Custom { priority, .. } => *priority,
2269                    ExpectedBlock::ExcerptBoundary { .. } => usize::MAX,
2270                }
2271            }
2272        }
2273
2274        impl ExpectedBlock {
2275            fn height(&self) -> u32 {
2276                match self {
2277                    ExpectedBlock::ExcerptBoundary { height, .. } => *height,
2278                    ExpectedBlock::Custom { height, .. } => *height,
2279                }
2280            }
2281
2282            fn disposition(&self) -> BlockDisposition {
2283                match self {
2284                    ExpectedBlock::ExcerptBoundary { is_last, .. } => {
2285                        if *is_last {
2286                            BlockDisposition::Below
2287                        } else {
2288                            BlockDisposition::Above
2289                        }
2290                    }
2291                    ExpectedBlock::Custom { disposition, .. } => *disposition,
2292                }
2293            }
2294        }
2295
2296        impl From<Block> for ExpectedBlock {
2297            fn from(block: Block) -> Self {
2298                match block {
2299                    Block::Custom(block) => ExpectedBlock::Custom {
2300                        id: block.id,
2301                        disposition: block.disposition,
2302                        height: block.height,
2303                        priority: block.priority,
2304                    },
2305                    Block::ExcerptBoundary {
2306                        height,
2307                        starts_new_buffer,
2308                        next_excerpt,
2309                        ..
2310                    } => ExpectedBlock::ExcerptBoundary {
2311                        height,
2312                        starts_new_buffer,
2313                        is_last: next_excerpt.is_none(),
2314                    },
2315                }
2316            }
2317        }
2318    }
2319
2320    fn init_test(cx: &mut gpui::AppContext) {
2321        let settings = SettingsStore::test(cx);
2322        cx.set_global(settings);
2323        theme::init(theme::LoadThemes::JustBase, cx);
2324        assets::Assets.load_test_fonts(cx);
2325    }
2326
2327    impl Block {
2328        fn as_custom(&self) -> Option<&CustomBlock> {
2329            match self {
2330                Block::Custom(block) => Some(block),
2331                Block::ExcerptBoundary { .. } => None,
2332            }
2333        }
2334    }
2335
2336    impl BlockSnapshot {
2337        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
2338            self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
2339        }
2340    }
2341}