fold_map.rs

   1use gpui::{AppContext, ModelHandle};
   2use language::{Anchor, AnchorRangeExt, Buffer, Chunk, Point, PointUtf16, TextSummary, ToOffset};
   3use parking_lot::Mutex;
   4use std::{
   5    cmp::{self, Ordering},
   6    iter, mem,
   7    ops::Range,
   8    sync::atomic::{AtomicUsize, Ordering::SeqCst},
   9};
  10use sum_tree::{Bias, Cursor, FilterCursor, SumTree};
  11use theme::SyntaxTheme;
  12
  13pub trait ToFoldPoint {
  14    fn to_fold_point(&self, snapshot: &Snapshot, bias: Bias) -> FoldPoint;
  15}
  16
  17#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  18pub struct FoldPoint(pub super::Point);
  19
  20impl FoldPoint {
  21    pub fn new(row: u32, column: u32) -> Self {
  22        Self(super::Point::new(row, column))
  23    }
  24
  25    pub fn row(self) -> u32 {
  26        self.0.row
  27    }
  28
  29    pub fn column(self) -> u32 {
  30        self.0.column
  31    }
  32
  33    pub fn row_mut(&mut self) -> &mut u32 {
  34        &mut self.0.row
  35    }
  36
  37    #[cfg(test)]
  38    pub fn column_mut(&mut self) -> &mut u32 {
  39        &mut self.0.column
  40    }
  41
  42    pub fn to_buffer_point(&self, snapshot: &Snapshot) -> Point {
  43        let mut cursor = snapshot.transforms.cursor::<(FoldPoint, Point)>();
  44        cursor.seek(self, Bias::Right, &());
  45        let overshoot = self.0 - cursor.start().0 .0;
  46        cursor.start().1 + overshoot
  47    }
  48
  49    pub fn to_buffer_offset(&self, snapshot: &Snapshot) -> usize {
  50        let mut cursor = snapshot.transforms.cursor::<(FoldPoint, Point)>();
  51        cursor.seek(self, Bias::Right, &());
  52        let overshoot = self.0 - cursor.start().0 .0;
  53        snapshot
  54            .buffer_snapshot
  55            .to_offset(cursor.start().1 + overshoot)
  56    }
  57
  58    pub fn to_offset(&self, snapshot: &Snapshot) -> FoldOffset {
  59        let mut cursor = snapshot
  60            .transforms
  61            .cursor::<(FoldPoint, TransformSummary)>();
  62        cursor.seek(self, Bias::Right, &());
  63        let overshoot = self.0 - cursor.start().1.output.lines;
  64        let mut offset = cursor.start().1.output.bytes;
  65        if !overshoot.is_zero() {
  66            let transform = cursor.item().expect("display point out of range");
  67            assert!(transform.output_text.is_none());
  68            let end_buffer_offset = snapshot
  69                .buffer_snapshot
  70                .to_offset(cursor.start().1.input.lines + overshoot);
  71            offset += end_buffer_offset - cursor.start().1.input.bytes;
  72        }
  73        FoldOffset(offset)
  74    }
  75}
  76
  77impl ToFoldPoint for Point {
  78    fn to_fold_point(&self, snapshot: &Snapshot, bias: Bias) -> FoldPoint {
  79        let mut cursor = snapshot.transforms.cursor::<(Point, FoldPoint)>();
  80        cursor.seek(self, Bias::Right, &());
  81        if cursor.item().map_or(false, |t| t.is_fold()) {
  82            if bias == Bias::Left || *self == cursor.start().0 {
  83                cursor.start().1
  84            } else {
  85                cursor.end(&()).1
  86            }
  87        } else {
  88            let overshoot = *self - cursor.start().0;
  89            FoldPoint(cmp::min(
  90                cursor.start().1 .0 + overshoot,
  91                cursor.end(&()).1 .0,
  92            ))
  93        }
  94    }
  95}
  96
  97pub struct FoldMapWriter<'a>(&'a mut FoldMap);
  98
  99impl<'a> FoldMapWriter<'a> {
 100    pub fn fold<T: ToOffset>(
 101        &mut self,
 102        ranges: impl IntoIterator<Item = Range<T>>,
 103        cx: &AppContext,
 104    ) -> (Snapshot, Vec<FoldEdit>) {
 105        let mut edits = Vec::new();
 106        let mut folds = Vec::new();
 107        let buffer = self.0.buffer.read(cx).snapshot();
 108        for range in ranges.into_iter() {
 109            let range = range.start.to_offset(&buffer)..range.end.to_offset(&buffer);
 110            if range.start != range.end {
 111                let fold = Fold(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
 112                folds.push(fold);
 113                edits.push(buffer::Edit {
 114                    old: range.clone(),
 115                    new: range,
 116                });
 117            }
 118        }
 119
 120        folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(a, b, &buffer));
 121
 122        self.0.folds = {
 123            let mut new_tree = SumTree::new();
 124            let mut cursor = self.0.folds.cursor::<Fold>();
 125            for fold in folds {
 126                new_tree.push_tree(cursor.slice(&fold, Bias::Right, &buffer), &buffer);
 127                new_tree.push(fold, &buffer);
 128            }
 129            new_tree.push_tree(cursor.suffix(&buffer), &buffer);
 130            new_tree
 131        };
 132
 133        consolidate_buffer_edits(&mut edits);
 134        let edits = self.0.apply_edits(edits, cx);
 135        let snapshot = Snapshot {
 136            transforms: self.0.transforms.lock().clone(),
 137            folds: self.0.folds.clone(),
 138            buffer_snapshot: self.0.buffer.read(cx).snapshot(),
 139            version: self.0.version.load(SeqCst),
 140        };
 141        (snapshot, edits)
 142    }
 143
 144    pub fn unfold<T: ToOffset>(
 145        &mut self,
 146        ranges: impl IntoIterator<Item = Range<T>>,
 147        cx: &AppContext,
 148    ) -> (Snapshot, Vec<FoldEdit>) {
 149        let mut edits = Vec::new();
 150        let mut fold_ixs_to_delete = Vec::new();
 151        let buffer = self.0.buffer.read(cx).snapshot();
 152        for range in ranges.into_iter() {
 153            // Remove intersecting folds and add their ranges to edits that are passed to apply_edits.
 154            let mut folds_cursor = intersecting_folds(&buffer, &self.0.folds, range, true);
 155            while let Some(fold) = folds_cursor.item() {
 156                let offset_range = fold.0.start.to_offset(&buffer)..fold.0.end.to_offset(&buffer);
 157                edits.push(buffer::Edit {
 158                    old: offset_range.clone(),
 159                    new: offset_range,
 160                });
 161                fold_ixs_to_delete.push(*folds_cursor.start());
 162                folds_cursor.next(&buffer);
 163            }
 164        }
 165
 166        fold_ixs_to_delete.sort_unstable();
 167        fold_ixs_to_delete.dedup();
 168
 169        self.0.folds = {
 170            let mut cursor = self.0.folds.cursor::<usize>();
 171            let mut folds = SumTree::new();
 172            for fold_ix in fold_ixs_to_delete {
 173                folds.push_tree(cursor.slice(&fold_ix, Bias::Right, &buffer), &buffer);
 174                cursor.next(&buffer);
 175            }
 176            folds.push_tree(cursor.suffix(&buffer), &buffer);
 177            folds
 178        };
 179
 180        consolidate_buffer_edits(&mut edits);
 181        let edits = self.0.apply_edits(edits, cx);
 182        let snapshot = Snapshot {
 183            transforms: self.0.transforms.lock().clone(),
 184            folds: self.0.folds.clone(),
 185            buffer_snapshot: self.0.buffer.read(cx).snapshot(),
 186            version: self.0.version.load(SeqCst),
 187        };
 188        (snapshot, edits)
 189    }
 190}
 191
 192pub struct FoldMap {
 193    buffer: ModelHandle<Buffer>,
 194    transforms: Mutex<SumTree<Transform>>,
 195    folds: SumTree<Fold>,
 196    last_sync: Mutex<SyncState>,
 197    version: AtomicUsize,
 198}
 199
 200#[derive(Clone)]
 201struct SyncState {
 202    version: clock::Global,
 203    parse_count: usize,
 204    diagnostics_update_count: usize,
 205}
 206
 207impl FoldMap {
 208    pub fn new(buffer_handle: ModelHandle<Buffer>, cx: &AppContext) -> (Self, Snapshot) {
 209        let buffer = buffer_handle.read(cx);
 210        let this = Self {
 211            buffer: buffer_handle,
 212            folds: Default::default(),
 213            transforms: Mutex::new(SumTree::from_item(
 214                Transform {
 215                    summary: TransformSummary {
 216                        input: buffer.text_summary(),
 217                        output: buffer.text_summary(),
 218                    },
 219                    output_text: None,
 220                },
 221                &(),
 222            )),
 223            last_sync: Mutex::new(SyncState {
 224                version: buffer.version(),
 225                parse_count: buffer.parse_count(),
 226                diagnostics_update_count: buffer.diagnostics_update_count(),
 227            }),
 228            version: AtomicUsize::new(0),
 229        };
 230        let (snapshot, _) = this.read(cx);
 231        (this, snapshot)
 232    }
 233
 234    pub fn read(&self, cx: &AppContext) -> (Snapshot, Vec<FoldEdit>) {
 235        let edits = self.sync(cx);
 236        self.check_invariants(cx);
 237        let snapshot = Snapshot {
 238            transforms: self.transforms.lock().clone(),
 239            folds: self.folds.clone(),
 240            buffer_snapshot: self.buffer.read(cx).snapshot(),
 241            version: self.version.load(SeqCst),
 242        };
 243        (snapshot, edits)
 244    }
 245
 246    pub fn write(&mut self, cx: &AppContext) -> (FoldMapWriter, Snapshot, Vec<FoldEdit>) {
 247        let (snapshot, edits) = self.read(cx);
 248        (FoldMapWriter(self), snapshot, edits)
 249    }
 250
 251    fn sync(&self, cx: &AppContext) -> Vec<FoldEdit> {
 252        let buffer = self.buffer.read(cx);
 253        let last_sync = mem::replace(
 254            &mut *self.last_sync.lock(),
 255            SyncState {
 256                version: buffer.version(),
 257                parse_count: buffer.parse_count(),
 258                diagnostics_update_count: buffer.diagnostics_update_count(),
 259            },
 260        );
 261        let edits = buffer
 262            .edits_since(&last_sync.version)
 263            .map(Into::into)
 264            .collect::<Vec<_>>();
 265        if edits.is_empty() {
 266            if last_sync.parse_count != buffer.parse_count()
 267                || last_sync.diagnostics_update_count != buffer.diagnostics_update_count()
 268            {
 269                self.version.fetch_add(1, SeqCst);
 270            }
 271            Vec::new()
 272        } else {
 273            self.apply_edits(edits, cx)
 274        }
 275    }
 276
 277    fn check_invariants(&self, cx: &AppContext) {
 278        if cfg!(test) {
 279            let buffer = self.buffer.read(cx);
 280            assert_eq!(
 281                self.transforms.lock().summary().input.bytes,
 282                buffer.len(),
 283                "transform tree does not match buffer's length"
 284            );
 285        }
 286    }
 287
 288    fn apply_edits(
 289        &self,
 290        buffer_edits: Vec<buffer::Edit<usize>>,
 291        cx: &AppContext,
 292    ) -> Vec<FoldEdit> {
 293        let buffer = self.buffer.read(cx).snapshot();
 294        let mut buffer_edits_iter = buffer_edits.iter().cloned().peekable();
 295
 296        let mut new_transforms = SumTree::new();
 297        let mut transforms = self.transforms.lock();
 298        let mut cursor = transforms.cursor::<usize>();
 299        cursor.seek(&0, Bias::Right, &());
 300
 301        while let Some(mut edit) = buffer_edits_iter.next() {
 302            new_transforms.push_tree(cursor.slice(&edit.old.start, Bias::Left, &()), &());
 303            edit.new.start -= edit.old.start - cursor.start();
 304            edit.old.start = *cursor.start();
 305
 306            cursor.seek(&edit.old.end, Bias::Right, &());
 307            cursor.next(&());
 308
 309            let mut delta = edit.new.len() as isize - edit.old.len() as isize;
 310            loop {
 311                edit.old.end = *cursor.start();
 312
 313                if let Some(next_edit) = buffer_edits_iter.peek() {
 314                    if next_edit.old.start > edit.old.end {
 315                        break;
 316                    }
 317
 318                    let next_edit = buffer_edits_iter.next().unwrap();
 319                    delta += next_edit.new.len() as isize - next_edit.old.len() as isize;
 320
 321                    if next_edit.old.end >= edit.old.end {
 322                        edit.old.end = next_edit.old.end;
 323                        cursor.seek(&edit.old.end, Bias::Right, &());
 324                        cursor.next(&());
 325                    }
 326                } else {
 327                    break;
 328                }
 329            }
 330
 331            edit.new.end = ((edit.new.start + edit.old.len()) as isize + delta) as usize;
 332
 333            let anchor = buffer.anchor_before(edit.new.start);
 334            let mut folds_cursor = self.folds.cursor::<Fold>();
 335            folds_cursor.seek(&Fold(anchor..Anchor::max()), Bias::Left, &buffer);
 336
 337            let mut folds = iter::from_fn({
 338                let buffer = &buffer;
 339                move || {
 340                    let item = folds_cursor
 341                        .item()
 342                        .map(|f| f.0.start.to_offset(buffer)..f.0.end.to_offset(buffer));
 343                    folds_cursor.next(buffer);
 344                    item
 345                }
 346            })
 347            .peekable();
 348
 349            while folds.peek().map_or(false, |fold| fold.start < edit.new.end) {
 350                let mut fold = folds.next().unwrap();
 351                let sum = new_transforms.summary();
 352
 353                assert!(fold.start >= sum.input.bytes);
 354
 355                while folds
 356                    .peek()
 357                    .map_or(false, |next_fold| next_fold.start <= fold.end)
 358                {
 359                    let next_fold = folds.next().unwrap();
 360                    if next_fold.end > fold.end {
 361                        fold.end = next_fold.end;
 362                    }
 363                }
 364
 365                if fold.start > sum.input.bytes {
 366                    let text_summary = buffer.text_summary_for_range(sum.input.bytes..fold.start);
 367                    new_transforms.push(
 368                        Transform {
 369                            summary: TransformSummary {
 370                                output: text_summary.clone(),
 371                                input: text_summary,
 372                            },
 373                            output_text: None,
 374                        },
 375                        &(),
 376                    );
 377                }
 378
 379                if fold.end > fold.start {
 380                    let output_text = "";
 381                    let chars = output_text.chars().count() as u32;
 382                    let lines = Point::new(0, output_text.len() as u32);
 383                    let lines_utf16 = PointUtf16::new(0, output_text.encode_utf16().count() as u32);
 384                    new_transforms.push(
 385                        Transform {
 386                            summary: TransformSummary {
 387                                output: TextSummary {
 388                                    bytes: output_text.len(),
 389                                    lines,
 390                                    lines_utf16,
 391                                    first_line_chars: chars,
 392                                    last_line_chars: chars,
 393                                    longest_row: 0,
 394                                    longest_row_chars: chars,
 395                                },
 396                                input: buffer.text_summary_for_range(fold.start..fold.end),
 397                            },
 398                            output_text: Some(output_text),
 399                        },
 400                        &(),
 401                    );
 402                }
 403            }
 404
 405            let sum = new_transforms.summary();
 406            if sum.input.bytes < edit.new.end {
 407                let text_summary = buffer.text_summary_for_range(sum.input.bytes..edit.new.end);
 408                new_transforms.push(
 409                    Transform {
 410                        summary: TransformSummary {
 411                            output: text_summary.clone(),
 412                            input: text_summary,
 413                        },
 414                        output_text: None,
 415                    },
 416                    &(),
 417                );
 418            }
 419        }
 420
 421        new_transforms.push_tree(cursor.suffix(&()), &());
 422        if new_transforms.is_empty() {
 423            let text_summary = buffer.text_summary();
 424            new_transforms.push(
 425                Transform {
 426                    summary: TransformSummary {
 427                        output: text_summary.clone(),
 428                        input: text_summary,
 429                    },
 430                    output_text: None,
 431                },
 432                &(),
 433            );
 434        }
 435
 436        drop(cursor);
 437
 438        let mut fold_edits = Vec::with_capacity(buffer_edits.len());
 439        {
 440            let mut old_transforms = transforms.cursor::<(usize, FoldOffset)>();
 441            let mut new_transforms = new_transforms.cursor::<(usize, FoldOffset)>();
 442
 443            for mut edit in buffer_edits {
 444                old_transforms.seek(&edit.old.start, Bias::Left, &());
 445                if old_transforms.item().map_or(false, |t| t.is_fold()) {
 446                    edit.old.start = old_transforms.start().0;
 447                }
 448                let old_start =
 449                    old_transforms.start().1 .0 + (edit.old.start - old_transforms.start().0);
 450
 451                old_transforms.seek_forward(&edit.old.end, Bias::Right, &());
 452                if old_transforms.item().map_or(false, |t| t.is_fold()) {
 453                    old_transforms.next(&());
 454                    edit.old.end = old_transforms.start().0;
 455                }
 456                let old_end =
 457                    old_transforms.start().1 .0 + (edit.old.end - old_transforms.start().0);
 458
 459                new_transforms.seek(&edit.new.start, Bias::Left, &());
 460                if new_transforms.item().map_or(false, |t| t.is_fold()) {
 461                    edit.new.start = new_transforms.start().0;
 462                }
 463                let new_start =
 464                    new_transforms.start().1 .0 + (edit.new.start - new_transforms.start().0);
 465
 466                new_transforms.seek_forward(&edit.new.end, Bias::Right, &());
 467                if new_transforms.item().map_or(false, |t| t.is_fold()) {
 468                    new_transforms.next(&());
 469                    edit.new.end = new_transforms.start().0;
 470                }
 471                let new_end =
 472                    new_transforms.start().1 .0 + (edit.new.end - new_transforms.start().0);
 473
 474                fold_edits.push(FoldEdit {
 475                    old_bytes: FoldOffset(old_start)..FoldOffset(old_end),
 476                    new_bytes: FoldOffset(new_start)..FoldOffset(new_end),
 477                });
 478            }
 479
 480            consolidate_fold_edits(&mut fold_edits);
 481        }
 482
 483        *transforms = new_transforms;
 484        self.version.fetch_add(1, SeqCst);
 485        fold_edits
 486    }
 487}
 488
 489#[derive(Clone)]
 490pub struct Snapshot {
 491    transforms: SumTree<Transform>,
 492    folds: SumTree<Fold>,
 493    buffer_snapshot: language::Snapshot,
 494    pub version: usize,
 495}
 496
 497impl Snapshot {
 498    #[cfg(test)]
 499    pub fn text(&self) -> String {
 500        self.chunks(FoldOffset(0)..self.len(), None)
 501            .map(|c| c.text)
 502            .collect()
 503    }
 504
 505    #[cfg(test)]
 506    pub fn fold_count(&self) -> usize {
 507        self.folds.items(&self.buffer_snapshot).len()
 508    }
 509
 510    pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
 511        let mut summary = TextSummary::default();
 512
 513        let mut cursor = self.transforms.cursor::<(FoldPoint, Point)>();
 514        cursor.seek(&range.start, Bias::Right, &());
 515        if let Some(transform) = cursor.item() {
 516            let start_in_transform = range.start.0 - cursor.start().0 .0;
 517            let end_in_transform = cmp::min(range.end, cursor.end(&()).0).0 - cursor.start().0 .0;
 518            if let Some(output_text) = transform.output_text {
 519                summary = TextSummary::from(
 520                    &output_text
 521                        [start_in_transform.column as usize..end_in_transform.column as usize],
 522                );
 523            } else {
 524                let buffer_start = cursor.start().1 + start_in_transform;
 525                let buffer_end = cursor.start().1 + end_in_transform;
 526                summary = self
 527                    .buffer_snapshot
 528                    .text_summary_for_range(buffer_start..buffer_end);
 529            }
 530        }
 531
 532        if range.end > cursor.end(&()).0 {
 533            cursor.next(&());
 534            summary += &cursor
 535                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
 536                .output;
 537            if let Some(transform) = cursor.item() {
 538                let end_in_transform = range.end.0 - cursor.start().0 .0;
 539                if let Some(output_text) = transform.output_text {
 540                    summary += TextSummary::from(&output_text[..end_in_transform.column as usize]);
 541                } else {
 542                    let buffer_start = cursor.start().1;
 543                    let buffer_end = cursor.start().1 + end_in_transform;
 544                    summary += self
 545                        .buffer_snapshot
 546                        .text_summary_for_range(buffer_start..buffer_end);
 547                }
 548            }
 549        }
 550
 551        summary
 552    }
 553
 554    pub fn len(&self) -> FoldOffset {
 555        FoldOffset(self.transforms.summary().output.bytes)
 556    }
 557
 558    #[cfg(test)]
 559    pub fn line_len(&self, row: u32) -> u32 {
 560        let line_start = FoldPoint::new(row, 0).to_offset(self).0;
 561        let line_end = if row >= self.max_point().row() {
 562            self.len().0
 563        } else {
 564            FoldPoint::new(row + 1, 0).to_offset(self).0 - 1
 565        };
 566        (line_end - line_start) as u32
 567    }
 568
 569    pub fn buffer_rows(&self, start_row: u32) -> BufferRows {
 570        if start_row > self.transforms.summary().output.lines.row {
 571            panic!("invalid display row {}", start_row);
 572        }
 573
 574        let fold_point = FoldPoint::new(start_row, 0);
 575        let mut cursor = self.transforms.cursor();
 576        cursor.seek(&fold_point, Bias::Left, &());
 577        BufferRows { fold_point, cursor }
 578    }
 579
 580    pub fn max_point(&self) -> FoldPoint {
 581        FoldPoint(self.transforms.summary().output.lines)
 582    }
 583
 584    #[cfg(test)]
 585    pub fn longest_row(&self) -> u32 {
 586        self.transforms.summary().output.longest_row
 587    }
 588
 589    pub fn folds_in_range<'a, T>(
 590        &'a self,
 591        range: Range<T>,
 592    ) -> impl Iterator<Item = &'a Range<Anchor>>
 593    where
 594        T: ToOffset,
 595    {
 596        let mut folds = intersecting_folds(&self.buffer_snapshot, &self.folds, range, false);
 597        iter::from_fn(move || {
 598            let item = folds.item().map(|f| &f.0);
 599            folds.next(&self.buffer_snapshot);
 600            item
 601        })
 602    }
 603
 604    pub fn intersects_fold<T>(&self, offset: T) -> bool
 605    where
 606        T: ToOffset,
 607    {
 608        let offset = offset.to_offset(&self.buffer_snapshot);
 609        let mut cursor = self.transforms.cursor::<usize>();
 610        cursor.seek(&offset, Bias::Right, &());
 611        cursor.item().map_or(false, |t| t.output_text.is_some())
 612    }
 613
 614    pub fn is_line_folded(&self, output_row: u32) -> bool {
 615        let mut cursor = self.transforms.cursor::<FoldPoint>();
 616        cursor.seek(&FoldPoint::new(output_row, 0), Bias::Right, &());
 617        while let Some(transform) = cursor.item() {
 618            if transform.output_text.is_some() {
 619                return true;
 620            }
 621            if cursor.end(&()).row() == output_row {
 622                cursor.next(&())
 623            } else {
 624                break;
 625            }
 626        }
 627        false
 628    }
 629
 630    pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
 631        let start = start.to_offset(self);
 632        self.chunks(start..self.len(), None)
 633            .flat_map(|chunk| chunk.text.chars())
 634    }
 635
 636    pub fn chunks<'a>(
 637        &'a self,
 638        range: Range<FoldOffset>,
 639        theme: Option<&'a SyntaxTheme>,
 640    ) -> Chunks<'a> {
 641        let mut transform_cursor = self.transforms.cursor::<(FoldOffset, usize)>();
 642
 643        transform_cursor.seek(&range.end, Bias::Right, &());
 644        let overshoot = range.end.0 - transform_cursor.start().0 .0;
 645        let buffer_end = transform_cursor.start().1 + overshoot;
 646
 647        transform_cursor.seek(&range.start, Bias::Right, &());
 648        let overshoot = range.start.0 - transform_cursor.start().0 .0;
 649        let buffer_start = transform_cursor.start().1 + overshoot;
 650
 651        Chunks {
 652            transform_cursor,
 653            buffer_chunks: self.buffer_snapshot.chunks(buffer_start..buffer_end, theme),
 654            buffer_chunk: None,
 655            buffer_offset: buffer_start,
 656            output_offset: range.start.0,
 657            max_output_offset: range.end.0,
 658        }
 659    }
 660
 661    #[cfg(test)]
 662    pub fn clip_offset(&self, offset: FoldOffset, bias: Bias) -> FoldOffset {
 663        let mut cursor = self.transforms.cursor::<(FoldOffset, usize)>();
 664        cursor.seek(&offset, Bias::Right, &());
 665        if let Some(transform) = cursor.item() {
 666            let transform_start = cursor.start().0 .0;
 667            if transform.output_text.is_some() {
 668                if offset.0 == transform_start || matches!(bias, Bias::Left) {
 669                    FoldOffset(transform_start)
 670                } else {
 671                    FoldOffset(cursor.end(&()).0 .0)
 672                }
 673            } else {
 674                let overshoot = offset.0 - transform_start;
 675                let buffer_offset = cursor.start().1 + overshoot;
 676                let clipped_buffer_offset = self.buffer_snapshot.clip_offset(buffer_offset, bias);
 677                FoldOffset(
 678                    (offset.0 as isize + (clipped_buffer_offset as isize - buffer_offset as isize))
 679                        as usize,
 680                )
 681            }
 682        } else {
 683            FoldOffset(self.transforms.summary().output.bytes)
 684        }
 685    }
 686
 687    pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
 688        let mut cursor = self.transforms.cursor::<(FoldPoint, Point)>();
 689        cursor.seek(&point, Bias::Right, &());
 690        if let Some(transform) = cursor.item() {
 691            let transform_start = cursor.start().0 .0;
 692            if transform.output_text.is_some() {
 693                if point.0 == transform_start || matches!(bias, Bias::Left) {
 694                    FoldPoint(transform_start)
 695                } else {
 696                    FoldPoint(cursor.end(&()).0 .0)
 697                }
 698            } else {
 699                let overshoot = point.0 - transform_start;
 700                let buffer_position = cursor.start().1 + overshoot;
 701                let clipped_buffer_position =
 702                    self.buffer_snapshot.clip_point(buffer_position, bias);
 703                FoldPoint::new(
 704                    point.row(),
 705                    ((point.column() as i32) + clipped_buffer_position.column as i32
 706                        - buffer_position.column as i32) as u32,
 707                )
 708            }
 709        } else {
 710            FoldPoint(self.transforms.summary().output.lines)
 711        }
 712    }
 713}
 714
 715fn intersecting_folds<'a, T>(
 716    buffer: &'a buffer::Snapshot,
 717    folds: &'a SumTree<Fold>,
 718    range: Range<T>,
 719    inclusive: bool,
 720) -> FilterCursor<'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize>
 721where
 722    T: ToOffset,
 723{
 724    let start = buffer.anchor_before(range.start.to_offset(buffer));
 725    let end = buffer.anchor_after(range.end.to_offset(buffer));
 726    folds.filter::<_, usize>(
 727        move |summary| {
 728            let start_cmp = start.cmp(&summary.max_end, buffer).unwrap();
 729            let end_cmp = end.cmp(&summary.min_start, buffer).unwrap();
 730
 731            if inclusive {
 732                start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
 733            } else {
 734                start_cmp == Ordering::Less && end_cmp == Ordering::Greater
 735            }
 736        },
 737        buffer,
 738    )
 739}
 740
 741fn consolidate_buffer_edits(edits: &mut Vec<buffer::Edit<usize>>) {
 742    edits.sort_unstable_by(|a, b| {
 743        a.old
 744            .start
 745            .cmp(&b.old.start)
 746            .then_with(|| b.old.end.cmp(&a.old.end))
 747    });
 748
 749    let mut i = 1;
 750    while i < edits.len() {
 751        let edit = edits[i].clone();
 752        let prev_edit = &mut edits[i - 1];
 753        if prev_edit.old.end >= edit.old.start {
 754            prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
 755            prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
 756            prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
 757            edits.remove(i);
 758            continue;
 759        }
 760        i += 1;
 761    }
 762}
 763
 764fn consolidate_fold_edits(edits: &mut Vec<FoldEdit>) {
 765    edits.sort_unstable_by(|a, b| {
 766        a.old_bytes
 767            .start
 768            .cmp(&b.old_bytes.start)
 769            .then_with(|| b.old_bytes.end.cmp(&a.old_bytes.end))
 770    });
 771
 772    let mut i = 1;
 773    while i < edits.len() {
 774        let edit = edits[i].clone();
 775        let prev_edit = &mut edits[i - 1];
 776        if prev_edit.old_bytes.end >= edit.old_bytes.start {
 777            prev_edit.old_bytes.end = prev_edit.old_bytes.end.max(edit.old_bytes.end);
 778            prev_edit.new_bytes.start = prev_edit.new_bytes.start.min(edit.new_bytes.start);
 779            prev_edit.new_bytes.end = prev_edit.new_bytes.end.max(edit.new_bytes.end);
 780            edits.remove(i);
 781            continue;
 782        }
 783        i += 1;
 784    }
 785}
 786
 787#[derive(Clone, Debug, Default, Eq, PartialEq)]
 788struct Transform {
 789    summary: TransformSummary,
 790    output_text: Option<&'static str>,
 791}
 792
 793impl Transform {
 794    fn is_fold(&self) -> bool {
 795        self.output_text.is_some()
 796    }
 797}
 798
 799#[derive(Clone, Debug, Default, Eq, PartialEq)]
 800struct TransformSummary {
 801    output: TextSummary,
 802    input: TextSummary,
 803}
 804
 805impl sum_tree::Item for Transform {
 806    type Summary = TransformSummary;
 807
 808    fn summary(&self) -> Self::Summary {
 809        self.summary.clone()
 810    }
 811}
 812
 813impl sum_tree::Summary for TransformSummary {
 814    type Context = ();
 815
 816    fn add_summary(&mut self, other: &Self, _: &()) {
 817        self.input += &other.input;
 818        self.output += &other.output;
 819    }
 820}
 821
 822#[derive(Clone, Debug)]
 823struct Fold(Range<Anchor>);
 824
 825impl Default for Fold {
 826    fn default() -> Self {
 827        Self(Anchor::min()..Anchor::max())
 828    }
 829}
 830
 831impl sum_tree::Item for Fold {
 832    type Summary = FoldSummary;
 833
 834    fn summary(&self) -> Self::Summary {
 835        FoldSummary {
 836            start: self.0.start.clone(),
 837            end: self.0.end.clone(),
 838            min_start: self.0.start.clone(),
 839            max_end: self.0.end.clone(),
 840            count: 1,
 841        }
 842    }
 843}
 844
 845#[derive(Clone, Debug)]
 846struct FoldSummary {
 847    start: Anchor,
 848    end: Anchor,
 849    min_start: Anchor,
 850    max_end: Anchor,
 851    count: usize,
 852}
 853
 854impl Default for FoldSummary {
 855    fn default() -> Self {
 856        Self {
 857            start: Anchor::min(),
 858            end: Anchor::max(),
 859            min_start: Anchor::max(),
 860            max_end: Anchor::min(),
 861            count: 0,
 862        }
 863    }
 864}
 865
 866impl sum_tree::Summary for FoldSummary {
 867    type Context = buffer::Snapshot;
 868
 869    fn add_summary(&mut self, other: &Self, buffer: &buffer::Snapshot) {
 870        if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
 871            self.min_start = other.min_start.clone();
 872        }
 873        if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
 874            self.max_end = other.max_end.clone();
 875        }
 876
 877        #[cfg(debug_assertions)]
 878        {
 879            let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
 880            assert!(start_comparison <= Ordering::Equal);
 881            if start_comparison == Ordering::Equal {
 882                assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
 883            }
 884        }
 885
 886        self.start = other.start.clone();
 887        self.end = other.end.clone();
 888        self.count += other.count;
 889    }
 890}
 891
 892impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
 893    fn add_summary(&mut self, summary: &'a FoldSummary, _: &buffer::Snapshot) {
 894        self.0.start = summary.start.clone();
 895        self.0.end = summary.end.clone();
 896    }
 897}
 898
 899impl<'a> sum_tree::SeekTarget<'a, FoldSummary, Fold> for Fold {
 900    fn cmp(&self, other: &Self, buffer: &buffer::Snapshot) -> Ordering {
 901        self.0.cmp(&other.0, buffer).unwrap()
 902    }
 903}
 904
 905impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
 906    fn add_summary(&mut self, summary: &'a FoldSummary, _: &buffer::Snapshot) {
 907        *self += summary.count;
 908    }
 909}
 910
 911pub struct BufferRows<'a> {
 912    cursor: Cursor<'a, Transform, (FoldPoint, Point)>,
 913    fold_point: FoldPoint,
 914}
 915
 916impl<'a> Iterator for BufferRows<'a> {
 917    type Item = u32;
 918
 919    fn next(&mut self) -> Option<Self::Item> {
 920        while self.fold_point > self.cursor.end(&()).0 {
 921            self.cursor.next(&());
 922            if self.cursor.item().is_none() {
 923                // TODO: Return a bool from next?
 924                break;
 925            }
 926        }
 927
 928        if self.cursor.item().is_some() {
 929            let overshoot = self.fold_point.0 - self.cursor.start().0 .0;
 930            let buffer_point = self.cursor.start().1 + overshoot;
 931            *self.fold_point.row_mut() += 1;
 932            Some(buffer_point.row)
 933        } else {
 934            None
 935        }
 936    }
 937}
 938
 939pub struct Chunks<'a> {
 940    transform_cursor: Cursor<'a, Transform, (FoldOffset, usize)>,
 941    buffer_chunks: language::Chunks<'a>,
 942    buffer_chunk: Option<(usize, Chunk<'a>)>,
 943    buffer_offset: usize,
 944    output_offset: usize,
 945    max_output_offset: usize,
 946}
 947
 948impl<'a> Iterator for Chunks<'a> {
 949    type Item = Chunk<'a>;
 950
 951    fn next(&mut self) -> Option<Self::Item> {
 952        if self.output_offset >= self.max_output_offset {
 953            return None;
 954        }
 955
 956        let transform = if let Some(item) = self.transform_cursor.item() {
 957            item
 958        } else {
 959            return None;
 960        };
 961
 962        // If we're in a fold, then return the fold's display text and
 963        // advance the transform and buffer cursors to the end of the fold.
 964        if let Some(output_text) = transform.output_text {
 965            self.buffer_chunk.take();
 966            self.buffer_offset += transform.summary.input.bytes;
 967            self.buffer_chunks.seek(self.buffer_offset);
 968
 969            while self.buffer_offset >= self.transform_cursor.end(&()).1
 970                && self.transform_cursor.item().is_some()
 971            {
 972                self.transform_cursor.next(&());
 973            }
 974
 975            self.output_offset += output_text.len();
 976            return Some(Chunk {
 977                text: output_text,
 978                highlight_style: None,
 979                diagnostic: None,
 980            });
 981        }
 982
 983        // Retrieve a chunk from the current location in the buffer.
 984        if self.buffer_chunk.is_none() {
 985            let chunk_offset = self.buffer_chunks.offset();
 986            self.buffer_chunk = self.buffer_chunks.next().map(|chunk| (chunk_offset, chunk));
 987        }
 988
 989        // Otherwise, take a chunk from the buffer's text.
 990        if let Some((chunk_offset, mut chunk)) = self.buffer_chunk {
 991            let offset_in_chunk = self.buffer_offset - chunk_offset;
 992            chunk.text = &chunk.text[offset_in_chunk..];
 993
 994            // Truncate the chunk so that it ends at the next fold.
 995            let region_end = self.transform_cursor.end(&()).1 - self.buffer_offset;
 996            if chunk.text.len() >= region_end {
 997                chunk.text = &chunk.text[0..region_end];
 998                self.transform_cursor.next(&());
 999            } else {
1000                self.buffer_chunk.take();
1001            }
1002
1003            self.buffer_offset += chunk.text.len();
1004            self.output_offset += chunk.text.len();
1005            return Some(chunk);
1006        }
1007
1008        None
1009    }
1010}
1011
1012impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint {
1013    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1014        self.0 += &summary.output.lines;
1015    }
1016}
1017
1018#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
1019pub struct FoldOffset(pub usize);
1020
1021impl FoldOffset {
1022    pub fn to_point(&self, snapshot: &Snapshot) -> FoldPoint {
1023        let mut cursor = snapshot
1024            .transforms
1025            .cursor::<(FoldOffset, TransformSummary)>();
1026        cursor.seek(self, Bias::Right, &());
1027        let overshoot = if cursor.item().map_or(true, |t| t.is_fold()) {
1028            Point::new(0, (self.0 - cursor.start().0 .0) as u32)
1029        } else {
1030            let buffer_offset = cursor.start().1.input.bytes + self.0 - cursor.start().0 .0;
1031            let buffer_point = snapshot.buffer_snapshot.to_point(buffer_offset);
1032            buffer_point - cursor.start().1.input.lines
1033        };
1034        FoldPoint(cursor.start().1.output.lines + overshoot)
1035    }
1036}
1037
1038impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset {
1039    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1040        self.0 += &summary.output.bytes;
1041    }
1042}
1043
1044impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
1045    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1046        *self += &summary.input.lines;
1047    }
1048}
1049
1050impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
1051    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1052        *self += &summary.input.bytes;
1053    }
1054}
1055
1056#[derive(Clone, Debug, PartialEq, Eq)]
1057pub struct FoldEdit {
1058    pub old_bytes: Range<FoldOffset>,
1059    pub new_bytes: Range<FoldOffset>,
1060}
1061
1062#[cfg(test)]
1063impl FoldEdit {
1064    pub fn delta(&self) -> isize {
1065        self.inserted_bytes() as isize - self.deleted_bytes() as isize
1066    }
1067
1068    pub fn deleted_bytes(&self) -> usize {
1069        self.old_bytes.end.0 - self.old_bytes.start.0
1070    }
1071
1072    pub fn inserted_bytes(&self) -> usize {
1073        self.new_bytes.end.0 - self.new_bytes.start.0
1074    }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079    use super::*;
1080    use crate::{test::sample_text, ToPoint};
1081    use buffer::RandomCharIter;
1082    use rand::prelude::*;
1083    use std::{env, mem};
1084    use Bias::{Left, Right};
1085
1086    #[gpui::test]
1087    fn test_basic_folds(cx: &mut gpui::MutableAppContext) {
1088        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
1089        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1090
1091        let (mut writer, _, _) = map.write(cx.as_ref());
1092        let (snapshot2, edits) = writer.fold(
1093            vec![
1094                Point::new(0, 2)..Point::new(2, 2),
1095                Point::new(2, 4)..Point::new(4, 1),
1096            ],
1097            cx.as_ref(),
1098        );
1099        assert_eq!(snapshot2.text(), "aa…cc…eeeee");
1100        assert_eq!(
1101            edits,
1102            &[
1103                FoldEdit {
1104                    old_bytes: FoldOffset(2)..FoldOffset(16),
1105                    new_bytes: FoldOffset(2)..FoldOffset(5),
1106                },
1107                FoldEdit {
1108                    old_bytes: FoldOffset(18)..FoldOffset(29),
1109                    new_bytes: FoldOffset(7)..FoldOffset(10)
1110                },
1111            ]
1112        );
1113
1114        buffer.update(cx, |buffer, cx| {
1115            buffer.edit(
1116                vec![
1117                    Point::new(0, 0)..Point::new(0, 1),
1118                    Point::new(2, 3)..Point::new(2, 3),
1119                ],
1120                "123",
1121                cx,
1122            );
1123        });
1124        let (snapshot3, edits) = map.read(cx.as_ref());
1125        assert_eq!(snapshot3.text(), "123a…c123c…eeeee");
1126        assert_eq!(
1127            edits,
1128            &[
1129                FoldEdit {
1130                    old_bytes: FoldOffset(0)..FoldOffset(1),
1131                    new_bytes: FoldOffset(0)..FoldOffset(3),
1132                },
1133                FoldEdit {
1134                    old_bytes: FoldOffset(6)..FoldOffset(6),
1135                    new_bytes: FoldOffset(8)..FoldOffset(11),
1136                },
1137            ]
1138        );
1139
1140        buffer.update(cx, |buffer, cx| {
1141            buffer.edit(vec![Point::new(2, 6)..Point::new(4, 3)], "456", cx)
1142        });
1143        let (snapshot4, _) = map.read(cx.as_ref());
1144        assert_eq!(snapshot4.text(), "123a…c123456eee");
1145
1146        let (mut writer, _, _) = map.write(cx.as_ref());
1147        writer.unfold(Some(Point::new(0, 4)..Point::new(0, 5)), cx.as_ref());
1148        let (snapshot5, _) = map.read(cx.as_ref());
1149        assert_eq!(snapshot5.text(), "123aaaaa\nbbbbbb\nccc123456eee");
1150    }
1151
1152    #[gpui::test]
1153    fn test_adjacent_folds(cx: &mut gpui::MutableAppContext) {
1154        let buffer = cx.add_model(|cx| Buffer::new(0, "abcdefghijkl", cx));
1155
1156        {
1157            let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1158
1159            let (mut writer, _, _) = map.write(cx.as_ref());
1160            writer.fold(vec![5..8], cx.as_ref());
1161            let (snapshot, _) = map.read(cx.as_ref());
1162            assert_eq!(snapshot.text(), "abcde…ijkl");
1163
1164            // Create an fold adjacent to the start of the first fold.
1165            let (mut writer, _, _) = map.write(cx.as_ref());
1166            writer.fold(vec![0..1, 2..5], cx.as_ref());
1167            let (snapshot, _) = map.read(cx.as_ref());
1168            assert_eq!(snapshot.text(), "…b…ijkl");
1169
1170            // Create an fold adjacent to the end of the first fold.
1171            let (mut writer, _, _) = map.write(cx.as_ref());
1172            writer.fold(vec![11..11, 8..10], cx.as_ref());
1173            let (snapshot, _) = map.read(cx.as_ref());
1174            assert_eq!(snapshot.text(), "…b…kl");
1175        }
1176
1177        {
1178            let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1179
1180            // Create two adjacent folds.
1181            let (mut writer, _, _) = map.write(cx.as_ref());
1182            writer.fold(vec![0..2, 2..5], cx.as_ref());
1183            let (snapshot, _) = map.read(cx.as_ref());
1184            assert_eq!(snapshot.text(), "…fghijkl");
1185
1186            // Edit within one of the folds.
1187            buffer.update(cx, |buffer, cx| buffer.edit(vec![0..1], "12345", cx));
1188            let (snapshot, _) = map.read(cx.as_ref());
1189            assert_eq!(snapshot.text(), "12345…fghijkl");
1190        }
1191    }
1192
1193    #[gpui::test]
1194    fn test_overlapping_folds(cx: &mut gpui::MutableAppContext) {
1195        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
1196        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1197        let (mut writer, _, _) = map.write(cx.as_ref());
1198        writer.fold(
1199            vec![
1200                Point::new(0, 2)..Point::new(2, 2),
1201                Point::new(0, 4)..Point::new(1, 0),
1202                Point::new(1, 2)..Point::new(3, 2),
1203                Point::new(3, 1)..Point::new(4, 1),
1204            ],
1205            cx.as_ref(),
1206        );
1207        let (snapshot, _) = map.read(cx.as_ref());
1208        assert_eq!(snapshot.text(), "aa…eeeee");
1209    }
1210
1211    #[gpui::test]
1212    fn test_merging_folds_via_edit(cx: &mut gpui::MutableAppContext) {
1213        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
1214        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1215
1216        let (mut writer, _, _) = map.write(cx.as_ref());
1217        writer.fold(
1218            vec![
1219                Point::new(0, 2)..Point::new(2, 2),
1220                Point::new(3, 1)..Point::new(4, 1),
1221            ],
1222            cx.as_ref(),
1223        );
1224        let (snapshot, _) = map.read(cx.as_ref());
1225        assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee");
1226
1227        buffer.update(cx, |buffer, cx| {
1228            buffer.edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", cx)
1229        });
1230        let (snapshot, _) = map.read(cx.as_ref());
1231        assert_eq!(snapshot.text(), "aa…eeeee");
1232    }
1233
1234    #[gpui::test]
1235    fn test_folds_in_range(cx: &mut gpui::MutableAppContext) {
1236        let buffer = cx.add_model(|cx| Buffer::new(0, sample_text(5, 6), cx));
1237        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1238        let buffer = buffer.read(cx);
1239
1240        let (mut writer, _, _) = map.write(cx.as_ref());
1241        writer.fold(
1242            vec![
1243                Point::new(0, 2)..Point::new(2, 2),
1244                Point::new(0, 4)..Point::new(1, 0),
1245                Point::new(1, 2)..Point::new(3, 2),
1246                Point::new(3, 1)..Point::new(4, 1),
1247            ],
1248            cx.as_ref(),
1249        );
1250        let (snapshot, _) = map.read(cx.as_ref());
1251        let fold_ranges = snapshot
1252            .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
1253            .map(|fold| fold.start.to_point(buffer)..fold.end.to_point(buffer))
1254            .collect::<Vec<_>>();
1255        assert_eq!(
1256            fold_ranges,
1257            vec![
1258                Point::new(0, 2)..Point::new(2, 2),
1259                Point::new(1, 2)..Point::new(3, 2)
1260            ]
1261        );
1262    }
1263
1264    #[gpui::test(iterations = 100)]
1265    fn test_random_folds(cx: &mut gpui::MutableAppContext, mut rng: StdRng) {
1266        let operations = env::var("OPERATIONS")
1267            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1268            .unwrap_or(10);
1269
1270        let buffer = cx.add_model(|cx| {
1271            let len = rng.gen_range(0..10);
1272            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1273            Buffer::new(0, text, cx)
1274        });
1275        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1276
1277        let (mut initial_snapshot, _) = map.read(cx.as_ref());
1278        let mut snapshot_edits = Vec::new();
1279
1280        for _ in 0..operations {
1281            log::info!("text: {:?}", buffer.read(cx).text());
1282            match rng.gen_range(0..=100) {
1283                0..=59 => {
1284                    snapshot_edits.extend(map.randomly_mutate(&mut rng, cx.as_ref()));
1285                }
1286                _ => {
1287                    let edits = buffer.update(cx, |buffer, _| {
1288                        let start_version = buffer.version.clone();
1289                        let edit_count = rng.gen_range(1..=5);
1290                        buffer.randomly_edit(&mut rng, edit_count);
1291                        buffer
1292                            .edits_since::<Point>(&start_version)
1293                            .collect::<Vec<_>>()
1294                    });
1295                    log::info!("editing {:?}", edits);
1296                }
1297            }
1298
1299            let buffer = map.buffer.read(cx).snapshot();
1300            let mut expected_text: String = buffer.text().to_string();
1301            let mut expected_buffer_rows = Vec::new();
1302            let mut next_row = buffer.max_point().row;
1303            for fold_range in map.merged_fold_ranges(cx.as_ref()).into_iter().rev() {
1304                let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
1305                let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
1306                expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
1307                next_row = fold_start.row;
1308
1309                expected_text.replace_range(fold_range.start..fold_range.end, "");
1310            }
1311            expected_buffer_rows.extend((0..=next_row).rev());
1312            expected_buffer_rows.reverse();
1313
1314            let (snapshot, edits) = map.read(cx.as_ref());
1315            assert_eq!(snapshot.text(), expected_text);
1316            snapshot_edits.push((snapshot.clone(), edits));
1317
1318            for (output_row, line) in expected_text.lines().enumerate() {
1319                let line_len = snapshot.line_len(output_row as u32);
1320                assert_eq!(line_len, line.len() as u32);
1321            }
1322
1323            let longest_row = snapshot.longest_row();
1324            let longest_char_column = expected_text
1325                .split('\n')
1326                .nth(longest_row as usize)
1327                .unwrap()
1328                .chars()
1329                .count();
1330            let mut fold_point = FoldPoint::new(0, 0);
1331            let mut fold_offset = FoldOffset(0);
1332            let mut char_column = 0;
1333            for c in expected_text.chars() {
1334                let buffer_point = fold_point.to_buffer_point(&snapshot);
1335                let buffer_offset = buffer_point.to_offset(&buffer);
1336                assert_eq!(
1337                    buffer_point.to_fold_point(&snapshot, Right),
1338                    fold_point,
1339                    "{:?} -> fold point",
1340                    buffer_point,
1341                );
1342                assert_eq!(
1343                    fold_point.to_buffer_offset(&snapshot),
1344                    buffer_offset,
1345                    "fold_point.to_buffer_offset({:?})",
1346                    fold_point,
1347                );
1348                assert_eq!(
1349                    fold_point.to_offset(&snapshot),
1350                    fold_offset,
1351                    "fold_point.to_offset({:?})",
1352                    fold_point,
1353                );
1354
1355                if c == '\n' {
1356                    *fold_point.row_mut() += 1;
1357                    *fold_point.column_mut() = 0;
1358                    char_column = 0;
1359                } else {
1360                    *fold_point.column_mut() += c.len_utf8() as u32;
1361                    char_column += 1;
1362                }
1363                fold_offset.0 += c.len_utf8();
1364                if char_column > longest_char_column {
1365                    panic!(
1366                        "invalid longest row {:?} (chars {}), found row {:?} (chars: {})",
1367                        longest_row,
1368                        longest_char_column,
1369                        fold_point.row(),
1370                        char_column
1371                    );
1372                }
1373            }
1374
1375            for _ in 0..5 {
1376                let mut start = snapshot
1377                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Left);
1378                let mut end = snapshot
1379                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Right);
1380                if start > end {
1381                    mem::swap(&mut start, &mut end);
1382                }
1383
1384                let text = &expected_text[start.0..end.0];
1385                log::info!("slicing {:?}..{:?} (text: {:?})", start, end, text);
1386                assert_eq!(
1387                    snapshot
1388                        .chunks(start..end, None)
1389                        .map(|c| c.text)
1390                        .collect::<String>(),
1391                    text,
1392                );
1393            }
1394
1395            for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
1396                let fold_row = Point::new(*buffer_row, 0)
1397                    .to_fold_point(&snapshot, Right)
1398                    .row();
1399                assert_eq!(
1400                    snapshot.buffer_rows(fold_row).collect::<Vec<_>>(),
1401                    expected_buffer_rows[idx..],
1402                );
1403            }
1404
1405            for fold_range in map.merged_fold_ranges(cx.as_ref()) {
1406                let fold_point = fold_range
1407                    .start
1408                    .to_point(&buffer)
1409                    .to_fold_point(&snapshot, Right);
1410                assert!(snapshot.is_line_folded(fold_point.row()));
1411            }
1412
1413            for _ in 0..5 {
1414                let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1415                let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1416                let expected_folds = map
1417                    .folds
1418                    .items(&buffer)
1419                    .into_iter()
1420                    .filter(|fold| {
1421                        let start = buffer.anchor_before(start);
1422                        let end = buffer.anchor_after(end);
1423                        start.cmp(&fold.0.end, &buffer).unwrap() == Ordering::Less
1424                            && end.cmp(&fold.0.start, &buffer).unwrap() == Ordering::Greater
1425                    })
1426                    .map(|fold| fold.0)
1427                    .collect::<Vec<_>>();
1428
1429                assert_eq!(
1430                    snapshot
1431                        .folds_in_range(start..end)
1432                        .cloned()
1433                        .collect::<Vec<_>>(),
1434                    expected_folds
1435                );
1436            }
1437
1438            let text = snapshot.text();
1439            for _ in 0..5 {
1440                let start_row = rng.gen_range(0..=snapshot.max_point().row());
1441                let start_column = rng.gen_range(0..=snapshot.line_len(start_row));
1442                let end_row = rng.gen_range(0..=snapshot.max_point().row());
1443                let end_column = rng.gen_range(0..=snapshot.line_len(end_row));
1444                let mut start =
1445                    snapshot.clip_point(FoldPoint::new(start_row, start_column), Bias::Left);
1446                let mut end = snapshot.clip_point(FoldPoint::new(end_row, end_column), Bias::Right);
1447                if start > end {
1448                    mem::swap(&mut start, &mut end);
1449                }
1450
1451                let lines = start..end;
1452                let bytes = start.to_offset(&snapshot)..end.to_offset(&snapshot);
1453                assert_eq!(
1454                    snapshot.text_summary_for_range(lines),
1455                    TextSummary::from(&text[bytes.start.0..bytes.end.0])
1456                )
1457            }
1458
1459            let mut text = initial_snapshot.text();
1460            for (snapshot, edits) in snapshot_edits.drain(..) {
1461                let new_text = snapshot.text();
1462                let mut delta = 0isize;
1463                for edit in edits {
1464                    let old_bytes = ((edit.old_bytes.start.0 as isize) + delta) as usize
1465                        ..((edit.old_bytes.end.0 as isize) + delta) as usize;
1466                    let new_bytes = edit.new_bytes.start.0..edit.new_bytes.end.0;
1467                    delta += edit.delta();
1468                    text.replace_range(old_bytes, &new_text[new_bytes]);
1469                }
1470
1471                assert_eq!(text, new_text);
1472                initial_snapshot = snapshot;
1473            }
1474        }
1475    }
1476
1477    #[gpui::test]
1478    fn test_buffer_rows(cx: &mut gpui::MutableAppContext) {
1479        let text = sample_text(6, 6) + "\n";
1480        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx));
1481
1482        let mut map = FoldMap::new(buffer.clone(), cx.as_ref()).0;
1483
1484        let (mut writer, _, _) = map.write(cx.as_ref());
1485        writer.fold(
1486            vec![
1487                Point::new(0, 2)..Point::new(2, 2),
1488                Point::new(3, 1)..Point::new(4, 1),
1489            ],
1490            cx.as_ref(),
1491        );
1492
1493        let (snapshot, _) = map.read(cx.as_ref());
1494        assert_eq!(snapshot.text(), "aa…cccc\nd…eeeee\nffffff\n");
1495        assert_eq!(snapshot.buffer_rows(0).collect::<Vec<_>>(), [0, 3, 5, 6]);
1496        assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [6]);
1497    }
1498
1499    impl FoldMap {
1500        fn merged_fold_ranges(&self, cx: &AppContext) -> Vec<Range<usize>> {
1501            let buffer = self.buffer.read(cx).snapshot();
1502            let mut folds = self.folds.items(&buffer);
1503            // Ensure sorting doesn't change how folds get merged and displayed.
1504            folds.sort_by(|a, b| a.0.cmp(&b.0, &buffer).unwrap());
1505            let mut fold_ranges = folds
1506                .iter()
1507                .map(|fold| fold.0.start.to_offset(&buffer)..fold.0.end.to_offset(&buffer))
1508                .peekable();
1509
1510            let mut merged_ranges = Vec::new();
1511            while let Some(mut fold_range) = fold_ranges.next() {
1512                while let Some(next_range) = fold_ranges.peek() {
1513                    if fold_range.end >= next_range.start {
1514                        if next_range.end > fold_range.end {
1515                            fold_range.end = next_range.end;
1516                        }
1517                        fold_ranges.next();
1518                    } else {
1519                        break;
1520                    }
1521                }
1522                if fold_range.end > fold_range.start {
1523                    merged_ranges.push(fold_range);
1524                }
1525            }
1526            merged_ranges
1527        }
1528
1529        pub fn randomly_mutate(
1530            &mut self,
1531            rng: &mut impl Rng,
1532            cx: &AppContext,
1533        ) -> Vec<(Snapshot, Vec<FoldEdit>)> {
1534            let mut snapshot_edits = Vec::new();
1535            match rng.gen_range(0..=100) {
1536                0..=39 if !self.folds.is_empty() => {
1537                    let buffer = self.buffer.read(cx);
1538                    let mut to_unfold = Vec::new();
1539                    for _ in 0..rng.gen_range(1..=3) {
1540                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1541                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1542                        to_unfold.push(start..end);
1543                    }
1544                    log::info!("unfolding {:?}", to_unfold);
1545                    let (mut writer, snapshot, edits) = self.write(cx.as_ref());
1546                    snapshot_edits.push((snapshot, edits));
1547                    let (snapshot, edits) = writer.fold(to_unfold, cx.as_ref());
1548                    snapshot_edits.push((snapshot, edits));
1549                }
1550                _ => {
1551                    let buffer = self.buffer.read(cx);
1552                    let mut to_fold = Vec::new();
1553                    for _ in 0..rng.gen_range(1..=2) {
1554                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1555                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1556                        to_fold.push(start..end);
1557                    }
1558                    log::info!("folding {:?}", to_fold);
1559                    let (mut writer, snapshot, edits) = self.write(cx.as_ref());
1560                    snapshot_edits.push((snapshot, edits));
1561                    let (snapshot, edits) = writer.fold(to_fold, cx.as_ref());
1562                    snapshot_edits.push((snapshot, edits));
1563                }
1564            }
1565            snapshot_edits
1566        }
1567    }
1568}