block_map.rs

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