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