block_map.rs

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