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