fold_map.rs

   1use super::{
   2    buffer::{AnchorRangeExt, TextSummary},
   3    Anchor, Bias, Buffer, DisplayPoint, Edit, Point, ToOffset,
   4};
   5use crate::{
   6    editor::buffer,
   7    settings::StyleId,
   8    sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
   9    time,
  10};
  11use gpui::{AppContext, ModelHandle};
  12use parking_lot::{Mutex, MutexGuard};
  13use std::{
  14    cmp::{self, Ordering},
  15    ops::Range,
  16};
  17
  18pub struct FoldMap {
  19    buffer: ModelHandle<Buffer>,
  20    transforms: Mutex<SumTree<Transform>>,
  21    folds: SumTree<Fold>,
  22    last_sync: Mutex<time::Global>,
  23}
  24
  25impl FoldMap {
  26    pub fn new(buffer_handle: ModelHandle<Buffer>, ctx: &AppContext) -> Self {
  27        let buffer = buffer_handle.read(ctx);
  28        let text_summary = buffer.text_summary();
  29        Self {
  30            buffer: buffer_handle,
  31            folds: Default::default(),
  32            transforms: Mutex::new(SumTree::from_item(
  33                Transform {
  34                    summary: TransformSummary {
  35                        buffer: text_summary.clone(),
  36                        display: text_summary,
  37                    },
  38                    display_text: None,
  39                },
  40                &(),
  41            )),
  42            last_sync: Mutex::new(buffer.version()),
  43        }
  44    }
  45
  46    pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
  47        FoldMapSnapshot {
  48            transforms: self.sync(ctx).clone(),
  49            buffer: self.buffer.read(ctx).snapshot(),
  50        }
  51    }
  52
  53    pub fn len(&self, ctx: &AppContext) -> usize {
  54        self.sync(ctx).summary().display.bytes
  55    }
  56
  57    pub fn line_len(&self, row: u32, ctx: &AppContext) -> u32 {
  58        let line_start = self.to_display_offset(DisplayPoint::new(row, 0), ctx).0;
  59        let line_end = if row >= self.max_point(ctx).row() {
  60            self.len(ctx)
  61        } else {
  62            self.to_display_offset(DisplayPoint::new(row + 1, 0), ctx).0 - 1
  63        };
  64        (line_end - line_start) as u32
  65    }
  66
  67    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
  68        DisplayPoint(self.sync(ctx).summary().display.lines)
  69    }
  70
  71    pub fn longest_row(&self, ctx: &AppContext) -> u32 {
  72        self.sync(ctx).summary().display.longest_row
  73    }
  74
  75    pub fn folds_in_range<'a, T>(
  76        &'a self,
  77        range: Range<T>,
  78        ctx: &'a AppContext,
  79    ) -> impl Iterator<Item = &'a Range<Anchor>>
  80    where
  81        T: ToOffset,
  82    {
  83        self.intersecting_folds(range, ctx).map(|f| &f.0)
  84    }
  85
  86    pub fn fold<T: ToOffset>(
  87        &mut self,
  88        ranges: impl IntoIterator<Item = Range<T>>,
  89        ctx: &AppContext,
  90    ) {
  91        let _ = self.sync(ctx);
  92
  93        let mut edits = Vec::new();
  94        let mut folds = Vec::new();
  95        let buffer = self.buffer.read(ctx);
  96        for range in ranges.into_iter() {
  97            let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
  98            if range.start != range.end {
  99                let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
 100                folds.push(fold);
 101                edits.push(Edit {
 102                    old_range: range.clone(),
 103                    new_range: range.clone(),
 104                    ..Default::default()
 105                });
 106            }
 107        }
 108
 109        folds.sort_unstable_by(|a, b| sum_tree::SeekDimension::cmp(a, b, buffer));
 110        edits.sort_unstable_by(|a, b| {
 111            a.old_range
 112                .start
 113                .cmp(&b.old_range.start)
 114                .then_with(|| b.old_range.end.cmp(&a.old_range.end))
 115        });
 116
 117        self.folds = {
 118            let mut new_tree = SumTree::new();
 119            let mut cursor = self.folds.cursor::<_, ()>();
 120            for fold in folds {
 121                new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
 122                new_tree.push(fold, buffer);
 123            }
 124            new_tree.push_tree(cursor.suffix(buffer), buffer);
 125            new_tree
 126        };
 127        self.apply_edits(edits, ctx);
 128    }
 129
 130    pub fn unfold<T: ToOffset>(
 131        &mut self,
 132        ranges: impl IntoIterator<Item = Range<T>>,
 133        ctx: &AppContext,
 134    ) {
 135        let _ = self.sync(ctx);
 136
 137        let buffer = self.buffer.read(ctx);
 138
 139        let mut edits = Vec::new();
 140        let mut fold_ixs_to_delete = Vec::new();
 141        for range in ranges.into_iter() {
 142            // Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
 143            let mut folds_cursor = self.intersecting_folds(range, ctx);
 144            while let Some(fold) = folds_cursor.item() {
 145                let offset_range = fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer);
 146                edits.push(Edit {
 147                    old_range: offset_range.clone(),
 148                    new_range: offset_range,
 149                    ..Default::default()
 150                });
 151                fold_ixs_to_delete.push(*folds_cursor.start());
 152                folds_cursor.next();
 153            }
 154        }
 155
 156        fold_ixs_to_delete.sort_unstable();
 157        fold_ixs_to_delete.dedup();
 158        edits.sort_unstable_by(|a, b| {
 159            a.old_range
 160                .start
 161                .cmp(&b.old_range.start)
 162                .then_with(|| b.old_range.end.cmp(&a.old_range.end))
 163        });
 164
 165        self.folds = {
 166            let mut cursor = self.folds.cursor::<_, ()>();
 167            let mut folds = SumTree::new();
 168            for fold_ix in fold_ixs_to_delete {
 169                folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer);
 170                cursor.next();
 171            }
 172            folds.push_tree(cursor.suffix(buffer), buffer);
 173            folds
 174        };
 175        self.apply_edits(edits, ctx);
 176    }
 177
 178    fn intersecting_folds<'a, T>(
 179        &self,
 180        range: Range<T>,
 181        ctx: &'a AppContext,
 182    ) -> FilterCursor<impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>
 183    where
 184        T: ToOffset,
 185    {
 186        let buffer = self.buffer.read(ctx);
 187        let start = buffer.anchor_before(range.start.to_offset(buffer));
 188        let end = buffer.anchor_after(range.end.to_offset(buffer));
 189        self.folds.filter::<_, usize>(move |summary| {
 190            start.cmp(&summary.max_end, buffer).unwrap() == Ordering::Less
 191                && end.cmp(&summary.min_start, buffer).unwrap() == Ordering::Greater
 192        })
 193    }
 194
 195    pub fn intersects_fold<T>(&self, offset: T, ctx: &AppContext) -> bool
 196    where
 197        T: ToOffset,
 198    {
 199        let buffer = self.buffer.read(ctx);
 200        let offset = offset.to_offset(buffer);
 201        let transforms = self.sync(ctx);
 202        let mut cursor = transforms.cursor::<usize, usize>();
 203        cursor.seek(&offset, SeekBias::Right, &());
 204        cursor.item().map_or(false, |t| t.display_text.is_some())
 205    }
 206
 207    pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
 208        let transforms = self.sync(ctx);
 209        let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
 210        cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
 211        while let Some(transform) = cursor.item() {
 212            if transform.display_text.is_some() {
 213                return true;
 214            }
 215            if cursor.end().row() == display_row {
 216                cursor.next()
 217            } else {
 218                break;
 219            }
 220        }
 221        false
 222    }
 223
 224    pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> usize {
 225        self.snapshot(ctx).to_buffer_offset(point)
 226    }
 227
 228    pub fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> DisplayOffset {
 229        self.snapshot(ctx).to_display_offset(point)
 230    }
 231
 232    pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
 233        let transforms = self.sync(ctx);
 234        let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
 235        cursor.seek(&display_point, SeekBias::Right, &());
 236        let overshoot = display_point.0 - cursor.start().display.lines;
 237        cursor.start().buffer.lines + overshoot
 238    }
 239
 240    pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
 241        let transforms = self.sync(ctx);
 242        let mut cursor = transforms.cursor::<Point, TransformSummary>();
 243        cursor.seek(&point, SeekBias::Right, &());
 244        let overshoot = point - cursor.start().buffer.lines;
 245        DisplayPoint(cmp::min(
 246            cursor.start().display.lines + overshoot,
 247            cursor.end().display.lines,
 248        ))
 249    }
 250
 251    fn sync(&self, ctx: &AppContext) -> MutexGuard<SumTree<Transform>> {
 252        let buffer = self.buffer.read(ctx);
 253        let mut edits = buffer.edits_since(self.last_sync.lock().clone()).peekable();
 254        if edits.peek().is_some() {
 255            self.apply_edits(edits, ctx);
 256        }
 257        *self.last_sync.lock() = buffer.version();
 258        self.transforms.lock()
 259    }
 260
 261    fn apply_edits(&self, edits: impl IntoIterator<Item = Edit>, ctx: &AppContext) {
 262        let buffer = self.buffer.read(ctx);
 263        let mut edits = edits.into_iter().peekable();
 264
 265        let mut new_transforms = SumTree::new();
 266        let mut transforms = self.transforms.lock();
 267        let mut cursor = transforms.cursor::<usize, usize>();
 268        cursor.seek(&0, SeekBias::Right, &());
 269
 270        while let Some(mut edit) = edits.next() {
 271            new_transforms.push_tree(
 272                cursor.slice(&edit.old_range.start, SeekBias::Left, &()),
 273                &(),
 274            );
 275            edit.new_range.start -= edit.old_range.start - cursor.start();
 276            edit.old_range.start = *cursor.start();
 277
 278            cursor.seek(&edit.old_range.end, SeekBias::Right, &());
 279            cursor.next();
 280
 281            let mut delta = edit.delta();
 282            loop {
 283                edit.old_range.end = *cursor.start();
 284
 285                if let Some(next_edit) = edits.peek() {
 286                    if next_edit.old_range.start > edit.old_range.end {
 287                        break;
 288                    }
 289
 290                    let next_edit = edits.next().unwrap();
 291                    delta += next_edit.delta();
 292
 293                    if next_edit.old_range.end >= edit.old_range.end {
 294                        edit.old_range.end = next_edit.old_range.end;
 295                        cursor.seek(&edit.old_range.end, SeekBias::Right, &());
 296                        cursor.next();
 297                    }
 298                } else {
 299                    break;
 300                }
 301            }
 302
 303            edit.new_range.end =
 304                ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
 305
 306            let anchor = buffer.anchor_before(edit.new_range.start);
 307            let mut folds_cursor = self.folds.cursor::<_, ()>();
 308            folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer);
 309            let mut folds = folds_cursor
 310                .map(|f| f.0.start.to_offset(buffer)..f.0.end.to_offset(buffer))
 311                .peekable();
 312
 313            while folds
 314                .peek()
 315                .map_or(false, |fold| fold.start < edit.new_range.end)
 316            {
 317                let mut fold = folds.next().unwrap();
 318                let sum = new_transforms.summary();
 319
 320                assert!(fold.start >= sum.buffer.bytes);
 321
 322                while folds
 323                    .peek()
 324                    .map_or(false, |next_fold| next_fold.start <= fold.end)
 325                {
 326                    let next_fold = folds.next().unwrap();
 327                    if next_fold.end > fold.end {
 328                        fold.end = next_fold.end;
 329                    }
 330                }
 331
 332                if fold.start > sum.buffer.bytes {
 333                    let text_summary = buffer.text_summary_for_range(sum.buffer.bytes..fold.start);
 334                    new_transforms.push(
 335                        Transform {
 336                            summary: TransformSummary {
 337                                display: text_summary.clone(),
 338                                buffer: text_summary,
 339                            },
 340                            display_text: None,
 341                        },
 342                        &(),
 343                    );
 344                }
 345
 346                if fold.end > fold.start {
 347                    let display_text = "";
 348                    let chars = display_text.chars().count() as u32;
 349                    let lines = Point::new(0, display_text.len() as u32);
 350                    new_transforms.push(
 351                        Transform {
 352                            summary: TransformSummary {
 353                                display: TextSummary {
 354                                    bytes: display_text.len(),
 355                                    lines,
 356                                    first_line_chars: chars,
 357                                    last_line_chars: chars,
 358                                    longest_row: 0,
 359                                    longest_row_chars: chars,
 360                                },
 361                                buffer: buffer.text_summary_for_range(fold.start..fold.end),
 362                            },
 363                            display_text: Some(display_text),
 364                        },
 365                        &(),
 366                    );
 367                }
 368            }
 369
 370            let sum = new_transforms.summary();
 371            if sum.buffer.bytes < edit.new_range.end {
 372                let text_summary =
 373                    buffer.text_summary_for_range(sum.buffer.bytes..edit.new_range.end);
 374                new_transforms.push(
 375                    Transform {
 376                        summary: TransformSummary {
 377                            display: text_summary.clone(),
 378                            buffer: text_summary,
 379                        },
 380                        display_text: None,
 381                    },
 382                    &(),
 383                );
 384            }
 385        }
 386
 387        new_transforms.push_tree(cursor.suffix(&()), &());
 388        if new_transforms.is_empty() {
 389            let text_summary = buffer.text_summary();
 390            new_transforms.push(
 391                Transform {
 392                    summary: TransformSummary {
 393                        display: text_summary.clone(),
 394                        buffer: text_summary,
 395                    },
 396                    display_text: None,
 397                },
 398                &(),
 399            );
 400        }
 401
 402        drop(cursor);
 403        *transforms = new_transforms;
 404    }
 405}
 406
 407pub struct FoldMapSnapshot {
 408    transforms: SumTree<Transform>,
 409    buffer: buffer::Snapshot,
 410}
 411
 412impl FoldMapSnapshot {
 413    pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
 414        if start_row > self.transforms.summary().display.lines.row {
 415            panic!("invalid display row {}", start_row);
 416        }
 417
 418        let display_point = Point::new(start_row, 0);
 419        let mut cursor = self.transforms.cursor();
 420        cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &());
 421
 422        BufferRows {
 423            display_point,
 424            cursor,
 425        }
 426    }
 427
 428    pub fn max_point(&self) -> DisplayPoint {
 429        DisplayPoint(self.transforms.summary().display.lines)
 430    }
 431
 432    pub fn chunks_at(&self, offset: DisplayOffset) -> Chunks {
 433        let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
 434        transform_cursor.seek(&offset, SeekBias::Right, &());
 435        let overshoot = offset.0 - transform_cursor.start().display.bytes;
 436        let buffer_offset = transform_cursor.start().buffer.bytes + overshoot;
 437        Chunks {
 438            transform_cursor,
 439            buffer_offset,
 440            buffer_chunks: self.buffer.text_for_range(buffer_offset..self.buffer.len()),
 441        }
 442    }
 443
 444    pub fn highlighted_chunks(&mut self, range: Range<DisplayOffset>) -> HighlightedChunks {
 445        let mut transform_cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
 446
 447        transform_cursor.seek(&range.end, SeekBias::Right, &());
 448        let overshoot = range.end.0 - transform_cursor.start().display.bytes;
 449        let buffer_end = transform_cursor.start().buffer.bytes + overshoot;
 450
 451        transform_cursor.seek(&range.start, SeekBias::Right, &());
 452        let overshoot = range.start.0 - transform_cursor.start().display.bytes;
 453        let buffer_start = transform_cursor.start().buffer.bytes + overshoot;
 454
 455        HighlightedChunks {
 456            transform_cursor,
 457            buffer_offset: buffer_start,
 458            buffer_chunks: self
 459                .buffer
 460                .highlighted_text_for_range(buffer_start..buffer_end),
 461            buffer_chunk: None,
 462        }
 463    }
 464
 465    pub fn chars_at<'a>(&'a self, point: DisplayPoint) -> impl Iterator<Item = char> + 'a {
 466        let offset = self.to_display_offset(point);
 467        self.chunks_at(offset).flat_map(str::chars)
 468    }
 469
 470    pub fn to_display_offset(&self, point: DisplayPoint) -> DisplayOffset {
 471        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
 472        cursor.seek(&point, SeekBias::Right, &());
 473        let overshoot = point.0 - cursor.start().display.lines;
 474        let mut offset = cursor.start().display.bytes;
 475        if !overshoot.is_zero() {
 476            let transform = cursor.item().expect("display point out of range");
 477            assert!(transform.display_text.is_none());
 478            let end_buffer_offset = self
 479                .buffer
 480                .to_offset(cursor.start().buffer.lines + overshoot);
 481            offset += end_buffer_offset - cursor.start().buffer.bytes;
 482        }
 483        DisplayOffset(offset)
 484    }
 485
 486    pub fn to_buffer_offset(&self, point: DisplayPoint) -> usize {
 487        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
 488        cursor.seek(&point, SeekBias::Right, &());
 489        let overshoot = point.0 - cursor.start().display.lines;
 490        self.buffer
 491            .to_offset(cursor.start().buffer.lines + overshoot)
 492    }
 493
 494    #[cfg(test)]
 495    pub fn clip_offset(&self, offset: DisplayOffset, bias: Bias) -> DisplayOffset {
 496        let mut cursor = self.transforms.cursor::<DisplayOffset, TransformSummary>();
 497        cursor.seek(&offset, SeekBias::Right, &());
 498        if let Some(transform) = cursor.item() {
 499            let transform_start = cursor.start().display.bytes;
 500            if transform.display_text.is_some() {
 501                if offset.0 == transform_start || matches!(bias, Bias::Left) {
 502                    DisplayOffset(transform_start)
 503                } else {
 504                    DisplayOffset(cursor.end().display.bytes)
 505                }
 506            } else {
 507                let overshoot = offset.0 - transform_start;
 508                let buffer_offset = cursor.start().buffer.bytes + overshoot;
 509                let clipped_buffer_offset = self.buffer.clip_offset(buffer_offset, bias);
 510                DisplayOffset(
 511                    (offset.0 as isize + (clipped_buffer_offset as isize - buffer_offset as isize))
 512                        as usize,
 513                )
 514            }
 515        } else {
 516            DisplayOffset(self.transforms.summary().display.bytes)
 517        }
 518    }
 519
 520    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
 521        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
 522        cursor.seek(&point, SeekBias::Right, &());
 523        if let Some(transform) = cursor.item() {
 524            let transform_start = cursor.start().display.lines;
 525            if transform.display_text.is_some() {
 526                if point.0 == transform_start || matches!(bias, Bias::Left) {
 527                    DisplayPoint(transform_start)
 528                } else {
 529                    DisplayPoint(cursor.end().display.lines)
 530                }
 531            } else {
 532                let overshoot = point.0 - transform_start;
 533                let buffer_position = cursor.start().buffer.lines + overshoot;
 534                let clipped_buffer_position = self.buffer.clip_point(buffer_position, bias);
 535                DisplayPoint::new(
 536                    point.row(),
 537                    ((point.column() as i32) + clipped_buffer_position.column as i32
 538                        - buffer_position.column as i32) as u32,
 539                )
 540            }
 541        } else {
 542            DisplayPoint(self.transforms.summary().display.lines)
 543        }
 544    }
 545}
 546
 547#[derive(Clone, Debug, Default, Eq, PartialEq)]
 548struct Transform {
 549    summary: TransformSummary,
 550    display_text: Option<&'static str>,
 551}
 552
 553#[derive(Clone, Debug, Default, Eq, PartialEq)]
 554struct TransformSummary {
 555    display: TextSummary,
 556    buffer: TextSummary,
 557}
 558
 559impl sum_tree::Item for Transform {
 560    type Summary = TransformSummary;
 561
 562    fn summary(&self) -> Self::Summary {
 563        self.summary.clone()
 564    }
 565}
 566
 567impl sum_tree::Summary for TransformSummary {
 568    type Context = ();
 569
 570    fn add_summary(&mut self, other: &Self, _: &()) {
 571        self.buffer += &other.buffer;
 572        self.display += &other.display;
 573    }
 574}
 575
 576impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
 577    fn add_summary(&mut self, summary: &'a TransformSummary) {
 578        sum_tree::Summary::add_summary(self, summary, &());
 579    }
 580}
 581
 582#[derive(Clone, Debug)]
 583struct Fold(Range<Anchor>);
 584
 585impl Default for Fold {
 586    fn default() -> Self {
 587        Self(Anchor::Start..Anchor::End)
 588    }
 589}
 590
 591impl sum_tree::Item for Fold {
 592    type Summary = FoldSummary;
 593
 594    fn summary(&self) -> Self::Summary {
 595        FoldSummary {
 596            start: self.0.start.clone(),
 597            end: self.0.end.clone(),
 598            min_start: self.0.start.clone(),
 599            max_end: self.0.end.clone(),
 600            count: 1,
 601        }
 602    }
 603}
 604
 605#[derive(Clone, Debug)]
 606struct FoldSummary {
 607    start: Anchor,
 608    end: Anchor,
 609    min_start: Anchor,
 610    max_end: Anchor,
 611    count: usize,
 612}
 613
 614impl Default for FoldSummary {
 615    fn default() -> Self {
 616        Self {
 617            start: Anchor::Start,
 618            end: Anchor::End,
 619            min_start: Anchor::End,
 620            max_end: Anchor::Start,
 621            count: 0,
 622        }
 623    }
 624}
 625
 626impl sum_tree::Summary for FoldSummary {
 627    type Context = Buffer;
 628
 629    fn add_summary(&mut self, other: &Self, buffer: &Buffer) {
 630        if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
 631            self.min_start = other.min_start.clone();
 632        }
 633        if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
 634            self.max_end = other.max_end.clone();
 635        }
 636
 637        #[cfg(debug_assertions)]
 638        {
 639            let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
 640            assert!(start_comparison <= Ordering::Equal);
 641            if start_comparison == Ordering::Equal {
 642                assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
 643            }
 644        }
 645        self.start = other.start.clone();
 646        self.end = other.end.clone();
 647        self.count += other.count;
 648    }
 649}
 650
 651impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
 652    fn add_summary(&mut self, summary: &'a FoldSummary) {
 653        self.0.start = summary.start.clone();
 654        self.0.end = summary.end.clone();
 655    }
 656}
 657
 658impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
 659    fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering {
 660        self.0.cmp(&other.0, buffer).unwrap()
 661    }
 662}
 663
 664impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
 665    fn add_summary(&mut self, summary: &'a FoldSummary) {
 666        *self += summary.count;
 667    }
 668}
 669
 670pub struct BufferRows<'a> {
 671    cursor: Cursor<'a, Transform, DisplayPoint, TransformSummary>,
 672    display_point: Point,
 673}
 674
 675impl<'a> Iterator for BufferRows<'a> {
 676    type Item = u32;
 677
 678    fn next(&mut self) -> Option<Self::Item> {
 679        while self.display_point > self.cursor.end().display.lines {
 680            self.cursor.next();
 681            if self.cursor.item().is_none() {
 682                // TODO: Return a bool from next?
 683                break;
 684            }
 685        }
 686
 687        if self.cursor.item().is_some() {
 688            let overshoot = self.display_point - self.cursor.start().display.lines;
 689            let buffer_point = self.cursor.start().buffer.lines + overshoot;
 690            self.display_point.row += 1;
 691            Some(buffer_point.row)
 692        } else {
 693            None
 694        }
 695    }
 696}
 697
 698pub struct Chunks<'a> {
 699    transform_cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
 700    buffer_chunks: buffer::Chunks<'a>,
 701    buffer_offset: usize,
 702}
 703
 704impl<'a> Iterator for Chunks<'a> {
 705    type Item = &'a str;
 706
 707    fn next(&mut self) -> Option<Self::Item> {
 708        let transform = if let Some(item) = self.transform_cursor.item() {
 709            item
 710        } else {
 711            return None;
 712        };
 713
 714        // If we're in a fold, then return the fold's display text and
 715        // advance the transform and buffer cursors to the end of the fold.
 716        if let Some(display_text) = transform.display_text {
 717            self.buffer_offset += transform.summary.buffer.bytes;
 718            self.buffer_chunks.seek(self.buffer_offset);
 719
 720            while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
 721                && self.transform_cursor.item().is_some()
 722            {
 723                self.transform_cursor.next();
 724            }
 725
 726            return Some(display_text);
 727        }
 728
 729        // Otherwise, take a chunk from the buffer's text.
 730        if let Some(mut chunk) = self.buffer_chunks.peek() {
 731            let offset_in_chunk = self.buffer_offset - self.buffer_chunks.offset();
 732            chunk = &chunk[offset_in_chunk..];
 733
 734            // Truncate the chunk so that it ends at the next fold.
 735            let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
 736            if chunk.len() >= region_end {
 737                chunk = &chunk[0..region_end];
 738                self.transform_cursor.next();
 739            } else {
 740                self.buffer_chunks.next();
 741            }
 742
 743            self.buffer_offset += chunk.len();
 744            return Some(chunk);
 745        }
 746
 747        None
 748    }
 749}
 750
 751pub struct HighlightedChunks<'a> {
 752    transform_cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
 753    buffer_chunks: buffer::HighlightedChunks<'a>,
 754    buffer_chunk: Option<(usize, &'a str, StyleId)>,
 755    buffer_offset: usize,
 756}
 757
 758impl<'a> Iterator for HighlightedChunks<'a> {
 759    type Item = (&'a str, StyleId);
 760
 761    fn next(&mut self) -> Option<Self::Item> {
 762        let transform = if let Some(item) = self.transform_cursor.item() {
 763            item
 764        } else {
 765            return None;
 766        };
 767
 768        // If we're in a fold, then return the fold's display text and
 769        // advance the transform and buffer cursors to the end of the fold.
 770        if let Some(display_text) = transform.display_text {
 771            self.buffer_chunk.take();
 772            self.buffer_offset += transform.summary.buffer.bytes;
 773            self.buffer_chunks.seek(self.buffer_offset);
 774
 775            while self.buffer_offset >= self.transform_cursor.end().buffer.bytes
 776                && self.transform_cursor.item().is_some()
 777            {
 778                self.transform_cursor.next();
 779            }
 780
 781            return Some((display_text, StyleId::default()));
 782        }
 783
 784        // Retrieve a chunk from the current location in the buffer.
 785        if self.buffer_chunk.is_none() {
 786            let chunk_offset = self.buffer_chunks.offset();
 787            self.buffer_chunk = self
 788                .buffer_chunks
 789                .next()
 790                .map(|(chunk, capture_ix)| (chunk_offset, chunk, capture_ix));
 791        }
 792
 793        // Otherwise, take a chunk from the buffer's text.
 794        if let Some((chunk_offset, mut chunk, capture_ix)) = self.buffer_chunk {
 795            let offset_in_chunk = self.buffer_offset - chunk_offset;
 796            chunk = &chunk[offset_in_chunk..];
 797
 798            // Truncate the chunk so that it ends at the next fold.
 799            let region_end = self.transform_cursor.end().buffer.bytes - self.buffer_offset;
 800            if chunk.len() >= region_end {
 801                chunk = &chunk[0..region_end];
 802                self.transform_cursor.next();
 803            } else {
 804                self.buffer_chunk.take();
 805            }
 806
 807            self.buffer_offset += chunk.len();
 808            return Some((chunk, capture_ix));
 809        }
 810
 811        None
 812    }
 813}
 814
 815impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
 816    fn add_summary(&mut self, summary: &'a TransformSummary) {
 817        self.0 += &summary.display.lines;
 818    }
 819}
 820
 821#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 822pub struct DisplayOffset(usize);
 823
 824impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
 825    fn add_summary(&mut self, summary: &'a TransformSummary) {
 826        self.0 += &summary.display.bytes;
 827    }
 828}
 829
 830impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
 831    fn add_summary(&mut self, summary: &'a TransformSummary) {
 832        *self += &summary.buffer.lines;
 833    }
 834}
 835
 836impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
 837    fn add_summary(&mut self, summary: &'a TransformSummary) {
 838        *self += &summary.buffer.bytes;
 839    }
 840}
 841
 842#[cfg(test)]
 843mod tests {
 844    use super::*;
 845    use crate::editor::buffer::ToPoint;
 846    use crate::test::sample_text;
 847
 848    #[gpui::test]
 849    fn test_basic_folds(app: &mut gpui::MutableAppContext) {
 850        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
 851        let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 852
 853        map.fold(
 854            vec![
 855                Point::new(0, 2)..Point::new(2, 2),
 856                Point::new(2, 4)..Point::new(4, 1),
 857            ],
 858            app.as_ref(),
 859        );
 860        assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee");
 861
 862        buffer.update(app, |buffer, ctx| {
 863            buffer
 864                .edit(
 865                    vec![
 866                        Point::new(0, 0)..Point::new(0, 1),
 867                        Point::new(2, 3)..Point::new(2, 3),
 868                    ],
 869                    "123",
 870                    Some(ctx),
 871                )
 872                .unwrap();
 873        });
 874        assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee");
 875
 876        buffer.update(app, |buffer, ctx| {
 877            let start_version = buffer.version.clone();
 878            buffer
 879                .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))
 880                .unwrap();
 881            buffer.edits_since(start_version).collect::<Vec<_>>()
 882        });
 883        assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
 884
 885        map.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), app.as_ref());
 886        assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
 887    }
 888
 889    #[gpui::test]
 890    fn test_adjacent_folds(app: &mut gpui::MutableAppContext) {
 891        let buffer = app.add_model(|ctx| Buffer::new(0, "abcdefghijkl", ctx));
 892
 893        {
 894            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 895
 896            map.fold(vec![5..8], app.as_ref());
 897            map.check_invariants(app.as_ref());
 898            assert_eq!(map.text(app.as_ref()), "abcde…ijkl");
 899
 900            // Create an fold adjacent to the start of the first fold.
 901            map.fold(vec![0..1, 2..5], app.as_ref());
 902            map.check_invariants(app.as_ref());
 903            assert_eq!(map.text(app.as_ref()), "…b…ijkl");
 904
 905            // Create an fold adjacent to the end of the first fold.
 906            map.fold(vec![11..11, 8..10], app.as_ref());
 907            map.check_invariants(app.as_ref());
 908            assert_eq!(map.text(app.as_ref()), "…b…kl");
 909        }
 910
 911        {
 912            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 913
 914            // Create two adjacent folds.
 915            map.fold(vec![0..2, 2..5], app.as_ref());
 916            map.check_invariants(app.as_ref());
 917            assert_eq!(map.text(app.as_ref()), "…fghijkl");
 918
 919            // Edit within one of the folds.
 920            buffer.update(app, |buffer, ctx| {
 921                let version = buffer.version();
 922                buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
 923                buffer.edits_since(version).collect::<Vec<_>>()
 924            });
 925            map.check_invariants(app.as_ref());
 926            assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
 927        }
 928    }
 929
 930    #[gpui::test]
 931    fn test_overlapping_folds(app: &mut gpui::MutableAppContext) {
 932        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
 933        let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 934        map.fold(
 935            vec![
 936                Point::new(0, 2)..Point::new(2, 2),
 937                Point::new(0, 4)..Point::new(1, 0),
 938                Point::new(1, 2)..Point::new(3, 2),
 939                Point::new(3, 1)..Point::new(4, 1),
 940            ],
 941            app.as_ref(),
 942        );
 943        assert_eq!(map.text(app.as_ref()), "aa…eeeee");
 944    }
 945
 946    #[gpui::test]
 947    fn test_merging_folds_via_edit(app: &mut gpui::MutableAppContext) {
 948        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
 949        let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 950
 951        map.fold(
 952            vec![
 953                Point::new(0, 2)..Point::new(2, 2),
 954                Point::new(3, 1)..Point::new(4, 1),
 955            ],
 956            app.as_ref(),
 957        );
 958        assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee");
 959
 960        buffer.update(app, |buffer, ctx| {
 961            buffer
 962                .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))
 963                .unwrap();
 964        });
 965        assert_eq!(map.text(app.as_ref()), "aa…eeeee");
 966    }
 967
 968    #[gpui::test]
 969    fn test_folds_in_range(app: &mut gpui::MutableAppContext) {
 970        let buffer = app.add_model(|ctx| Buffer::new(0, sample_text(5, 6), ctx));
 971        let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 972        let buffer = buffer.read(app);
 973
 974        map.fold(
 975            vec![
 976                Point::new(0, 2)..Point::new(2, 2),
 977                Point::new(0, 4)..Point::new(1, 0),
 978                Point::new(1, 2)..Point::new(3, 2),
 979                Point::new(3, 1)..Point::new(4, 1),
 980            ],
 981            app.as_ref(),
 982        );
 983        let fold_ranges = map
 984            .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref())
 985            .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer))
 986            .collect::<Vec<_>>();
 987        assert_eq!(
 988            fold_ranges,
 989            vec![
 990                Point::new(0, 2)..Point::new(2, 2),
 991                Point::new(1, 2)..Point::new(3, 2)
 992            ]
 993        );
 994    }
 995
 996    #[gpui::test]
 997    fn test_random_folds(app: &mut gpui::MutableAppContext) {
 998        use crate::editor::ToPoint;
 999        use crate::util::RandomCharIter;
1000        use rand::prelude::*;
1001        use std::env;
1002        use Bias::{Left, Right};
1003
1004        let iterations = env::var("ITERATIONS")
1005            .map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
1006            .unwrap_or(100);
1007        let operations = env::var("OPERATIONS")
1008            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1009            .unwrap_or(10);
1010        let seed_range = if let Ok(seed) = env::var("SEED") {
1011            let seed = seed.parse().expect("invalid `SEED` variable");
1012            seed..seed + 1
1013        } else {
1014            0..iterations
1015        };
1016
1017        for seed in seed_range {
1018            dbg!(seed);
1019            let mut rng = StdRng::seed_from_u64(seed);
1020
1021            let buffer = app.add_model(|ctx| {
1022                let len = rng.gen_range(0..10);
1023                let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1024                Buffer::new(0, text, ctx)
1025            });
1026            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
1027
1028            for _ in 0..operations {
1029                log::info!("text: {:?}", buffer.read(app).text());
1030                match rng.gen_range(0..=100) {
1031                    0..=34 => {
1032                        let buffer = buffer.read(app);
1033                        let mut to_fold = Vec::new();
1034                        for _ in 0..rng.gen_range(1..=5) {
1035                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1036                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1037                            to_fold.push(start..end);
1038                        }
1039                        log::info!("folding {:?}", to_fold);
1040                        map.fold(to_fold, app.as_ref());
1041                    }
1042                    35..=59 if !map.folds.is_empty() => {
1043                        let buffer = buffer.read(app);
1044                        let mut to_unfold = Vec::new();
1045                        for _ in 0..rng.gen_range(1..=3) {
1046                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1047                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1048                            to_unfold.push(start..end);
1049                        }
1050                        log::info!("unfolding {:?}", to_unfold);
1051                        map.unfold(to_unfold, app.as_ref());
1052                    }
1053                    _ => {
1054                        let edits = buffer.update(app, |buffer, ctx| {
1055                            let start_version = buffer.version.clone();
1056                            let edit_count = rng.gen_range(1..=5);
1057                            buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
1058                            buffer.edits_since(start_version).collect::<Vec<_>>()
1059                        });
1060                        log::info!("editing {:?}", edits);
1061                    }
1062                }
1063                map.check_invariants(app.as_ref());
1064
1065                let buffer = map.buffer.read(app);
1066                let mut expected_text = buffer.text();
1067                let mut expected_buffer_rows = Vec::new();
1068                let mut next_row = buffer.max_point().row;
1069                for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
1070                    let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
1071                    let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
1072                    expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
1073                    next_row = fold_start.row;
1074
1075                    expected_text.replace_range(fold_range.start..fold_range.end, "");
1076                }
1077                expected_buffer_rows.extend((0..=next_row).rev());
1078                expected_buffer_rows.reverse();
1079
1080                assert_eq!(map.text(app.as_ref()), expected_text);
1081
1082                for (display_row, line) in expected_text.lines().enumerate() {
1083                    let line_len = map.line_len(display_row as u32, app.as_ref());
1084                    assert_eq!(line_len, line.len() as u32);
1085                }
1086
1087                let longest_row = map.longest_row(app.as_ref());
1088                let longest_char_column = expected_text
1089                    .split('\n')
1090                    .nth(longest_row as usize)
1091                    .unwrap()
1092                    .chars()
1093                    .count();
1094                let mut display_point = DisplayPoint::new(0, 0);
1095                let mut display_offset = DisplayOffset(0);
1096                let mut char_column = 0;
1097                for c in expected_text.chars() {
1098                    let buffer_point = map.to_buffer_point(display_point, app.as_ref());
1099                    let buffer_offset = buffer_point.to_offset(buffer);
1100                    assert_eq!(
1101                        map.to_display_point(buffer_point, app.as_ref()),
1102                        display_point,
1103                        "to_display_point({:?})",
1104                        buffer_point,
1105                    );
1106                    assert_eq!(
1107                        map.to_buffer_offset(display_point, app.as_ref()),
1108                        buffer_offset,
1109                        "to_buffer_offset({:?})",
1110                        display_point,
1111                    );
1112                    assert_eq!(
1113                        map.to_display_offset(display_point, app.as_ref()),
1114                        display_offset,
1115                        "to_display_offset({:?})",
1116                        display_point,
1117                    );
1118
1119                    if c == '\n' {
1120                        *display_point.row_mut() += 1;
1121                        *display_point.column_mut() = 0;
1122                        char_column = 0;
1123                    } else {
1124                        *display_point.column_mut() += c.len_utf8() as u32;
1125                        char_column += 1;
1126                    }
1127                    display_offset.0 += c.len_utf8();
1128                    if char_column > longest_char_column {
1129                        panic!(
1130                            "invalid longest row {:?} (chars {}), found row {:?} (chars: {})",
1131                            longest_row,
1132                            longest_char_column,
1133                            display_point.row(),
1134                            char_column
1135                        );
1136                    }
1137                }
1138
1139                for _ in 0..5 {
1140                    let offset = map.snapshot(app.as_ref()).clip_offset(
1141                        DisplayOffset(rng.gen_range(0..=map.len(app.as_ref()))),
1142                        Bias::Right,
1143                    );
1144                    assert_eq!(
1145                        map.snapshot(app.as_ref())
1146                            .chunks_at(offset)
1147                            .collect::<String>(),
1148                        &expected_text[offset.0..],
1149                    );
1150                }
1151
1152                for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
1153                    let display_row = map
1154                        .to_display_point(Point::new(*buffer_row, 0), app.as_ref())
1155                        .row();
1156                    assert_eq!(
1157                        map.snapshot(app.as_ref())
1158                            .buffer_rows(display_row)
1159                            .collect::<Vec<_>>(),
1160                        expected_buffer_rows[idx..],
1161                    );
1162                }
1163
1164                for fold_range in map.merged_fold_ranges(app.as_ref()) {
1165                    let display_point =
1166                        map.to_display_point(fold_range.start.to_point(buffer), app.as_ref());
1167                    assert!(map.is_line_folded(display_point.row(), app.as_ref()));
1168                }
1169
1170                for _ in 0..5 {
1171                    let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1172                    let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1173                    let expected_folds = map
1174                        .folds
1175                        .items()
1176                        .into_iter()
1177                        .filter(|fold| {
1178                            let start = buffer.anchor_before(start);
1179                            let end = buffer.anchor_after(end);
1180                            start.cmp(&fold.0.end, buffer).unwrap() == Ordering::Less
1181                                && end.cmp(&fold.0.start, buffer).unwrap() == Ordering::Greater
1182                        })
1183                        .map(|fold| fold.0)
1184                        .collect::<Vec<_>>();
1185
1186                    assert_eq!(
1187                        map.folds_in_range(start..end, app.as_ref())
1188                            .cloned()
1189                            .collect::<Vec<_>>(),
1190                        expected_folds
1191                    );
1192                }
1193            }
1194        }
1195    }
1196
1197    #[gpui::test]
1198    fn test_buffer_rows(app: &mut gpui::MutableAppContext) {
1199        let text = sample_text(6, 6) + "\n";
1200        let buffer = app.add_model(|ctx| Buffer::new(0, text, ctx));
1201
1202        let mut map = FoldMap::new(buffer.clone(), app.as_ref());
1203
1204        map.fold(
1205            vec![
1206                Point::new(0, 2)..Point::new(2, 2),
1207                Point::new(3, 1)..Point::new(4, 1),
1208            ],
1209            app.as_ref(),
1210        );
1211
1212        assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n");
1213        assert_eq!(
1214            map.snapshot(app.as_ref())
1215                .buffer_rows(0)
1216                .collect::<Vec<_>>(),
1217            vec![0, 3, 5, 6]
1218        );
1219        assert_eq!(
1220            map.snapshot(app.as_ref())
1221                .buffer_rows(3)
1222                .collect::<Vec<_>>(),
1223            vec![6]
1224        );
1225    }
1226
1227    impl FoldMap {
1228        fn text(&self, app: &AppContext) -> String {
1229            self.snapshot(app).chunks_at(DisplayOffset(0)).collect()
1230        }
1231
1232        fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
1233            let buffer = self.buffer.read(app);
1234            let mut folds = self.folds.items();
1235            // Ensure sorting doesn't change how folds get merged and displayed.
1236            folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
1237            let mut fold_ranges = folds
1238                .iter()
1239                .map(|fold| fold.0.start.to_offset(buffer)..fold.0.end.to_offset(buffer))
1240                .peekable();
1241
1242            let mut merged_ranges = Vec::new();
1243            while let Some(mut fold_range) = fold_ranges.next() {
1244                while let Some(next_range) = fold_ranges.peek() {
1245                    if fold_range.end >= next_range.start {
1246                        if next_range.end > fold_range.end {
1247                            fold_range.end = next_range.end;
1248                        }
1249                        fold_ranges.next();
1250                    } else {
1251                        break;
1252                    }
1253                }
1254                if fold_range.end > fold_range.start {
1255                    merged_ranges.push(fold_range);
1256                }
1257            }
1258            merged_ranges
1259        }
1260
1261        fn check_invariants(&self, ctx: &AppContext) {
1262            let transforms = self.sync(ctx);
1263            let buffer = self.buffer.read(ctx);
1264            assert_eq!(
1265                transforms.summary().buffer.bytes,
1266                buffer.len(),
1267                "transform tree does not match buffer's length"
1268            );
1269        }
1270    }
1271}