block_map.rs

   1use super::wrap_map::{self, Edit as WrapEdit, Snapshot as WrapSnapshot, WrapPoint};
   2use buffer::{rope, Anchor, Bias, Edit, Point, Rope, ToOffset, ToPoint as _};
   3use gpui::{fonts::HighlightStyle, AppContext, ModelHandle};
   4use language::{Buffer, Chunk};
   5use parking_lot::Mutex;
   6use std::{
   7    cmp::{self, Ordering},
   8    collections::HashSet,
   9    iter,
  10    ops::Range,
  11    slice,
  12    sync::{
  13        atomic::{AtomicUsize, Ordering::SeqCst},
  14        Arc,
  15    },
  16};
  17use sum_tree::SumTree;
  18
  19pub struct BlockMap {
  20    buffer: ModelHandle<Buffer>,
  21    next_block_id: AtomicUsize,
  22    wrap_snapshot: Mutex<WrapSnapshot>,
  23    blocks: Vec<Arc<Block>>,
  24    transforms: Mutex<SumTree<Transform>>,
  25}
  26
  27pub struct BlockMapWriter<'a>(&'a mut BlockMap);
  28
  29pub struct BlockSnapshot {
  30    wrap_snapshot: WrapSnapshot,
  31    transforms: SumTree<Transform>,
  32}
  33
  34#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
  35pub struct BlockId(usize);
  36
  37#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  38pub struct BlockPoint(pub super::Point);
  39
  40#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  41struct BlockRow(u32);
  42
  43#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  44struct WrapRow(u32);
  45
  46#[derive(Debug)]
  47struct Block {
  48    id: BlockId,
  49    position: Anchor,
  50    text: Rope,
  51    runs: Vec<(usize, HighlightStyle)>,
  52    disposition: BlockDisposition,
  53}
  54
  55#[derive(Clone)]
  56pub struct BlockProperties<P, T>
  57where
  58    P: Clone,
  59    T: Clone,
  60{
  61    pub position: P,
  62    pub text: T,
  63    pub runs: Vec<(usize, HighlightStyle)>,
  64    pub disposition: BlockDisposition,
  65}
  66
  67#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
  68pub enum BlockDisposition {
  69    Above,
  70    Below,
  71}
  72
  73#[derive(Clone, Debug)]
  74struct Transform {
  75    summary: TransformSummary,
  76    block: Option<Arc<Block>>,
  77}
  78
  79#[derive(Clone, Debug, Default)]
  80struct TransformSummary {
  81    input_rows: u32,
  82    output_rows: u32,
  83}
  84
  85pub struct Chunks<'a> {
  86    transforms: sum_tree::Cursor<'a, Transform, (BlockRow, WrapRow)>,
  87    input_chunks: wrap_map::Chunks<'a>,
  88    input_chunk: Chunk<'a>,
  89    block_chunks: Option<BlockChunks<'a>>,
  90    output_row: u32,
  91    max_output_row: u32,
  92    max_input_row: u32,
  93}
  94
  95struct BlockChunks<'a> {
  96    chunks: rope::Chunks<'a>,
  97    runs: iter::Peekable<slice::Iter<'a, (usize, HighlightStyle)>>,
  98    chunk: Option<&'a str>,
  99    run_start: usize,
 100    offset: usize,
 101}
 102
 103impl BlockMap {
 104    pub fn new(buffer: ModelHandle<Buffer>, wrap_snapshot: WrapSnapshot) -> Self {
 105        Self {
 106            buffer,
 107            next_block_id: AtomicUsize::new(0),
 108            blocks: Vec::new(),
 109            transforms: Mutex::new(SumTree::from_item(
 110                Transform::isomorphic(wrap_snapshot.text_summary().lines.row + 1),
 111                &(),
 112            )),
 113            wrap_snapshot: Mutex::new(wrap_snapshot),
 114        }
 115    }
 116
 117    pub fn read(
 118        &self,
 119        wrap_snapshot: WrapSnapshot,
 120        edits: Vec<WrapEdit>,
 121        cx: &AppContext,
 122    ) -> BlockSnapshot {
 123        self.sync(&wrap_snapshot, edits, cx);
 124        *self.wrap_snapshot.lock() = wrap_snapshot.clone();
 125        BlockSnapshot {
 126            wrap_snapshot,
 127            transforms: self.transforms.lock().clone(),
 128        }
 129    }
 130
 131    pub fn write(
 132        &mut self,
 133        wrap_snapshot: WrapSnapshot,
 134        edits: Vec<WrapEdit>,
 135        cx: &AppContext,
 136    ) -> BlockMapWriter {
 137        self.sync(&wrap_snapshot, edits, cx);
 138        *self.wrap_snapshot.lock() = wrap_snapshot;
 139        BlockMapWriter(self)
 140    }
 141
 142    pub fn sync(&self, wrap_snapshot: &WrapSnapshot, edits: Vec<WrapEdit>, cx: &AppContext) {
 143        if edits.is_empty() {
 144            return;
 145        }
 146
 147        let buffer = self.buffer.read(cx);
 148        let mut transforms = self.transforms.lock();
 149        let mut new_transforms = SumTree::new();
 150        let old_row_count = transforms.summary().input_rows;
 151        let new_row_count = wrap_snapshot.max_point().row() + 1;
 152        let mut cursor = transforms.cursor::<WrapRow>();
 153        let mut last_block_ix = 0;
 154        let mut blocks_in_edit = Vec::new();
 155        let mut edits = edits.into_iter().peekable();
 156
 157        while let Some(edit) = edits.next() {
 158            // Preserve any old transforms that precede this edit.
 159            let old_start = WrapRow(edit.old.start);
 160            let new_start = WrapRow(edit.new.start);
 161            new_transforms.push_tree(cursor.slice(&old_start, Bias::Left, &()), &());
 162            if let Some(transform) = cursor.item() {
 163                if transform.is_isomorphic() && old_start == cursor.end(&()) {
 164                    new_transforms.push(transform.clone(), &());
 165                    cursor.next(&());
 166                    while let Some(transform) = cursor.item() {
 167                        if transform
 168                            .block
 169                            .as_ref()
 170                            .map_or(false, |b| b.disposition.is_below())
 171                        {
 172                            new_transforms.push(transform.clone(), &());
 173                            cursor.next(&());
 174                        } else {
 175                            break;
 176                        }
 177                    }
 178                }
 179            }
 180
 181            // Preserve any portion of an old transform that precedes this edit.
 182            let extent_before_edit = old_start.0 - cursor.start().0;
 183            push_isomorphic(&mut new_transforms, extent_before_edit);
 184
 185            // Skip over any old transforms that intersect this edit.
 186            let mut old_end = WrapRow(edit.old.end);
 187            let mut new_end = WrapRow(edit.new.end);
 188            cursor.seek(&old_end, Bias::Left, &());
 189            cursor.next(&());
 190            if old_end == *cursor.start() {
 191                while let Some(transform) = cursor.item() {
 192                    if transform
 193                        .block
 194                        .as_ref()
 195                        .map_or(false, |b| b.disposition.is_below())
 196                    {
 197                        cursor.next(&());
 198                    } else {
 199                        break;
 200                    }
 201                }
 202            }
 203
 204            // Combine this edit with any subsequent edits that intersect the same transform.
 205            while let Some(next_edit) = edits.peek() {
 206                if next_edit.old.start <= cursor.start().0 {
 207                    old_end = WrapRow(next_edit.old.end);
 208                    new_end = WrapRow(next_edit.new.end);
 209                    cursor.seek(&old_end, Bias::Left, &());
 210                    cursor.next(&());
 211                    if old_end == *cursor.start() {
 212                        while let Some(transform) = cursor.item() {
 213                            if transform
 214                                .block
 215                                .as_ref()
 216                                .map_or(false, |b| b.disposition.is_below())
 217                            {
 218                                cursor.next(&());
 219                            } else {
 220                                break;
 221                            }
 222                        }
 223                    }
 224                    edits.next();
 225                } else {
 226                    break;
 227                }
 228            }
 229
 230            // Find the blocks within this edited region.
 231            let new_start = wrap_snapshot.to_point(WrapPoint::new(new_start.0, 0), Bias::Left);
 232            let start_anchor = buffer.anchor_before(new_start);
 233            let start_block_ix = match self.blocks[last_block_ix..].binary_search_by(|probe| {
 234                probe
 235                    .position
 236                    .cmp(&start_anchor, buffer)
 237                    .unwrap()
 238                    .then(Ordering::Greater)
 239            }) {
 240                Ok(ix) | Err(ix) => last_block_ix + ix,
 241            };
 242            let end_block_ix = if new_end.0 > wrap_snapshot.max_point().row() {
 243                self.blocks.len()
 244            } else {
 245                let new_end = wrap_snapshot.to_point(WrapPoint::new(new_end.0, 0), Bias::Left);
 246                let end_anchor = buffer.anchor_before(new_end);
 247                match self.blocks[start_block_ix..].binary_search_by(|probe| {
 248                    probe
 249                        .position
 250                        .cmp(&end_anchor, buffer)
 251                        .unwrap()
 252                        .then(Ordering::Greater)
 253                }) {
 254                    Ok(ix) | Err(ix) => start_block_ix + ix,
 255                }
 256            };
 257            last_block_ix = end_block_ix;
 258            blocks_in_edit.clear();
 259            blocks_in_edit.extend(
 260                self.blocks[start_block_ix..end_block_ix]
 261                    .iter()
 262                    .map(|block| {
 263                        let mut position = block.position.to_point(buffer);
 264                        match block.disposition {
 265                            BlockDisposition::Above => position.column = 0,
 266                            BlockDisposition::Below => {
 267                                position.column = buffer.line_len(position.row)
 268                            }
 269                        }
 270                        let position = wrap_snapshot.from_point(position, Bias::Left);
 271                        (position.row(), block)
 272                    }),
 273            );
 274            blocks_in_edit.sort_unstable_by_key(|(row, block)| (*row, block.disposition, block.id));
 275
 276            // For each of these blocks, insert a new isomorphic transform preceding the block,
 277            // and then insert the block itself.
 278            for (block_row, block) in blocks_in_edit.iter().copied() {
 279                let insertion_row = match block.disposition {
 280                    BlockDisposition::Above => block_row,
 281                    BlockDisposition::Below => block_row + 1,
 282                };
 283                let extent_before_block = insertion_row - new_transforms.summary().input_rows;
 284                push_isomorphic(&mut new_transforms, extent_before_block);
 285                new_transforms.push(Transform::block(block.clone()), &());
 286            }
 287
 288            old_end = WrapRow(old_end.0.min(old_row_count));
 289            new_end = WrapRow(new_end.0.min(new_row_count));
 290
 291            // Insert an isomorphic transform after the final block.
 292            let extent_after_last_block = new_end.0 - new_transforms.summary().input_rows;
 293            push_isomorphic(&mut new_transforms, extent_after_last_block);
 294
 295            // Preserve any portion of the old transform after this edit.
 296            let extent_after_edit = cursor.start().0 - old_end.0;
 297            push_isomorphic(&mut new_transforms, extent_after_edit);
 298        }
 299
 300        new_transforms.push_tree(cursor.suffix(&()), &());
 301        debug_assert_eq!(
 302            new_transforms.summary().input_rows,
 303            wrap_snapshot.max_point().row() + 1
 304        );
 305
 306        drop(cursor);
 307        *transforms = new_transforms;
 308    }
 309}
 310
 311fn push_isomorphic(tree: &mut SumTree<Transform>, rows: u32) {
 312    if rows == 0 {
 313        return;
 314    }
 315
 316    let mut extent = Some(rows);
 317    tree.update_last(
 318        |last_transform| {
 319            if last_transform.is_isomorphic() {
 320                let extent = extent.take().unwrap();
 321                last_transform.summary.input_rows += extent;
 322                last_transform.summary.output_rows += extent;
 323            }
 324        },
 325        &(),
 326    );
 327    if let Some(extent) = extent {
 328        tree.push(Transform::isomorphic(extent), &());
 329    }
 330}
 331
 332impl BlockPoint {
 333    fn new(row: u32, column: u32) -> Self {
 334        Self(Point::new(row, column))
 335    }
 336}
 337
 338impl std::ops::Deref for BlockPoint {
 339    type Target = Point;
 340
 341    fn deref(&self) -> &Self::Target {
 342        &self.0
 343    }
 344}
 345
 346impl std::ops::DerefMut for BlockPoint {
 347    fn deref_mut(&mut self) -> &mut Self::Target {
 348        &mut self.0
 349    }
 350}
 351
 352impl<'a> BlockMapWriter<'a> {
 353    pub fn insert<P, T>(
 354        &mut self,
 355        blocks: impl IntoIterator<Item = BlockProperties<P, T>>,
 356        cx: &AppContext,
 357    ) -> Vec<BlockId>
 358    where
 359        P: ToOffset + Clone,
 360        T: Into<Rope> + Clone,
 361    {
 362        let buffer = self.0.buffer.read(cx);
 363        let mut ids = Vec::new();
 364        let mut edits = Vec::<Edit<u32>>::new();
 365        let wrap_snapshot = &*self.0.wrap_snapshot.lock();
 366
 367        for block in blocks {
 368            let id = BlockId(self.0.next_block_id.fetch_add(1, SeqCst));
 369            ids.push(id);
 370
 371            let position = buffer.anchor_before(block.position);
 372            let point = position.to_point(buffer);
 373            let start_row = wrap_snapshot
 374                .from_point(Point::new(point.row, 0), Bias::Left)
 375                .row();
 376            let end_row = if point.row == buffer.max_point().row {
 377                wrap_snapshot.max_point().row() + 1
 378            } else {
 379                wrap_snapshot
 380                    .from_point(Point::new(point.row + 1, 0), Bias::Left)
 381                    .row()
 382            };
 383
 384            let block_ix = match self
 385                .0
 386                .blocks
 387                .binary_search_by(|probe| probe.position.cmp(&position, buffer).unwrap())
 388            {
 389                Ok(ix) | Err(ix) => ix,
 390            };
 391            self.0.blocks.insert(
 392                block_ix,
 393                Arc::new(Block {
 394                    id,
 395                    position,
 396                    text: block.text.into(),
 397                    runs: block.runs,
 398                    disposition: block.disposition,
 399                }),
 400            );
 401
 402            if let Err(edit_ix) = edits.binary_search_by_key(&start_row, |edit| edit.old.start) {
 403                edits.insert(
 404                    edit_ix,
 405                    Edit {
 406                        old: start_row..end_row,
 407                        new: start_row..end_row,
 408                    },
 409                );
 410            }
 411        }
 412
 413        self.0.sync(wrap_snapshot, edits, cx);
 414        ids
 415    }
 416
 417    pub fn remove(&mut self, block_ids: HashSet<BlockId>, cx: &AppContext) {
 418        let buffer = self.0.buffer.read(cx);
 419        let wrap_snapshot = &*self.0.wrap_snapshot.lock();
 420        let mut edits = Vec::new();
 421        let mut last_block_buffer_row = None;
 422        self.0.blocks.retain(|block| {
 423            if block_ids.contains(&block.id) {
 424                let buffer_row = block.position.to_point(buffer).row;
 425                if last_block_buffer_row != Some(buffer_row) {
 426                    last_block_buffer_row = Some(buffer_row);
 427                    let start_row = wrap_snapshot
 428                        .from_point(Point::new(buffer_row, 0), Bias::Left)
 429                        .row();
 430                    let end_row = wrap_snapshot
 431                        .from_point(
 432                            Point::new(buffer_row, buffer.line_len(buffer_row)),
 433                            Bias::Left,
 434                        )
 435                        .row()
 436                        + 1;
 437                    edits.push(Edit {
 438                        old: start_row..end_row,
 439                        new: start_row..end_row,
 440                    })
 441                }
 442                false
 443            } else {
 444                true
 445            }
 446        });
 447        self.0.sync(wrap_snapshot, edits, cx);
 448    }
 449}
 450
 451impl BlockSnapshot {
 452    #[cfg(test)]
 453    fn text(&mut self) -> String {
 454        self.chunks(0..self.transforms.summary().output_rows, false)
 455            .map(|chunk| chunk.text)
 456            .collect()
 457    }
 458
 459    pub fn chunks(&self, rows: Range<u32>, highlights: bool) -> Chunks {
 460        let max_input_row = self.transforms.summary().input_rows;
 461        let max_output_row = cmp::min(rows.end, self.transforms.summary().output_rows);
 462        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 463        let output_row = rows.start;
 464        cursor.seek(&BlockRow(output_row), Bias::Right, &());
 465        let (input_start, output_start) = cursor.start();
 466        let overshoot = rows.start - output_start.0;
 467        let output_end_row = max_output_row.saturating_sub(1);
 468        let input_start_row = input_start.0 + overshoot;
 469        let input_end_row = self
 470            .to_wrap_point(BlockPoint::new(
 471                output_end_row,
 472                self.wrap_snapshot.line_len(output_end_row),
 473            ))
 474            .row()
 475            + 1;
 476        let input_chunks = self
 477            .wrap_snapshot
 478            .chunks(input_start_row..input_end_row, highlights);
 479        Chunks {
 480            input_chunks,
 481            input_chunk: Default::default(),
 482            block_chunks: None,
 483            transforms: cursor,
 484            output_row,
 485            max_output_row,
 486            max_input_row,
 487        }
 488    }
 489
 490    pub fn max_point(&self) -> BlockPoint {
 491        todo!()
 492    }
 493
 494    pub fn clip_point(&self, point: BlockPoint, bias: Bias) -> BlockPoint {
 495        todo!()
 496        // let mut cursor = self.transforms.cursor::<(BlockPoint, WrapPoint)>();
 497        // cursor.seek(&point, Bias::Right, &());
 498        // if let Some(transform) = cursor.prev_item() {
 499        //     if transform.is_isomorphic() && point == cursor.start().0 {
 500        //         return point;
 501        //     }
 502        // }
 503        // if let Some(transform) = cursor.item() {
 504        //     if transform.is_isomorphic() {
 505        //         let (output_start, input_start) = cursor.start();
 506        //         let output_overshoot = point.0 - output_start.0;
 507        //         let input_point = self
 508        //             .wrap_snapshot
 509        //             .clip_point(WrapPoint(input_start.0 + output_overshoot), bias);
 510        //         let input_overshoot = input_point.0 - input_start.0;
 511        //         BlockPoint(output_start.0 + input_overshoot)
 512        //     } else {
 513        //         if bias == Bias::Left && cursor.start().1 .0 > Point::zero()
 514        //             || cursor.end(&()).1 == self.wrap_snapshot.max_point()
 515        //         {
 516        //             loop {
 517        //                 cursor.prev(&());
 518        //                 let transform = cursor.item().unwrap();
 519        //                 if transform.is_isomorphic() {
 520        //                     return BlockPoint(cursor.end(&()).0 .0);
 521        //                 }
 522        //             }
 523        //         } else {
 524        //             loop {
 525        //                 cursor.next(&());
 526        //                 let transform = cursor.item().unwrap();
 527        //                 if transform.is_isomorphic() {
 528        //                     return BlockPoint(cursor.start().0 .0);
 529        //                 }
 530        //             }
 531        //         }
 532        //     }
 533        // } else {
 534        //     self.max_point()
 535        // }
 536    }
 537
 538    pub fn to_block_point(&self, wrap_point: WrapPoint, bias: Bias) -> BlockPoint {
 539        todo!()
 540        // let mut cursor = self.transforms.cursor::<(WrapPoint, BlockPoint)>();
 541        // cursor.seek(&wrap_point, bias, &());
 542        // while let Some(item) = cursor.item() {
 543        //     if item.is_isomorphic() {
 544        //         break;
 545        //     }
 546        //     cursor.next(&());
 547        // }
 548        // let (input_start, output_start) = cursor.start();
 549        // let input_overshoot = wrap_point.0 - input_start.0;
 550        // BlockPoint(output_start.0 + input_overshoot)
 551    }
 552
 553    pub fn to_wrap_point(&self, block_point: BlockPoint) -> WrapPoint {
 554        let mut cursor = self.transforms.cursor::<(BlockRow, WrapRow)>();
 555        cursor.seek(&BlockRow(block_point.row), Bias::Right, &());
 556        if let Some(transform) = cursor.item() {
 557            match transform.block.as_ref().map(|b| b.disposition) {
 558                Some(BlockDisposition::Above) => WrapPoint::new(cursor.start().1 .0, 0),
 559                Some(BlockDisposition::Below) => {
 560                    let wrap_row = cursor.start().1 .0 - 1;
 561                    WrapPoint::new(wrap_row, self.wrap_snapshot.line_len(wrap_row))
 562                }
 563                None => {
 564                    let overshoot = block_point.row - cursor.start().0 .0;
 565                    let wrap_row = cursor.start().1 .0 + overshoot;
 566                    WrapPoint::new(wrap_row, block_point.column)
 567                }
 568            }
 569        } else {
 570            self.wrap_snapshot.max_point()
 571        }
 572    }
 573}
 574
 575impl Transform {
 576    fn isomorphic(rows: u32) -> Self {
 577        Self {
 578            summary: TransformSummary {
 579                input_rows: rows,
 580                output_rows: rows,
 581            },
 582            block: None,
 583        }
 584    }
 585
 586    fn block(block: Arc<Block>) -> Self {
 587        Self {
 588            summary: TransformSummary {
 589                input_rows: 0,
 590                output_rows: block.text.summary().lines.row + 1,
 591            },
 592            block: Some(block),
 593        }
 594    }
 595
 596    fn is_isomorphic(&self) -> bool {
 597        self.block.is_none()
 598    }
 599}
 600
 601impl<'a> Iterator for Chunks<'a> {
 602    type Item = Chunk<'a>;
 603
 604    fn next(&mut self) -> Option<Self::Item> {
 605        if self.output_row >= self.max_output_row {
 606            return None;
 607        }
 608
 609        if let Some(block_chunks) = self.block_chunks.as_mut() {
 610            if let Some(block_chunk) = block_chunks.next() {
 611                self.output_row += block_chunk.text.matches('\n').count() as u32;
 612                return Some(block_chunk);
 613            } else {
 614                self.block_chunks.take();
 615                self.output_row += 1;
 616                if self.output_row < self.max_output_row {
 617                    return Some(Chunk {
 618                        text: "\n",
 619                        ..Default::default()
 620                    });
 621                } else {
 622                    return None;
 623                }
 624            }
 625        }
 626
 627        let transform = self.transforms.item()?;
 628        if let Some(block) = transform.block.as_ref() {
 629            let block_start = self.transforms.start().0 .0;
 630            let block_end = self.transforms.end(&()).0 .0;
 631            let start_in_block = self.output_row - block_start;
 632            let end_in_block = cmp::min(self.max_output_row, block_end) - block_start;
 633            self.transforms.next(&());
 634            self.block_chunks = Some(BlockChunks::new(block, start_in_block..end_in_block));
 635            return self.next();
 636        }
 637
 638        if self.input_chunk.text.is_empty() {
 639            if let Some(input_chunk) = self.input_chunks.next() {
 640                self.input_chunk = input_chunk;
 641            } else {
 642                self.output_row += 1;
 643                if self.output_row < self.max_output_row {
 644                    self.transforms.next(&());
 645                    return Some(Chunk {
 646                        text: "\n",
 647                        ..Default::default()
 648                    });
 649                } else {
 650                    return None;
 651                }
 652            }
 653        }
 654
 655        let transform_end = self.transforms.end(&()).0 .0;
 656        let (prefix_rows, prefix_bytes) =
 657            offset_for_row(self.input_chunk.text, transform_end - self.output_row);
 658        self.output_row += prefix_rows;
 659        let (prefix, suffix) = self.input_chunk.text.split_at(prefix_bytes);
 660        self.input_chunk.text = suffix;
 661        if self.output_row == transform_end {
 662            self.transforms.next(&());
 663        }
 664
 665        Some(Chunk {
 666            text: prefix,
 667            ..self.input_chunk
 668        })
 669    }
 670}
 671
 672impl<'a> BlockChunks<'a> {
 673    fn new(block: &'a Block, rows: Range<u32>) -> Self {
 674        let offset_range = block.text.point_to_offset(Point::new(rows.start, 0))
 675            ..block.text.point_to_offset(Point::new(rows.end, 0));
 676
 677        let mut runs = block.runs.iter().peekable();
 678        let mut run_start = 0;
 679        while let Some((run_len, _)) = runs.peek() {
 680            let run_end = run_start + run_len;
 681            if run_end <= offset_range.start {
 682                run_start = run_end;
 683                runs.next();
 684            } else {
 685                break;
 686            }
 687        }
 688
 689        Self {
 690            chunk: None,
 691            run_start,
 692            chunks: block.text.chunks_in_range(offset_range.clone()),
 693            runs,
 694            offset: offset_range.start,
 695        }
 696    }
 697}
 698
 699impl<'a> Iterator for BlockChunks<'a> {
 700    type Item = Chunk<'a>;
 701
 702    fn next(&mut self) -> Option<Self::Item> {
 703        if self.chunk.is_none() {
 704            self.chunk = self.chunks.next();
 705        }
 706
 707        let chunk = self.chunk?;
 708        let mut chunk_len = chunk.len();
 709        // let mut highlight_style = None;
 710        if let Some((run_len, _)) = self.runs.peek() {
 711            // highlight_style = Some(style.clone());
 712            let run_end_in_chunk = self.run_start + run_len - self.offset;
 713            if run_end_in_chunk <= chunk_len {
 714                chunk_len = run_end_in_chunk;
 715                self.run_start += run_len;
 716                self.runs.next();
 717            }
 718        }
 719
 720        self.offset += chunk_len;
 721        let (chunk, suffix) = chunk.split_at(chunk_len);
 722        self.chunk = if suffix.is_empty() {
 723            None
 724        } else {
 725            Some(suffix)
 726        };
 727
 728        Some(Chunk {
 729            text: chunk,
 730            highlight_id: Default::default(),
 731            diagnostic: None,
 732        })
 733    }
 734}
 735
 736impl sum_tree::Item for Transform {
 737    type Summary = TransformSummary;
 738
 739    fn summary(&self) -> Self::Summary {
 740        self.summary.clone()
 741    }
 742}
 743
 744impl sum_tree::Summary for TransformSummary {
 745    type Context = ();
 746
 747    fn add_summary(&mut self, summary: &Self, _: &()) {
 748        self.input_rows += summary.input_rows;
 749        self.output_rows += summary.output_rows;
 750    }
 751}
 752
 753impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapRow {
 754    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 755        self.0 += summary.input_rows;
 756    }
 757}
 758
 759impl<'a> sum_tree::Dimension<'a, TransformSummary> for BlockRow {
 760    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 761        self.0 += summary.output_rows;
 762    }
 763}
 764
 765impl BlockDisposition {
 766    fn is_above(&self) -> bool {
 767        matches!(self, BlockDisposition::Above)
 768    }
 769
 770    fn is_below(&self) -> bool {
 771        matches!(self, BlockDisposition::Below)
 772    }
 773}
 774
 775// Count the number of bytes prior to a target point. If the string doesn't contain the target
 776// point, return its total extent. Otherwise return the target point itself.
 777fn offset_for_row(s: &str, target: u32) -> (u32, usize) {
 778    let mut row = 0;
 779    let mut offset = 0;
 780    for (ix, line) in s.split('\n').enumerate() {
 781        if ix > 0 {
 782            row += 1;
 783            offset += 1;
 784        }
 785        if row >= target {
 786            break;
 787        }
 788        offset += line.len() as usize;
 789    }
 790    (row, offset)
 791}
 792
 793#[cfg(test)]
 794mod tests {
 795    use super::*;
 796    use crate::display_map::{fold_map::FoldMap, tab_map::TabMap, wrap_map::WrapMap};
 797    use buffer::RandomCharIter;
 798    use language::Buffer;
 799    use rand::prelude::*;
 800    use std::env;
 801
 802    #[gpui::test]
 803    fn test_offset_for_row() {
 804        assert_eq!(offset_for_row("", 0), (0, 0));
 805        assert_eq!(offset_for_row("", 1), (0, 0));
 806        assert_eq!(offset_for_row("abcd", 0), (0, 0));
 807        assert_eq!(offset_for_row("abcd", 1), (0, 4));
 808        assert_eq!(offset_for_row("\n", 0), (0, 0));
 809        assert_eq!(offset_for_row("\n", 1), (1, 1));
 810        assert_eq!(offset_for_row("abc\ndef\nghi", 0), (0, 0));
 811        assert_eq!(offset_for_row("abc\ndef\nghi", 1), (1, 4));
 812        assert_eq!(offset_for_row("abc\ndef\nghi", 2), (2, 8));
 813        assert_eq!(offset_for_row("abc\ndef\nghi", 3), (2, 11));
 814    }
 815
 816    #[gpui::test]
 817    fn test_basic_blocks(cx: &mut gpui::MutableAppContext) {
 818        let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
 819        let font_id = cx
 820            .font_cache()
 821            .select_font(family_id, &Default::default())
 822            .unwrap();
 823
 824        let text = "aaa\nbbb\nccc\nddd";
 825
 826        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 827        let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
 828        let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
 829        let (wrap_map, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, None, cx);
 830        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
 831
 832        let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
 833        writer.insert(
 834            vec![
 835                BlockProperties {
 836                    position: Point::new(1, 0),
 837                    text: "BLOCK 1",
 838                    disposition: BlockDisposition::Above,
 839                    runs: vec![],
 840                },
 841                BlockProperties {
 842                    position: Point::new(1, 2),
 843                    text: "BLOCK 2",
 844                    disposition: BlockDisposition::Above,
 845                    runs: vec![],
 846                },
 847                BlockProperties {
 848                    position: Point::new(3, 2),
 849                    text: "BLOCK 3",
 850                    disposition: BlockDisposition::Below,
 851                    runs: vec![],
 852                },
 853            ],
 854            cx,
 855        );
 856
 857        let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
 858        assert_eq!(
 859            snapshot.text(),
 860            "aaa\nBLOCK 1\nBLOCK 2\nbbb\nccc\nddd\nBLOCK 3"
 861        );
 862        // assert_eq!(
 863        //     snapshot.to_block_point(WrapPoint::new(1, 0), Bias::Right),
 864        //     BlockPoint::new(3, 0)
 865        // );
 866        // assert_eq!(
 867        //     snapshot.clip_point(BlockPoint::new(1, 0), Bias::Left),
 868        //     BlockPoint::new(1, 0)
 869        // );
 870        // assert_eq!(
 871        //     snapshot.clip_point(BlockPoint::new(1, 0), Bias::Right),
 872        //     BlockPoint::new(1, 0)
 873        // );
 874        // assert_eq!(
 875        //     snapshot.clip_point(BlockPoint::new(1, 1), Bias::Left),
 876        //     BlockPoint::new(1, 0)
 877        // );
 878        // assert_eq!(
 879        //     snapshot.clip_point(BlockPoint::new(1, 1), Bias::Right),
 880        //     BlockPoint::new(3, 0)
 881        // );
 882        // assert_eq!(
 883        //     snapshot.clip_point(BlockPoint::new(3, 0), Bias::Left),
 884        //     BlockPoint::new(3, 0)
 885        // );
 886        // assert_eq!(
 887        //     snapshot.clip_point(BlockPoint::new(3, 0), Bias::Right),
 888        //     BlockPoint::new(3, 0)
 889        // );
 890        // assert_eq!(
 891        //     snapshot.clip_point(BlockPoint::new(5, 3), Bias::Left),
 892        //     BlockPoint::new(5, 3)
 893        // );
 894        // assert_eq!(
 895        //     snapshot.clip_point(BlockPoint::new(5, 3), Bias::Right),
 896        //     BlockPoint::new(5, 3)
 897        // );
 898        // assert_eq!(
 899        //     snapshot.clip_point(BlockPoint::new(6, 0), Bias::Left),
 900        //     BlockPoint::new(5, 3)
 901        // );
 902        // assert_eq!(
 903        //     snapshot.clip_point(BlockPoint::new(6, 0), Bias::Right),
 904        //     BlockPoint::new(5, 3)
 905        // );
 906
 907        // assert_eq!(
 908        //     buffer_rows_from_chunks(snapshot.chunks(0..snapshot.max_point().row + 1, false)),
 909        //     &[Some(0), None, None, Some(1), Some(2), Some(3), None]
 910        // );
 911
 912        // Insert a line break, separating two block decorations into separate
 913        // lines.
 914        buffer.update(cx, |buffer, cx| {
 915            buffer.edit([Point::new(1, 1)..Point::new(1, 1)], "!!!\n", cx)
 916        });
 917
 918        let (folds_snapshot, fold_edits) = fold_map.read(cx);
 919        let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
 920        let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
 921            wrap_map.sync(tabs_snapshot, tab_edits, cx)
 922        });
 923        let mut snapshot = block_map.read(wraps_snapshot, wrap_edits, cx);
 924        assert_eq!(
 925            snapshot.text(),
 926            "aaa\nBLOCK 1\nb!!!\nBLOCK 2\nbb\nccc\nddd\nBLOCK 3"
 927        );
 928    }
 929
 930    #[gpui::test]
 931    fn test_blocks_on_wrapped_lines(cx: &mut gpui::MutableAppContext) {
 932        let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
 933        let font_id = cx
 934            .font_cache()
 935            .select_font(family_id, &Default::default())
 936            .unwrap();
 937
 938        let text = "one two three\nfour five six\nseven eight";
 939
 940        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
 941        let (_, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
 942        let (_, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), 1);
 943        let (_, wraps_snapshot) = WrapMap::new(tabs_snapshot, font_id, 14.0, Some(60.), cx);
 944        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot.clone());
 945
 946        let mut writer = block_map.write(wraps_snapshot.clone(), vec![], cx);
 947        writer.insert(
 948            vec![
 949                BlockProperties {
 950                    position: Point::new(1, 12),
 951                    text: "<BLOCK 1",
 952                    disposition: BlockDisposition::Above,
 953                    runs: vec![],
 954                },
 955                BlockProperties {
 956                    position: Point::new(1, 1),
 957                    text: ">BLOCK 2",
 958                    disposition: BlockDisposition::Below,
 959                    runs: vec![],
 960                },
 961            ],
 962            cx,
 963        );
 964
 965        // Blocks with an 'above' disposition go above their corresponding buffer line.
 966        // Blocks with a 'below' disposition go below their corresponding buffer line.
 967        let mut snapshot = block_map.read(wraps_snapshot, vec![], cx);
 968        assert_eq!(
 969            snapshot.text(),
 970            "one two \nthree\n<BLOCK 1\nfour five \nsix\n>BLOCK 2\nseven \neight"
 971        );
 972    }
 973
 974    #[gpui::test(iterations = 100)]
 975    fn test_random_blocks(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
 976        let operations = env::var("OPERATIONS")
 977            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 978            .unwrap_or(1);
 979
 980        let wrap_width = None;
 981        let tab_size = 1;
 982        let family_id = cx.font_cache().load_family(&["Helvetica"]).unwrap();
 983        let font_id = cx
 984            .font_cache()
 985            .select_font(family_id, &Default::default())
 986            .unwrap();
 987        let font_size = 14.0;
 988
 989        log::info!("Wrap width: {:?}", wrap_width);
 990
 991        let buffer = cx.add_model(|cx| {
 992            let len = rng.gen_range(0..10);
 993            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
 994            log::info!("initial buffer text: {:?}", text);
 995            Buffer::new(0, text, cx)
 996        });
 997        let (fold_map, folds_snapshot) = FoldMap::new(buffer.clone(), cx);
 998        let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), tab_size);
 999        let (wrap_map, wraps_snapshot) =
1000            WrapMap::new(tabs_snapshot, font_id, font_size, wrap_width, cx);
1001        let mut block_map = BlockMap::new(buffer.clone(), wraps_snapshot);
1002        let mut expected_blocks = Vec::new();
1003
1004        for _ in 0..operations {
1005            match rng.gen_range(0..=100) {
1006                // 0..=19 => {
1007                //     let wrap_width = if rng.gen_bool(0.2) {
1008                //         None
1009                //     } else {
1010                //         Some(rng.gen_range(0.0..=100.0))
1011                //     };
1012                //     log::info!("Setting wrap width to {:?}", wrap_width);
1013                //     wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1014                // }
1015                20..=39 => {
1016                    let block_count = rng.gen_range(1..=1);
1017                    let block_properties = (0..block_count)
1018                        .map(|_| {
1019                            let buffer = buffer.read(cx);
1020                            let position = buffer.anchor_before(rng.gen_range(0..=buffer.len()));
1021
1022                            let len = rng.gen_range(0..10);
1023                            let mut text = Rope::from(
1024                                RandomCharIter::new(&mut rng)
1025                                    .take(len)
1026                                    .collect::<String>()
1027                                    .to_uppercase()
1028                                    .as_str(),
1029                            );
1030                            let disposition = if rng.gen() {
1031                                text.push_front("<");
1032                                BlockDisposition::Above
1033                            } else {
1034                                text.push_front(">");
1035                                BlockDisposition::Below
1036                            };
1037                            log::info!(
1038                                "inserting block {:?} {:?} with text {:?}",
1039                                disposition,
1040                                position.to_point(buffer),
1041                                text.to_string()
1042                            );
1043                            BlockProperties {
1044                                position,
1045                                text,
1046                                runs: Vec::<(usize, HighlightStyle)>::new(),
1047                                disposition,
1048                            }
1049                        })
1050                        .collect::<Vec<_>>();
1051
1052                    let (folds_snapshot, fold_edits) = fold_map.read(cx);
1053                    let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1054                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1055                        wrap_map.sync(tabs_snapshot, tab_edits, cx)
1056                    });
1057                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1058                    let block_ids = block_map.insert(block_properties.clone(), cx);
1059                    for (block_id, props) in block_ids.into_iter().zip(block_properties) {
1060                        expected_blocks.push((block_id, props));
1061                    }
1062                }
1063                40..=59 if !expected_blocks.is_empty() => {
1064                    let block_count = rng.gen_range(1..=4.min(expected_blocks.len()));
1065                    let block_ids_to_remove = (0..block_count)
1066                        .map(|_| {
1067                            expected_blocks
1068                                .remove(rng.gen_range(0..expected_blocks.len()))
1069                                .0
1070                        })
1071                        .collect();
1072
1073                    let (folds_snapshot, fold_edits) = fold_map.read(cx);
1074                    let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1075                    let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1076                        wrap_map.sync(tabs_snapshot, tab_edits, cx)
1077                    });
1078                    let mut block_map = block_map.write(wraps_snapshot, wrap_edits, cx);
1079                    block_map.remove(block_ids_to_remove, cx);
1080                }
1081                _ => {
1082                    buffer.update(cx, |buffer, _| {
1083                        buffer.randomly_edit(&mut rng, 1);
1084                        log::info!("buffer text: {:?}", buffer.text());
1085                    });
1086                }
1087            }
1088
1089            let (folds_snapshot, fold_edits) = fold_map.read(cx);
1090            let (tabs_snapshot, tab_edits) = tab_map.sync(folds_snapshot, fold_edits);
1091            let (wraps_snapshot, wrap_edits) = wrap_map.update(cx, |wrap_map, cx| {
1092                wrap_map.sync(tabs_snapshot, tab_edits, cx)
1093            });
1094            let mut blocks_snapshot = block_map.read(wraps_snapshot.clone(), wrap_edits, cx);
1095            assert_eq!(
1096                blocks_snapshot.transforms.summary().input_rows,
1097                wraps_snapshot.max_point().row() + 1
1098            );
1099            log::info!("blocks text: {:?}", blocks_snapshot.text());
1100
1101            let buffer = buffer.read(cx);
1102            let mut sorted_blocks = expected_blocks
1103                .iter()
1104                .cloned()
1105                .map(|(id, block)| {
1106                    let mut position = block.position.to_point(buffer);
1107                    match block.disposition {
1108                        BlockDisposition::Above => {
1109                            position.column = 0;
1110                        }
1111                        BlockDisposition::Below => {
1112                            position.column = buffer.line_len(position.row);
1113                        }
1114                    };
1115                    let row = wraps_snapshot.from_point(position, Bias::Left).row();
1116                    (
1117                        id,
1118                        BlockProperties {
1119                            position: row,
1120                            text: block.text,
1121                            runs: block.runs,
1122                            disposition: block.disposition,
1123                        },
1124                    )
1125                })
1126                .collect::<Vec<_>>();
1127            sorted_blocks
1128                .sort_unstable_by_key(|(id, block)| (block.position, block.disposition, *id));
1129            let mut sorted_blocks = sorted_blocks.into_iter().peekable();
1130
1131            let mut expected_buffer_rows = Vec::new();
1132            let mut expected_text = String::new();
1133            let input_text = wraps_snapshot.text();
1134            for (row, input_line) in input_text.split('\n').enumerate() {
1135                let row = row as u32;
1136                if row > 0 {
1137                    expected_text.push('\n');
1138                }
1139
1140                let buffer_row = wraps_snapshot
1141                    .to_point(WrapPoint::new(row, 0), Bias::Left)
1142                    .row;
1143
1144                while let Some((_, block)) = sorted_blocks.peek() {
1145                    if block.position == row && block.disposition == BlockDisposition::Above {
1146                        let text = block.text.to_string();
1147                        expected_text.push_str(&text);
1148                        expected_text.push('\n');
1149                        for _ in text.split('\n') {
1150                            expected_buffer_rows.push(None);
1151                        }
1152                        sorted_blocks.next();
1153                    } else {
1154                        break;
1155                    }
1156                }
1157
1158                let soft_wrapped = wraps_snapshot.to_tab_point(WrapPoint::new(row, 0)).column() > 0;
1159                expected_buffer_rows.push(if soft_wrapped { None } else { Some(buffer_row) });
1160                expected_text.push_str(input_line);
1161
1162                while let Some((_, block)) = sorted_blocks.peek() {
1163                    if block.position == row && block.disposition == BlockDisposition::Below {
1164                        let text = block.text.to_string();
1165                        expected_text.push('\n');
1166                        expected_text.push_str(&text);
1167                        for _ in text.split('\n') {
1168                            expected_buffer_rows.push(None);
1169                        }
1170                        sorted_blocks.next();
1171                    } else {
1172                        break;
1173                    }
1174                }
1175            }
1176
1177            assert_eq!(blocks_snapshot.text(), expected_text);
1178            // for row in 0..=blocks_snapshot.wrap_snapshot.max_point().row() {
1179            //     let wrap_point = WrapPoint::new(row, 0);
1180            //     let block_point = blocks_snapshot.to_block_point(wrap_point, Bias::Right);
1181            //     assert_eq!(blocks_snapshot.to_wrap_point(block_point), wrap_point);
1182            // }
1183            // assert_eq!(
1184            //     buffer_rows_from_chunks(
1185            //         blocks_snapshot.chunks(0..blocks_snapshot.max_point().row + 1, false)
1186            //     ),
1187            //     expected_buffer_rows
1188            // );
1189        }
1190    }
1191}