block_map.rs

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