block_map.rs

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