block_map.rs

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