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