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        let buffer_edits = buffer.update(cx, |buffer, cx| {
1232            let v0 = buffer.version();
1233            buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx);
1234            buffer.edits_since(&v0).collect()
1235        });
1236
1237        let (folds_snapshot, fold_edits) = fold_map.read(buffer_edits, cx);
1238        let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1239        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1240            wrap_map.sync(tabs_snapshot, tab_edits, cx)
1241        });
1242        let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx);
1243        assert_eq!(
1244            snapshot.text(),
1245            "aaa\nBLOCK 1\nb!!!\n BLOCK 2\nbb\nccc\nddd\n  BLOCK 3"
1246        );
1247    }
1248
1249    #[gpui::test]
1250    fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
1251        let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1252        let font_id = cx
1253            .font_cache()
1254            .select_font(family_id, &Default::default())
1255            .unwrap();
1256
1257        let text = "one two three\nfour five six\nseven eight";
1258
1259        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
1260        let (_, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1261        let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
1262        let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
1263        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
1264
1265        let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
1266        writer.insert(
1267            vec![
1268                BlockProperties {
1269                    position: Point::new(1, 12),
1270                    text: "<BLOCK 1",
1271                    disposition: BlockDisposition::Above,
1272                    build_runs: None,
1273                    build_style: None,
1274                },
1275                BlockProperties {
1276                    position: Point::new(1, 1),
1277                    text: ">BLOCK 2",
1278                    disposition: BlockDisposition::Below,
1279                    build_runs: None,
1280                    build_style: None,
1281                },
1282            ],
1283            cx,
1284        );
1285
1286        // Blocks with an 'above' disposition go above their corresponding buffer line.
1287        // Blocks with a 'below' disposition go below their corresponding buffer line.
1288        let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
1289        assert_eq!(
1290            snapshot.text(),
1291            "one two \nthree\n  <BLOCK 1\nfour five \nsix\n >BLOCK 2\nseven \neight"
1292        );
1293    }
1294
1295    #[gpui::test(iterations = 100)]
1296    fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
1297        let operations = env::var("OPERATIONS")
1298            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1299            .unwrap_or(10);
1300
1301        let wrap_width = if rng.gen_bool(0.2) {
1302            None
1303        } else {
1304            Some(rng.gen_range(0.0..=100.0))
1305        };
1306        let tab_size = 1;
1307        let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
1308        let font_id = cx
1309            .font_cache()
1310            .select_font(family_id, &Default::default())
1311            .unwrap();
1312        let font_size = 14.0;
1313
1314        log::info!("Wrap width: {:?}", wrap_width);
1315
1316        let buffer = cx.add_model(|cx| {
1317            let len = rng.gen_range(0..10);
1318            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1319            log::info!("initial buffer text: {:?}", text);
1320            Buffer::new(0, text, cx)
1321        });
1322        let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
1323        let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
1324        let (wrap_map, wraps_snapshot) =
1325            WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1326        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1327        let mut expected_blocks = Vec::new();
1328
1329        for _ in 0..operations {
1330            let mut buffer_edits = Vec::new();
1331            match rng.gen_range(0..=100) {
1332                0..=19 => {
1333                    let wrap_width = if rng.gen_bool(0.2) {
1334                        None
1335                    } else {
1336                        Some(rng.gen_range(0.0..=100.0))
1337                    };
1338                    log::info!("Setting wrap width to {:?}", wrap_width);
1339                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1340                }
1341                20..=39 => {
1342                    let block_count = rng.gen_range(1..=1);
1343                    let block_properties = (0..block_count)
1344                        .map(|_| {
1345                            let buffer = buffer.read(cx);
1346                            let position = buffer.anchor_after(
1347                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1348                            );
1349
1350                            let len = rng.gen_range(0..10);
1351                            let mut text = Rope::from(
1352                                RandomCharIter::new(&mut rng)
1353                                    .take(len)
1354                                    .collect::<String>()
1355                                    .to_uppercase()
1356                                    .as_str(),
1357                            );
1358                            let disposition = if rng.gen() {
1359                                text.push_front("<");
1360                                BlockDisposition::Above
1361                            } else {
1362                                text.push_front(">");
1363                                BlockDisposition::Below
1364                            };
1365                            log::info!(
1366                                "inserting block {:?} {:?} with text {:?}",
1367                                disposition,
1368                                position.to_point(buffer),
1369                                text.to_string()
1370                            );
1371                            BlockProperties {
1372                                position,
1373                                text,
1374                                disposition,
1375                                build_runs: None,
1376                                build_style: None,
1377                            }
1378                        })
1379                        .collect::<Vec<_>>();
1380
1381                    let (folds_snapshot, fold_edits) = fold_map.read(vec![], 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                    let block_ids = block_map.insert(block_properties.clone(), cx);
1388                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1389                        expected_blocks.push((block_id, props));
1390                    }
1391                }
1392                40..=59 if !expected_blocks.is_empty() => {
1393                    let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1394                    let block_ids_to_remove = (0..block_count)
1395                        .map(|_| {
1396                            expected_blocks
1397                                .remove(rng.gen_range(0..expected_blocks.len()))
1398                                .0
1399                        })
1400                        .collect();
1401
1402                    let (folds_snapshot, fold_edits) = fold_map.read(vec![], cx);
1403                    let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1404                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1405                        wrap_map.sync(tabs_snapshot, tab_edits, cx)
1406                    });
1407                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1408                    block_map.remove(block_ids_to_remove, cx);
1409                }
1410                _ => {
1411                    buffer.update(cx, |buffer, _| {
1412                        let v0 = buffer.version();
1413                        buffer.randomly_edit(&mut rng, 1);
1414                        log::info!("buffer text: {:?}", buffer.text());
1415                        buffer_edits.extend(buffer.edits_since(&v0));
1416                    });
1417                }
1418            }
1419
1420            let (folds_snapshot, fold_edits) = fold_map.read(buffer_edits, cx);
1421            let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1422            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1423                wrap_map.sync(tabs_snapshot, tab_edits, cx)
1424            });
1425            let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1426            assert_eq!(
1427                blocks_snapshot.transforms.summary().input_rows,
1428                wraps_snapshot.max_point().row() + 1
1429            );
1430            log::info!("blocks text: {:?}", blocks_snapshot.text());
1431
1432            let buffer = buffer.read(cx);
1433            let mut sorted_blocks = expected_blocks
1434                .iter()
1435                .cloned()
1436                .map(|(id, block)| {
1437                    let mut position = block.position.to_point(buffer);
1438                    let column = wraps_snapshot.from_point(position, Bias::Left).column();
1439                    match block.disposition {
1440                        BlockDisposition::Above => {
1441                            position.column = 0;
1442                        }
1443                        BlockDisposition::Below => {
1444                            position.column = buffer.line_len(position.row);
1445                        }
1446                    };
1447                    let row = wraps_snapshot.from_point(position, Bias::Left).row();
1448                    (
1449                        id,
1450                        BlockProperties {
1451                            position: BlockPoint::new(row, column),
1452                            text: block.text,
1453                            build_runs: block.build_runs.clone(),
1454                            build_style: None,
1455                            disposition: block.disposition,
1456                        },
1457                    )
1458                })
1459                .collect::<Vec<_>>();
1460            sorted_blocks
1461                .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id));
1462            let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1463
1464            let mut expected_buffer_rows = Vec::new();
1465            let mut expected_text = String::new();
1466            let input_text = wraps_snapshot.text();
1467            for (row, input_line) in input_text.split('\n').enumerate() {
1468                let row = row as u32;
1469                if row > 0 {
1470                    expected_text.push('\n');
1471                }
1472
1473                let buffer_row = wraps_snapshot
1474                    .to_point(WrapPoint::new(row, 0), Bias::Left)
1475                    .row;
1476
1477                while let Some((block_id, block)) = sorted_blocks.peek() {
1478                    if block.position.row == row && block.disposition == BlockDisposition::Above {
1479                        let text = block.text.to_string();
1480                        let padding = " ".repeat(block.position.column as usize);
1481                        for line in text.split('\n') {
1482                            if !line.is_empty() {
1483                                expected_text.push_str(&padding);
1484                                expected_text.push_str(line);
1485                            }
1486                            expected_text.push('\n');
1487                            expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1488                        }
1489                        sorted_blocks.next();
1490                    } else {
1491                        break;
1492                    }
1493                }
1494
1495                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1496                expected_buffer_rows.push(if soft_wrapped {
1497                    DisplayRow::Wrap
1498                } else {
1499                    DisplayRow::Buffer(buffer_row)
1500                });
1501                expected_text.push_str(input_line);
1502
1503                while let Some((block_id, block)) = sorted_blocks.peek() {
1504                    if block.position.row == row && block.disposition == BlockDisposition::Below {
1505                        let text = block.text.to_string();
1506                        let padding = " ".repeat(block.position.column as usize);
1507                        for line in text.split('\n') {
1508                            expected_text.push('\n');
1509                            if !line.is_empty() {
1510                                expected_text.push_str(&padding);
1511                                expected_text.push_str(line);
1512                            }
1513                            expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1514                        }
1515                        sorted_blocks.next();
1516                    } else {
1517                        break;
1518                    }
1519                }
1520            }
1521
1522            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1523            let expected_row_count = expected_lines.len();
1524            for start_row in 0..expected_row_count {
1525                let expected_text = expected_lines[start_row..].join("\n");
1526                let actual_text = blocks_snapshot
1527                    .chunks(start_row as u32..expected_row_count as u32, None, None)
1528                    .map(|chunk| chunk.text)
1529                    .collect::<String>();
1530                assert_eq!(
1531                    actual_text, expected_text,
1532                    "incorrect text starting from row {}",
1533                    start_row
1534                );
1535                assert_eq!(
1536                    blocks_snapshot
1537                        .buffer_rows(start_row as u32, None)
1538                        .collect::<Vec<_>>(),
1539                    &expected_buffer_rows[start_row..]
1540                );
1541            }
1542
1543            let mut expected_longest_rows = Vec::new();
1544            let mut longest_line_len = -1_isize;
1545            for (row, line) in expected_lines.iter().enumerate() {
1546                let row = row as u32;
1547
1548                assert_eq!(
1549                    blocks_snapshot.line_len(row),
1550                    line.len() as u32,
1551                    "invalid line len for row {}",
1552                    row
1553                );
1554
1555                let line_char_count = line.chars().count() as isize;
1556                match line_char_count.cmp(&longest_line_len) {
1557                    Ordering::Less => {}
1558                    Ordering::Equal => expected_longest_rows.push(row),
1559                    Ordering::Greater => {
1560                        longest_line_len = line_char_count;
1561                        expected_longest_rows.clear();
1562                        expected_longest_rows.push(row);
1563                    }
1564                }
1565            }
1566
1567            log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>");
1568            let longest_row = blocks_snapshot.longest_row();
1569            assert!(
1570                expected_longest_rows.contains(&longest_row),
1571                "incorrect longest row {}. expected {:?} with length {}",
1572                longest_row,
1573                expected_longest_rows,
1574                longest_line_len,
1575            );
1576
1577            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1578                let wrap_point = WrapPoint::new(row, 0);
1579                let block_point = blocks_snapshot.to_block_point(wrap_point);
1580                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1581            }
1582
1583            let mut block_point = BlockPoint::new(0, 0);
1584            for c in expected_text.chars() {
1585                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1586                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1587
1588                assert_eq!(
1589                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1590                    left_point
1591                );
1592                assert_eq!(
1593                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1594                    right_point
1595                );
1596
1597                if c == '\n' {
1598                    block_point.0 += Point::new(1, 0);
1599                } else {
1600                    block_point.column += c.len_utf8() as u32;
1601                }
1602            }
1603        }
1604    }
1605}