fold_map.rs

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