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, ElementContext, Pixels};
   8use language::{BufferSnapshot, Chunk, Patch, Point};
   9use multi_buffer::{Anchor, ExcerptId, ExcerptRange, ToPoint as _};
  10use parking_lot::Mutex;
  11use std::{
  12    cell::RefCell,
  13    cmp::{self, Ordering},
  14    fmt::Debug,
  15    ops::{Deref, DerefMut, Range},
  16    sync::{
  17        atomic::{AtomicUsize, Ordering::SeqCst},
  18        Arc,
  19    },
  20};
  21use sum_tree::{Bias, SumTree};
  22use text::Edit;
  23
  24const NEWLINES: &[u8] = &[b'\n'; u8::MAX as usize];
  25
  26/// Tracks custom blocks such as diagnostics that should be displayed within buffer.
  27///
  28/// See the [`display_map` module documentation](crate::display_map) for more information.
  29pub struct BlockMap {
  30    next_block_id: AtomicUsize,
  31    wrap_snapshot: RefCell<WrapSnapshot>,
  32    blocks: Vec<Arc<Block>>,
  33    transforms: RefCell<SumTree<Transform>>,
  34    buffer_header_height: u8,
  35    excerpt_header_height: u8,
  36}
  37
  38pub struct BlockMapWriter<'a>(&'a mut BlockMap);
  39
  40pub struct BlockSnapshot {
  41    wrap_snapshot: WrapSnapshot,
  42    transforms: SumTree<Transform>,
  43}
  44
  45#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
  46pub struct BlockId(usize);
  47
  48#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  49pub struct BlockPoint(pub Point);
  50
  51#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  52struct BlockRow(u32);
  53
  54#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  55struct WrapRow(u32);
  56
  57pub type RenderBlock = Arc<dyn Fn(&mut BlockContext) -> AnyElement>;
  58
  59pub struct Block {
  60    id: BlockId,
  61    position: Anchor,
  62    height: u8,
  63    style: BlockStyle,
  64    render: Mutex<RenderBlock>,
  65    disposition: BlockDisposition,
  66}
  67
  68#[derive(Clone)]
  69pub struct BlockProperties<P>
  70where
  71    P: Clone,
  72{
  73    pub position: P,
  74    pub height: u8,
  75    pub style: BlockStyle,
  76    pub render: Arc<dyn Fn(&mut BlockContext) -> AnyElement>,
  77    pub disposition: BlockDisposition,
  78}
  79
  80#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
  81pub enum BlockStyle {
  82    Fixed,
  83    Flex,
  84    Sticky,
  85}
  86
  87pub struct BlockContext<'a, 'b> {
  88    pub context: &'b mut ElementContext<'a>,
  89    pub anchor_x: Pixels,
  90    pub max_width: Pixels,
  91    pub gutter_dimensions: &'b GutterDimensions,
  92    pub em_width: Pixels,
  93    pub line_height: Pixels,
  94    pub block_id: usize,
  95    pub editor_style: &'b EditorStyle,
  96}
  97
  98#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
  99pub enum BlockDisposition {
 100    Above,
 101    Below,
 102}
 103
 104#[derive(Clone, Debug)]
 105struct Transform {
 106    summary: TransformSummary,
 107    block: Option<TransformBlock>,
 108}
 109
 110#[allow(clippy::large_enum_variant)]
 111#[derive(Clone)]
 112pub enum TransformBlock {
 113    Custom(Arc<Block>),
 114    ExcerptHeader {
 115        id: ExcerptId,
 116        buffer: BufferSnapshot,
 117        range: ExcerptRange<text::Anchor>,
 118        height: u8,
 119        starts_new_buffer: bool,
 120    },
 121}
 122
 123impl TransformBlock {
 124    fn disposition(&self) -> BlockDisposition {
 125        match self {
 126            TransformBlock::Custom(block) => block.disposition,
 127            TransformBlock::ExcerptHeader { .. } => BlockDisposition::Above,
 128        }
 129    }
 130
 131    pub fn height(&self) -> u8 {
 132        match self {
 133            TransformBlock::Custom(block) => block.height,
 134            TransformBlock::ExcerptHeader { height, .. } => *height,
 135        }
 136    }
 137}
 138
 139impl Debug for TransformBlock {
 140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 141        match self {
 142            Self::Custom(block) => f.debug_struct("Custom").field("block", block).finish(),
 143            Self::ExcerptHeader { buffer, .. } => f
 144                .debug_struct("ExcerptHeader")
 145                .field("path", &buffer.file().map(|f| f.path()))
 146                .finish(),
 147        }
 148    }
 149}
 150
 151#[derive(Clone, Debug, Default)]
 152struct TransformSummary {
 153    input_rows: u32,
 154    output_rows: u32,
 155}
 156
 157pub struct BlockChunks<'a> {
 158    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
 159    input_chunks: wrap_map::WrapChunks<'a>,
 160    input_chunk: Chunk<'a>,
 161    output_row: u32,
 162    max_output_row: u32,
 163}
 164
 165#[derive(Clone)]
 166pub struct BlockBufferRows<'a> {
 167    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
 168    input_buffer_rows: wrap_map::WrapBufferRows<'a>,
 169    output_row: u32,
 170    started: bool,
 171}
 172
 173impl BlockMap {
 174    pub fn new(
 175        wrap_snapshot: WrapSnapshot,
 176        buffer_header_height: u8,
 177        excerpt_header_height: u8,
 178    ) -> Self {
 179        let row_count = wrap_snapshot.max_point().row() + 1;
 180        let map = Self {
 181            next_block_id: AtomicUsize::new(0),
 182            blocks: Vec::new(),
 183            transforms: RefCell::new(SumTree::from_item(Transform::isomorphic(row_count), &())),
 184            wrap_snapshot: RefCell::new(wrap_snapshot.clone()),
 185            buffer_header_height,
 186            excerpt_header_height,
 187        };
 188        map.sync(
 189            &wrap_snapshot,
 190            Patch::new(vec![Edit {
 191                old: 0..row_count,
 192                new: 0..row_count,
 193            }]),
 194        );
 195        map
 196    }
 197
 198    pub fn read(&self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockSnapshot {
 199        self.sync(&wrap_snapshot, edits);
 200        *self.wrap_snapshot.borrow_mut() = wrap_snapshot.clone();
 201        BlockSnapshot {
 202            wrap_snapshot,
 203            transforms: self.transforms.borrow().clone(),
 204        }
 205    }
 206
 207    pub fn write(&mut self, wrap_snapshot: WrapSnapshot, edits: Patch<u32>) -> BlockMapWriter {
 208        self.sync(&wrap_snapshot, edits);
 209        *self.wrap_snapshot.borrow_mut() = wrap_snapshot;
 210        BlockMapWriter(self)
 211    }
 212
 213    fn sync(&self, wrap_snapshot: &WrapSnapshot, mut edits: Patch<u32>) {
 214        let buffer = wrap_snapshot.buffer_snapshot();
 215
 216        // Handle changing the last excerpt if it is empty.
 217        if buffer.trailing_excerpt_update_count()
 218            != self
 219                .wrap_snapshot
 220                .borrow()
 221                .buffer_snapshot()
 222                .trailing_excerpt_update_count()
 223        {
 224            let max_point = wrap_snapshot.max_point();
 225            let edit_start = wrap_snapshot.prev_row_boundary(max_point);
 226            let edit_end = max_point.row() + 1;
 227            edits = edits.compose([WrapEdit {
 228                old: edit_start..edit_end,
 229                new: edit_start..edit_end,
 230            }]);
 231        }
 232
 233        let edits = edits.into_inner();
 234        if edits.is_empty() {
 235            return;
 236        }
 237
 238        let mut transforms = self.transforms.borrow_mut();
 239        let mut new_transforms = SumTree::new();
 240        let old_row_count = transforms.summary().input_rows;
 241        let new_row_count = wrap_snapshot.max_point().row() + 1;
 242        let mut cursor = transforms.cursor::<WrapRow>();
 243        let mut last_block_ix = 0;
 244        let mut blocks_in_edit = Vec::new();
 245        let mut edits = edits.into_iter().peekable();
 246
 247        while let Some(edit) = edits.next() {
 248            // Preserve any old transforms that precede this edit.
 249            let old_start = WrapRow(edit.old.start);
 250            let new_start = WrapRow(edit.new.start);
 251            new_transforms.append(cursor.slice(&old_start, Bias::Left, &()), &());
 252            if let Some(transform) = cursor.item() {
 253                if transform.is_isomorphic() && old_start == cursor.end(&()) {
 254                    new_transforms.push(transform.clone(), &());
 255                    cursor.next(&());
 256                    while let Some(transform) = cursor.item() {
 257                        if transform
 258                            .block
 259                            .as_ref()
 260                            .map_or(false, |b| b.disposition().is_below())
 261                        {
 262                            new_transforms.push(transform.clone(), &());
 263                            cursor.next(&());
 264                        } else {
 265                            break;
 266                        }
 267                    }
 268                }
 269            }
 270
 271            // Preserve any portion of an old transform that precedes this edit.
 272            let extent_before_edit = old_start.0 - cursor.start().0;
 273            push_isomorphic(&mut new_transforms, extent_before_edit);
 274
 275            // Skip over any old transforms that intersect this edit.
 276            let mut old_end = WrapRow(edit.old.end);
 277            let mut new_end = WrapRow(edit.new.end);
 278            cursor.seek(&old_end, Bias::Left, &());
 279            cursor.next(&());
 280            if old_end == *cursor.start() {
 281                while let Some(transform) = cursor.item() {
 282                    if transform
 283                        .block
 284                        .as_ref()
 285                        .map_or(false, |b| b.disposition().is_below())
 286                    {
 287                        cursor.next(&());
 288                    } else {
 289                        break;
 290                    }
 291                }
 292            }
 293
 294            // Combine this edit with any subsequent edits that intersect the same transform.
 295            while let Some(next_edit) = edits.peek() {
 296                if next_edit.old.start <= cursor.start().0 {
 297                    old_end = WrapRow(next_edit.old.end);
 298                    new_end = WrapRow(next_edit.new.end);
 299                    cursor.seek(&old_end, Bias::Left, &());
 300                    cursor.next(&());
 301                    if old_end == *cursor.start() {
 302                        while let Some(transform) = cursor.item() {
 303                            if transform
 304                                .block
 305                                .as_ref()
 306                                .map_or(false, |b| b.disposition().is_below())
 307                            {
 308                                cursor.next(&());
 309                            } else {
 310                                break;
 311                            }
 312                        }
 313                    }
 314                    edits.next();
 315                } else {
 316                    break;
 317                }
 318            }
 319
 320            // Find the blocks within this edited region.
 321            let new_buffer_start =
 322                wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
 323            let start_bound = Bound::Included(new_buffer_start);
 324            let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
 325                probe
 326                    .position
 327                    .to_point(buffer)
 328                    .cmp(&new_buffer_start)
 329                    .then(Ordering::Greater)
 330            }) {
 331                Ok(ix) | Err(ix) => last_block_ix + ix,
 332            };
 333
 334            let end_bound;
 335            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
 336                end_bound = Bound::Unbounded;
 337                self.blocks.len()
 338            } else {
 339                let new_buffer_end =
 340                    wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
 341                end_bound = Bound::Excluded(new_buffer_end);
 342                match self.blocks[start_block_ix..].binary_search_by(|probe| {
 343                    probe
 344                        .position
 345                        .to_point(buffer)
 346                        .cmp(&new_buffer_end)
 347                        .then(Ordering::Greater)
 348                }) {
 349                    Ok(ix) | Err(ix) => start_block_ix + ix,
 350                }
 351            };
 352            last_block_ix = end_block_ix;
 353
 354            debug_assert!(blocks_in_edit.is_empty());
 355            blocks_in_edit.extend(
 356                self.blocks[start_block_ix..end_block_ix]
 357                    .iter()
 358                    .map(|block| {
 359                        let mut position = block.position.to_point(buffer);
 360                        match block.disposition {
 361                            BlockDisposition::Above => position.column = 0,
 362                            BlockDisposition::Below => {
 363                                position.column = buffer.line_len(position.row)
 364                            }
 365                        }
 366                        let position = wrap_snapshot.make_wrap_point(position, Bias::Left);
 367                        (position.row(), TransformBlock::Custom(block.clone()))
 368                    }),
 369            );
 370            blocks_in_edit.extend(
 371                buffer
 372                    .excerpt_boundaries_in_range((start_bound, end_bound))
 373                    .map(|excerpt_boundary| {
 374                        (
 375                            wrap_snapshot
 376                                .make_wrap_point(Point::new(excerpt_boundary.row, 0), Bias::Left)
 377                                .row(),
 378                            TransformBlock::ExcerptHeader {
 379                                id: excerpt_boundary.id,
 380                                buffer: excerpt_boundary.buffer,
 381                                range: excerpt_boundary.range,
 382                                height: if excerpt_boundary.starts_new_buffer {
 383                                    self.buffer_header_height
 384                                } else {
 385                                    self.excerpt_header_height
 386                                },
 387                                starts_new_buffer: excerpt_boundary.starts_new_buffer,
 388                            },
 389                        )
 390                    }),
 391            );
 392
 393            // Place excerpt headers above custom blocks on the same row.
 394            blocks_in_edit.sort_unstable_by(|(row_a, block_a), (row_b, block_b)| {
 395                row_a.cmp(row_b).then_with(|| match (block_a, block_b) {
 396                    (
 397                        TransformBlock::ExcerptHeader { .. },
 398                        TransformBlock::ExcerptHeader { .. },
 399                    ) => Ordering::Equal,
 400                    (TransformBlock::ExcerptHeader { .. }, _) => Ordering::Less,
 401                    (_, TransformBlock::ExcerptHeader { .. }) => Ordering::Greater,
 402                    (TransformBlock::Custom(block_a), TransformBlock::Custom(block_b)) => block_a
 403                        .disposition
 404                        .cmp(&block_b.disposition)
 405                        .then_with(|| block_a.id.cmp(&block_b.id)),
 406                })
 407            });
 408
 409            // For each of these blocks, insert a new isomorphic transform preceding the block,
 410            // and then insert the block itself.
 411            for (block_row, block) in blocks_in_edit.drain(..) {
 412                let insertion_row = match block.disposition() {
 413                    BlockDisposition::Above => block_row,
 414                    BlockDisposition::Below => block_row + 1,
 415                };
 416                let extent_before_block = insertion_row - new_transforms.summary().input_rows;
 417                push_isomorphic(&mut new_transforms, extent_before_block);
 418                new_transforms.push(Transform::block(block), &());
 419            }
 420
 421            old_end = WrapRow(old_end.0.min(old_row_count));
 422            new_end = WrapRow(new_end.0.min(new_row_count));
 423
 424            // Insert an isomorphic transform after the final block.
 425            let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
 426            push_isomorphic(&mut new_transforms, extent_after_last_block);
 427
 428            // Preserve any portion of the old transform after this edit.
 429            let extent_after_edit = cursor.start().0 - old_end.0;
 430            push_isomorphic(&mut new_transforms, extent_after_edit);
 431        }
 432
 433        new_transforms.append(cursor.suffix(&()), &());
 434        debug_assert_eq!(
 435            new_transforms.summary().input_rows,
 436            wrap_snapshot.max_point().row() + 1
 437        );
 438
 439        drop(cursor);
 440        *transforms = new_transforms;
 441    }
 442
 443    pub fn replace(&mut self, mut renderers: HashMap<BlockId, RenderBlock>) {
 444        for block in &self.blocks {
 445            if let Some(render) = renderers.remove(&block.id) {
 446                *block.render.lock() = render;
 447            }
 448        }
 449    }
 450}
 451
 452fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
 453    if rows == 0 {
 454        return;
 455    }
 456
 457    let mut extent = Some(rows);
 458    tree.update_last(
 459        |last_transform| {
 460            if last_transform.is_isomorphic() {
 461                let extent = extent.take().unwrap();
 462                last_transform.summary.input_rows += extent;
 463                last_transform.summary.output_rows += extent;
 464            }
 465        },
 466        &(),
 467    );
 468    if let Some(extent) = extent {
 469        tree.push(Transform::isomorphic(extent), &());
 470    }
 471}
 472
 473impl BlockPoint {
 474    pub fn new(row: u32, column: u32) -> Self {
 475        Self(Point::new(row, column))
 476    }
 477}
 478
 479impl Deref for BlockPoint {
 480    type Target = Point;
 481
 482    fn deref(&self) -> &Self::Target {
 483        &self.0
 484    }
 485}
 486
 487impl std::ops::DerefMut for BlockPoint {
 488    fn deref_mut(&mut self) -> &mut Self::Target {
 489        &mut self.0
 490    }
 491}
 492
 493impl<'a> BlockMapWriter<'a> {
 494    pub fn insert(
 495        &mut self,
 496        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
 497    ) -> Vec<BlockId> {
 498        let mut ids = Vec::new();
 499        let mut edits = Patch::default();
 500        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
 501        let buffer = wrap_snapshot.buffer_snapshot();
 502
 503        for block in blocks {
 504            let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
 505            ids.push(id);
 506
 507            let position = block.position;
 508            let point = position.to_point(buffer);
 509            let wrap_row = wrap_snapshot
 510                .make_wrap_point(Point::new(point.row, 0), Bias::Left)
 511                .row();
 512            let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
 513            let end_row = wrap_snapshot
 514                .next_row_boundary(WrapPoint::new(wrap_row, 0))
 515                .unwrap_or(wrap_snapshot.max_point().row() + 1);
 516
 517            let block_ix = match self
 518                .0
 519                .blocks
 520                .binary_search_by(|probe| probe.position.cmp(&position, buffer))
 521            {
 522                Ok(ix) | Err(ix) => ix,
 523            };
 524            self.0.blocks.insert(
 525                block_ix,
 526                Arc::new(Block {
 527                    id,
 528                    position,
 529                    height: block.height,
 530                    render: Mutex::new(block.render),
 531                    disposition: block.disposition,
 532                    style: block.style,
 533                }),
 534            );
 535
 536            edits = edits.compose([Edit {
 537                old: start_row..end_row,
 538                new: start_row..end_row,
 539            }]);
 540        }
 541
 542        self.0.sync(wrap_snapshot, edits);
 543        ids
 544    }
 545
 546    pub fn remove(&mut self, block_ids: HashSet<BlockId>) {
 547        let wrap_snapshot = &*self.0.wrap_snapshot.borrow();
 548        let buffer = wrap_snapshot.buffer_snapshot();
 549        let mut edits = Patch::default();
 550        let mut last_block_buffer_row = None;
 551        self.0.blocks.retain(|block| {
 552            if block_ids.contains(&block.id) {
 553                let buffer_row = block.position.to_point(buffer).row;
 554                if last_block_buffer_row != Some(buffer_row) {
 555                    last_block_buffer_row = Some(buffer_row);
 556                    let wrap_row = wrap_snapshot
 557                        .make_wrap_point(Point::new(buffer_row, 0), Bias::Left)
 558                        .row();
 559                    let start_row = wrap_snapshot.prev_row_boundary(WrapPoint::new(wrap_row, 0));
 560                    let end_row = wrap_snapshot
 561                        .next_row_boundary(WrapPoint::new(wrap_row, 0))
 562                        .unwrap_or(wrap_snapshot.max_point().row() + 1);
 563                    edits.push(Edit {
 564                        old: start_row..end_row,
 565                        new: start_row..end_row,
 566                    })
 567                }
 568                false
 569            } else {
 570                true
 571            }
 572        });
 573        self.0.sync(wrap_snapshot, edits);
 574    }
 575}
 576
 577impl BlockSnapshot {
 578    #[cfg(test)]
 579    pub fn text(&self) -> String {
 580        self.chunks(
 581            0..self.transforms.summary().output_rows,
 582            false,
 583            Highlights::default(),
 584        )
 585        .map(|chunk| chunk.text)
 586        .collect()
 587    }
 588
 589    pub(crate) fn chunks<'a>(
 590        &'a self,
 591        rows: Range<u32>,
 592        language_aware: bool,
 593        highlights: Highlights<'a>,
 594    ) -> BlockChunks<'a> {
 595        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
 596        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 597        let input_end = {
 598            cursor.seek(&BlockRow(rows.end), Bias::Right, &());
 599            let overshoot = if cursor
 600                .item()
 601                .map_or(false, |transform| transform.is_isomorphic())
 602            {
 603                rows.end - cursor.start().0 .0
 604            } else {
 605                0
 606            };
 607            cursor.start().1 .0 + overshoot
 608        };
 609        let input_start = {
 610            cursor.seek(&BlockRow(rows.start), Bias::Right, &());
 611            let overshoot = if cursor
 612                .item()
 613                .map_or(false, |transform| transform.is_isomorphic())
 614            {
 615                rows.start - cursor.start().0 .0
 616            } else {
 617                0
 618            };
 619            cursor.start().1 .0 + overshoot
 620        };
 621        BlockChunks {
 622            input_chunks: self.wrap_snapshot.chunks(
 623                input_start..input_end,
 624                language_aware,
 625                highlights,
 626            ),
 627            input_chunk: Default::default(),
 628            transforms: cursor,
 629            output_row: rows.start,
 630            max_output_row,
 631        }
 632    }
 633
 634    pub fn buffer_rows(&self, start_row: u32) -> BlockBufferRows {
 635        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 636        cursor.seek(&BlockRow(start_row), Bias::Right, &());
 637        let (output_start, input_start) = cursor.start();
 638        let overshoot = if cursor.item().map_or(false, |t| t.is_isomorphic()) {
 639            start_row - output_start.0
 640        } else {
 641            0
 642        };
 643        let input_start_row = input_start.0 + overshoot;
 644        BlockBufferRows {
 645            transforms: cursor,
 646            input_buffer_rows: self.wrap_snapshot.buffer_rows(input_start_row),
 647            output_row: start_row,
 648            started: false,
 649        }
 650    }
 651
 652    pub fn blocks_in_range(
 653        &self,
 654        rows: Range<u32>,
 655    ) -> impl Iterator<Item = (u32, &TransformBlock)> {
 656        let mut cursor = self.transforms.cursor::<BlockRow>();
 657        cursor.seek(&BlockRow(rows.start), Bias::Right, &());
 658        std::iter::from_fn(move || {
 659            while let Some(transform) = cursor.item() {
 660                let start_row = cursor.start().0;
 661                if start_row >= rows.end {
 662                    break;
 663                }
 664                if let Some(block) = &transform.block {
 665                    cursor.next(&());
 666                    return Some((start_row, block));
 667                } else {
 668                    cursor.next(&());
 669                }
 670            }
 671            None
 672        })
 673    }
 674
 675    pub fn max_point(&self) -> BlockPoint {
 676        let row = self.transforms.summary().output_rows - 1;
 677        BlockPoint::new(row, self.line_len(row))
 678    }
 679
 680    pub fn longest_row(&self) -> u32 {
 681        let input_row = self.wrap_snapshot.longest_row();
 682        self.to_block_point(WrapPoint::new(input_row, 0)).row
 683    }
 684
 685    pub fn line_len(&self, row: u32) -> u32 {
 686        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 687        cursor.seek(&BlockRow(row), Bias::Right, &());
 688        if let Some(transform) = cursor.item() {
 689            let (output_start, input_start) = cursor.start();
 690            let overshoot = row - output_start.0;
 691            if transform.block.is_some() {
 692                0
 693            } else {
 694                self.wrap_snapshot.line_len(input_start.0 + overshoot)
 695            }
 696        } else {
 697            panic!("row out of range");
 698        }
 699    }
 700
 701    pub fn is_block_line(&self, row: u32) -> bool {
 702        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 703        cursor.seek(&BlockRow(row), Bias::Right, &());
 704        cursor.item().map_or(false, |t| t.block.is_some())
 705    }
 706
 707    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
 708        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 709        cursor.seek(&BlockRow(point.row), Bias::Right, &());
 710
 711        let max_input_row = WrapRow(self.transforms.summary().input_rows);
 712        let mut search_left =
 713            (bias == Bias::Left && cursor.start().1 .0 > 0) || cursor.end(&()).1 == max_input_row;
 714        let mut reversed = false;
 715
 716        loop {
 717            if let Some(transform) = cursor.item() {
 718                if transform.is_isomorphic() {
 719                    let (output_start_row, input_start_row) = cursor.start();
 720                    let (output_end_row, input_end_row) = cursor.end(&());
 721                    let output_start = Point::new(output_start_row.0, 0);
 722                    let input_start = Point::new(input_start_row.0, 0);
 723                    let input_end = Point::new(input_end_row.0, 0);
 724                    let input_point = if point.row >= output_end_row.0 {
 725                        let line_len = self.wrap_snapshot.line_len(input_end_row.0 - 1);
 726                        self.wrap_snapshot
 727                            .clip_point(WrapPoint::new(input_end_row.0 - 1, line_len), bias)
 728                    } else {
 729                        let output_overshoot = point.0.saturating_sub(output_start);
 730                        self.wrap_snapshot
 731                            .clip_point(WrapPoint(input_start + output_overshoot), bias)
 732                    };
 733
 734                    if (input_start..input_end).contains(&input_point.0) {
 735                        let input_overshoot = input_point.0.saturating_sub(input_start);
 736                        return BlockPoint(output_start + input_overshoot);
 737                    }
 738                }
 739
 740                if search_left {
 741                    cursor.prev(&());
 742                } else {
 743                    cursor.next(&());
 744                }
 745            } else if reversed {
 746                return self.max_point();
 747            } else {
 748                reversed = true;
 749                search_left = !search_left;
 750                cursor.seek(&BlockRow(point.row), Bias::Right, &());
 751            }
 752        }
 753    }
 754
 755    pub fn to_block_point(&self, wrap_point: WrapPoint) -> BlockPoint {
 756        let mut cursor = self.transforms.cursor::<(WrapRow, BlockRow)>();
 757        cursor.seek(&WrapRow(wrap_point.row()), Bias::Right, &());
 758        if let Some(transform) = cursor.item() {
 759            debug_assert!(transform.is_isomorphic());
 760        } else {
 761            return self.max_point();
 762        }
 763
 764        let (input_start_row, output_start_row) = cursor.start();
 765        let input_start = Point::new(input_start_row.0, 0);
 766        let output_start = Point::new(output_start_row.0, 0);
 767        let input_overshoot = wrap_point.0 - input_start;
 768        BlockPoint(output_start + input_overshoot)
 769    }
 770
 771    pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
 772        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 773        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
 774        if let Some(transform) = cursor.item() {
 775            match transform.block.as_ref().map(|b| b.disposition()) {
 776                Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
 777                Some(BlockDisposition::Below) => {
 778                    let wrap_row = cursor.start().1 .0 - 1;
 779                    WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
 780                }
 781                None => {
 782                    let overshoot = block_point.row - cursor.start().0 .0;
 783                    let wrap_row = cursor.start().1 .0 + overshoot;
 784                    WrapPoint::new(wrap_row, block_point.column)
 785                }
 786            }
 787        } else {
 788            self.wrap_snapshot.max_point()
 789        }
 790    }
 791}
 792
 793impl Transform {
 794    fn isomorphic(rows: u32) -> Self {
 795        Self {
 796            summary: TransformSummary {
 797                input_rows: rows,
 798                output_rows: rows,
 799            },
 800            block: None,
 801        }
 802    }
 803
 804    fn block(block: TransformBlock) -> Self {
 805        Self {
 806            summary: TransformSummary {
 807                input_rows: 0,
 808                output_rows: block.height() as u32,
 809            },
 810            block: Some(block),
 811        }
 812    }
 813
 814    fn is_isomorphic(&self) -> bool {
 815        self.block.is_none()
 816    }
 817}
 818
 819impl<'a> Iterator for BlockChunks<'a> {
 820    type Item = Chunk<'a>;
 821
 822    fn next(&mut self) -> Option<Self::Item> {
 823        if self.output_row >= self.max_output_row {
 824            return None;
 825        }
 826
 827        let transform = self.transforms.item()?;
 828        if transform.block.is_some() {
 829            let block_start = self.transforms.start().0 .0;
 830            let mut block_end = self.transforms.end(&()).0 .0;
 831            self.transforms.next(&());
 832            if self.transforms.item().is_none() {
 833                block_end -= 1;
 834            }
 835
 836            let start_in_block = self.output_row - block_start;
 837            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
 838            let line_count = end_in_block - start_in_block;
 839            self.output_row += line_count;
 840
 841            return Some(Chunk {
 842                text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
 843                ..Default::default()
 844            });
 845        }
 846
 847        if self.input_chunk.text.is_empty() {
 848            if let Some(input_chunk) = self.input_chunks.next() {
 849                self.input_chunk = input_chunk;
 850            } else {
 851                self.output_row += 1;
 852                if self.output_row < self.max_output_row {
 853                    self.transforms.next(&());
 854                    return Some(Chunk {
 855                        text: "\n",
 856                        ..Default::default()
 857                    });
 858                } else {
 859                    return None;
 860                }
 861            }
 862        }
 863
 864        let transform_end = self.transforms.end(&()).0 .0;
 865        let (prefix_rows, prefix_bytes) =
 866            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
 867        self.output_row += prefix_rows;
 868        let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
 869        self.input_chunk.text = suffix;
 870        if self.output_row == transform_end {
 871            self.transforms.next(&());
 872        }
 873
 874        Some(Chunk {
 875            text: prefix,
 876            ..self.input_chunk
 877        })
 878    }
 879}
 880
 881impl<'a> Iterator for BlockBufferRows<'a> {
 882    type Item = Option<u32>;
 883
 884    fn next(&mut self) -> Option<Self::Item> {
 885        if self.started {
 886            self.output_row += 1;
 887        } else {
 888            self.started = true;
 889        }
 890
 891        if self.output_row >= self.transforms.end(&()).0 .0 {
 892            self.transforms.next(&());
 893        }
 894
 895        let transform = self.transforms.item()?;
 896        if transform.block.is_some() {
 897            Some(None)
 898        } else {
 899            Some(self.input_buffer_rows.next().unwrap())
 900        }
 901    }
 902}
 903
 904impl sum_tree::Item for Transform {
 905    type Summary = TransformSummary;
 906
 907    fn summary(&self) -> Self::Summary {
 908        self.summary.clone()
 909    }
 910}
 911
 912impl sum_tree::Summary for TransformSummary {
 913    type Context = ();
 914
 915    fn add_summary(&mut self, summary: &Self, _: &()) {
 916        self.input_rows += summary.input_rows;
 917        self.output_rows += summary.output_rows;
 918    }
 919}
 920
 921impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
 922    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 923        self.0 += summary.input_rows;
 924    }
 925}
 926
 927impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
 928    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 929        self.0 += summary.output_rows;
 930    }
 931}
 932
 933impl BlockDisposition {
 934    fn is_below(&self) -> bool {
 935        matches!(self, BlockDisposition::Below)
 936    }
 937}
 938
 939impl<'a> Deref for BlockContext<'a, '_> {
 940    type Target = ElementContext<'a>;
 941
 942    fn deref(&self) -> &Self::Target {
 943        self.context
 944    }
 945}
 946
 947impl DerefMut for BlockContext<'_, '_> {
 948    fn deref_mut(&mut self) -> &mut Self::Target {
 949        self.context
 950    }
 951}
 952
 953impl Block {
 954    pub fn render(&self, cx: &mut BlockContext) -> AnyElement {
 955        self.render.lock()(cx)
 956    }
 957
 958    pub fn position(&self) -> &Anchor {
 959        &self.position
 960    }
 961
 962    pub fn style(&self) -> BlockStyle {
 963        self.style
 964    }
 965}
 966
 967impl Debug for Block {
 968    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 969        f.debug_struct("Block")
 970            .field("id", &self.id)
 971            .field("position", &self.position)
 972            .field("disposition", &self.disposition)
 973            .finish()
 974    }
 975}
 976
 977// Count the number of bytes prior to a target point. If the string doesn't contain the target
 978// point, return its total extent. Otherwise return the target point itself.
 979fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
 980    let mut row = 0;
 981    let mut offset = 0;
 982    for (ix, line) in s.split('\n').enumerate() {
 983        if ix > 0 {
 984            row += 1;
 985            offset += 1;
 986        }
 987        if row >= target {
 988            break;
 989        }
 990        offset += line.len();
 991    }
 992    (row, offset)
 993}
 994
 995#[cfg(test)]
 996mod tests {
 997    use super::*;
 998    use crate::display_map::inlay_map::InlayMap;
 999    use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
1000    use gpui::{div, font, px, Element};
1001    use multi_buffer::MultiBuffer;
1002    use rand::prelude::*;
1003    use settings::SettingsStore;
1004    use std::env;
1005    use util::RandomCharIter;
1006
1007    #[gpui::test]
1008    fn test_offset_for_row() {
1009        assert_eq!(offset_for_row("", 0), (0, 0));
1010        assert_eq!(offset_for_row("", 1), (0, 0));
1011        assert_eq!(offset_for_row("abcd", 0), (0, 0));
1012        assert_eq!(offset_for_row("abcd", 1), (0, 4));
1013        assert_eq!(offset_for_row("\n", 0), (0, 0));
1014        assert_eq!(offset_for_row("\n", 1), (1, 1));
1015        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
1016        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
1017        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
1018        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
1019    }
1020
1021    #[gpui::test]
1022    fn test_basic_blocks(cx: &mut gpui::TestAppContext) {
1023        cx.update(|cx| init_test(cx));
1024
1025        let text = "aaa\nbbb\nccc\nddd";
1026
1027        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1028        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1029        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1030        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1031        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1032        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 1.try_into().unwrap());
1033        let (wrap_map, wraps_snapshot) =
1034            cx.update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), None, cx));
1035        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
1036
1037        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1038        let block_ids = writer.insert(vec![
1039            BlockProperties {
1040                style: BlockStyle::Fixed,
1041                position: buffer_snapshot.anchor_after(Point::new(1, 0)),
1042                height: 1,
1043                disposition: BlockDisposition::Above,
1044                render: Arc::new(|_| div().into_any()),
1045            },
1046            BlockProperties {
1047                style: BlockStyle::Fixed,
1048                position: buffer_snapshot.anchor_after(Point::new(1, 2)),
1049                height: 2,
1050                disposition: BlockDisposition::Above,
1051                render: Arc::new(|_| div().into_any()),
1052            },
1053            BlockProperties {
1054                style: BlockStyle::Fixed,
1055                position: buffer_snapshot.anchor_after(Point::new(3, 3)),
1056                height: 3,
1057                disposition: BlockDisposition::Below,
1058                render: Arc::new(|_| div().into_any()),
1059            },
1060        ]);
1061
1062        let snapshot = block_map.read(wraps_snapshot, Default::default());
1063        assert_eq!(snapshot.text(), "aaa\n\n\n\nbbb\nccc\nddd\n\n\n");
1064
1065        let blocks = snapshot
1066            .blocks_in_range(0..8)
1067            .map(|(start_row, block)| {
1068                let block = block.as_custom().unwrap();
1069                (start_row..start_row + block.height as u32, block.id)
1070            })
1071            .collect::<Vec<_>>();
1072
1073        // When multiple blocks are on the same line, the newer blocks appear first.
1074        assert_eq!(
1075            blocks,
1076            &[
1077                (1..2, block_ids[0]),
1078                (2..4, block_ids[1]),
1079                (7..10, block_ids[2]),
1080            ]
1081        );
1082
1083        assert_eq!(
1084            snapshot.to_block_point(WrapPoint::new(0, 3)),
1085            BlockPoint::new(0, 3)
1086        );
1087        assert_eq!(
1088            snapshot.to_block_point(WrapPoint::new(1, 0)),
1089            BlockPoint::new(4, 0)
1090        );
1091        assert_eq!(
1092            snapshot.to_block_point(WrapPoint::new(3, 3)),
1093            BlockPoint::new(6, 3)
1094        );
1095
1096        assert_eq!(
1097            snapshot.to_wrap_point(BlockPoint::new(0, 3)),
1098            WrapPoint::new(0, 3)
1099        );
1100        assert_eq!(
1101            snapshot.to_wrap_point(BlockPoint::new(1, 0)),
1102            WrapPoint::new(1, 0)
1103        );
1104        assert_eq!(
1105            snapshot.to_wrap_point(BlockPoint::new(3, 0)),
1106            WrapPoint::new(1, 0)
1107        );
1108        assert_eq!(
1109            snapshot.to_wrap_point(BlockPoint::new(7, 0)),
1110            WrapPoint::new(3, 3)
1111        );
1112
1113        assert_eq!(
1114            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
1115            BlockPoint::new(0, 3)
1116        );
1117        assert_eq!(
1118            snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
1119            BlockPoint::new(4, 0)
1120        );
1121        assert_eq!(
1122            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
1123            BlockPoint::new(0, 3)
1124        );
1125        assert_eq!(
1126            snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
1127            BlockPoint::new(4, 0)
1128        );
1129        assert_eq!(
1130            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Left),
1131            BlockPoint::new(4, 0)
1132        );
1133        assert_eq!(
1134            snapshot.clip_point(BlockPoint::new(4, 0), Bias::Right),
1135            BlockPoint::new(4, 0)
1136        );
1137        assert_eq!(
1138            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Left),
1139            BlockPoint::new(6, 3)
1140        );
1141        assert_eq!(
1142            snapshot.clip_point(BlockPoint::new(6, 3), Bias::Right),
1143            BlockPoint::new(6, 3)
1144        );
1145        assert_eq!(
1146            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Left),
1147            BlockPoint::new(6, 3)
1148        );
1149        assert_eq!(
1150            snapshot.clip_point(BlockPoint::new(7, 0), Bias::Right),
1151            BlockPoint::new(6, 3)
1152        );
1153
1154        assert_eq!(
1155            snapshot.buffer_rows(0).collect::<Vec<_>>(),
1156            &[
1157                Some(0),
1158                None,
1159                None,
1160                None,
1161                Some(1),
1162                Some(2),
1163                Some(3),
1164                None,
1165                None,
1166                None
1167            ]
1168        );
1169
1170        // Insert a line break, separating two block decorations into separate lines.
1171        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1172            buffer.edit([(Point::new(1, 1)..Point::new(1, 1), "!!!\n")], None, cx);
1173            buffer.snapshot(cx)
1174        });
1175
1176        let (inlay_snapshot, inlay_edits) =
1177            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1178        let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1179        let (tab_snapshot, tab_edits) =
1180            tab_map.sync(fold_snapshot, fold_edits, 4.try_into().unwrap());
1181        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1182            wrap_map.sync(tab_snapshot, tab_edits, cx)
1183        });
1184        let snapshot = block_map.read(wraps_snapshot, wrap_edits);
1185        assert_eq!(snapshot.text(), "aaa\n\nb!!!\n\n\nbb\nccc\nddd\n\n\n");
1186    }
1187
1188    #[gpui::test]
1189    fn test_blocks_on_wrapped_lines(cx: &mut gpui::TestAppContext) {
1190        cx.update(|cx| init_test(cx));
1191
1192        let _font_id = cx.text_system().font_id(&font("Helvetica")).unwrap();
1193
1194        let text = "one two three\nfour five six\nseven eight";
1195
1196        let buffer = cx.update(|cx| MultiBuffer::build_simple(text, cx));
1197        let buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1198        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1199        let (_, fold_snapshot) = FoldMap::new(inlay_snapshot);
1200        let (_, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1201        let (_, wraps_snapshot) = cx.update(|cx| {
1202            WrapMap::new(tab_snapshot, font("Helvetica"), px(14.0), Some(px(60.)), cx)
1203        });
1204        let mut block_map = BlockMap::new(wraps_snapshot.clone(), 1, 1);
1205
1206        let mut writer = block_map.write(wraps_snapshot.clone(), Default::default());
1207        writer.insert(vec![
1208            BlockProperties {
1209                style: BlockStyle::Fixed,
1210                position: buffer_snapshot.anchor_after(Point::new(1, 12)),
1211                disposition: BlockDisposition::Above,
1212                render: Arc::new(|_| div().into_any()),
1213                height: 1,
1214            },
1215            BlockProperties {
1216                style: BlockStyle::Fixed,
1217                position: buffer_snapshot.anchor_after(Point::new(1, 1)),
1218                disposition: BlockDisposition::Below,
1219                render: Arc::new(|_| div().into_any()),
1220                height: 1,
1221            },
1222        ]);
1223
1224        // Blocks with an 'above' disposition go above their corresponding buffer line.
1225        // Blocks with a 'below' disposition go below their corresponding buffer line.
1226        let snapshot = block_map.read(wraps_snapshot, Default::default());
1227        assert_eq!(
1228            snapshot.text(),
1229            "one two \nthree\n\nfour five \nsix\n\nseven \neight"
1230        );
1231    }
1232
1233    #[gpui::test(iterations = 100)]
1234    fn test_random_blocks(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1235        cx.update(|cx| init_test(cx));
1236
1237        let operations = env::var("OPERATIONS")
1238            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1239            .unwrap_or(10);
1240
1241        let wrap_width = if rng.gen_bool(0.2) {
1242            None
1243        } else {
1244            Some(px(rng.gen_range(0.0..=100.0)))
1245        };
1246        let tab_size = 1.try_into().unwrap();
1247        let font_size = px(14.0);
1248        let buffer_start_header_height = rng.gen_range(1..=5);
1249        let excerpt_header_height = rng.gen_range(1..=5);
1250
1251        log::info!("Wrap width: {:?}", wrap_width);
1252        log::info!("Excerpt Header Height: {:?}", excerpt_header_height);
1253
1254        let buffer = if rng.gen() {
1255            let len = rng.gen_range(0..10);
1256            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1257            log::info!("initial buffer text: {:?}", text);
1258            cx.update(|cx| MultiBuffer::build_simple(&text, cx))
1259        } else {
1260            cx.update(|cx| MultiBuffer::build_random(&mut rng, cx))
1261        };
1262
1263        let mut buffer_snapshot = cx.update(|cx| buffer.read(cx).snapshot(cx));
1264        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1265        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot);
1266        let (mut tab_map, tab_snapshot) = TabMap::new(fold_snapshot, 4.try_into().unwrap());
1267        let (wrap_map, wraps_snapshot) = cx
1268            .update(|cx| WrapMap::new(tab_snapshot, font("Helvetica"), font_size, wrap_width, cx));
1269        let mut block_map = BlockMap::new(
1270            wraps_snapshot,
1271            buffer_start_header_height,
1272            excerpt_header_height,
1273        );
1274        let mut custom_blocks = Vec::new();
1275
1276        for _ in 0..operations {
1277            let mut buffer_edits = Vec::new();
1278            match rng.gen_range(0..=100) {
1279                0..=19 => {
1280                    let wrap_width = if rng.gen_bool(0.2) {
1281                        None
1282                    } else {
1283                        Some(px(rng.gen_range(0.0..=100.0)))
1284                    };
1285                    log::info!("Setting wrap width to {:?}", wrap_width);
1286                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1287                }
1288                20..=39 => {
1289                    let block_count = rng.gen_range(1..=5);
1290                    let block_properties = (0..block_count)
1291                        .map(|_| {
1292                            let buffer = cx.update(|cx| buffer.read(cx).read(cx).clone());
1293                            let position = buffer.anchor_after(
1294                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1295                            );
1296
1297                            let disposition = if rng.gen() {
1298                                BlockDisposition::Above
1299                            } else {
1300                                BlockDisposition::Below
1301                            };
1302                            let height = rng.gen_range(1..5);
1303                            log::info!(
1304                                "inserting block {:?} {:?} with height {}",
1305                                disposition,
1306                                position.to_point(&buffer),
1307                                height
1308                            );
1309                            BlockProperties {
1310                                style: BlockStyle::Fixed,
1311                                position,
1312                                height,
1313                                disposition,
1314                                render: Arc::new(|_| div().into_any()),
1315                            }
1316                        })
1317                        .collect::<Vec<_>>();
1318
1319                    let (inlay_snapshot, inlay_edits) =
1320                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1321                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1322                    let (tab_snapshot, tab_edits) =
1323                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1324                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1325                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1326                    });
1327                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1328                    let block_ids = block_map.insert(block_properties.clone());
1329                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1330                        custom_blocks.push((block_id, props));
1331                    }
1332                }
1333                40..=59 if !custom_blocks.is_empty() => {
1334                    let block_count = rng.gen_range(1..=4.min(custom_blocks.len()));
1335                    let block_ids_to_remove = (0..block_count)
1336                        .map(|_| {
1337                            custom_blocks
1338                                .remove(rng.gen_range(0..custom_blocks.len()))
1339                                .0
1340                        })
1341                        .collect();
1342
1343                    let (inlay_snapshot, inlay_edits) =
1344                        inlay_map.sync(buffer_snapshot.clone(), vec![]);
1345                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1346                    let (tab_snapshot, tab_edits) =
1347                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1348                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1349                        wrap_map.sync(tab_snapshot, tab_edits, cx)
1350                    });
1351                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits);
1352                    block_map.remove(block_ids_to_remove);
1353                }
1354                _ => {
1355                    buffer.update(cx, |buffer, cx| {
1356                        let mutation_count = rng.gen_range(1..=5);
1357                        let subscription = buffer.subscribe();
1358                        buffer.randomly_mutate(&mut rng, mutation_count, cx);
1359                        buffer_snapshot = buffer.snapshot(cx);
1360                        buffer_edits.extend(subscription.consume());
1361                        log::info!("buffer text: {:?}", buffer_snapshot.text());
1362                    });
1363                }
1364            }
1365
1366            let (inlay_snapshot, inlay_edits) =
1367                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1368            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1369            let (tab_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
1370            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1371                wrap_map.sync(tab_snapshot, tab_edits, cx)
1372            });
1373            let blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits);
1374            assert_eq!(
1375                blocks_snapshot.transforms.summary().input_rows,
1376                wraps_snapshot.max_point().row() + 1
1377            );
1378            log::info!("blocks text: {:?}", blocks_snapshot.text());
1379
1380            let mut expected_blocks = Vec::new();
1381            expected_blocks.extend(custom_blocks.iter().map(|(id, block)| {
1382                let mut position = block.position.to_point(&buffer_snapshot);
1383                match block.disposition {
1384                    BlockDisposition::Above => {
1385                        position.column = 0;
1386                    }
1387                    BlockDisposition::Below => {
1388                        position.column = buffer_snapshot.line_len(position.row);
1389                    }
1390                };
1391                let row = wraps_snapshot.make_wrap_point(position, Bias::Left).row();
1392                (
1393                    row,
1394                    ExpectedBlock::Custom {
1395                        disposition: block.disposition,
1396                        id: *id,
1397                        height: block.height,
1398                    },
1399                )
1400            }));
1401            expected_blocks.extend(buffer_snapshot.excerpt_boundaries_in_range(0..).map(
1402                |boundary| {
1403                    let position =
1404                        wraps_snapshot.make_wrap_point(Point::new(boundary.row, 0), Bias::Left);
1405                    (
1406                        position.row(),
1407                        ExpectedBlock::ExcerptHeader {
1408                            height: if boundary.starts_new_buffer {
1409                                buffer_start_header_height
1410                            } else {
1411                                excerpt_header_height
1412                            },
1413                            starts_new_buffer: boundary.starts_new_buffer,
1414                        },
1415                    )
1416                },
1417            ));
1418            expected_blocks.sort_unstable();
1419            let mut sorted_blocks_iter = expected_blocks.into_iter().peekable();
1420
1421            let input_buffer_rows = buffer_snapshot.buffer_rows(0).collect::<Vec<_>>();
1422            let mut expected_buffer_rows = Vec::new();
1423            let mut expected_text = String::new();
1424            let mut expected_block_positions = Vec::new();
1425            let input_text = wraps_snapshot.text();
1426            for (row, input_line) in input_text.split('\n').enumerate() {
1427                let row = row as u32;
1428                if row > 0 {
1429                    expected_text.push('\n');
1430                }
1431
1432                let buffer_row = input_buffer_rows[wraps_snapshot
1433                    .to_point(WrapPoint::new(row, 0), Bias::Left)
1434                    .row as usize];
1435
1436                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
1437                    if *block_row == row && block.disposition() == BlockDisposition::Above {
1438                        let (_, block) = sorted_blocks_iter.next().unwrap();
1439                        let height = block.height() as usize;
1440                        expected_block_positions
1441                            .push((expected_text.matches('\n').count() as u32, block));
1442                        let text = "\n".repeat(height);
1443                        expected_text.push_str(&text);
1444                        for _ in 0..height {
1445                            expected_buffer_rows.push(None);
1446                        }
1447                    } else {
1448                        break;
1449                    }
1450                }
1451
1452                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1453                expected_buffer_rows.push(if soft_wrapped { None } else { buffer_row });
1454                expected_text.push_str(input_line);
1455
1456                while let Some((block_row, block)) = sorted_blocks_iter.peek() {
1457                    if *block_row == row && block.disposition() == BlockDisposition::Below {
1458                        let (_, block) = sorted_blocks_iter.next().unwrap();
1459                        let height = block.height() as usize;
1460                        expected_block_positions
1461                            .push((expected_text.matches('\n').count() as u32 + 1, block));
1462                        let text = "\n".repeat(height);
1463                        expected_text.push_str(&text);
1464                        for _ in 0..height {
1465                            expected_buffer_rows.push(None);
1466                        }
1467                    } else {
1468                        break;
1469                    }
1470                }
1471            }
1472
1473            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1474            let expected_row_count = expected_lines.len();
1475            for start_row in 0..expected_row_count {
1476                let expected_text = expected_lines[start_row..].join("\n");
1477                let actual_text = blocks_snapshot
1478                    .chunks(
1479                        start_row as u32..blocks_snapshot.max_point().row + 1,
1480                        false,
1481                        Highlights::default(),
1482                    )
1483                    .map(|chunk| chunk.text)
1484                    .collect::<String>();
1485                assert_eq!(
1486                    actual_text, expected_text,
1487                    "incorrect text starting from row {}",
1488                    start_row
1489                );
1490                assert_eq!(
1491                    blocks_snapshot
1492                        .buffer_rows(start_row as u32)
1493                        .collect::<Vec<_>>(),
1494                    &expected_buffer_rows[start_row..]
1495                );
1496            }
1497
1498            assert_eq!(
1499                blocks_snapshot
1500                    .blocks_in_range(0..(expected_row_count as u32))
1501                    .map(|(row, block)| (row, block.clone().into()))
1502                    .collect::<Vec<_>>(),
1503                expected_block_positions
1504            );
1505
1506            let mut expected_longest_rows = Vec::new();
1507            let mut longest_line_len = -1_isize;
1508            for (row, line) in expected_lines.iter().enumerate() {
1509                let row = row as u32;
1510
1511                assert_eq!(
1512                    blocks_snapshot.line_len(row),
1513                    line.len() as u32,
1514                    "invalid line len for row {}",
1515                    row
1516                );
1517
1518                let line_char_count = line.chars().count() as isize;
1519                match line_char_count.cmp(&longest_line_len) {
1520                    Ordering::Less => {}
1521                    Ordering::Equal => expected_longest_rows.push(row),
1522                    Ordering::Greater => {
1523                        longest_line_len = line_char_count;
1524                        expected_longest_rows.clear();
1525                        expected_longest_rows.push(row);
1526                    }
1527                }
1528            }
1529
1530            let longest_row = blocks_snapshot.longest_row();
1531            assert!(
1532                expected_longest_rows.contains(&longest_row),
1533                "incorrect longest row {}. expected {:?} with length {}",
1534                longest_row,
1535                expected_longest_rows,
1536                longest_line_len,
1537            );
1538
1539            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1540                let wrap_point = WrapPoint::new(row, 0);
1541                let block_point = blocks_snapshot.to_block_point(wrap_point);
1542                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1543            }
1544
1545            let mut block_point = BlockPoint::new(0, 0);
1546            for c in expected_text.chars() {
1547                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1548                let left_buffer_point = blocks_snapshot.to_point(left_point, Bias::Left);
1549                assert_eq!(
1550                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1551                    left_point
1552                );
1553                assert_eq!(
1554                    left_buffer_point,
1555                    buffer_snapshot.clip_point(left_buffer_point, Bias::Right),
1556                    "{:?} is not valid in buffer coordinates",
1557                    left_point
1558                );
1559
1560                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1561                let right_buffer_point = blocks_snapshot.to_point(right_point, Bias::Right);
1562                assert_eq!(
1563                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1564                    right_point
1565                );
1566                assert_eq!(
1567                    right_buffer_point,
1568                    buffer_snapshot.clip_point(right_buffer_point, Bias::Left),
1569                    "{:?} is not valid in buffer coordinates",
1570                    right_point
1571                );
1572
1573                if c == '\n' {
1574                    block_point.0 += Point::new(1, 0);
1575                } else {
1576                    block_point.column += c.len_utf8() as u32;
1577                }
1578            }
1579        }
1580
1581        #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
1582        enum ExpectedBlock {
1583            ExcerptHeader {
1584                height: u8,
1585                starts_new_buffer: bool,
1586            },
1587            Custom {
1588                disposition: BlockDisposition,
1589                id: BlockId,
1590                height: u8,
1591            },
1592        }
1593
1594        impl ExpectedBlock {
1595            fn height(&self) -> u8 {
1596                match self {
1597                    ExpectedBlock::ExcerptHeader { height, .. } => *height,
1598                    ExpectedBlock::Custom { height, .. } => *height,
1599                }
1600            }
1601
1602            fn disposition(&self) -> BlockDisposition {
1603                match self {
1604                    ExpectedBlock::ExcerptHeader { .. } => BlockDisposition::Above,
1605                    ExpectedBlock::Custom { disposition, .. } => *disposition,
1606                }
1607            }
1608        }
1609
1610        impl From<TransformBlock> for ExpectedBlock {
1611            fn from(block: TransformBlock) -> Self {
1612                match block {
1613                    TransformBlock::Custom(block) => ExpectedBlock::Custom {
1614                        id: block.id,
1615                        disposition: block.disposition,
1616                        height: block.height,
1617                    },
1618                    TransformBlock::ExcerptHeader {
1619                        height,
1620                        starts_new_buffer,
1621                        ..
1622                    } => ExpectedBlock::ExcerptHeader {
1623                        height,
1624                        starts_new_buffer,
1625                    },
1626                }
1627            }
1628        }
1629    }
1630
1631    fn init_test(cx: &mut gpui::AppContext) {
1632        let settings = SettingsStore::test(cx);
1633        cx.set_global(settings);
1634        theme::init(theme::LoadThemes::JustBase, cx);
1635    }
1636
1637    impl TransformBlock {
1638        fn as_custom(&self) -> Option<&Block> {
1639            match self {
1640                TransformBlock::Custom(block) => Some(block),
1641                TransformBlock::ExcerptHeader { .. } => None,
1642            }
1643        }
1644    }
1645
1646    impl BlockSnapshot {
1647        fn to_point(&self, point: BlockPoint, bias: Bias) -> Point {
1648            self.wrap_snapshot.to_point(self.to_wrap_point(point), bias)
1649        }
1650    }
1651}