block_map.rs

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