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.read(cx).snapshot());
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_snapshot, 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.snapshot(), buffer.edits_since(&v0).collect())
1235        });
1236
1237        let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot, buffer_edits);
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.read(cx).snapshot());
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 mut buffer_snapshot = buffer.read(cx).snapshot();
1323        let (fold_map, folds_snapshot) = FoldMap::new(buffer_snapshot.clone());
1324        let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
1325        let (wrap_map, wraps_snapshot) =
1326            WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1327        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1328        let mut expected_blocks = Vec::new();
1329
1330        for _ in 0..operations {
1331            let mut buffer_edits = Vec::new();
1332            match rng.gen_range(0..=100) {
1333                0..=19 => {
1334                    let wrap_width = if rng.gen_bool(0.2) {
1335                        None
1336                    } else {
1337                        Some(rng.gen_range(0.0..=100.0))
1338                    };
1339                    log::info!("Setting wrap width to {:?}", wrap_width);
1340                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1341                }
1342                20..=39 => {
1343                    let block_count = rng.gen_range(1..=1);
1344                    let block_properties = (0..block_count)
1345                        .map(|_| {
1346                            let buffer = buffer.read(cx);
1347                            let position = buffer.anchor_after(
1348                                buffer.clip_offset(rng.gen_range(0..=buffer.len()), Bias::Left),
1349                            );
1350
1351                            let len = rng.gen_range(0..10);
1352                            let mut text = Rope::from(
1353                                RandomCharIter::new(&mut rng)
1354                                    .take(len)
1355                                    .collect::<String>()
1356                                    .to_uppercase()
1357                                    .as_str(),
1358                            );
1359                            let disposition = if rng.gen() {
1360                                text.push_front("<");
1361                                BlockDisposition::Above
1362                            } else {
1363                                text.push_front(">");
1364                                BlockDisposition::Below
1365                            };
1366                            log::info!(
1367                                "inserting block {:?} {:?} with text {:?}",
1368                                disposition,
1369                                position.to_point(buffer),
1370                                text.to_string()
1371                            );
1372                            BlockProperties {
1373                                position,
1374                                text,
1375                                disposition,
1376                                build_runs: None,
1377                                build_style: None,
1378                            }
1379                        })
1380                        .collect::<Vec<_>>();
1381
1382                    let (folds_snapshot, fold_edits) =
1383                        fold_map.read(buffer_snapshot.clone(), vec![]);
1384                    let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1385                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1386                        wrap_map.sync(tabs_snapshot, tab_edits, cx)
1387                    });
1388                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1389                    let block_ids = block_map.insert(block_properties.clone(), cx);
1390                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1391                        expected_blocks.push((block_id, props));
1392                    }
1393                }
1394                40..=59 if !expected_blocks.is_empty() => {
1395                    let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1396                    let block_ids_to_remove = (0..block_count)
1397                        .map(|_| {
1398                            expected_blocks
1399                                .remove(rng.gen_range(0..expected_blocks.len()))
1400                                .0
1401                        })
1402                        .collect();
1403
1404                    let (folds_snapshot, fold_edits) =
1405                        fold_map.read(buffer_snapshot.clone(), vec![]);
1406                    let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1407                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1408                        wrap_map.sync(tabs_snapshot, tab_edits, cx)
1409                    });
1410                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1411                    block_map.remove(block_ids_to_remove, cx);
1412                }
1413                _ => {
1414                    buffer.update(cx, |buffer, cx| {
1415                        let v0 = buffer.version();
1416                        let edit_count = rng.gen_range(1..=5);
1417                        buffer.randomly_edit(&mut rng, edit_count, cx);
1418                        log::info!("buffer text: {:?}", buffer.text());
1419                        buffer_edits.extend(buffer.edits_since(&v0));
1420                        buffer_snapshot = buffer.snapshot();
1421                    });
1422                }
1423            }
1424
1425            let (folds_snapshot, fold_edits) = fold_map.read(buffer_snapshot.clone(), buffer_edits);
1426            let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1427            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1428                wrap_map.sync(tabs_snapshot, tab_edits, cx)
1429            });
1430            let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1431            assert_eq!(
1432                blocks_snapshot.transforms.summary().input_rows,
1433                wraps_snapshot.max_point().row() + 1
1434            );
1435            log::info!("blocks text: {:?}", blocks_snapshot.text());
1436
1437            let buffer = buffer.read(cx);
1438            let mut sorted_blocks = expected_blocks
1439                .iter()
1440                .cloned()
1441                .map(|(id, block)| {
1442                    let mut position = block.position.to_point(buffer);
1443                    let column = wraps_snapshot.from_point(position, Bias::Left).column();
1444                    match block.disposition {
1445                        BlockDisposition::Above => {
1446                            position.column = 0;
1447                        }
1448                        BlockDisposition::Below => {
1449                            position.column = buffer.line_len(position.row);
1450                        }
1451                    };
1452                    let row = wraps_snapshot.from_point(position, Bias::Left).row();
1453                    (
1454                        id,
1455                        BlockProperties {
1456                            position: BlockPoint::new(row, column),
1457                            text: block.text,
1458                            build_runs: block.build_runs.clone(),
1459                            build_style: None,
1460                            disposition: block.disposition,
1461                        },
1462                    )
1463                })
1464                .collect::<Vec<_>>();
1465            sorted_blocks
1466                .sort_unstable_by_key(|(id, block)| (block.position.row, block.disposition, *id));
1467            let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1468
1469            let mut expected_buffer_rows = Vec::new();
1470            let mut expected_text = String::new();
1471            let input_text = wraps_snapshot.text();
1472            for (row, input_line) in input_text.split('\n').enumerate() {
1473                let row = row as u32;
1474                if row > 0 {
1475                    expected_text.push('\n');
1476                }
1477
1478                let buffer_row = wraps_snapshot
1479                    .to_point(WrapPoint::new(row, 0), Bias::Left)
1480                    .row;
1481
1482                while let Some((block_id, block)) = sorted_blocks.peek() {
1483                    if block.position.row == row && block.disposition == BlockDisposition::Above {
1484                        let text = block.text.to_string();
1485                        let padding = " ".repeat(block.position.column as usize);
1486                        for line in text.split('\n') {
1487                            if !line.is_empty() {
1488                                expected_text.push_str(&padding);
1489                                expected_text.push_str(line);
1490                            }
1491                            expected_text.push('\n');
1492                            expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1493                        }
1494                        sorted_blocks.next();
1495                    } else {
1496                        break;
1497                    }
1498                }
1499
1500                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1501                expected_buffer_rows.push(if soft_wrapped {
1502                    DisplayRow::Wrap
1503                } else {
1504                    DisplayRow::Buffer(buffer_row)
1505                });
1506                expected_text.push_str(input_line);
1507
1508                while let Some((block_id, block)) = sorted_blocks.peek() {
1509                    if block.position.row == row && block.disposition == BlockDisposition::Below {
1510                        let text = block.text.to_string();
1511                        let padding = " ".repeat(block.position.column as usize);
1512                        for line in text.split('\n') {
1513                            expected_text.push('\n');
1514                            if !line.is_empty() {
1515                                expected_text.push_str(&padding);
1516                                expected_text.push_str(line);
1517                            }
1518                            expected_buffer_rows.push(DisplayRow::Block(*block_id, None));
1519                        }
1520                        sorted_blocks.next();
1521                    } else {
1522                        break;
1523                    }
1524                }
1525            }
1526
1527            let expected_lines = expected_text.split('\n').collect::<Vec<_>>();
1528            let expected_row_count = expected_lines.len();
1529            for start_row in 0..expected_row_count {
1530                let expected_text = expected_lines[start_row..].join("\n");
1531                let actual_text = blocks_snapshot
1532                    .chunks(start_row as u32..expected_row_count as u32, None, None)
1533                    .map(|chunk| chunk.text)
1534                    .collect::<String>();
1535                assert_eq!(
1536                    actual_text, expected_text,
1537                    "incorrect text starting from row {}",
1538                    start_row
1539                );
1540                assert_eq!(
1541                    blocks_snapshot
1542                        .buffer_rows(start_row as u32, None)
1543                        .collect::<Vec<_>>(),
1544                    &expected_buffer_rows[start_row..]
1545                );
1546            }
1547
1548            let mut expected_longest_rows = Vec::new();
1549            let mut longest_line_len = -1_isize;
1550            for (row, line) in expected_lines.iter().enumerate() {
1551                let row = row as u32;
1552
1553                assert_eq!(
1554                    blocks_snapshot.line_len(row),
1555                    line.len() as u32,
1556                    "invalid line len for row {}",
1557                    row
1558                );
1559
1560                let line_char_count = line.chars().count() as isize;
1561                match line_char_count.cmp(&longest_line_len) {
1562                    Ordering::Less => {}
1563                    Ordering::Equal => expected_longest_rows.push(row),
1564                    Ordering::Greater => {
1565                        longest_line_len = line_char_count;
1566                        expected_longest_rows.clear();
1567                        expected_longest_rows.push(row);
1568                    }
1569                }
1570            }
1571
1572            log::info!("getting longest row >>>>>>>>>>>>>>>>>>>>>>>>");
1573            let longest_row = blocks_snapshot.longest_row();
1574            assert!(
1575                expected_longest_rows.contains(&longest_row),
1576                "incorrect longest row {}. expected {:?} with length {}",
1577                longest_row,
1578                expected_longest_rows,
1579                longest_line_len,
1580            );
1581
1582            for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1583                let wrap_point = WrapPoint::new(row, 0);
1584                let block_point = blocks_snapshot.to_block_point(wrap_point);
1585                assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1586            }
1587
1588            let mut block_point = BlockPoint::new(0, 0);
1589            for c in expected_text.chars() {
1590                let left_point = blocks_snapshot.clip_point(block_point, Bias::Left);
1591                let right_point = blocks_snapshot.clip_point(block_point, Bias::Right);
1592
1593                assert_eq!(
1594                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(left_point)),
1595                    left_point
1596                );
1597                assert_eq!(
1598                    blocks_snapshot.to_block_point(blocks_snapshot.to_wrap_point(right_point)),
1599                    right_point
1600                );
1601
1602                if c == '\n' {
1603                    block_point.0 += Point::new(1, 0);
1604                } else {
1605                    block_point.column += c.len_utf8() as u32;
1606                }
1607            }
1608        }
1609    }
1610}