fold_map.rs

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