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