fold_map.rs

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