fold_map.rs

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