block_map.rs

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