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