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