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::Left, &());
 976        while cursor.start().0 < rows.start && cursor.end(&()).0 <= rows.start {
 977            cursor.next(&());
 978        }
 979
 980        std::iter::from_fn(move || {
 981            while let Some(transform) = cursor.item() {
 982                let start_row = cursor.start().0;
 983                if start_row > rows.end
 984                    || (start_row == rows.end
 985                        && transform
 986                            .block
 987                            .as_ref()
 988                            .map_or(false, |block| block.height() > 0))
 989                {
 990                    break;
 991                }
 992                if let Some(block) = &transform.block {
 993                    cursor.next(&());
 994                    return Some((start_row, block));
 995                } else {
 996                    cursor.next(&());
 997                }
 998            }
 999            None
1000        })
1001    }
1002
1003    pub fn block_for_id(&self, block_id: BlockId) -> Option<Block> {
1004        let buffer = self.wrap_snapshot.buffer_snapshot();
1005
1006        match block_id {
1007            BlockId::Custom(custom_block_id) => {
1008                let custom_block = self.custom_blocks_by_id.get(&custom_block_id)?;
1009                Some(Block::Custom(custom_block.clone()))
1010            }
1011            BlockId::ExcerptHeader(excerpt_id) => {
1012                let excerpt_range = buffer.range_for_excerpt::<Point>(excerpt_id)?;
1013                let wrap_point = self
1014                    .wrap_snapshot
1015                    .make_wrap_point(excerpt_range.start, Bias::Left);
1016                let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
1017                cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
1018                while let Some(transform) = cursor.item() {
1019                    if let Some(block) = transform.block.as_ref() {
1020                        if block.id() == block_id {
1021                            return Some(block.clone());
1022                        }
1023                    } else if cursor.start().0 > WrapRow(wrap_point.row()) {
1024                        break;
1025                    }
1026
1027                    cursor.next(&());
1028                }
1029
1030                None
1031            }
1032            BlockId::ExcerptFooter(excerpt_id) => {
1033                let excerpt_range = buffer.range_for_excerpt::<Point>(excerpt_id)?;
1034                let wrap_point = self
1035                    .wrap_snapshot
1036                    .make_wrap_point(excerpt_range.end, Bias::Left);
1037
1038                let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
1039                cursor.seek(&WrapRow(wrap_point.row()), Bias::Left, &());
1040                while let Some(transform) = cursor.item() {
1041                    if let Some(block) = transform.block.as_ref() {
1042                        if block.id() == block_id {
1043                            return Some(block.clone());
1044                        }
1045                    } else if cursor.start().0 > WrapRow(wrap_point.row()) {
1046                        break;
1047                    }
1048
1049                    cursor.next(&());
1050                }
1051
1052                None
1053            }
1054        }
1055    }
1056
1057    pub fn max_point(&self) -> BlockPoint {
1058        let row = self.transforms.summary().output_rows - 1;
1059        BlockPoint::new(row, self.line_len(BlockRow(row)))
1060    }
1061
1062    pub fn longest_row(&self) -> u32 {
1063        let input_row = self.wrap_snapshot.longest_row();
1064        self.to_block_point(WrapPoint::new(input_row, 0)).row
1065    }
1066
1067    pub(super) fn line_len(&self, row: BlockRow) -> u32 {
1068        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
1069        cursor.seek(&BlockRow(row.0), Bias::Right, &());
1070        if let Some(transform) = cursor.item() {
1071            let (output_start, input_start) = cursor.start();
1072            let overshoot = row.0 - output_start.0;
1073            if transform.block.is_some() {
1074                0
1075            } else {
1076                self.wrap_snapshot.line_len(input_start.0 + overshoot)
1077            }
1078        } else {
1079            panic!("row out of range");
1080        }
1081    }
1082
1083    pub(super) fn is_block_line(&self, row: BlockRow) -> bool {
1084        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
1085        cursor.seek(&row, Bias::Right, &());
1086        cursor.item().map_or(false, |t| t.block.is_some())
1087    }
1088
1089    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
1090        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
1091        cursor.seek(&BlockRow(point.row), Bias::Right, &());
1092
1093        let max_input_row = WrapRow(self.transforms.summary().input_rows);
1094        let mut search_left =
1095            (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
1096        let mut reversed = false;
1097
1098        loop {
1099            if let Some(transform) = cursor.item() {
1100                if transform.is_isomorphic() {
1101                    let (output_start_row, input_start_row) = cursor.start();
1102                    let (output_end_row, input_end_row) = cursor.end(&());
1103                    let output_start = Point::new(output_start_row.0, 0);
1104                    let input_start = Point::new(input_start_row.0, 0);
1105                    let input_end = Point::new(input_end_row.0, 0);
1106                    let input_point = if point.row >= output_end_row.0 {
1107                        let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
1108                        self.wrap_snapshot
1109                            .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
1110                    } else {
1111                        let output_overshoot = point.0.saturating_sub(output_start);
1112                        self.wrap_snapshot
1113                            .clip_point(WrapPoint(input_start + output_overshoot), bias)
1114                    };
1115
1116                    if (input_start..input_end).contains(&input_point.0) {
1117                        let input_overshoot = input_point.0.saturating_sub(input_start);
1118                        return BlockPoint(output_start + input_overshoot);
1119                    }
1120                }
1121
1122                if search_left {
1123                    cursor.prev(&());
1124                } else {
1125                    cursor.next(&());
1126                }
1127            } else if reversed {
1128                return self.max_point();
1129            } else {
1130                reversed = true;
1131                search_left = !search_left;
1132                cursor.seek(&BlockRow(point.row), Bias::Right, &());
1133            }
1134        }
1135    }
1136
1137    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
1138        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
1139        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
1140        if let Some(transform) = cursor.item() {
1141            debug_assert!(transform.is_isomorphic());
1142        } else {
1143            return self.max_point();
1144        }
1145
1146        let (input_start_row, output_start_row) = cursor.start();
1147        let input_start = Point::new(input_start_row.0, 0);
1148        let output_start = Point::new(output_start_row.0, 0);
1149        let input_overshoot = wrap_point.0 - input_start;
1150        BlockPoint(output_start + input_overshoot)
1151    }
1152
1153    pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
1154        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
1155        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
1156        if let Some(transform) = cursor.item() {
1157            match transform.block.as_ref().map(|b| b.disposition()) {
1158                Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
1159                Some(BlockDisposition::Below) => {
1160                    let wrap_row = cursor.start().1 .0 - 1;
1161                    WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
1162                }
1163                None => {
1164                    let overshoot = block_point.row - cursor.start().0 .0;
1165                    let wrap_row = cursor.start().1 .0 + overshoot;
1166                    WrapPoint::new(wrap_row, block_point.column)
1167                }
1168            }
1169        } else {
1170            self.wrap_snapshot.max_point()
1171        }
1172    }
1173}
1174
1175impl Transform {
1176    fn isomorphic(rows: u32) -> Self {
1177        Self {
1178            summary: TransformSummary {
1179                input_rows: rows,
1180                output_rows: rows,
1181            },
1182            block: None,
1183        }
1184    }
1185
1186    fn block(block: Block) -> Self {
1187        Self {
1188            summary: TransformSummary {
1189                input_rows: 0,
1190                output_rows: block.height(),
1191            },
1192            block: Some(block),
1193        }
1194    }
1195
1196    fn is_isomorphic(&self) -> bool {
1197        self.block.is_none()
1198    }
1199}
1200
1201impl<'a> BlockChunks<'a> {
1202    fn advance(&mut self) {
1203        self.transforms.next(&());
1204        while let Some(transform) = self.transforms.item() {
1205            if transform
1206                .block
1207                .as_ref()
1208                .map_or(false, |block| block.height() == 0)
1209            {
1210                self.transforms.next(&());
1211            } else {
1212                break;
1213            }
1214        }
1215    }
1216}
1217
1218impl<'a> Iterator for BlockChunks<'a> {
1219    type Item = Chunk<'a>;
1220
1221    fn next(&mut self) -> Option<Self::Item> {
1222        if self.output_row >= self.max_output_row {
1223            return None;
1224        }
1225
1226        let transform = self.transforms.item()?;
1227        if transform.block.is_some() {
1228            let block_start = self.transforms.start().0 .0;
1229            let mut block_end = self.transforms.end(&()).0 .0;
1230            self.advance();
1231            if self.transforms.item().is_none() {
1232                block_end -= 1;
1233            }
1234
1235            let start_in_block = self.output_row - block_start;
1236            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
1237            let line_count = end_in_block - start_in_block;
1238            self.output_row += line_count;
1239
1240            return Some(Chunk {
1241                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
1242                ..Default::default()
1243            });
1244        }
1245
1246        if self.input_chunk.text.is_empty() {
1247            if let Some(input_chunk) = self.input_chunks.next() {
1248                self.input_chunk = input_chunk;
1249            } else {
1250                self.output_row += 1;
1251                if self.output_row < self.max_output_row {
1252                    self.advance();
1253                    return Some(Chunk {
1254                        text: "\n",
1255                        ..Default::default()
1256                    });
1257                } else {
1258                    return None;
1259                }
1260            }
1261        }
1262
1263        let transform_end = self.transforms.end(&()).0 .0;
1264        let (prefix_rows, prefix_bytes) =
1265            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
1266        self.output_row += prefix_rows;
1267        let (mut prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
1268        self.input_chunk.text = suffix;
1269        if self.output_row == transform_end {
1270            self.advance();
1271        }
1272
1273        if self.masked {
1274            // Not great for multibyte text because to keep cursor math correct we
1275            // need to have the same number of bytes in the input as output.
1276            let chars = prefix.chars().count();
1277            let bullet_len = chars;
1278            prefix = &BULLETS[..bullet_len];
1279        }
1280
1281        Some(Chunk {
1282            text: prefix,
1283            ..self.input_chunk.clone()
1284        })
1285    }
1286}
1287
1288impl<'a> Iterator for BlockBufferRows<'a> {
1289    type Item = Option<BlockRow>;
1290
1291    fn next(&mut self) -> Option<Self::Item> {
1292        if self.started {
1293            self.output_row.0 += 1;
1294        } else {
1295            self.started = true;
1296        }
1297
1298        if self.output_row.0 >= self.transforms.end(&()).0 .0 {
1299            self.transforms.next(&());
1300        }
1301
1302        while let Some(transform) = self.transforms.item() {
1303            if transform
1304                .block
1305                .as_ref()
1306                .map_or(false, |block| block.height() == 0)
1307            {
1308                self.transforms.next(&());
1309            } else {
1310                break;
1311            }
1312        }
1313
1314        let transform = self.transforms.item()?;
1315        if transform.block.is_some() {
1316            Some(None)
1317        } else {
1318            Some(self.input_buffer_rows.next().unwrap().map(BlockRow))
1319        }
1320    }
1321}
1322
1323impl sum_tree::Item for Transform {
1324    type Summary = TransformSummary;
1325
1326    fn summary(&self) -> Self::Summary {
1327        self.summary.clone()
1328    }
1329}
1330
1331impl sum_tree::Summary for TransformSummary {
1332    type Context = ();
1333
1334    fn add_summary(&mut self, summary: &Self, _: &()) {
1335        self.input_rows += summary.input_rows;
1336        self.output_rows += summary.output_rows;
1337    }
1338}
1339
1340impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
1341    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1342        self.0 += summary.input_rows;
1343    }
1344}
1345
1346impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
1347    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1348        self.0 += summary.output_rows;
1349    }
1350}
1351
1352impl BlockDisposition {
1353    fn is_below(&self) -> bool {
1354        matches!(self, BlockDisposition::Below)
1355    }
1356}
1357
1358impl<'a> Deref for BlockContext<'a, '_> {
1359    type Target = WindowContext<'a>;
1360
1361    fn deref(&self) -> &Self::Target {
1362        self.context
1363    }
1364}
1365
1366impl DerefMut for BlockContext<'_, '_> {
1367    fn deref_mut(&mut self) -> &mut Self::Target {
1368        self.context
1369    }
1370}
1371
1372impl CustomBlock {
1373    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
1374        self.render.lock()(cx)
1375    }
1376
1377    pub fn position(&self) -> &Anchor {
1378        &self.position
1379    }
1380
1381    pub fn style(&self) -> BlockStyle {
1382        self.style
1383    }
1384}
1385
1386impl Debug for CustomBlock {
1387    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1388        f.debug_struct("Block")
1389            .field("id", &self.id)
1390            .field("position", &self.position)
1391            .field("disposition", &self.disposition)
1392            .finish()
1393    }
1394}
1395
1396// Count the number of bytes prior to a target point. If the string doesn't contain the target
1397// point, return its total extent. Otherwise return the target point itself.
1398fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
1399    let mut row = 0;
1400    let mut offset = 0;
1401    for (ix, line) in s.split('\n').enumerate() {
1402        if ix > 0 {
1403            row += 1;
1404            offset += 1;
1405        }
1406        if row >= target {
1407            break;
1408        }
1409        offset += line.len();
1410    }
1411    (row, offset)
1412}
1413
1414#[cfg(test)]
1415mod tests {
1416    use super::*;
1417    use crate::display_map::{
1418        fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap, wrap_map::WrapMap,
1419    };
1420    use gpui::{div, font, px, AppContext, Context as _, Element};
1421    use language::{Buffer, Capability};
1422    use multi_buffer::MultiBuffer;
1423    use rand::prelude::*;
1424    use settings::SettingsStore;
1425    use std::env;
1426    use util::RandomCharIter;
1427
1428    #[gpui::test]
1429    fn test_offset_for_row() {
1430        assert_eq!(offset_for_row("", 0), (0, 0));
1431        assert_eq!(offset_for_row("", 1), (0, 0));
1432        assert_eq!(offset_for_row("abcd", 0), (0, 0));
1433        assert_eq!(offset_for_row("abcd", 1), (0, 4));
1434        assert_eq!(offset_for_row("\n", 0), (0, 0));
1435        assert_eq!(offset_for_row("\n", 1), (1, 1));
1436        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1437        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1438        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1439        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1440    }
1441
1442    #[gpui::test]
1443    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1444        cx.update(|cx| init_test(cx));
1445
1446        let text = "aaa\nbbb\nccc\nddd";
1447
1448        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1449        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1450        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1451        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1452        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1453        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1454        let (wrap_map, wraps_snapshot) =
1455            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1456        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1457
1458        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1459        let block_ids = writer.insert(vec![
1460            BlockProperties {
1461                style: BlockStyle::Fixed,
1462                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
1463                height: 1,
1464                disposition: BlockDisposition::Above,
1465                render: Box::new(|_| div().into_any()),
1466            },
1467            BlockProperties {
1468                style: BlockStyle::Fixed,
1469                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
1470                height: 2,
1471                disposition: BlockDisposition::Above,
1472                render: Box::new(|_| div().into_any()),
1473            },
1474            BlockProperties {
1475                style: BlockStyle::Fixed,
1476                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
1477                height: 3,
1478                disposition: BlockDisposition::Below,
1479                render: Box::new(|_| div().into_any()),
1480            },
1481        ]);
1482
1483        let snapshot = block_map.read(wraps_snapshot, Default::default());
1484        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1485
1486        let blocks = snapshot
1487            .blocks_in_range(0..8)
1488            .map(|(start_row, block)| {
1489                let block = block.as_custom().unwrap();
1490                (start_row..start_row + block.height, block.id)
1491            })
1492            .collect::<Vec<_>>();
1493
1494        // When multiple blocks are on the same line, the newer blocks appear first.
1495        assert_eq!(
1496            blocks,
1497            &[
1498                (1..2, block_ids[0]),
1499                (2..4, block_ids[1]),
1500                (7..10, block_ids[2]),
1501            ]
1502        );
1503
1504        assert_eq!(
1505            snapshot.to_block_point(WrapPoint::new(0, 3)),
1506            BlockPoint::new(0, 3)
1507        );
1508        assert_eq!(
1509            snapshot.to_block_point(WrapPoint::new(1, 0)),
1510            BlockPoint::new(4, 0)
1511        );
1512        assert_eq!(
1513            snapshot.to_block_point(WrapPoint::new(3, 3)),
1514            BlockPoint::new(6, 3)
1515        );
1516
1517        assert_eq!(
1518            snapshot.to_wrap_point(BlockPoint::new(0, 3)),
1519            WrapPoint::new(0, 3)
1520        );
1521        assert_eq!(
1522            snapshot.to_wrap_point(BlockPoint::new(1, 0)),
1523            WrapPoint::new(1, 0)
1524        );
1525        assert_eq!(
1526            snapshot.to_wrap_point(BlockPoint::new(3, 0)),
1527            WrapPoint::new(1, 0)
1528        );
1529        assert_eq!(
1530            snapshot.to_wrap_point(BlockPoint::new(7, 0)),
1531            WrapPoint::new(3, 3)
1532        );
1533
1534        assert_eq!(
1535            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1536            BlockPoint::new(0, 3)
1537        );
1538        assert_eq!(
1539            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1540            BlockPoint::new(4, 0)
1541        );
1542        assert_eq!(
1543            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1544            BlockPoint::new(0, 3)
1545        );
1546        assert_eq!(
1547            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1548            BlockPoint::new(4, 0)
1549        );
1550        assert_eq!(
1551            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1552            BlockPoint::new(4, 0)
1553        );
1554        assert_eq!(
1555            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1556            BlockPoint::new(4, 0)
1557        );
1558        assert_eq!(
1559            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1560            BlockPoint::new(6, 3)
1561        );
1562        assert_eq!(
1563            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1564            BlockPoint::new(6, 3)
1565        );
1566        assert_eq!(
1567            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1568            BlockPoint::new(6, 3)
1569        );
1570        assert_eq!(
1571            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1572            BlockPoint::new(6, 3)
1573        );
1574
1575        assert_eq!(
1576            snapshot
1577                .buffer_rows(BlockRow(0))
1578                .map(|row| row.map(|r| r.0))
1579                .collect::<Vec<_>>(),
1580            &[
1581                Some(0),
1582                None,
1583                None,
1584                None,
1585                Some(1),
1586                Some(2),
1587                Some(3),
1588                None,
1589                None,
1590                None
1591            ]
1592        );
1593
1594        // Insert a line break, separating two block decorations into separate lines.
1595        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1596            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1597            buffer.snapshot(cx)
1598        });
1599
1600        let (inlay_snapshot, inlay_edits) =
1601            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1602        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1603        let (tab_snapshot, tab_edits) =
1604            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1605        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1606            wrap_map.sync(tab_snapshot, tab_edits, cx)
1607        });
1608        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1609        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1610    }
1611
1612    #[gpui::test]
1613    fn test_multibuffer_headers_and_footers(cx: &mut AppContext) {
1614        init_test(cx);
1615
1616        let buffer1 = cx.new_model(|cx| Buffer::local("Buffer 1", cx));
1617        let buffer2 = cx.new_model(|cx| Buffer::local("Buffer 2", cx));
1618        let buffer3 = cx.new_model(|cx| Buffer::local("Buffer 3", cx));
1619
1620        let mut excerpt_ids = Vec::new();
1621        let multi_buffer = cx.new_model(|cx| {
1622            let mut multi_buffer = MultiBuffer::new(0, Capability::ReadWrite);
1623            excerpt_ids.extend(multi_buffer.push_excerpts(
1624                buffer1.clone(),
1625                [ExcerptRange {
1626                    context: 0..buffer1.read(cx).len(),
1627                    primary: None,
1628                }],
1629                cx,
1630            ));
1631            excerpt_ids.extend(multi_buffer.push_excerpts(
1632                buffer2.clone(),
1633                [ExcerptRange {
1634                    context: 0..buffer2.read(cx).len(),
1635                    primary: None,
1636                }],
1637                cx,
1638            ));
1639            excerpt_ids.extend(multi_buffer.push_excerpts(
1640                buffer3.clone(),
1641                [ExcerptRange {
1642                    context: 0..buffer3.read(cx).len(),
1643                    primary: None,
1644                }],
1645                cx,
1646            ));
1647
1648            multi_buffer
1649        });
1650
1651        let font = font("Helvetica");
1652        let font_size = px(14.);
1653        let font_id = cx.text_system().resolve_font(&font);
1654        let mut wrap_width = px(0.);
1655        for c in "Buff".chars() {
1656            wrap_width += cx
1657                .text_system()
1658                .advance(font_id, font_size, c)
1659                .unwrap()
1660                .width;
1661        }
1662
1663        let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
1664        let (_, inlay_snapshot) = InlayMap::new(multi_buffer_snapshot.clone());
1665        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
1666        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1667        let (_, wraps_snapshot) = WrapMap::new(tab_snapshot, font, font_size, Some(wrap_width), cx);
1668
1669        let block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 1);
1670        let snapshot = block_map.read(wraps_snapshot, Default::default());
1671
1672        // Each excerpt has a header above and footer below. Excerpts are also *separated* by a newline.
1673        assert_eq!(
1674            snapshot.text(),
1675            "\nBuff\ner 1\n\n\nBuff\ner 2\n\n\nBuff\ner 3\n"
1676        );
1677
1678        let blocks: Vec<_> = snapshot
1679            .blocks_in_range(0..u32::MAX)
1680            .map(|(row, block)| (row, block.id()))
1681            .collect();
1682        assert_eq!(
1683            blocks,
1684            vec![
1685                (0, BlockId::ExcerptHeader(excerpt_ids[0])),
1686                (3, BlockId::ExcerptFooter(excerpt_ids[0])),
1687                (4, BlockId::ExcerptHeader(excerpt_ids[1])),
1688                (7, BlockId::ExcerptFooter(excerpt_ids[1])),
1689                (8, BlockId::ExcerptHeader(excerpt_ids[2])),
1690                (11, BlockId::ExcerptFooter(excerpt_ids[2]))
1691            ]
1692        );
1693    }
1694
1695    #[gpui::test]
1696    fn test_replace_with_heights(cx: &mut gpui::TestAppContext) {
1697        let _update = cx.update(|cx| init_test(cx));
1698
1699        let text = "aaa\nbbb\nccc\nddd";
1700
1701        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1702        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1703        let _subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1704        let (_inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1705        let (_fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1706        let (_tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1707        let (_wrap_map, wraps_snapshot) =
1708            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1709        let mut block_map = BlockMap::new(wraps_snapshot.clone(), false, 1, 1, 0);
1710
1711        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1712        let block_ids = writer.insert(vec![
1713            BlockProperties {
1714                style: BlockStyle::Fixed,
1715                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
1716                height: 1,
1717                disposition: BlockDisposition::Above,
1718                render: Box::new(|_| div().into_any()),
1719            },
1720            BlockProperties {
1721                style: BlockStyle::Fixed,
1722                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
1723                height: 2,
1724                disposition: BlockDisposition::Above,
1725                render: Box::new(|_| div().into_any()),
1726            },
1727            BlockProperties {
1728                style: BlockStyle::Fixed,
1729                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
1730                height: 3,
1731                disposition: BlockDisposition::Below,
1732                render: Box::new(|_| div().into_any()),
1733            },
1734        ]);
1735
1736        {
1737            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1738            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1739
1740            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1741
1742            let mut new_heights = HashMap::default();
1743            new_heights.insert(block_ids[0], 2);
1744            block_map_writer.resize(new_heights);
1745            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1746            assert_eq!(snapshot.text(), "aaa\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1747        }
1748
1749        {
1750            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1751
1752            let mut new_heights = HashMap::default();
1753            new_heights.insert(block_ids[0], 1);
1754            block_map_writer.resize(new_heights);
1755
1756            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1757            assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1758        }
1759
1760        {
1761            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1762
1763            let mut new_heights = HashMap::default();
1764            new_heights.insert(block_ids[0], 0);
1765            block_map_writer.resize(new_heights);
1766
1767            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1768            assert_eq!(snapshot.text(), "aaa\n\n\nbbb\nccc\nddd\n\n\n");
1769        }
1770
1771        {
1772            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1773
1774            let mut new_heights = HashMap::default();
1775            new_heights.insert(block_ids[0], 3);
1776            block_map_writer.resize(new_heights);
1777
1778            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1779            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1780        }
1781
1782        {
1783            let mut block_map_writer = block_map.write(wraps_snapshot.clone(), Default::default());
1784
1785            let mut new_heights = HashMap::default();
1786            new_heights.insert(block_ids[0], 3);
1787            block_map_writer.resize(new_heights);
1788
1789            let snapshot = block_map.read(wraps_snapshot.clone(), Default::default());
1790            // Same height as before, should remain the same
1791            assert_eq!(snapshot.text(), "aaa\n\n\n\n\n\nbbb\nccc\nddd\n\n\n");
1792        }
1793    }
1794
1795    #[cfg(target_os = "macos")]
1796    #[gpui::test]
1797    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
1798        cx.update(|cx| init_test(cx));
1799
1800        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
1801
1802        let text = "one two three\nfour five six\nseven eight";
1803
1804        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1805        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1806        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1807        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
1808        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1809        let (_, wraps_snapshot) = cx.update(|cx| {
1810            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
1811        });
1812        let mut block_map = BlockMap::new(wraps_snapshot.clone(), true, 1, 1, 0);
1813
1814        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1815        writer.insert(vec![
1816            BlockProperties {
1817                style: BlockStyle::Fixed,
1818                position: buffer_snapshot.anchor_after(Point::new(1, 12)),
1819                disposition: BlockDisposition::Above,
1820                render: Box::new(|_| div().into_any()),
1821                height: 1,
1822            },
1823            BlockProperties {
1824                style: BlockStyle::Fixed,
1825                position: buffer_snapshot.anchor_after(Point::new(1, 1)),
1826                disposition: BlockDisposition::Below,
1827                render: Box::new(|_| div().into_any()),
1828                height: 1,
1829            },
1830        ]);
1831
1832        // Blocks with an 'above' disposition go above their corresponding buffer line.
1833        // Blocks with a 'below' disposition go below their corresponding buffer line.
1834        let snapshot = block_map.read(wraps_snapshot, Default::default());
1835        assert_eq!(
1836            snapshot.text(),
1837            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
1838        );
1839    }
1840
1841    #[gpui::test(iterations = 100)]
1842    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1843        cx.update(|cx| init_test(cx));
1844
1845        let operations = env::var("OPERATIONS")
1846            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1847            .unwrap_or(10);
1848
1849        let wrap_width = if rng.gen_bool(0.2) {
1850            None
1851        } else {
1852            Some(px(rng.gen_range(0.0..=100.0)))
1853        };
1854        let tab_size = 1.try_into().unwrap();
1855        let font_size = px(14.0);
1856        let buffer_start_header_height = rng.gen_range(1..=5);
1857        let excerpt_header_height = rng.gen_range(1..=5);
1858        let excerpt_footer_height = rng.gen_range(1..=5);
1859
1860        log::info!("Wrap width: {:?}", wrap_width);
1861        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
1862        log::info!("Excerpt Footer Height: {:?}", excerpt_footer_height);
1863
1864        let buffer = if rng.gen() {
1865            let len = rng.gen_range(0..10);
1866            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1867            log::info!("initial buffer text: {:?}", text);
1868            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
1869        } else {
1870            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
1871        };
1872
1873        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1874        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1875        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1876        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1877        let (wrap_map, wraps_snapshot) = cx
1878            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
1879        let mut block_map = BlockMap::new(
1880            wraps_snapshot,
1881            true,
1882            buffer_start_header_height,
1883            excerpt_header_height,
1884            excerpt_footer_height,
1885        );
1886        let mut custom_blocks = Vec::new();
1887
1888        for _ in 0..operations {
1889            let mut buffer_edits = Vec::new();
1890            match rng.gen_range(0..=100) {
1891                0..=19 => {
1892                    let wrap_width = if rng.gen_bool(0.2) {
1893                        None
1894                    } else {
1895                        Some(px(rng.gen_range(0.0..=100.0)))
1896                    };
1897                    log::info!("Setting wrap width to {:?}", wrap_width);
1898                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1899                }
1900                20..=39 => {
1901                    let block_count = rng.gen_range(1..=5);
1902                    let block_properties = (0..block_count)
1903                        .map(|_| {
1904                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
1905                            let position = buffer.anchor_after(
1906                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1907                            );
1908
1909                            let disposition = if rng.gen() {
1910                                BlockDisposition::Above
1911                            } else {
1912                                BlockDisposition::Below
1913                            };
1914                            let height = rng.gen_range(0..5);
1915                            log::info!(
1916                                "inserting block {:?} {:?} with height {}",
1917                                disposition,
1918                                position.to_point(&buffer),
1919                                height
1920                            );
1921                            BlockProperties {
1922                                style: BlockStyle::Fixed,
1923                                position,
1924                                height,
1925                                disposition,
1926                                render: Box::new(|_| div().into_any()),
1927                            }
1928                        })
1929                        .collect::<Vec<_>>();
1930
1931                    let (inlay_snapshot, inlay_edits) =
1932                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1933                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1934                    let (tab_snapshot, tab_edits) =
1935                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1936                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1937                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1938                    });
1939                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1940                    let block_ids =
1941                        block_map.insert(block_properties.iter().map(|props| BlockProperties {
1942                            position: props.position,
1943                            height: props.height,
1944                            style: props.style,
1945                            render: Box::new(|_| div().into_any()),
1946                            disposition: props.disposition,
1947                        }));
1948                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1949                        custom_blocks.push((block_id, props));
1950                    }
1951                }
1952                40..=59 if !custom_blocks.is_empty() => {
1953                    let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
1954                    let block_ids_to_remove = (0..block_count)
1955                        .map(|_| {
1956                            custom_blocks
1957                                .remove(rng.gen_range(0..custom_blocks.len()))
1958                                .0
1959                        })
1960                        .collect();
1961
1962                    let (inlay_snapshot, inlay_edits) =
1963                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1964                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1965                    let (tab_snapshot, tab_edits) =
1966                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1967                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1968                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1969                    });
1970                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1971                    block_map.remove(block_ids_to_remove);
1972                }
1973                _ => {
1974                    buffer.update(cx, |buffer, cx| {
1975                        let mutation_count = rng.gen_range(1..=5);
1976                        let subscription = buffer.subscribe();
1977                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
1978                        buffer_snapshot = buffer.snapshot(cx);
1979                        buffer_edits.extend(subscription.consume());
1980                        log::info!("buffer text: {:?}", buffer_snapshot.text());
1981                    });
1982                }
1983            }
1984
1985            let (inlay_snapshot, inlay_edits) =
1986                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1987            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1988            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
1989            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1990                wrap_map.sync(tab_snapshot, tab_edits, cx)
1991            });
1992            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
1993            assert_eq!(
1994                blocks_snapshot.transforms.summary().input_rows,
1995                wraps_snapshot.max_point().row() + 1
1996            );
1997            log::info!("blocks text: {:?}", blocks_snapshot.text());
1998
1999            let mut expected_blocks = Vec::new();
2000            expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
2001                let mut position = block.position.to_point(&buffer_snapshot);
2002                match block.disposition {
2003                    BlockDisposition::Above => {
2004                        position.column = 0;
2005                    }
2006                    BlockDisposition::Below => {
2007                        position.column = buffer_snapshot.line_len(MultiBufferRow(position.row));
2008                    }
2009                };
2010                let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
2011                (
2012                    row,
2013                    ExpectedBlock::Custom {
2014                        disposition: block.disposition,
2015                        id: *id,
2016                        height: block.height,
2017                    },
2018                )
2019            }));
2020
2021            // Note that this needs to be synced with the related section in BlockMap::sync
2022            expected_blocks.extend(
2023                BlockMap::header_and_footer_blocks(
2024                    true,
2025                    excerpt_footer_height,
2026                    buffer_start_header_height,
2027                    excerpt_header_height,
2028                    &buffer_snapshot,
2029                    0..,
2030                    &wraps_snapshot,
2031                )
2032                .map(|(row, block)| (row, block.into())),
2033            );
2034
2035            BlockMap::sort_blocks(&mut expected_blocks);
2036
2037            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
2038
2039            let input_buffer_rows = buffer_snapshot
2040                .buffer_rows(MultiBufferRow(0))
2041                .collect::<Vec<_>>();
2042            let mut expected_buffer_rows = Vec::new();
2043            let mut expected_text = String::new();
2044            let mut expected_block_positions = Vec::new();
2045            let input_text = wraps_snapshot.text();
2046            for (row, input_line) in input_text.split('\n').enumerate() {
2047                let row = row as u32;
2048                if row > 0 {
2049                    expected_text.push('\n');
2050                }
2051
2052                let buffer_row = input_buffer_rows[wraps_snapshot
2053                    .to_point(WrapPoint::new(row, 0), Bias::Left)
2054                    .row as usize];
2055
2056                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
2057                    if *block_row == row && block.disposition() == BlockDisposition::Above {
2058                        let (_, block) = sorted_blocks_iter.next().unwrap();
2059                        let height = block.height() as usize;
2060                        expected_block_positions
2061                            .push((expected_text.matches('\n').count() as u32, block));
2062                        let text = "\n".repeat(height);
2063                        expected_text.push_str(&text);
2064                        for _ in 0..height {
2065                            expected_buffer_rows.push(None);
2066                        }
2067                    } else {
2068                        break;
2069                    }
2070                }
2071
2072                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
2073                expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
2074                expected_text.push_str(input_line);
2075
2076                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
2077                    if *block_row == row && block.disposition() == BlockDisposition::Below {
2078                        let (_, block) = sorted_blocks_iter.next().unwrap();
2079                        let height = block.height() as usize;
2080                        expected_block_positions
2081                            .push((expected_text.matches('\n').count() as u32 + 1, block));
2082                        let text = "\n".repeat(height);
2083                        expected_text.push_str(&text);
2084                        for _ in 0..height {
2085                            expected_buffer_rows.push(None);
2086                        }
2087                    } else {
2088                        break;
2089                    }
2090                }
2091            }
2092
2093            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
2094            let expected_row_count = expected_lines.len();
2095            for start_row in 0..expected_row_count {
2096                let expected_text = expected_lines[start_row..].join("\n");
2097                let actual_text = blocks_snapshot
2098                    .chunks(
2099                        start_row as u32..blocks_snapshot.max_point().row + 1,
2100                        false,
2101                        false,
2102                        Highlights::default(),
2103                    )
2104                    .map(|chunk| chunk.text)
2105                    .collect::<String>();
2106                assert_eq!(
2107                    actual_text, expected_text,
2108                    "incorrect text starting from row {}",
2109                    start_row
2110                );
2111                assert_eq!(
2112                    blocks_snapshot
2113                        .buffer_rows(BlockRow(start_row as u32))
2114                        .map(|row| row.map(|r| r.0))
2115                        .collect::<Vec<_>>(),
2116                    &expected_buffer_rows[start_row..]
2117                );
2118            }
2119
2120            assert_eq!(
2121                blocks_snapshot
2122                    .blocks_in_range(0..(expected_row_count as u32))
2123                    .map(|(row, block)| (row, block.clone().into()))
2124                    .collect::<Vec<_>>(),
2125                expected_block_positions,
2126                "invalid blocks_in_range({:?})",
2127                0..expected_row_count
2128            );
2129
2130            for (_, expected_block) in
2131                blocks_snapshot.blocks_in_range(0..(expected_row_count as u32))
2132            {
2133                let actual_block = blocks_snapshot.block_for_id(expected_block.id());
2134                assert_eq!(
2135                    actual_block.map(|block| block.id()),
2136                    Some(expected_block.id())
2137                );
2138            }
2139
2140            for (block_row, block) in expected_block_positions {
2141                if let BlockType::Custom(block_id) = block.block_type() {
2142                    assert_eq!(
2143                        blocks_snapshot.row_for_block(block_id),
2144                        Some(BlockRow(block_row))
2145                    );
2146                }
2147            }
2148
2149            let mut expected_longest_rows = Vec::new();
2150            let mut longest_line_len = -1_isize;
2151            for (row, line) in expected_lines.iter().enumerate() {
2152                let row = row as u32;
2153
2154                assert_eq!(
2155                    blocks_snapshot.line_len(BlockRow(row)),
2156                    line.len() as u32,
2157                    "invalid line len for row {}",
2158                    row
2159                );
2160
2161                let line_char_count = line.chars().count() as isize;
2162                match line_char_count.cmp(&longest_line_len) {
2163                    Ordering::Less => {}
2164                    Ordering::Equal => expected_longest_rows.push(row),
2165                    Ordering::Greater => {
2166                        longest_line_len = line_char_count;
2167                        expected_longest_rows.clear();
2168                        expected_longest_rows.push(row);
2169                    }
2170                }
2171            }
2172
2173            let longest_row = blocks_snapshot.longest_row();
2174            assert!(
2175                expected_longest_rows.contains(&longest_row),
2176                "incorrect longest row {}. expected {:?} with length {}",
2177                longest_row,
2178                expected_longest_rows,
2179                longest_line_len,
2180            );
2181
2182            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
2183                let wrap_point = WrapPoint::new(row, 0);
2184                let block_point = blocks_snapshot.to_block_point(wrap_point);
2185                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
2186            }
2187
2188            let mut block_point = BlockPoint::new(0, 0);
2189            for c in expected_text.chars() {
2190                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
2191                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
2192                assert_eq!(
2193                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
2194                    left_point
2195                );
2196                assert_eq!(
2197                    left_buffer_point,
2198                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
2199                    "{:?} is not valid in buffer coordinates",
2200                    left_point
2201                );
2202
2203                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
2204                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
2205                assert_eq!(
2206                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
2207                    right_point
2208                );
2209                assert_eq!(
2210                    right_buffer_point,
2211                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
2212                    "{:?} is not valid in buffer coordinates",
2213                    right_point
2214                );
2215
2216                if c == '\n' {
2217                    block_point.0 += Point::new(1, 0);
2218                } else {
2219                    block_point.column += c.len_utf8() as u32;
2220                }
2221            }
2222        }
2223
2224        #[derive(Debug, Eq, PartialEq)]
2225        enum ExpectedBlock {
2226            ExcerptHeader {
2227                height: u32,
2228                starts_new_buffer: bool,
2229            },
2230            ExcerptFooter {
2231                height: u32,
2232                disposition: BlockDisposition,
2233            },
2234            Custom {
2235                disposition: BlockDisposition,
2236                id: CustomBlockId,
2237                height: u32,
2238            },
2239        }
2240
2241        impl BlockLike for ExpectedBlock {
2242            fn block_type(&self) -> BlockType {
2243                match self {
2244                    ExpectedBlock::Custom { id, .. } => BlockType::Custom(*id),
2245                    ExpectedBlock::ExcerptHeader { .. } => BlockType::Header,
2246                    ExpectedBlock::ExcerptFooter { .. } => BlockType::Footer,
2247                }
2248            }
2249
2250            fn disposition(&self) -> BlockDisposition {
2251                self.disposition()
2252            }
2253        }
2254
2255        impl ExpectedBlock {
2256            fn height(&self) -> u32 {
2257                match self {
2258                    ExpectedBlock::ExcerptHeader { height, .. } => *height,
2259                    ExpectedBlock::Custom { height, .. } => *height,
2260                    ExpectedBlock::ExcerptFooter { height, .. } => *height,
2261                }
2262            }
2263
2264            fn disposition(&self) -> BlockDisposition {
2265                match self {
2266                    ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
2267                    ExpectedBlock::Custom { disposition, .. } => *disposition,
2268                    ExpectedBlock::ExcerptFooter { disposition, .. } => *disposition,
2269                }
2270            }
2271        }
2272
2273        impl From<Block> for ExpectedBlock {
2274            fn from(block: Block) -> Self {
2275                match block {
2276                    Block::Custom(block) => ExpectedBlock::Custom {
2277                        id: block.id,
2278                        disposition: block.disposition,
2279                        height: block.height,
2280                    },
2281                    Block::ExcerptHeader {
2282                        height,
2283                        starts_new_buffer,
2284                        ..
2285                    } => ExpectedBlock::ExcerptHeader {
2286                        height,
2287                        starts_new_buffer,
2288                    },
2289                    Block::ExcerptFooter {
2290                        height,
2291                        disposition,
2292                        ..
2293                    } => ExpectedBlock::ExcerptFooter {
2294                        height,
2295                        disposition,
2296                    },
2297                }
2298            }
2299        }
2300    }
2301
2302    fn init_test(cx: &mut gpui::AppContext) {
2303        let settings = SettingsStore::test(cx);
2304        cx.set_global(settings);
2305        theme::init(theme::LoadThemes::JustBase, cx);
2306        assets::Assets.load_test_fonts(cx);
2307    }
2308
2309    impl Block {
2310        fn as_custom(&self) -> Option<&CustomBlock> {
2311            match self {
2312                Block::Custom(block) => Some(block),
2313                Block::ExcerptHeader { .. } => None,
2314                Block::ExcerptFooter { .. } => None,
2315            }
2316        }
2317    }
2318
2319    impl BlockSnapshot {
2320        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
2321            self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
2322        }
2323    }
2324}