fold_map.rs

   1use super::{
   2    inlay_map::{InlayBufferRows, InlayChunks, InlayEdit, InlayOffset, InlayPoint, InlaySnapshot},
   3    Highlights,
   4};
   5use gpui::{AnyElement, ElementId, WindowContext};
   6use language::{Chunk, ChunkRenderer, Edit, Point, TextSummary};
   7use multi_buffer::{Anchor, AnchorRangeExt, MultiBufferRow, MultiBufferSnapshot, ToOffset};
   8use std::{
   9    any::TypeId,
  10    cmp::{self, Ordering},
  11    fmt, iter,
  12    ops::{Add, AddAssign, Deref, DerefMut, Range, Sub},
  13    sync::Arc,
  14};
  15use sum_tree::{Bias, Cursor, FilterCursor, SumTree, Summary};
  16use ui::IntoElement as _;
  17use util::post_inc;
  18
  19#[derive(Clone)]
  20pub struct FoldPlaceholder {
  21    /// Creates an element to represent this fold's placeholder.
  22    pub render: Arc<dyn Send + Sync + Fn(FoldId, Range<Anchor>, &mut WindowContext) -> AnyElement>,
  23    /// If true, the element is constrained to the shaped width of an ellipsis.
  24    pub constrain_width: bool,
  25    /// If true, merges the fold with an adjacent one.
  26    pub merge_adjacent: bool,
  27    /// Category of the fold. Useful for carefully removing from overlapping folds.
  28    pub type_tag: Option<TypeId>,
  29}
  30
  31impl Default for FoldPlaceholder {
  32    fn default() -> Self {
  33        Self {
  34            render: Arc::new(|_, _, _| gpui::Empty.into_any_element()),
  35            constrain_width: true,
  36            merge_adjacent: true,
  37            type_tag: None,
  38        }
  39    }
  40}
  41
  42impl FoldPlaceholder {
  43    #[cfg(any(test, feature = "test-support"))]
  44    pub fn test() -> Self {
  45        Self {
  46            render: Arc::new(|_id, _range, _cx| gpui::Empty.into_any_element()),
  47            constrain_width: true,
  48            merge_adjacent: true,
  49            type_tag: None,
  50        }
  51    }
  52}
  53
  54impl fmt::Debug for FoldPlaceholder {
  55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
  56        f.debug_struct("FoldPlaceholder")
  57            .field("constrain_width", &self.constrain_width)
  58            .finish()
  59    }
  60}
  61
  62impl Eq for FoldPlaceholder {}
  63
  64impl PartialEq for FoldPlaceholder {
  65    fn eq(&self, other: &Self) -> bool {
  66        Arc::ptr_eq(&self.render, &other.render) && self.constrain_width == other.constrain_width
  67    }
  68}
  69
  70#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  71pub struct FoldPoint(pub Point);
  72
  73impl FoldPoint {
  74    pub fn new(row: u32, column: u32) -> Self {
  75        Self(Point::new(row, column))
  76    }
  77
  78    pub fn row(self) -> u32 {
  79        self.0.row
  80    }
  81
  82    pub fn column(self) -> u32 {
  83        self.0.column
  84    }
  85
  86    pub fn row_mut(&mut self) -> &mut u32 {
  87        &mut self.0.row
  88    }
  89
  90    #[cfg(test)]
  91    pub fn column_mut(&mut self) -> &mut u32 {
  92        &mut self.0.column
  93    }
  94
  95    pub fn to_inlay_point(self, snapshot: &FoldSnapshot) -> InlayPoint {
  96        let mut cursor = snapshot.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
  97        cursor.seek(&self, Bias::Right, &());
  98        let overshoot = self.0 - cursor.start().0 .0;
  99        InlayPoint(cursor.start().1 .0 + overshoot)
 100    }
 101
 102    pub fn to_offset(self, snapshot: &FoldSnapshot) -> FoldOffset {
 103        let mut cursor = snapshot
 104            .transforms
 105            .cursor::<(FoldPoint, TransformSummary)>(&());
 106        cursor.seek(&self, Bias::Right, &());
 107        let overshoot = self.0 - cursor.start().1.output.lines;
 108        let mut offset = cursor.start().1.output.len;
 109        if !overshoot.is_zero() {
 110            let transform = cursor.item().expect("display point out of range");
 111            assert!(transform.placeholder.is_none());
 112            let end_inlay_offset = snapshot
 113                .inlay_snapshot
 114                .to_offset(InlayPoint(cursor.start().1.input.lines + overshoot));
 115            offset += end_inlay_offset.0 - cursor.start().1.input.len;
 116        }
 117        FoldOffset(offset)
 118    }
 119}
 120
 121impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldPoint {
 122    fn zero(_cx: &()) -> Self {
 123        Default::default()
 124    }
 125
 126    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 127        self.0 += &summary.output.lines;
 128    }
 129}
 130
 131pub(crate) struct FoldMapWriter<'a>(&'a mut FoldMap);
 132
 133impl<'a> FoldMapWriter<'a> {
 134    pub(crate) fn fold<T: ToOffset>(
 135        &mut self,
 136        ranges: impl IntoIterator<Item = (Range<T>, FoldPlaceholder)>,
 137    ) -> (FoldSnapshot, Vec<FoldEdit>) {
 138        let mut edits = Vec::new();
 139        let mut folds = Vec::new();
 140        let snapshot = self.0.snapshot.inlay_snapshot.clone();
 141        for (range, fold_text) in ranges.into_iter() {
 142            let buffer = &snapshot.buffer;
 143            let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
 144
 145            // Ignore any empty ranges.
 146            if range.start == range.end {
 147                continue;
 148            }
 149
 150            // For now, ignore any ranges that span an excerpt boundary.
 151            let fold_range =
 152                FoldRange(buffer.anchor_after(range.start)..buffer.anchor_before(range.end));
 153            if fold_range.0.start.excerpt_id != fold_range.0.end.excerpt_id {
 154                continue;
 155            }
 156
 157            folds.push(Fold {
 158                id: FoldId(post_inc(&mut self.0.next_fold_id.0)),
 159                range: fold_range,
 160                placeholder: fold_text,
 161            });
 162
 163            let inlay_range =
 164                snapshot.to_inlay_offset(range.start)..snapshot.to_inlay_offset(range.end);
 165            edits.push(InlayEdit {
 166                old: inlay_range.clone(),
 167                new: inlay_range,
 168            });
 169        }
 170
 171        let buffer = &snapshot.buffer;
 172        folds.sort_unstable_by(|a, b| sum_tree::SeekTarget::cmp(&a.range, &b.range, buffer));
 173
 174        self.0.snapshot.folds = {
 175            let mut new_tree = SumTree::new(buffer);
 176            let mut cursor = self.0.snapshot.folds.cursor::<FoldRange>(buffer);
 177            for fold in folds {
 178                new_tree.append(cursor.slice(&fold.range, Bias::Right, buffer), buffer);
 179                new_tree.push(fold, buffer);
 180            }
 181            new_tree.append(cursor.suffix(buffer), buffer);
 182            new_tree
 183        };
 184
 185        let edits = consolidate_inlay_edits(edits);
 186        let edits = self.0.sync(snapshot.clone(), edits);
 187        (self.0.snapshot.clone(), edits)
 188    }
 189
 190    /// Removes any folds with the given ranges.
 191    pub(crate) fn remove_folds<T: ToOffset>(
 192        &mut self,
 193        ranges: impl IntoIterator<Item = Range<T>>,
 194        type_id: TypeId,
 195    ) -> (FoldSnapshot, Vec<FoldEdit>) {
 196        self.remove_folds_with(
 197            ranges,
 198            |fold| fold.placeholder.type_tag == Some(type_id),
 199            false,
 200        )
 201    }
 202
 203    /// Removes any folds whose ranges intersect the given ranges.
 204    pub(crate) fn unfold_intersecting<T: ToOffset>(
 205        &mut self,
 206        ranges: impl IntoIterator<Item = Range<T>>,
 207        inclusive: bool,
 208    ) -> (FoldSnapshot, Vec<FoldEdit>) {
 209        self.remove_folds_with(ranges, |_| true, inclusive)
 210    }
 211
 212    /// Removes any folds that intersect the given ranges and for which the given predicate
 213    /// returns true.
 214    fn remove_folds_with<T: ToOffset>(
 215        &mut self,
 216        ranges: impl IntoIterator<Item = Range<T>>,
 217        should_unfold: impl Fn(&Fold) -> bool,
 218        inclusive: bool,
 219    ) -> (FoldSnapshot, Vec<FoldEdit>) {
 220        let mut edits = Vec::new();
 221        let mut fold_ixs_to_delete = Vec::new();
 222        let snapshot = self.0.snapshot.inlay_snapshot.clone();
 223        let buffer = &snapshot.buffer;
 224        for range in ranges.into_iter() {
 225            let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
 226            let mut folds_cursor =
 227                intersecting_folds(&snapshot, &self.0.snapshot.folds, range.clone(), inclusive);
 228            while let Some(fold) = folds_cursor.item() {
 229                let offset_range =
 230                    fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer);
 231                if should_unfold(fold) {
 232                    if offset_range.end > offset_range.start {
 233                        let inlay_range = snapshot.to_inlay_offset(offset_range.start)
 234                            ..snapshot.to_inlay_offset(offset_range.end);
 235                        edits.push(InlayEdit {
 236                            old: inlay_range.clone(),
 237                            new: inlay_range,
 238                        });
 239                    }
 240                    fold_ixs_to_delete.push(*folds_cursor.start());
 241                }
 242                folds_cursor.next(buffer);
 243            }
 244        }
 245
 246        fold_ixs_to_delete.sort_unstable();
 247        fold_ixs_to_delete.dedup();
 248
 249        self.0.snapshot.folds = {
 250            let mut cursor = self.0.snapshot.folds.cursor::<usize>(buffer);
 251            let mut folds = SumTree::new(buffer);
 252            for fold_ix in fold_ixs_to_delete {
 253                folds.append(cursor.slice(&fold_ix, Bias::Right, buffer), buffer);
 254                cursor.next(buffer);
 255            }
 256            folds.append(cursor.suffix(buffer), buffer);
 257            folds
 258        };
 259
 260        let edits = consolidate_inlay_edits(edits);
 261        let edits = self.0.sync(snapshot.clone(), edits);
 262        (self.0.snapshot.clone(), edits)
 263    }
 264}
 265
 266/// Decides where the fold indicators should be; also tracks parts of a source file that are currently folded.
 267///
 268/// See the [`display_map` module documentation](crate::display_map) for more information.
 269pub(crate) struct FoldMap {
 270    snapshot: FoldSnapshot,
 271    next_fold_id: FoldId,
 272}
 273
 274impl FoldMap {
 275    pub(crate) fn new(inlay_snapshot: InlaySnapshot) -> (Self, FoldSnapshot) {
 276        let this = Self {
 277            snapshot: FoldSnapshot {
 278                folds: SumTree::new(&inlay_snapshot.buffer),
 279                transforms: SumTree::from_item(
 280                    Transform {
 281                        summary: TransformSummary {
 282                            input: inlay_snapshot.text_summary(),
 283                            output: inlay_snapshot.text_summary(),
 284                        },
 285                        placeholder: None,
 286                    },
 287                    &(),
 288                ),
 289                inlay_snapshot: inlay_snapshot.clone(),
 290                version: 0,
 291            },
 292            next_fold_id: FoldId::default(),
 293        };
 294        let snapshot = this.snapshot.clone();
 295        (this, snapshot)
 296    }
 297
 298    pub fn read(
 299        &mut self,
 300        inlay_snapshot: InlaySnapshot,
 301        edits: Vec<InlayEdit>,
 302    ) -> (FoldSnapshot, Vec<FoldEdit>) {
 303        let edits = self.sync(inlay_snapshot, edits);
 304        self.check_invariants();
 305        (self.snapshot.clone(), edits)
 306    }
 307
 308    pub fn write(
 309        &mut self,
 310        inlay_snapshot: InlaySnapshot,
 311        edits: Vec<InlayEdit>,
 312    ) -> (FoldMapWriter, FoldSnapshot, Vec<FoldEdit>) {
 313        let (snapshot, edits) = self.read(inlay_snapshot, edits);
 314        (FoldMapWriter(self), snapshot, edits)
 315    }
 316
 317    fn check_invariants(&self) {
 318        if cfg!(test) {
 319            assert_eq!(
 320                self.snapshot.transforms.summary().input.len,
 321                self.snapshot.inlay_snapshot.len().0,
 322                "transform tree does not match inlay snapshot's length"
 323            );
 324
 325            let mut prev_transform_isomorphic = false;
 326            for transform in self.snapshot.transforms.iter() {
 327                if !transform.is_fold() && prev_transform_isomorphic {
 328                    panic!(
 329                        "found adjacent isomorphic transforms: {:?}",
 330                        self.snapshot.transforms.items(&())
 331                    );
 332                }
 333                prev_transform_isomorphic = !transform.is_fold();
 334            }
 335
 336            let mut folds = self.snapshot.folds.iter().peekable();
 337            while let Some(fold) = folds.next() {
 338                if let Some(next_fold) = folds.peek() {
 339                    let comparison = fold
 340                        .range
 341                        .cmp(&next_fold.range, &self.snapshot.inlay_snapshot.buffer);
 342                    assert!(comparison.is_le());
 343                }
 344            }
 345        }
 346    }
 347
 348    fn sync(
 349        &mut self,
 350        inlay_snapshot: InlaySnapshot,
 351        inlay_edits: Vec<InlayEdit>,
 352    ) -> Vec<FoldEdit> {
 353        if inlay_edits.is_empty() {
 354            if self.snapshot.inlay_snapshot.version != inlay_snapshot.version {
 355                self.snapshot.version += 1;
 356            }
 357            self.snapshot.inlay_snapshot = inlay_snapshot;
 358            Vec::new()
 359        } else {
 360            let mut inlay_edits_iter = inlay_edits.iter().cloned().peekable();
 361
 362            let mut new_transforms = SumTree::<Transform>::default();
 363            let mut cursor = self.snapshot.transforms.cursor::<InlayOffset>(&());
 364            cursor.seek(&InlayOffset(0), Bias::Right, &());
 365
 366            while let Some(mut edit) = inlay_edits_iter.next() {
 367                if let Some(item) = cursor.item() {
 368                    if !item.is_fold() {
 369                        new_transforms.update_last(
 370                            |transform| {
 371                                if !transform.is_fold() {
 372                                    transform.summary.add_summary(&item.summary, &());
 373                                    cursor.next(&());
 374                                }
 375                            },
 376                            &(),
 377                        );
 378                    }
 379                }
 380                new_transforms.append(cursor.slice(&edit.old.start, Bias::Left, &()), &());
 381                edit.new.start -= edit.old.start - *cursor.start();
 382                edit.old.start = *cursor.start();
 383
 384                cursor.seek(&edit.old.end, Bias::Right, &());
 385                cursor.next(&());
 386
 387                let mut delta = edit.new_len().0 as isize - edit.old_len().0 as isize;
 388                loop {
 389                    edit.old.end = *cursor.start();
 390
 391                    if let Some(next_edit) = inlay_edits_iter.peek() {
 392                        if next_edit.old.start > edit.old.end {
 393                            break;
 394                        }
 395
 396                        let next_edit = inlay_edits_iter.next().unwrap();
 397                        delta += next_edit.new_len().0 as isize - next_edit.old_len().0 as isize;
 398
 399                        if next_edit.old.end >= edit.old.end {
 400                            edit.old.end = next_edit.old.end;
 401                            cursor.seek(&edit.old.end, Bias::Right, &());
 402                            cursor.next(&());
 403                        }
 404                    } else {
 405                        break;
 406                    }
 407                }
 408
 409                edit.new.end =
 410                    InlayOffset(((edit.new.start + edit.old_len()).0 as isize + delta) as usize);
 411
 412                let anchor = inlay_snapshot
 413                    .buffer
 414                    .anchor_before(inlay_snapshot.to_buffer_offset(edit.new.start));
 415                let mut folds_cursor = self
 416                    .snapshot
 417                    .folds
 418                    .cursor::<FoldRange>(&inlay_snapshot.buffer);
 419                folds_cursor.seek(
 420                    &FoldRange(anchor..Anchor::max()),
 421                    Bias::Left,
 422                    &inlay_snapshot.buffer,
 423                );
 424
 425                let mut folds = iter::from_fn({
 426                    let inlay_snapshot = &inlay_snapshot;
 427                    move || {
 428                        let item = folds_cursor.item().map(|fold| {
 429                            let buffer_start = fold.range.start.to_offset(&inlay_snapshot.buffer);
 430                            let buffer_end = fold.range.end.to_offset(&inlay_snapshot.buffer);
 431                            (
 432                                fold.clone(),
 433                                inlay_snapshot.to_inlay_offset(buffer_start)
 434                                    ..inlay_snapshot.to_inlay_offset(buffer_end),
 435                            )
 436                        });
 437                        folds_cursor.next(&inlay_snapshot.buffer);
 438                        item
 439                    }
 440                })
 441                .peekable();
 442
 443                while folds
 444                    .peek()
 445                    .map_or(false, |(_, fold_range)| fold_range.start < edit.new.end)
 446                {
 447                    let (fold, mut fold_range) = folds.next().unwrap();
 448                    let sum = new_transforms.summary();
 449
 450                    assert!(fold_range.start.0 >= sum.input.len);
 451
 452                    while folds.peek().map_or(false, |(next_fold, next_fold_range)| {
 453                        next_fold_range.start < fold_range.end
 454                            || (next_fold_range.start == fold_range.end
 455                                && fold.placeholder.merge_adjacent
 456                                && next_fold.placeholder.merge_adjacent)
 457                    }) {
 458                        let (_, next_fold_range) = folds.next().unwrap();
 459                        if next_fold_range.end > fold_range.end {
 460                            fold_range.end = next_fold_range.end;
 461                        }
 462                    }
 463
 464                    if fold_range.start.0 > sum.input.len {
 465                        let text_summary = inlay_snapshot
 466                            .text_summary_for_range(InlayOffset(sum.input.len)..fold_range.start);
 467                        push_isomorphic(&mut new_transforms, text_summary);
 468                    }
 469
 470                    if fold_range.end > fold_range.start {
 471                        const ELLIPSIS: &str = "";
 472
 473                        let fold_id = fold.id;
 474                        new_transforms.push(
 475                            Transform {
 476                                summary: TransformSummary {
 477                                    output: TextSummary::from(ELLIPSIS),
 478                                    input: inlay_snapshot
 479                                        .text_summary_for_range(fold_range.start..fold_range.end),
 480                                },
 481                                placeholder: Some(TransformPlaceholder {
 482                                    text: ELLIPSIS,
 483                                    renderer: ChunkRenderer {
 484                                        render: Arc::new(move |cx| {
 485                                            (fold.placeholder.render)(
 486                                                fold_id,
 487                                                fold.range.0.clone(),
 488                                                cx,
 489                                            )
 490                                        }),
 491                                        constrain_width: fold.placeholder.constrain_width,
 492                                    },
 493                                }),
 494                            },
 495                            &(),
 496                        );
 497                    }
 498                }
 499
 500                let sum = new_transforms.summary();
 501                if sum.input.len < edit.new.end.0 {
 502                    let text_summary = inlay_snapshot
 503                        .text_summary_for_range(InlayOffset(sum.input.len)..edit.new.end);
 504                    push_isomorphic(&mut new_transforms, text_summary);
 505                }
 506            }
 507
 508            new_transforms.append(cursor.suffix(&()), &());
 509            if new_transforms.is_empty() {
 510                let text_summary = inlay_snapshot.text_summary();
 511                push_isomorphic(&mut new_transforms, text_summary);
 512            }
 513
 514            drop(cursor);
 515
 516            let mut fold_edits = Vec::with_capacity(inlay_edits.len());
 517            {
 518                let mut old_transforms = self
 519                    .snapshot
 520                    .transforms
 521                    .cursor::<(InlayOffset, FoldOffset)>(&());
 522                let mut new_transforms = new_transforms.cursor::<(InlayOffset, FoldOffset)>(&());
 523
 524                for mut edit in inlay_edits {
 525                    old_transforms.seek(&edit.old.start, Bias::Left, &());
 526                    if old_transforms.item().map_or(false, |t| t.is_fold()) {
 527                        edit.old.start = old_transforms.start().0;
 528                    }
 529                    let old_start =
 530                        old_transforms.start().1 .0 + (edit.old.start - old_transforms.start().0).0;
 531
 532                    old_transforms.seek_forward(&edit.old.end, Bias::Right, &());
 533                    if old_transforms.item().map_or(false, |t| t.is_fold()) {
 534                        old_transforms.next(&());
 535                        edit.old.end = old_transforms.start().0;
 536                    }
 537                    let old_end =
 538                        old_transforms.start().1 .0 + (edit.old.end - old_transforms.start().0).0;
 539
 540                    new_transforms.seek(&edit.new.start, Bias::Left, &());
 541                    if new_transforms.item().map_or(false, |t| t.is_fold()) {
 542                        edit.new.start = new_transforms.start().0;
 543                    }
 544                    let new_start =
 545                        new_transforms.start().1 .0 + (edit.new.start - new_transforms.start().0).0;
 546
 547                    new_transforms.seek_forward(&edit.new.end, Bias::Right, &());
 548                    if new_transforms.item().map_or(false, |t| t.is_fold()) {
 549                        new_transforms.next(&());
 550                        edit.new.end = new_transforms.start().0;
 551                    }
 552                    let new_end =
 553                        new_transforms.start().1 .0 + (edit.new.end - new_transforms.start().0).0;
 554
 555                    fold_edits.push(FoldEdit {
 556                        old: FoldOffset(old_start)..FoldOffset(old_end),
 557                        new: FoldOffset(new_start)..FoldOffset(new_end),
 558                    });
 559                }
 560
 561                fold_edits = consolidate_fold_edits(fold_edits);
 562            }
 563
 564            self.snapshot.transforms = new_transforms;
 565            self.snapshot.inlay_snapshot = inlay_snapshot;
 566            self.snapshot.version += 1;
 567            fold_edits
 568        }
 569    }
 570}
 571
 572#[derive(Clone)]
 573pub struct FoldSnapshot {
 574    transforms: SumTree<Transform>,
 575    folds: SumTree<Fold>,
 576    pub inlay_snapshot: InlaySnapshot,
 577    pub version: usize,
 578}
 579
 580impl FoldSnapshot {
 581    #[cfg(test)]
 582    pub fn text(&self) -> String {
 583        self.chunks(FoldOffset(0)..self.len(), false, Highlights::default())
 584            .map(|c| c.text)
 585            .collect()
 586    }
 587
 588    #[cfg(test)]
 589    pub fn fold_count(&self) -> usize {
 590        self.folds.items(&self.inlay_snapshot.buffer).len()
 591    }
 592
 593    pub fn text_summary_for_range(&self, range: Range<FoldPoint>) -> TextSummary {
 594        let mut summary = TextSummary::default();
 595
 596        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
 597        cursor.seek(&range.start, Bias::Right, &());
 598        if let Some(transform) = cursor.item() {
 599            let start_in_transform = range.start.0 - cursor.start().0 .0;
 600            let end_in_transform = cmp::min(range.end, cursor.end(&()).0).0 - cursor.start().0 .0;
 601            if let Some(placeholder) = transform.placeholder.as_ref() {
 602                summary = TextSummary::from(
 603                    &placeholder.text
 604                        [start_in_transform.column as usize..end_in_transform.column as usize],
 605                );
 606            } else {
 607                let inlay_start = self
 608                    .inlay_snapshot
 609                    .to_offset(InlayPoint(cursor.start().1 .0 + start_in_transform));
 610                let inlay_end = self
 611                    .inlay_snapshot
 612                    .to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
 613                summary = self
 614                    .inlay_snapshot
 615                    .text_summary_for_range(inlay_start..inlay_end);
 616            }
 617        }
 618
 619        if range.end > cursor.end(&()).0 {
 620            cursor.next(&());
 621            summary += &cursor
 622                .summary::<_, TransformSummary>(&range.end, Bias::Right, &())
 623                .output;
 624            if let Some(transform) = cursor.item() {
 625                let end_in_transform = range.end.0 - cursor.start().0 .0;
 626                if let Some(placeholder) = transform.placeholder.as_ref() {
 627                    summary +=
 628                        TextSummary::from(&placeholder.text[..end_in_transform.column as usize]);
 629                } else {
 630                    let inlay_start = self.inlay_snapshot.to_offset(cursor.start().1);
 631                    let inlay_end = self
 632                        .inlay_snapshot
 633                        .to_offset(InlayPoint(cursor.start().1 .0 + end_in_transform));
 634                    summary += self
 635                        .inlay_snapshot
 636                        .text_summary_for_range(inlay_start..inlay_end);
 637                }
 638            }
 639        }
 640
 641        summary
 642    }
 643
 644    pub fn to_fold_point(&self, point: InlayPoint, bias: Bias) -> FoldPoint {
 645        let mut cursor = self.transforms.cursor::<(InlayPoint, FoldPoint)>(&());
 646        cursor.seek(&point, Bias::Right, &());
 647        if cursor.item().map_or(false, |t| t.is_fold()) {
 648            if bias == Bias::Left || point == cursor.start().0 {
 649                cursor.start().1
 650            } else {
 651                cursor.end(&()).1
 652            }
 653        } else {
 654            let overshoot = point.0 - cursor.start().0 .0;
 655            FoldPoint(cmp::min(
 656                cursor.start().1 .0 + overshoot,
 657                cursor.end(&()).1 .0,
 658            ))
 659        }
 660    }
 661
 662    pub fn len(&self) -> FoldOffset {
 663        FoldOffset(self.transforms.summary().output.len)
 664    }
 665
 666    pub fn line_len(&self, row: u32) -> u32 {
 667        let line_start = FoldPoint::new(row, 0).to_offset(self).0;
 668        let line_end = if row >= self.max_point().row() {
 669            self.len().0
 670        } else {
 671            FoldPoint::new(row + 1, 0).to_offset(self).0 - 1
 672        };
 673        (line_end - line_start) as u32
 674    }
 675
 676    pub fn buffer_rows(&self, start_row: u32) -> FoldBufferRows {
 677        if start_row > self.transforms.summary().output.lines.row {
 678            panic!("invalid display row {}", start_row);
 679        }
 680
 681        let fold_point = FoldPoint::new(start_row, 0);
 682        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
 683        cursor.seek(&fold_point, Bias::Left, &());
 684
 685        let overshoot = fold_point.0 - cursor.start().0 .0;
 686        let inlay_point = InlayPoint(cursor.start().1 .0 + overshoot);
 687        let input_buffer_rows = self.inlay_snapshot.buffer_rows(inlay_point.row());
 688
 689        FoldBufferRows {
 690            fold_point,
 691            input_buffer_rows,
 692            cursor,
 693        }
 694    }
 695
 696    pub fn max_point(&self) -> FoldPoint {
 697        FoldPoint(self.transforms.summary().output.lines)
 698    }
 699
 700    #[cfg(test)]
 701    pub fn longest_row(&self) -> u32 {
 702        self.transforms.summary().output.longest_row
 703    }
 704
 705    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
 706    where
 707        T: ToOffset,
 708    {
 709        let buffer = &self.inlay_snapshot.buffer;
 710        let range = range.start.to_offset(buffer)..range.end.to_offset(buffer);
 711        let mut folds = intersecting_folds(&self.inlay_snapshot, &self.folds, range, false);
 712        iter::from_fn(move || {
 713            let item = folds.item();
 714            folds.next(&self.inlay_snapshot.buffer);
 715            item
 716        })
 717    }
 718
 719    pub fn intersects_fold<T>(&self, offset: T) -> bool
 720    where
 721        T: ToOffset,
 722    {
 723        let buffer_offset = offset.to_offset(&self.inlay_snapshot.buffer);
 724        let inlay_offset = self.inlay_snapshot.to_inlay_offset(buffer_offset);
 725        let mut cursor = self.transforms.cursor::<InlayOffset>(&());
 726        cursor.seek(&inlay_offset, Bias::Right, &());
 727        cursor.item().map_or(false, |t| t.placeholder.is_some())
 728    }
 729
 730    pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
 731        let mut inlay_point = self
 732            .inlay_snapshot
 733            .to_inlay_point(Point::new(buffer_row.0, 0));
 734        let mut cursor = self.transforms.cursor::<InlayPoint>(&());
 735        cursor.seek(&inlay_point, Bias::Right, &());
 736        loop {
 737            match cursor.item() {
 738                Some(transform) => {
 739                    let buffer_point = self.inlay_snapshot.to_buffer_point(inlay_point);
 740                    if buffer_point.row != buffer_row.0 {
 741                        return false;
 742                    } else if transform.placeholder.is_some() {
 743                        return true;
 744                    }
 745                }
 746                None => return false,
 747            }
 748
 749            if cursor.end(&()).row() == inlay_point.row() {
 750                cursor.next(&());
 751            } else {
 752                inlay_point.0 += Point::new(1, 0);
 753                cursor.seek(&inlay_point, Bias::Right, &());
 754            }
 755        }
 756    }
 757
 758    pub(crate) fn chunks<'a>(
 759        &'a self,
 760        range: Range<FoldOffset>,
 761        language_aware: bool,
 762        highlights: Highlights<'a>,
 763    ) -> FoldChunks<'a> {
 764        let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
 765        transform_cursor.seek(&range.start, Bias::Right, &());
 766
 767        let inlay_start = {
 768            let overshoot = range.start.0 - transform_cursor.start().0 .0;
 769            transform_cursor.start().1 + InlayOffset(overshoot)
 770        };
 771
 772        let transform_end = transform_cursor.end(&());
 773
 774        let inlay_end = if transform_cursor
 775            .item()
 776            .map_or(true, |transform| transform.is_fold())
 777        {
 778            inlay_start
 779        } else if range.end < transform_end.0 {
 780            let overshoot = range.end.0 - transform_cursor.start().0 .0;
 781            transform_cursor.start().1 + InlayOffset(overshoot)
 782        } else {
 783            transform_end.1
 784        };
 785
 786        FoldChunks {
 787            transform_cursor,
 788            inlay_chunks: self.inlay_snapshot.chunks(
 789                inlay_start..inlay_end,
 790                language_aware,
 791                highlights,
 792            ),
 793            inlay_chunk: None,
 794            inlay_offset: inlay_start,
 795            output_offset: range.start,
 796            max_output_offset: range.end,
 797        }
 798    }
 799
 800    pub fn chars_at(&self, start: FoldPoint) -> impl '_ + Iterator<Item = char> {
 801        self.chunks(
 802            start.to_offset(self)..self.len(),
 803            false,
 804            Highlights::default(),
 805        )
 806        .flat_map(|chunk| chunk.text.chars())
 807    }
 808
 809    #[cfg(test)]
 810    pub fn clip_offset(&self, offset: FoldOffset, bias: Bias) -> FoldOffset {
 811        if offset > self.len() {
 812            self.len()
 813        } else {
 814            self.clip_point(offset.to_point(self), bias).to_offset(self)
 815        }
 816    }
 817
 818    pub fn clip_point(&self, point: FoldPoint, bias: Bias) -> FoldPoint {
 819        let mut cursor = self.transforms.cursor::<(FoldPoint, InlayPoint)>(&());
 820        cursor.seek(&point, Bias::Right, &());
 821        if let Some(transform) = cursor.item() {
 822            let transform_start = cursor.start().0 .0;
 823            if transform.placeholder.is_some() {
 824                if point.0 == transform_start || matches!(bias, Bias::Left) {
 825                    FoldPoint(transform_start)
 826                } else {
 827                    FoldPoint(cursor.end(&()).0 .0)
 828                }
 829            } else {
 830                let overshoot = InlayPoint(point.0 - transform_start);
 831                let inlay_point = cursor.start().1 + overshoot;
 832                let clipped_inlay_point = self.inlay_snapshot.clip_point(inlay_point, bias);
 833                FoldPoint(cursor.start().0 .0 + (clipped_inlay_point - cursor.start().1).0)
 834            }
 835        } else {
 836            FoldPoint(self.transforms.summary().output.lines)
 837        }
 838    }
 839}
 840
 841fn push_isomorphic(transforms: &mut SumTree<Transform>, summary: TextSummary) {
 842    let mut did_merge = false;
 843    transforms.update_last(
 844        |last| {
 845            if !last.is_fold() {
 846                last.summary.input += summary.clone();
 847                last.summary.output += summary.clone();
 848                did_merge = true;
 849            }
 850        },
 851        &(),
 852    );
 853    if !did_merge {
 854        transforms.push(
 855            Transform {
 856                summary: TransformSummary {
 857                    input: summary.clone(),
 858                    output: summary,
 859                },
 860                placeholder: None,
 861            },
 862            &(),
 863        )
 864    }
 865}
 866
 867fn intersecting_folds<'a>(
 868    inlay_snapshot: &'a InlaySnapshot,
 869    folds: &'a SumTree<Fold>,
 870    range: Range<usize>,
 871    inclusive: bool,
 872) -> FilterCursor<'a, impl 'a + FnMut(&FoldSummary) -> bool, Fold, usize> {
 873    let buffer = &inlay_snapshot.buffer;
 874    let start = buffer.anchor_before(range.start.to_offset(buffer));
 875    let end = buffer.anchor_after(range.end.to_offset(buffer));
 876    let mut cursor = folds.filter::<_, usize>(buffer, move |summary| {
 877        let start_cmp = start.cmp(&summary.max_end, buffer);
 878        let end_cmp = end.cmp(&summary.min_start, buffer);
 879
 880        if inclusive {
 881            start_cmp <= Ordering::Equal && end_cmp >= Ordering::Equal
 882        } else {
 883            start_cmp == Ordering::Less && end_cmp == Ordering::Greater
 884        }
 885    });
 886    cursor.next(buffer);
 887    cursor
 888}
 889
 890fn consolidate_inlay_edits(mut edits: Vec<InlayEdit>) -> Vec<InlayEdit> {
 891    edits.sort_unstable_by(|a, b| {
 892        a.old
 893            .start
 894            .cmp(&b.old.start)
 895            .then_with(|| b.old.end.cmp(&a.old.end))
 896    });
 897
 898    let _old_alloc_ptr = edits.as_ptr();
 899    let mut inlay_edits = edits.into_iter();
 900
 901    if let Some(mut first_edit) = inlay_edits.next() {
 902        // This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
 903        #[allow(clippy::filter_map_identity)]
 904        let mut v: Vec<_> = inlay_edits
 905            .scan(&mut first_edit, |prev_edit, edit| {
 906                if prev_edit.old.end >= edit.old.start {
 907                    prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
 908                    prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
 909                    prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
 910                    Some(None) // Skip this edit, it's merged
 911                } else {
 912                    let prev = std::mem::replace(*prev_edit, edit);
 913                    Some(Some(prev)) // Yield the previous edit
 914                }
 915            })
 916            .filter_map(|x| x)
 917            .collect();
 918        v.push(first_edit.clone());
 919        debug_assert_eq!(_old_alloc_ptr, v.as_ptr(), "Inlay edits were reallocated");
 920        v
 921    } else {
 922        vec![]
 923    }
 924}
 925
 926fn consolidate_fold_edits(mut edits: Vec<FoldEdit>) -> Vec<FoldEdit> {
 927    edits.sort_unstable_by(|a, b| {
 928        a.old
 929            .start
 930            .cmp(&b.old.start)
 931            .then_with(|| b.old.end.cmp(&a.old.end))
 932    });
 933    let _old_alloc_ptr = edits.as_ptr();
 934    let mut fold_edits = edits.into_iter();
 935
 936    if let Some(mut first_edit) = fold_edits.next() {
 937        // This code relies on reusing allocations from the Vec<_> - at the time of writing .flatten() prevents them.
 938        #[allow(clippy::filter_map_identity)]
 939        let mut v: Vec<_> = fold_edits
 940            .scan(&mut first_edit, |prev_edit, edit| {
 941                if prev_edit.old.end >= edit.old.start {
 942                    prev_edit.old.end = prev_edit.old.end.max(edit.old.end);
 943                    prev_edit.new.start = prev_edit.new.start.min(edit.new.start);
 944                    prev_edit.new.end = prev_edit.new.end.max(edit.new.end);
 945                    Some(None) // Skip this edit, it's merged
 946                } else {
 947                    let prev = std::mem::replace(*prev_edit, edit);
 948                    Some(Some(prev)) // Yield the previous edit
 949                }
 950            })
 951            .filter_map(|x| x)
 952            .collect();
 953        v.push(first_edit.clone());
 954        v
 955    } else {
 956        vec![]
 957    }
 958}
 959
 960#[derive(Clone, Debug, Default)]
 961struct Transform {
 962    summary: TransformSummary,
 963    placeholder: Option<TransformPlaceholder>,
 964}
 965
 966#[derive(Clone, Debug)]
 967struct TransformPlaceholder {
 968    text: &'static str,
 969    renderer: ChunkRenderer,
 970}
 971
 972impl Transform {
 973    fn is_fold(&self) -> bool {
 974        self.placeholder.is_some()
 975    }
 976}
 977
 978#[derive(Clone, Debug, Default, Eq, PartialEq)]
 979struct TransformSummary {
 980    output: TextSummary,
 981    input: TextSummary,
 982}
 983
 984impl sum_tree::Item for Transform {
 985    type Summary = TransformSummary;
 986
 987    fn summary(&self, _cx: &()) -> Self::Summary {
 988        self.summary.clone()
 989    }
 990}
 991
 992impl sum_tree::Summary for TransformSummary {
 993    type Context = ();
 994
 995    fn zero(_cx: &()) -> Self {
 996        Default::default()
 997    }
 998
 999    fn add_summary(&mut self, other: &Self, _: &()) {
1000        self.input += &other.input;
1001        self.output += &other.output;
1002    }
1003}
1004
1005#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
1006pub struct FoldId(usize);
1007
1008impl From<FoldId> for ElementId {
1009    fn from(val: FoldId) -> Self {
1010        ElementId::Integer(val.0)
1011    }
1012}
1013
1014#[derive(Clone, Debug, Eq, PartialEq)]
1015pub struct Fold {
1016    pub id: FoldId,
1017    pub range: FoldRange,
1018    pub placeholder: FoldPlaceholder,
1019}
1020
1021#[derive(Clone, Debug, Eq, PartialEq)]
1022pub struct FoldRange(Range<Anchor>);
1023
1024impl Deref for FoldRange {
1025    type Target = Range<Anchor>;
1026
1027    fn deref(&self) -> &Self::Target {
1028        &self.0
1029    }
1030}
1031
1032impl DerefMut for FoldRange {
1033    fn deref_mut(&mut self) -> &mut Self::Target {
1034        &mut self.0
1035    }
1036}
1037
1038impl Default for FoldRange {
1039    fn default() -> Self {
1040        Self(Anchor::min()..Anchor::max())
1041    }
1042}
1043
1044impl sum_tree::Item for Fold {
1045    type Summary = FoldSummary;
1046
1047    fn summary(&self, _cx: &MultiBufferSnapshot) -> Self::Summary {
1048        FoldSummary {
1049            start: self.range.start,
1050            end: self.range.end,
1051            min_start: self.range.start,
1052            max_end: self.range.end,
1053            count: 1,
1054        }
1055    }
1056}
1057
1058#[derive(Clone, Debug)]
1059pub struct FoldSummary {
1060    start: Anchor,
1061    end: Anchor,
1062    min_start: Anchor,
1063    max_end: Anchor,
1064    count: usize,
1065}
1066
1067impl Default for FoldSummary {
1068    fn default() -> Self {
1069        Self {
1070            start: Anchor::min(),
1071            end: Anchor::max(),
1072            min_start: Anchor::max(),
1073            max_end: Anchor::min(),
1074            count: 0,
1075        }
1076    }
1077}
1078
1079impl sum_tree::Summary for FoldSummary {
1080    type Context = MultiBufferSnapshot;
1081
1082    fn zero(_cx: &MultiBufferSnapshot) -> Self {
1083        Default::default()
1084    }
1085
1086    fn add_summary(&mut self, other: &Self, buffer: &Self::Context) {
1087        if other.min_start.cmp(&self.min_start, buffer) == Ordering::Less {
1088            self.min_start = other.min_start;
1089        }
1090        if other.max_end.cmp(&self.max_end, buffer) == Ordering::Greater {
1091            self.max_end = other.max_end;
1092        }
1093
1094        #[cfg(debug_assertions)]
1095        {
1096            let start_comparison = self.start.cmp(&other.start, buffer);
1097            assert!(start_comparison <= Ordering::Equal);
1098            if start_comparison == Ordering::Equal {
1099                assert!(self.end.cmp(&other.end, buffer) >= Ordering::Equal);
1100            }
1101        }
1102
1103        self.start = other.start;
1104        self.end = other.end;
1105        self.count += other.count;
1106    }
1107}
1108
1109impl<'a> sum_tree::Dimension<'a, FoldSummary> for FoldRange {
1110    fn zero(_cx: &MultiBufferSnapshot) -> Self {
1111        Default::default()
1112    }
1113
1114    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
1115        self.0.start = summary.start;
1116        self.0.end = summary.end;
1117    }
1118}
1119
1120impl<'a> sum_tree::SeekTarget<'a, FoldSummary, FoldRange> for FoldRange {
1121    fn cmp(&self, other: &Self, buffer: &MultiBufferSnapshot) -> Ordering {
1122        AnchorRangeExt::cmp(&self.0, &other.0, buffer)
1123    }
1124}
1125
1126impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
1127    fn zero(_cx: &MultiBufferSnapshot) -> Self {
1128        Default::default()
1129    }
1130
1131    fn add_summary(&mut self, summary: &'a FoldSummary, _: &MultiBufferSnapshot) {
1132        *self += summary.count;
1133    }
1134}
1135
1136#[derive(Clone)]
1137pub struct FoldBufferRows<'a> {
1138    cursor: Cursor<'a, Transform, (FoldPoint, InlayPoint)>,
1139    input_buffer_rows: InlayBufferRows<'a>,
1140    fold_point: FoldPoint,
1141}
1142
1143impl<'a> FoldBufferRows<'a> {
1144    pub(crate) fn seek(&mut self, row: u32) {
1145        let fold_point = FoldPoint::new(row, 0);
1146        self.cursor.seek(&fold_point, Bias::Left, &());
1147        let overshoot = fold_point.0 - self.cursor.start().0 .0;
1148        let inlay_point = InlayPoint(self.cursor.start().1 .0 + overshoot);
1149        self.input_buffer_rows.seek(inlay_point.row());
1150        self.fold_point = fold_point;
1151    }
1152}
1153
1154impl<'a> Iterator for FoldBufferRows<'a> {
1155    type Item = Option<u32>;
1156
1157    fn next(&mut self) -> Option<Self::Item> {
1158        let mut traversed_fold = false;
1159        while self.fold_point > self.cursor.end(&()).0 {
1160            self.cursor.next(&());
1161            traversed_fold = true;
1162            if self.cursor.item().is_none() {
1163                break;
1164            }
1165        }
1166
1167        if self.cursor.item().is_some() {
1168            if traversed_fold {
1169                self.input_buffer_rows.seek(self.cursor.start().1.row());
1170                self.input_buffer_rows.next();
1171            }
1172            *self.fold_point.row_mut() += 1;
1173            self.input_buffer_rows.next()
1174        } else {
1175            None
1176        }
1177    }
1178}
1179
1180pub struct FoldChunks<'a> {
1181    transform_cursor: Cursor<'a, Transform, (FoldOffset, InlayOffset)>,
1182    inlay_chunks: InlayChunks<'a>,
1183    inlay_chunk: Option<(InlayOffset, Chunk<'a>)>,
1184    inlay_offset: InlayOffset,
1185    output_offset: FoldOffset,
1186    max_output_offset: FoldOffset,
1187}
1188
1189impl<'a> FoldChunks<'a> {
1190    pub(crate) fn seek(&mut self, range: Range<FoldOffset>) {
1191        self.transform_cursor.seek(&range.start, Bias::Right, &());
1192
1193        let inlay_start = {
1194            let overshoot = range.start.0 - self.transform_cursor.start().0 .0;
1195            self.transform_cursor.start().1 + InlayOffset(overshoot)
1196        };
1197
1198        let transform_end = self.transform_cursor.end(&());
1199
1200        let inlay_end = if self
1201            .transform_cursor
1202            .item()
1203            .map_or(true, |transform| transform.is_fold())
1204        {
1205            inlay_start
1206        } else if range.end < transform_end.0 {
1207            let overshoot = range.end.0 - self.transform_cursor.start().0 .0;
1208            self.transform_cursor.start().1 + InlayOffset(overshoot)
1209        } else {
1210            transform_end.1
1211        };
1212
1213        self.inlay_chunks.seek(inlay_start..inlay_end);
1214        self.inlay_chunk = None;
1215        self.inlay_offset = inlay_start;
1216        self.output_offset = range.start;
1217        self.max_output_offset = range.end;
1218    }
1219}
1220
1221impl<'a> Iterator for FoldChunks<'a> {
1222    type Item = Chunk<'a>;
1223
1224    fn next(&mut self) -> Option<Self::Item> {
1225        if self.output_offset >= self.max_output_offset {
1226            return None;
1227        }
1228
1229        let transform = self.transform_cursor.item()?;
1230
1231        // If we're in a fold, then return the fold's display text and
1232        // advance the transform and buffer cursors to the end of the fold.
1233        if let Some(placeholder) = transform.placeholder.as_ref() {
1234            self.inlay_chunk.take();
1235            self.inlay_offset += InlayOffset(transform.summary.input.len);
1236
1237            while self.inlay_offset >= self.transform_cursor.end(&()).1
1238                && self.transform_cursor.item().is_some()
1239            {
1240                self.transform_cursor.next(&());
1241            }
1242
1243            self.output_offset.0 += placeholder.text.len();
1244            return Some(Chunk {
1245                text: placeholder.text,
1246                renderer: Some(placeholder.renderer.clone()),
1247                ..Default::default()
1248            });
1249        }
1250
1251        // When we reach a non-fold region, seek the underlying text
1252        // chunk iterator to the next unfolded range.
1253        if self.inlay_offset == self.transform_cursor.start().1
1254            && self.inlay_chunks.offset() != self.inlay_offset
1255        {
1256            let transform_start = self.transform_cursor.start();
1257            let transform_end = self.transform_cursor.end(&());
1258            let inlay_end = if self.max_output_offset < transform_end.0 {
1259                let overshoot = self.max_output_offset.0 - transform_start.0 .0;
1260                transform_start.1 + InlayOffset(overshoot)
1261            } else {
1262                transform_end.1
1263            };
1264
1265            self.inlay_chunks.seek(self.inlay_offset..inlay_end);
1266        }
1267
1268        // Retrieve a chunk from the current location in the buffer.
1269        if self.inlay_chunk.is_none() {
1270            let chunk_offset = self.inlay_chunks.offset();
1271            self.inlay_chunk = self.inlay_chunks.next().map(|chunk| (chunk_offset, chunk));
1272        }
1273
1274        // Otherwise, take a chunk from the buffer's text.
1275        if let Some((buffer_chunk_start, mut chunk)) = self.inlay_chunk.clone() {
1276            let buffer_chunk_end = buffer_chunk_start + InlayOffset(chunk.text.len());
1277            let transform_end = self.transform_cursor.end(&()).1;
1278            let chunk_end = buffer_chunk_end.min(transform_end);
1279
1280            chunk.text = &chunk.text
1281                [(self.inlay_offset - buffer_chunk_start).0..(chunk_end - buffer_chunk_start).0];
1282
1283            if chunk_end == transform_end {
1284                self.transform_cursor.next(&());
1285            } else if chunk_end == buffer_chunk_end {
1286                self.inlay_chunk.take();
1287            }
1288
1289            self.inlay_offset = chunk_end;
1290            self.output_offset.0 += chunk.text.len();
1291            return Some(chunk);
1292        }
1293
1294        None
1295    }
1296}
1297
1298#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
1299pub struct FoldOffset(pub usize);
1300
1301impl FoldOffset {
1302    pub fn to_point(self, snapshot: &FoldSnapshot) -> FoldPoint {
1303        let mut cursor = snapshot
1304            .transforms
1305            .cursor::<(FoldOffset, TransformSummary)>(&());
1306        cursor.seek(&self, Bias::Right, &());
1307        let overshoot = if cursor.item().map_or(true, |t| t.is_fold()) {
1308            Point::new(0, (self.0 - cursor.start().0 .0) as u32)
1309        } else {
1310            let inlay_offset = cursor.start().1.input.len + self.0 - cursor.start().0 .0;
1311            let inlay_point = snapshot.inlay_snapshot.to_point(InlayOffset(inlay_offset));
1312            inlay_point.0 - cursor.start().1.input.lines
1313        };
1314        FoldPoint(cursor.start().1.output.lines + overshoot)
1315    }
1316
1317    #[cfg(test)]
1318    pub fn to_inlay_offset(self, snapshot: &FoldSnapshot) -> InlayOffset {
1319        let mut cursor = snapshot.transforms.cursor::<(FoldOffset, InlayOffset)>(&());
1320        cursor.seek(&self, Bias::Right, &());
1321        let overshoot = self.0 - cursor.start().0 .0;
1322        InlayOffset(cursor.start().1 .0 + overshoot)
1323    }
1324}
1325
1326impl Add for FoldOffset {
1327    type Output = Self;
1328
1329    fn add(self, rhs: Self) -> Self::Output {
1330        Self(self.0 + rhs.0)
1331    }
1332}
1333
1334impl AddAssign for FoldOffset {
1335    fn add_assign(&mut self, rhs: Self) {
1336        self.0 += rhs.0;
1337    }
1338}
1339
1340impl Sub for FoldOffset {
1341    type Output = Self;
1342
1343    fn sub(self, rhs: Self) -> Self::Output {
1344        Self(self.0 - rhs.0)
1345    }
1346}
1347
1348impl<'a> sum_tree::Dimension<'a, TransformSummary> for FoldOffset {
1349    fn zero(_cx: &()) -> Self {
1350        Default::default()
1351    }
1352
1353    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1354        self.0 += &summary.output.len;
1355    }
1356}
1357
1358impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayPoint {
1359    fn zero(_cx: &()) -> Self {
1360        Default::default()
1361    }
1362
1363    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1364        self.0 += &summary.input.lines;
1365    }
1366}
1367
1368impl<'a> sum_tree::Dimension<'a, TransformSummary> for InlayOffset {
1369    fn zero(_cx: &()) -> Self {
1370        Default::default()
1371    }
1372
1373    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1374        self.0 += &summary.input.len;
1375    }
1376}
1377
1378pub type FoldEdit = Edit<FoldOffset>;
1379
1380#[cfg(test)]
1381mod tests {
1382    use super::*;
1383    use crate::{display_map::inlay_map::InlayMap, MultiBuffer, ToPoint};
1384    use collections::HashSet;
1385    use rand::prelude::*;
1386    use settings::SettingsStore;
1387    use std::{env, mem};
1388    use text::Patch;
1389    use util::test::sample_text;
1390    use util::RandomCharIter;
1391    use Bias::{Left, Right};
1392
1393    #[gpui::test]
1394    fn test_basic_folds(cx: &mut gpui::AppContext) {
1395        init_test(cx);
1396        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
1397        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1398        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1399        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1400        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1401
1402        let (mut writer, _, _) = map.write(inlay_snapshot, vec![]);
1403        let (snapshot2, edits) = writer.fold(vec![
1404            (Point::new(0, 2)..Point::new(2, 2), FoldPlaceholder::test()),
1405            (Point::new(2, 4)..Point::new(4, 1), FoldPlaceholder::test()),
1406        ]);
1407        assert_eq!(snapshot2.text(), "aa⋯cc⋯eeeee");
1408        assert_eq!(
1409            edits,
1410            &[
1411                FoldEdit {
1412                    old: FoldOffset(2)..FoldOffset(16),
1413                    new: FoldOffset(2)..FoldOffset(5),
1414                },
1415                FoldEdit {
1416                    old: FoldOffset(18)..FoldOffset(29),
1417                    new: FoldOffset(7)..FoldOffset(10)
1418                },
1419            ]
1420        );
1421
1422        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1423            buffer.edit(
1424                vec![
1425                    (Point::new(0, 0)..Point::new(0, 1), "123"),
1426                    (Point::new(2, 3)..Point::new(2, 3), "123"),
1427                ],
1428                None,
1429                cx,
1430            );
1431            buffer.snapshot(cx)
1432        });
1433
1434        let (inlay_snapshot, inlay_edits) =
1435            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1436        let (snapshot3, edits) = map.read(inlay_snapshot, inlay_edits);
1437        assert_eq!(snapshot3.text(), "123a⋯c123c⋯eeeee");
1438        assert_eq!(
1439            edits,
1440            &[
1441                FoldEdit {
1442                    old: FoldOffset(0)..FoldOffset(1),
1443                    new: FoldOffset(0)..FoldOffset(3),
1444                },
1445                FoldEdit {
1446                    old: FoldOffset(6)..FoldOffset(6),
1447                    new: FoldOffset(8)..FoldOffset(11),
1448                },
1449            ]
1450        );
1451
1452        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1453            buffer.edit([(Point::new(2, 6)..Point::new(4, 3), "456")], None, cx);
1454            buffer.snapshot(cx)
1455        });
1456        let (inlay_snapshot, inlay_edits) =
1457            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1458        let (snapshot4, _) = map.read(inlay_snapshot.clone(), inlay_edits);
1459        assert_eq!(snapshot4.text(), "123a⋯c123456eee");
1460
1461        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1462        writer.unfold_intersecting(Some(Point::new(0, 4)..Point::new(0, 4)), false);
1463        let (snapshot5, _) = map.read(inlay_snapshot.clone(), vec![]);
1464        assert_eq!(snapshot5.text(), "123a⋯c123456eee");
1465
1466        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1467        writer.unfold_intersecting(Some(Point::new(0, 4)..Point::new(0, 4)), true);
1468        let (snapshot6, _) = map.read(inlay_snapshot, vec![]);
1469        assert_eq!(snapshot6.text(), "123aaaaa\nbbbbbb\nccc123456eee");
1470    }
1471
1472    #[gpui::test]
1473    fn test_adjacent_folds(cx: &mut gpui::AppContext) {
1474        init_test(cx);
1475        let buffer = MultiBuffer::build_simple("abcdefghijkl", cx);
1476        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1477        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1478        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1479
1480        {
1481            let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1482
1483            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1484            writer.fold(vec![(5..8, FoldPlaceholder::test())]);
1485            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1486            assert_eq!(snapshot.text(), "abcde⋯ijkl");
1487
1488            // Create an fold adjacent to the start of the first fold.
1489            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1490            writer.fold(vec![
1491                (0..1, FoldPlaceholder::test()),
1492                (2..5, FoldPlaceholder::test()),
1493            ]);
1494            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1495            assert_eq!(snapshot.text(), "⋯b⋯ijkl");
1496
1497            // Create an fold adjacent to the end of the first fold.
1498            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1499            writer.fold(vec![
1500                (11..11, FoldPlaceholder::test()),
1501                (8..10, FoldPlaceholder::test()),
1502            ]);
1503            let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1504            assert_eq!(snapshot.text(), "⋯b⋯kl");
1505        }
1506
1507        {
1508            let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1509
1510            // Create two adjacent folds.
1511            let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1512            writer.fold(vec![
1513                (0..2, FoldPlaceholder::test()),
1514                (2..5, FoldPlaceholder::test()),
1515            ]);
1516            let (snapshot, _) = map.read(inlay_snapshot, vec![]);
1517            assert_eq!(snapshot.text(), "⋯fghijkl");
1518
1519            // Edit within one of the folds.
1520            let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1521                buffer.edit([(0..1, "12345")], None, cx);
1522                buffer.snapshot(cx)
1523            });
1524            let (inlay_snapshot, inlay_edits) =
1525                inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1526            let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
1527            assert_eq!(snapshot.text(), "12345⋯fghijkl");
1528        }
1529    }
1530
1531    #[gpui::test]
1532    fn test_overlapping_folds(cx: &mut gpui::AppContext) {
1533        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
1534        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1535        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
1536        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1537        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1538        writer.fold(vec![
1539            (Point::new(0, 2)..Point::new(2, 2), FoldPlaceholder::test()),
1540            (Point::new(0, 4)..Point::new(1, 0), FoldPlaceholder::test()),
1541            (Point::new(1, 2)..Point::new(3, 2), FoldPlaceholder::test()),
1542            (Point::new(3, 1)..Point::new(4, 1), FoldPlaceholder::test()),
1543        ]);
1544        let (snapshot, _) = map.read(inlay_snapshot, vec![]);
1545        assert_eq!(snapshot.text(), "aa⋯eeeee");
1546    }
1547
1548    #[gpui::test]
1549    fn test_merging_folds_via_edit(cx: &mut gpui::AppContext) {
1550        init_test(cx);
1551        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
1552        let subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
1553        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1554        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1555        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1556
1557        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1558        writer.fold(vec![
1559            (Point::new(0, 2)..Point::new(2, 2), FoldPlaceholder::test()),
1560            (Point::new(3, 1)..Point::new(4, 1), FoldPlaceholder::test()),
1561        ]);
1562        let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1563        assert_eq!(snapshot.text(), "aa⋯cccc\nd⋯eeeee");
1564
1565        let buffer_snapshot = buffer.update(cx, |buffer, cx| {
1566            buffer.edit([(Point::new(2, 2)..Point::new(3, 1), "")], None, cx);
1567            buffer.snapshot(cx)
1568        });
1569        let (inlay_snapshot, inlay_edits) =
1570            inlay_map.sync(buffer_snapshot, subscription.consume().into_inner());
1571        let (snapshot, _) = map.read(inlay_snapshot, inlay_edits);
1572        assert_eq!(snapshot.text(), "aa⋯eeeee");
1573    }
1574
1575    #[gpui::test]
1576    fn test_folds_in_range(cx: &mut gpui::AppContext) {
1577        let buffer = MultiBuffer::build_simple(&sample_text(5, 6, 'a'), cx);
1578        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1579        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1580        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1581
1582        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1583        writer.fold(vec![
1584            (Point::new(0, 2)..Point::new(2, 2), FoldPlaceholder::test()),
1585            (Point::new(0, 4)..Point::new(1, 0), FoldPlaceholder::test()),
1586            (Point::new(1, 2)..Point::new(3, 2), FoldPlaceholder::test()),
1587            (Point::new(3, 1)..Point::new(4, 1), FoldPlaceholder::test()),
1588        ]);
1589        let (snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1590        let fold_ranges = snapshot
1591            .folds_in_range(Point::new(1, 0)..Point::new(1, 3))
1592            .map(|fold| {
1593                fold.range.start.to_point(&buffer_snapshot)
1594                    ..fold.range.end.to_point(&buffer_snapshot)
1595            })
1596            .collect::<Vec<_>>();
1597        assert_eq!(
1598            fold_ranges,
1599            vec![
1600                Point::new(0, 2)..Point::new(2, 2),
1601                Point::new(1, 2)..Point::new(3, 2)
1602            ]
1603        );
1604    }
1605
1606    #[gpui::test(iterations = 100)]
1607    fn test_random_folds(cx: &mut gpui::AppContext, mut rng: StdRng) {
1608        init_test(cx);
1609        let operations = env::var("OPERATIONS")
1610            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1611            .unwrap_or(10);
1612
1613        let len = rng.gen_range(0..10);
1614        let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
1615        let buffer = if rng.gen() {
1616            MultiBuffer::build_simple(&text, cx)
1617        } else {
1618            MultiBuffer::build_random(&mut rng, cx)
1619        };
1620        let mut buffer_snapshot = buffer.read(cx).snapshot(cx);
1621        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1622        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1623
1624        let (mut initial_snapshot, _) = map.read(inlay_snapshot.clone(), vec![]);
1625        let mut snapshot_edits = Vec::new();
1626
1627        let mut next_inlay_id = 0;
1628        for _ in 0..operations {
1629            log::info!("text: {:?}", buffer_snapshot.text());
1630            let mut buffer_edits = Vec::new();
1631            let mut inlay_edits = Vec::new();
1632            match rng.gen_range(0..=100) {
1633                0..=39 => {
1634                    snapshot_edits.extend(map.randomly_mutate(&mut rng));
1635                }
1636                40..=59 => {
1637                    let (_, edits) = inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1638                    inlay_edits = edits;
1639                }
1640                _ => buffer.update(cx, |buffer, cx| {
1641                    let subscription = buffer.subscribe();
1642                    let edit_count = rng.gen_range(1..=5);
1643                    buffer.randomly_mutate(&mut rng, edit_count, cx);
1644                    buffer_snapshot = buffer.snapshot(cx);
1645                    let edits = subscription.consume().into_inner();
1646                    log::info!("editing {:?}", edits);
1647                    buffer_edits.extend(edits);
1648                }),
1649            };
1650
1651            let (inlay_snapshot, new_inlay_edits) =
1652                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1653            log::info!("inlay text {:?}", inlay_snapshot.text());
1654
1655            let inlay_edits = Patch::new(inlay_edits)
1656                .compose(new_inlay_edits)
1657                .into_inner();
1658            let (snapshot, edits) = map.read(inlay_snapshot.clone(), inlay_edits);
1659            snapshot_edits.push((snapshot.clone(), edits));
1660
1661            let mut expected_text: String = inlay_snapshot.text().to_string();
1662            for fold_range in map.merged_folds().into_iter().rev() {
1663                let fold_inlay_start = inlay_snapshot.to_inlay_offset(fold_range.start);
1664                let fold_inlay_end = inlay_snapshot.to_inlay_offset(fold_range.end);
1665                expected_text.replace_range(fold_inlay_start.0..fold_inlay_end.0, "");
1666            }
1667
1668            assert_eq!(snapshot.text(), expected_text);
1669            log::info!(
1670                "fold text {:?} ({} lines)",
1671                expected_text,
1672                expected_text.matches('\n').count() + 1
1673            );
1674
1675            let mut prev_row = 0;
1676            let mut expected_buffer_rows = Vec::new();
1677            for fold_range in map.merged_folds() {
1678                let fold_start = inlay_snapshot
1679                    .to_point(inlay_snapshot.to_inlay_offset(fold_range.start))
1680                    .row();
1681                let fold_end = inlay_snapshot
1682                    .to_point(inlay_snapshot.to_inlay_offset(fold_range.end))
1683                    .row();
1684                expected_buffer_rows.extend(
1685                    inlay_snapshot
1686                        .buffer_rows(prev_row)
1687                        .take((1 + fold_start - prev_row) as usize),
1688                );
1689                prev_row = 1 + fold_end;
1690            }
1691            expected_buffer_rows.extend(inlay_snapshot.buffer_rows(prev_row));
1692
1693            assert_eq!(
1694                expected_buffer_rows.len(),
1695                expected_text.matches('\n').count() + 1,
1696                "wrong expected buffer rows {:?}. text: {:?}",
1697                expected_buffer_rows,
1698                expected_text
1699            );
1700
1701            for (output_row, line) in expected_text.lines().enumerate() {
1702                let line_len = snapshot.line_len(output_row as u32);
1703                assert_eq!(line_len, line.len() as u32);
1704            }
1705
1706            let longest_row = snapshot.longest_row();
1707            let longest_char_column = expected_text
1708                .split('\n')
1709                .nth(longest_row as usize)
1710                .unwrap()
1711                .chars()
1712                .count();
1713            let mut fold_point = FoldPoint::new(0, 0);
1714            let mut fold_offset = FoldOffset(0);
1715            let mut char_column = 0;
1716            for c in expected_text.chars() {
1717                let inlay_point = fold_point.to_inlay_point(&snapshot);
1718                let inlay_offset = fold_offset.to_inlay_offset(&snapshot);
1719                assert_eq!(
1720                    snapshot.to_fold_point(inlay_point, Right),
1721                    fold_point,
1722                    "{:?} -> fold point",
1723                    inlay_point,
1724                );
1725                assert_eq!(
1726                    inlay_snapshot.to_offset(inlay_point),
1727                    inlay_offset,
1728                    "inlay_snapshot.to_offset({:?})",
1729                    inlay_point,
1730                );
1731                assert_eq!(
1732                    fold_point.to_offset(&snapshot),
1733                    fold_offset,
1734                    "fold_point.to_offset({:?})",
1735                    fold_point,
1736                );
1737
1738                if c == '\n' {
1739                    *fold_point.row_mut() += 1;
1740                    *fold_point.column_mut() = 0;
1741                    char_column = 0;
1742                } else {
1743                    *fold_point.column_mut() += c.len_utf8() as u32;
1744                    char_column += 1;
1745                }
1746                fold_offset.0 += c.len_utf8();
1747                if char_column > longest_char_column {
1748                    panic!(
1749                        "invalid longest row {:?} (chars {}), found row {:?} (chars: {})",
1750                        longest_row,
1751                        longest_char_column,
1752                        fold_point.row(),
1753                        char_column
1754                    );
1755                }
1756            }
1757
1758            for _ in 0..5 {
1759                let mut start = snapshot
1760                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Left);
1761                let mut end = snapshot
1762                    .clip_offset(FoldOffset(rng.gen_range(0..=snapshot.len().0)), Bias::Right);
1763                if start > end {
1764                    mem::swap(&mut start, &mut end);
1765                }
1766
1767                let text = &expected_text[start.0..end.0];
1768                assert_eq!(
1769                    snapshot
1770                        .chunks(start..end, false, Highlights::default())
1771                        .map(|c| c.text)
1772                        .collect::<String>(),
1773                    text,
1774                );
1775            }
1776
1777            let mut fold_row = 0;
1778            while fold_row < expected_buffer_rows.len() as u32 {
1779                assert_eq!(
1780                    snapshot.buffer_rows(fold_row).collect::<Vec<_>>(),
1781                    expected_buffer_rows[(fold_row as usize)..],
1782                    "wrong buffer rows starting at fold row {}",
1783                    fold_row,
1784                );
1785                fold_row += 1;
1786            }
1787
1788            let folded_buffer_rows = map
1789                .merged_folds()
1790                .iter()
1791                .flat_map(|fold_range| {
1792                    let start_row = fold_range.start.to_point(&buffer_snapshot).row;
1793                    let end = fold_range.end.to_point(&buffer_snapshot);
1794                    if end.column == 0 {
1795                        start_row..end.row
1796                    } else {
1797                        start_row..end.row + 1
1798                    }
1799                })
1800                .collect::<HashSet<_>>();
1801            for row in 0..=buffer_snapshot.max_point().row {
1802                assert_eq!(
1803                    snapshot.is_line_folded(MultiBufferRow(row)),
1804                    folded_buffer_rows.contains(&row),
1805                    "expected buffer row {}{} to be folded",
1806                    row,
1807                    if folded_buffer_rows.contains(&row) {
1808                        ""
1809                    } else {
1810                        " not"
1811                    }
1812                );
1813            }
1814
1815            for _ in 0..5 {
1816                let end =
1817                    buffer_snapshot.clip_offset(rng.gen_range(0..=buffer_snapshot.len()), Right);
1818                let start = buffer_snapshot.clip_offset(rng.gen_range(0..=end), Left);
1819                let expected_folds = map
1820                    .snapshot
1821                    .folds
1822                    .items(&buffer_snapshot)
1823                    .into_iter()
1824                    .filter(|fold| {
1825                        let start = buffer_snapshot.anchor_before(start);
1826                        let end = buffer_snapshot.anchor_after(end);
1827                        start.cmp(&fold.range.end, &buffer_snapshot) == Ordering::Less
1828                            && end.cmp(&fold.range.start, &buffer_snapshot) == Ordering::Greater
1829                    })
1830                    .collect::<Vec<_>>();
1831
1832                assert_eq!(
1833                    snapshot
1834                        .folds_in_range(start..end)
1835                        .cloned()
1836                        .collect::<Vec<_>>(),
1837                    expected_folds
1838                );
1839            }
1840
1841            let text = snapshot.text();
1842            for _ in 0..5 {
1843                let start_row = rng.gen_range(0..=snapshot.max_point().row());
1844                let start_column = rng.gen_range(0..=snapshot.line_len(start_row));
1845                let end_row = rng.gen_range(0..=snapshot.max_point().row());
1846                let end_column = rng.gen_range(0..=snapshot.line_len(end_row));
1847                let mut start =
1848                    snapshot.clip_point(FoldPoint::new(start_row, start_column), Bias::Left);
1849                let mut end = snapshot.clip_point(FoldPoint::new(end_row, end_column), Bias::Right);
1850                if start > end {
1851                    mem::swap(&mut start, &mut end);
1852                }
1853
1854                let lines = start..end;
1855                let bytes = start.to_offset(&snapshot)..end.to_offset(&snapshot);
1856                assert_eq!(
1857                    snapshot.text_summary_for_range(lines),
1858                    TextSummary::from(&text[bytes.start.0..bytes.end.0])
1859                )
1860            }
1861
1862            let mut text = initial_snapshot.text();
1863            for (snapshot, edits) in snapshot_edits.drain(..) {
1864                let new_text = snapshot.text();
1865                for edit in edits {
1866                    let old_bytes = edit.new.start.0..edit.new.start.0 + edit.old_len().0;
1867                    let new_bytes = edit.new.start.0..edit.new.end.0;
1868                    text.replace_range(old_bytes, &new_text[new_bytes]);
1869                }
1870
1871                assert_eq!(text, new_text);
1872                initial_snapshot = snapshot;
1873            }
1874        }
1875    }
1876
1877    #[gpui::test]
1878    fn test_buffer_rows(cx: &mut gpui::AppContext) {
1879        let text = sample_text(6, 6, 'a') + "\n";
1880        let buffer = MultiBuffer::build_simple(&text, cx);
1881
1882        let buffer_snapshot = buffer.read(cx).snapshot(cx);
1883        let (_, inlay_snapshot) = InlayMap::new(buffer_snapshot);
1884        let mut map = FoldMap::new(inlay_snapshot.clone()).0;
1885
1886        let (mut writer, _, _) = map.write(inlay_snapshot.clone(), vec![]);
1887        writer.fold(vec![
1888            (Point::new(0, 2)..Point::new(2, 2), FoldPlaceholder::test()),
1889            (Point::new(3, 1)..Point::new(4, 1), FoldPlaceholder::test()),
1890        ]);
1891
1892        let (snapshot, _) = map.read(inlay_snapshot, vec![]);
1893        assert_eq!(snapshot.text(), "aa⋯cccc\nd⋯eeeee\nffffff\n");
1894        assert_eq!(
1895            snapshot.buffer_rows(0).collect::<Vec<_>>(),
1896            [Some(0), Some(3), Some(5), Some(6)]
1897        );
1898        assert_eq!(snapshot.buffer_rows(3).collect::<Vec<_>>(), [Some(6)]);
1899    }
1900
1901    fn init_test(cx: &mut gpui::AppContext) {
1902        let store = SettingsStore::test(cx);
1903        cx.set_global(store);
1904    }
1905
1906    impl FoldMap {
1907        fn merged_folds(&self) -> Vec<Range<usize>> {
1908            let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
1909            let buffer = &inlay_snapshot.buffer;
1910            let mut folds = self.snapshot.folds.items(buffer);
1911            // Ensure sorting doesn't change how folds get merged and displayed.
1912            folds.sort_by(|a, b| a.range.cmp(&b.range, buffer));
1913            let mut folds = folds
1914                .iter()
1915                .map(|fold| fold.range.start.to_offset(buffer)..fold.range.end.to_offset(buffer))
1916                .peekable();
1917
1918            let mut merged_folds = Vec::new();
1919            while let Some(mut fold_range) = folds.next() {
1920                while let Some(next_range) = folds.peek() {
1921                    if fold_range.end >= next_range.start {
1922                        if next_range.end > fold_range.end {
1923                            fold_range.end = next_range.end;
1924                        }
1925                        folds.next();
1926                    } else {
1927                        break;
1928                    }
1929                }
1930                if fold_range.end > fold_range.start {
1931                    merged_folds.push(fold_range);
1932                }
1933            }
1934            merged_folds
1935        }
1936
1937        pub fn randomly_mutate(
1938            &mut self,
1939            rng: &mut impl Rng,
1940        ) -> Vec<(FoldSnapshot, Vec<FoldEdit>)> {
1941            let mut snapshot_edits = Vec::new();
1942            match rng.gen_range(0..=100) {
1943                0..=39 if !self.snapshot.folds.is_empty() => {
1944                    let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
1945                    let buffer = &inlay_snapshot.buffer;
1946                    let mut to_unfold = Vec::new();
1947                    for _ in 0..rng.gen_range(1..=3) {
1948                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1949                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1950                        to_unfold.push(start..end);
1951                    }
1952                    let inclusive = rng.gen();
1953                    log::info!("unfolding {:?} (inclusive: {})", to_unfold, inclusive);
1954                    let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
1955                    snapshot_edits.push((snapshot, edits));
1956                    let (snapshot, edits) = writer.unfold_intersecting(to_unfold, inclusive);
1957                    snapshot_edits.push((snapshot, edits));
1958                }
1959                _ => {
1960                    let inlay_snapshot = self.snapshot.inlay_snapshot.clone();
1961                    let buffer = &inlay_snapshot.buffer;
1962                    let mut to_fold = Vec::new();
1963                    for _ in 0..rng.gen_range(1..=2) {
1964                        let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1965                        let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1966                        to_fold.push((start..end, FoldPlaceholder::test()));
1967                    }
1968                    log::info!("folding {:?}", to_fold);
1969                    let (mut writer, snapshot, edits) = self.write(inlay_snapshot, vec![]);
1970                    snapshot_edits.push((snapshot, edits));
1971                    let (snapshot, edits) = writer.fold(to_fold);
1972                    snapshot_edits.push((snapshot, edits));
1973                }
1974            }
1975            snapshot_edits
1976        }
1977    }
1978}