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