fold_map.rs

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