fold_map.rs

   1use super::{
   2    buffer::{self, AnchorRangeExt},
   3    Anchor, Buffer, DisplayPoint, Edit, Point, TextSummary, ToOffset,
   4};
   5use crate::{
   6    sum_tree::{self, Cursor, FilterCursor, SeekBias, SumTree},
   7    time,
   8};
   9use anyhow::{anyhow, Result};
  10use gpui::{AppContext, ModelHandle};
  11use parking_lot::{Mutex, MutexGuard};
  12use std::{
  13    cmp::{self, Ordering},
  14    iter::Take,
  15    ops::Range,
  16};
  17
  18pub struct FoldMap {
  19    buffer: ModelHandle<Buffer>,
  20    transforms: Mutex<SumTree<Transform>>,
  21    folds: SumTree<Fold>,
  22    last_sync: Mutex<time::Global>,
  23}
  24
  25impl FoldMap {
  26    pub fn new(buffer_handle: ModelHandle<Buffer>, ctx: &AppContext) -> Self {
  27        let buffer = buffer_handle.read(ctx);
  28        let text_summary = buffer.text_summary();
  29        Self {
  30            buffer: buffer_handle,
  31            folds: Default::default(),
  32            transforms: Mutex::new(SumTree::from_item(
  33                Transform {
  34                    summary: TransformSummary {
  35                        buffer: text_summary.clone(),
  36                        display: text_summary,
  37                    },
  38                    display_text: None,
  39                },
  40                &(),
  41            )),
  42            last_sync: Mutex::new(buffer.version()),
  43        }
  44    }
  45
  46    pub fn snapshot(&self, ctx: &AppContext) -> FoldMapSnapshot {
  47        FoldMapSnapshot {
  48            transforms: self.sync(ctx).clone(),
  49            buffer: self.buffer.clone(),
  50        }
  51    }
  52
  53    pub fn len(&self, ctx: &AppContext) -> usize {
  54        self.sync(ctx).summary().display.chars
  55    }
  56
  57    pub fn line_len(&self, row: u32, ctx: &AppContext) -> Result<u32> {
  58        let line_start = self.to_display_offset(DisplayPoint::new(row, 0), ctx)?.0;
  59        let line_end = if row >= self.max_point(ctx).row() {
  60            self.len(ctx)
  61        } else {
  62            self.to_display_offset(DisplayPoint::new(row + 1, 0), ctx)?
  63                .0
  64                - 1
  65        };
  66
  67        Ok((line_end - line_start) as u32)
  68    }
  69
  70    pub fn max_point(&self, ctx: &AppContext) -> DisplayPoint {
  71        DisplayPoint(self.sync(ctx).summary().display.lines)
  72    }
  73
  74    pub fn rightmost_point(&self, ctx: &AppContext) -> DisplayPoint {
  75        DisplayPoint(self.sync(ctx).summary().display.rightmost_point)
  76    }
  77
  78    pub fn folds_in_range<'a, T>(
  79        &'a self,
  80        range: Range<T>,
  81        ctx: &'a AppContext,
  82    ) -> Result<impl Iterator<Item = &'a Range<Anchor>>>
  83    where
  84        T: ToOffset,
  85    {
  86        Ok(self.intersecting_folds(range, ctx)?.map(|f| &f.0))
  87    }
  88
  89    pub fn fold<T: ToOffset>(
  90        &mut self,
  91        ranges: impl IntoIterator<Item = Range<T>>,
  92        ctx: &AppContext,
  93    ) -> Result<()> {
  94        let _ = self.sync(ctx);
  95
  96        let mut edits = Vec::new();
  97        let buffer = self.buffer.read(ctx);
  98        for range in ranges.into_iter() {
  99            let range = range.start.to_offset(buffer)?..range.end.to_offset(buffer)?;
 100            if range.start == range.end {
 101                continue;
 102            }
 103
 104            let fold = Fold(buffer.anchor_after(range.start)?..buffer.anchor_before(range.end)?);
 105            edits.push(Edit {
 106                old_range: range.clone(),
 107                new_range: range.clone(),
 108            });
 109            self.folds = {
 110                let mut new_tree = SumTree::new();
 111                let mut cursor = self.folds.cursor::<_, ()>();
 112                new_tree.push_tree(cursor.slice(&fold, SeekBias::Right, buffer), buffer);
 113                new_tree.push(fold, buffer);
 114                new_tree.push_tree(cursor.suffix(buffer), buffer);
 115                new_tree
 116            };
 117        }
 118        edits.sort_unstable_by(|a, b| {
 119            a.old_range
 120                .start
 121                .cmp(&b.old_range.start)
 122                .then_with(|| b.old_range.end.cmp(&a.old_range.end))
 123        });
 124
 125        self.apply_edits(edits, ctx);
 126        Ok(())
 127    }
 128
 129    pub fn unfold<T: ToOffset>(
 130        &mut self,
 131        ranges: impl IntoIterator<Item = Range<T>>,
 132        ctx: &AppContext,
 133    ) -> Result<()> {
 134        let _ = self.sync(ctx);
 135
 136        let buffer = self.buffer.read(ctx);
 137
 138        let mut edits = Vec::new();
 139        let mut fold_ixs_to_delete = Vec::new();
 140        for range in ranges.into_iter() {
 141            // Remove intersecting folds and add their ranges to edits that are passed to apply_edits
 142            let mut folds_cursor = self.intersecting_folds(range, ctx)?;
 143            while let Some(fold) = folds_cursor.item() {
 144                let offset_range =
 145                    fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap();
 146                edits.push(Edit {
 147                    old_range: offset_range.clone(),
 148                    new_range: offset_range,
 149                });
 150                fold_ixs_to_delete.push(*folds_cursor.start());
 151                folds_cursor.next();
 152            }
 153        }
 154        fold_ixs_to_delete.sort_unstable();
 155        fold_ixs_to_delete.dedup();
 156
 157        self.folds = {
 158            let mut cursor = self.folds.cursor::<_, ()>();
 159            let mut folds = SumTree::new();
 160            for fold_ix in fold_ixs_to_delete {
 161                folds.push_tree(cursor.slice(&fold_ix, SeekBias::Right, buffer), buffer);
 162                cursor.next();
 163            }
 164            folds.push_tree(cursor.suffix(buffer), buffer);
 165            folds
 166        };
 167
 168        self.apply_edits(edits, ctx);
 169        Ok(())
 170    }
 171
 172    fn intersecting_folds<'a, T>(
 173        &self,
 174        range: Range<T>,
 175        ctx: &'a AppContext,
 176    ) -> Result<FilterCursor<impl 'a + Fn(&FoldSummary) -> bool, Fold, usize>>
 177    where
 178        T: ToOffset,
 179    {
 180        let buffer = self.buffer.read(ctx);
 181        let start = buffer.anchor_before(range.start.to_offset(buffer)?)?;
 182        let end = buffer.anchor_after(range.end.to_offset(buffer)?)?;
 183        Ok(self.folds.filter::<_, usize>(move |summary| {
 184            start.cmp(&summary.max_end, buffer).unwrap() <= Ordering::Equal
 185                && end.cmp(&summary.min_start, buffer).unwrap() >= Ordering::Equal
 186        }))
 187    }
 188
 189    pub fn is_line_folded(&self, display_row: u32, ctx: &AppContext) -> bool {
 190        let transforms = self.sync(ctx);
 191        let mut cursor = transforms.cursor::<DisplayPoint, DisplayPoint>();
 192        cursor.seek(&DisplayPoint::new(display_row, 0), SeekBias::Right, &());
 193        while let Some(transform) = cursor.item() {
 194            if transform.display_text.is_some() {
 195                return true;
 196            }
 197            if cursor.end().row() == display_row {
 198                cursor.next()
 199            } else {
 200                break;
 201            }
 202        }
 203        false
 204    }
 205
 206    pub fn to_buffer_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<usize> {
 207        let transforms = self.sync(ctx);
 208        let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
 209        cursor.seek(&point, SeekBias::Right, &());
 210        let overshoot = point.0 - cursor.start().display.lines;
 211        (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))
 212    }
 213
 214    pub fn to_display_offset(
 215        &self,
 216        point: DisplayPoint,
 217        ctx: &AppContext,
 218    ) -> Result<DisplayOffset> {
 219        self.snapshot(ctx).to_display_offset(point, ctx)
 220    }
 221
 222    pub fn to_buffer_point(&self, display_point: DisplayPoint, ctx: &AppContext) -> Point {
 223        let transforms = self.sync(ctx);
 224        let mut cursor = transforms.cursor::<DisplayPoint, TransformSummary>();
 225        cursor.seek(&display_point, SeekBias::Right, &());
 226        let overshoot = display_point.0 - cursor.start().display.lines;
 227        cursor.start().buffer.lines + overshoot
 228    }
 229
 230    pub fn to_display_point(&self, point: Point, ctx: &AppContext) -> DisplayPoint {
 231        let transforms = self.sync(ctx);
 232        let mut cursor = transforms.cursor::<Point, TransformSummary>();
 233        cursor.seek(&point, SeekBias::Right, &());
 234        let overshoot = point - cursor.start().buffer.lines;
 235        DisplayPoint(cmp::min(
 236            cursor.start().display.lines + overshoot,
 237            cursor.end().display.lines,
 238        ))
 239    }
 240
 241    fn sync(&self, ctx: &AppContext) -> MutexGuard<SumTree<Transform>> {
 242        let buffer = self.buffer.read(ctx);
 243        let mut edits = buffer.edits_since(self.last_sync.lock().clone()).peekable();
 244        if edits.peek().is_some() {
 245            self.apply_edits(edits, ctx);
 246        }
 247        *self.last_sync.lock() = buffer.version();
 248        self.transforms.lock()
 249    }
 250
 251    fn apply_edits(&self, edits: impl IntoIterator<Item = Edit>, ctx: &AppContext) {
 252        let buffer = self.buffer.read(ctx);
 253        let mut edits = edits.into_iter().peekable();
 254
 255        let mut new_transforms = SumTree::new();
 256        let mut transforms = self.transforms.lock();
 257        let mut cursor = transforms.cursor::<usize, usize>();
 258        cursor.seek(&0, SeekBias::Right, &());
 259
 260        while let Some(mut edit) = edits.next() {
 261            new_transforms.push_tree(
 262                cursor.slice(&edit.old_range.start, SeekBias::Left, &()),
 263                &(),
 264            );
 265            edit.new_range.start -= edit.old_range.start - cursor.start();
 266            edit.old_range.start = *cursor.start();
 267
 268            cursor.seek(&edit.old_range.end, SeekBias::Right, &());
 269            cursor.next();
 270
 271            let mut delta = edit.delta();
 272            loop {
 273                edit.old_range.end = *cursor.start();
 274
 275                if let Some(next_edit) = edits.peek() {
 276                    if next_edit.old_range.start > edit.old_range.end {
 277                        break;
 278                    }
 279
 280                    let next_edit = edits.next().unwrap();
 281                    delta += next_edit.delta();
 282
 283                    if next_edit.old_range.end >= edit.old_range.end {
 284                        edit.old_range.end = next_edit.old_range.end;
 285                        cursor.seek(&edit.old_range.end, SeekBias::Right, &());
 286                        cursor.next();
 287                    }
 288                } else {
 289                    break;
 290                }
 291            }
 292
 293            edit.new_range.end =
 294                ((edit.new_range.start + edit.old_extent()) as isize + delta) as usize;
 295
 296            let anchor = buffer.anchor_before(edit.new_range.start).unwrap();
 297            let mut folds_cursor = self.folds.cursor::<_, ()>();
 298            folds_cursor.seek(&Fold(anchor..Anchor::End), SeekBias::Left, buffer);
 299            let mut folds = folds_cursor
 300                .map(|f| f.0.start.to_offset(buffer).unwrap()..f.0.end.to_offset(buffer).unwrap())
 301                .peekable();
 302
 303            while folds
 304                .peek()
 305                .map_or(false, |fold| fold.start < edit.new_range.end)
 306            {
 307                let mut fold = folds.next().unwrap();
 308                let sum = new_transforms.summary();
 309
 310                assert!(fold.start >= sum.buffer.chars);
 311
 312                while folds
 313                    .peek()
 314                    .map_or(false, |next_fold| next_fold.start <= fold.end)
 315                {
 316                    let next_fold = folds.next().unwrap();
 317                    if next_fold.end > fold.end {
 318                        fold.end = next_fold.end;
 319                    }
 320                }
 321
 322                if fold.start > sum.buffer.chars {
 323                    let text_summary = buffer.text_summary_for_range(sum.buffer.chars..fold.start);
 324                    new_transforms.push(
 325                        Transform {
 326                            summary: TransformSummary {
 327                                display: text_summary.clone(),
 328                                buffer: text_summary,
 329                            },
 330                            display_text: None,
 331                        },
 332                        &(),
 333                    );
 334                }
 335
 336                if fold.end > fold.start {
 337                    new_transforms.push(
 338                        Transform {
 339                            summary: TransformSummary {
 340                                display: TextSummary {
 341                                    chars: 1,
 342                                    bytes: ''.len_utf8(),
 343                                    lines: Point::new(0, 1),
 344                                    first_line_len: 1,
 345                                    rightmost_point: Point::new(0, 1),
 346                                },
 347                                buffer: buffer.text_summary_for_range(fold.start..fold.end),
 348                            },
 349                            display_text: Some('…'),
 350                        },
 351                        &(),
 352                    );
 353                }
 354            }
 355
 356            let sum = new_transforms.summary();
 357            if sum.buffer.chars < edit.new_range.end {
 358                let text_summary =
 359                    buffer.text_summary_for_range(sum.buffer.chars..edit.new_range.end);
 360                new_transforms.push(
 361                    Transform {
 362                        summary: TransformSummary {
 363                            display: text_summary.clone(),
 364                            buffer: text_summary,
 365                        },
 366                        display_text: None,
 367                    },
 368                    &(),
 369                );
 370            }
 371        }
 372
 373        new_transforms.push_tree(cursor.suffix(&()), &());
 374        if new_transforms.is_empty() {
 375            let text_summary = buffer.text_summary();
 376            new_transforms.push(
 377                Transform {
 378                    summary: TransformSummary {
 379                        display: text_summary.clone(),
 380                        buffer: text_summary,
 381                    },
 382                    display_text: None,
 383                },
 384                &(),
 385            );
 386        }
 387
 388        drop(cursor);
 389        *transforms = new_transforms;
 390    }
 391}
 392
 393pub struct FoldMapSnapshot {
 394    transforms: SumTree<Transform>,
 395    buffer: ModelHandle<Buffer>,
 396}
 397
 398impl FoldMapSnapshot {
 399    pub fn buffer_rows(&self, start_row: u32) -> Result<BufferRows> {
 400        if start_row > self.transforms.summary().display.lines.row {
 401            return Err(anyhow!("invalid display row {}", start_row));
 402        }
 403
 404        let display_point = Point::new(start_row, 0);
 405        let mut cursor = self.transforms.cursor();
 406        cursor.seek(&DisplayPoint(display_point), SeekBias::Left, &());
 407
 408        Ok(BufferRows {
 409            display_point,
 410            cursor,
 411        })
 412    }
 413
 414    pub fn chars_at<'a>(&'a self, point: DisplayPoint, ctx: &'a AppContext) -> Result<Chars<'a>> {
 415        let offset = self.to_display_offset(point, ctx)?;
 416        let mut cursor = self.transforms.cursor();
 417        cursor.seek(&offset, SeekBias::Right, &());
 418        Ok(Chars {
 419            cursor,
 420            offset: offset.0,
 421            buffer: self.buffer.read(ctx),
 422            buffer_chars: None,
 423        })
 424    }
 425
 426    fn to_display_offset(&self, point: DisplayPoint, ctx: &AppContext) -> Result<DisplayOffset> {
 427        let mut cursor = self.transforms.cursor::<DisplayPoint, TransformSummary>();
 428        cursor.seek(&point, SeekBias::Right, &());
 429        let overshoot = point.0 - cursor.start().display.lines;
 430        let mut offset = cursor.start().display.chars;
 431        if !overshoot.is_zero() {
 432            let transform = cursor
 433                .item()
 434                .ok_or_else(|| anyhow!("display point {:?} is out of range", point))?;
 435            assert!(transform.display_text.is_none());
 436            let end_buffer_offset =
 437                (cursor.start().buffer.lines + overshoot).to_offset(self.buffer.read(ctx))?;
 438            offset += end_buffer_offset - cursor.start().buffer.chars;
 439        }
 440        Ok(DisplayOffset(offset))
 441    }
 442}
 443
 444#[derive(Clone, Debug, Default, Eq, PartialEq)]
 445struct Transform {
 446    summary: TransformSummary,
 447    display_text: Option<char>,
 448}
 449
 450#[derive(Clone, Debug, Default, Eq, PartialEq)]
 451struct TransformSummary {
 452    display: TextSummary,
 453    buffer: TextSummary,
 454}
 455
 456impl sum_tree::Item for Transform {
 457    type Summary = TransformSummary;
 458
 459    fn summary(&self) -> Self::Summary {
 460        self.summary.clone()
 461    }
 462}
 463
 464impl sum_tree::Summary for TransformSummary {
 465    type Context = ();
 466
 467    fn add_summary(&mut self, other: &Self, _: &()) {
 468        self.buffer += &other.buffer;
 469        self.display += &other.display;
 470    }
 471}
 472
 473impl<'a> sum_tree::Dimension<'a, TransformSummary> for TransformSummary {
 474    fn add_summary(&mut self, summary: &'a TransformSummary) {
 475        sum_tree::Summary::add_summary(self, summary, &());
 476    }
 477}
 478
 479#[derive(Clone, Debug)]
 480struct Fold(Range<Anchor>);
 481
 482impl Default for Fold {
 483    fn default() -> Self {
 484        Self(Anchor::Start..Anchor::End)
 485    }
 486}
 487
 488impl sum_tree::Item for Fold {
 489    type Summary = FoldSummary;
 490
 491    fn summary(&self) -> Self::Summary {
 492        FoldSummary {
 493            start: self.0.start.clone(),
 494            end: self.0.end.clone(),
 495            min_start: self.0.start.clone(),
 496            max_end: self.0.end.clone(),
 497            count: 1,
 498        }
 499    }
 500}
 501
 502#[derive(Clone, Debug)]
 503struct FoldSummary {
 504    start: Anchor,
 505    end: Anchor,
 506    min_start: Anchor,
 507    max_end: Anchor,
 508    count: usize,
 509}
 510
 511impl Default for FoldSummary {
 512    fn default() -> Self {
 513        Self {
 514            start: Anchor::Start,
 515            end: Anchor::End,
 516            min_start: Anchor::End,
 517            max_end: Anchor::Start,
 518            count: 0,
 519        }
 520    }
 521}
 522
 523impl sum_tree::Summary for FoldSummary {
 524    type Context = Buffer;
 525
 526    fn add_summary(&mut self, other: &Self, buffer: &Buffer) {
 527        if other.min_start.cmp(&self.min_start, buffer).unwrap() == Ordering::Less {
 528            self.min_start = other.min_start.clone();
 529        }
 530        if other.max_end.cmp(&self.max_end, buffer).unwrap() == Ordering::Greater {
 531            self.max_end = other.max_end.clone();
 532        }
 533
 534        #[cfg(debug_assertions)]
 535        {
 536            let start_comparison = self.start.cmp(&other.start, buffer).unwrap();
 537            assert!(start_comparison <= Ordering::Equal);
 538            if start_comparison == Ordering::Equal {
 539                assert!(self.end.cmp(&other.end, buffer).unwrap() >= Ordering::Equal);
 540            }
 541        }
 542        self.start = other.start.clone();
 543        self.end = other.end.clone();
 544        self.count += other.count;
 545    }
 546}
 547
 548impl<'a> sum_tree::Dimension<'a, FoldSummary> for Fold {
 549    fn add_summary(&mut self, summary: &'a FoldSummary) {
 550        self.0.start = summary.start.clone();
 551        self.0.end = summary.end.clone();
 552    }
 553}
 554
 555impl<'a> sum_tree::SeekDimension<'a, FoldSummary> for Fold {
 556    fn cmp(&self, other: &Self, buffer: &Buffer) -> Ordering {
 557        self.0.cmp(&other.0, buffer).unwrap()
 558    }
 559}
 560
 561impl<'a> sum_tree::Dimension<'a, FoldSummary> for usize {
 562    fn add_summary(&mut self, summary: &'a FoldSummary) {
 563        *self += summary.count;
 564    }
 565}
 566
 567pub struct BufferRows<'a> {
 568    cursor: Cursor<'a, Transform, DisplayPoint, TransformSummary>,
 569    display_point: Point,
 570}
 571
 572impl<'a> Iterator for BufferRows<'a> {
 573    type Item = u32;
 574
 575    fn next(&mut self) -> Option<Self::Item> {
 576        while self.display_point > self.cursor.end().display.lines {
 577            self.cursor.next();
 578            if self.cursor.item().is_none() {
 579                // TODO: Return a bool from next?
 580                break;
 581            }
 582        }
 583
 584        if self.cursor.item().is_some() {
 585            let overshoot = self.display_point - self.cursor.start().display.lines;
 586            let buffer_point = self.cursor.start().buffer.lines + overshoot;
 587            self.display_point.row += 1;
 588            Some(buffer_point.row)
 589        } else {
 590            None
 591        }
 592    }
 593}
 594
 595pub struct Chars<'a> {
 596    cursor: Cursor<'a, Transform, DisplayOffset, TransformSummary>,
 597    offset: usize,
 598    buffer: &'a Buffer,
 599    buffer_chars: Option<Take<buffer::CharIter<'a>>>,
 600}
 601
 602impl<'a> Iterator for Chars<'a> {
 603    type Item = char;
 604
 605    fn next(&mut self) -> Option<Self::Item> {
 606        if let Some(c) = self.buffer_chars.as_mut().and_then(|chars| chars.next()) {
 607            self.offset += 1;
 608            return Some(c);
 609        }
 610
 611        while self.offset == self.cursor.end().display.chars && self.cursor.item().is_some() {
 612            self.cursor.next();
 613        }
 614
 615        self.cursor.item().and_then(|transform| {
 616            if let Some(c) = transform.display_text {
 617                self.offset += 1;
 618                Some(c)
 619            } else {
 620                let overshoot = self.offset - self.cursor.start().display.chars;
 621                let buffer_start = self.cursor.start().buffer.chars + overshoot;
 622                let char_count = self.cursor.end().buffer.chars - buffer_start;
 623                self.buffer_chars =
 624                    Some(self.buffer.chars_at(buffer_start).unwrap().take(char_count));
 625                self.next()
 626            }
 627        })
 628    }
 629}
 630
 631impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayPoint {
 632    fn add_summary(&mut self, summary: &'a TransformSummary) {
 633        self.0 += &summary.display.lines;
 634    }
 635}
 636
 637#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
 638pub struct DisplayOffset(usize);
 639
 640impl<'a> sum_tree::Dimension<'a, TransformSummary> for DisplayOffset {
 641    fn add_summary(&mut self, summary: &'a TransformSummary) {
 642        self.0 += &summary.display.chars;
 643    }
 644}
 645
 646impl<'a> sum_tree::Dimension<'a, TransformSummary> for Point {
 647    fn add_summary(&mut self, summary: &'a TransformSummary) {
 648        *self += &summary.buffer.lines;
 649    }
 650}
 651
 652impl<'a> sum_tree::Dimension<'a, TransformSummary> for usize {
 653    fn add_summary(&mut self, summary: &'a TransformSummary) {
 654        *self += &summary.buffer.chars;
 655    }
 656}
 657
 658#[cfg(test)]
 659mod tests {
 660    use super::*;
 661    use crate::test::sample_text;
 662    use buffer::ToPoint;
 663    use gpui::App;
 664
 665    #[test]
 666    fn test_basic_folds() {
 667        App::test((), |app| {
 668            let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
 669            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 670
 671            map.fold(
 672                vec![
 673                    Point::new(0, 2)..Point::new(2, 2),
 674                    Point::new(2, 4)..Point::new(4, 1),
 675                ],
 676                app.as_ref(),
 677            )
 678            .unwrap();
 679            assert_eq!(map.text(app.as_ref()), "aa…cc…eeeee");
 680
 681            buffer.update(app, |buffer, ctx| {
 682                buffer
 683                    .edit(
 684                        vec![
 685                            Point::new(0, 0)..Point::new(0, 1),
 686                            Point::new(2, 3)..Point::new(2, 3),
 687                        ],
 688                        "123",
 689                        Some(ctx),
 690                    )
 691                    .unwrap();
 692            });
 693            assert_eq!(map.text(app.as_ref()), "123a…c123c…eeeee");
 694
 695            buffer.update(app, |buffer, ctx| {
 696                let start_version = buffer.version.clone();
 697                buffer
 698                    .edit(Some(Point::new(2, 6)..Point::new(4, 3)), "456", Some(ctx))
 699                    .unwrap();
 700                buffer.edits_since(start_version).collect::<Vec<_>>()
 701            });
 702            assert_eq!(map.text(app.as_ref()), "123a…c123456eee");
 703
 704            map.unfold(Some(Point::new(0, 4)..Point::new(0, 4)), app.as_ref())
 705                .unwrap();
 706            assert_eq!(map.text(app.as_ref()), "123aaaaa\nbbbbbb\nccc123456eee");
 707        });
 708    }
 709
 710    #[test]
 711    fn test_adjacent_folds() {
 712        App::test((), |app| {
 713            let buffer = app.add_model(|_| Buffer::new(0, "abcdefghijkl"));
 714
 715            {
 716                let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 717
 718                map.fold(vec![5..8], app.as_ref()).unwrap();
 719                map.check_invariants(app.as_ref());
 720                assert_eq!(map.text(app.as_ref()), "abcde…ijkl");
 721
 722                // Create an fold adjacent to the start of the first fold.
 723                map.fold(vec![0..1, 2..5], app.as_ref()).unwrap();
 724                map.check_invariants(app.as_ref());
 725                assert_eq!(map.text(app.as_ref()), "…b…ijkl");
 726
 727                // Create an fold adjacent to the end of the first fold.
 728                map.fold(vec![11..11, 8..10], app.as_ref()).unwrap();
 729                map.check_invariants(app.as_ref());
 730                assert_eq!(map.text(app.as_ref()), "…b…kl");
 731            }
 732
 733            {
 734                let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 735
 736                // Create two adjacent folds.
 737                map.fold(vec![0..2, 2..5], app.as_ref()).unwrap();
 738                map.check_invariants(app.as_ref());
 739                assert_eq!(map.text(app.as_ref()), "…fghijkl");
 740
 741                // Edit within one of the folds.
 742                buffer.update(app, |buffer, ctx| {
 743                    let version = buffer.version();
 744                    buffer.edit(vec![0..1], "12345", Some(ctx)).unwrap();
 745                    buffer.edits_since(version).collect::<Vec<_>>()
 746                });
 747                map.check_invariants(app.as_ref());
 748                assert_eq!(map.text(app.as_ref()), "12345…fghijkl");
 749            }
 750        });
 751    }
 752
 753    #[test]
 754    fn test_overlapping_folds() {
 755        App::test((), |app| {
 756            let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
 757            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 758            map.fold(
 759                vec![
 760                    Point::new(0, 2)..Point::new(2, 2),
 761                    Point::new(0, 4)..Point::new(1, 0),
 762                    Point::new(1, 2)..Point::new(3, 2),
 763                    Point::new(3, 1)..Point::new(4, 1),
 764                ],
 765                app.as_ref(),
 766            )
 767            .unwrap();
 768            assert_eq!(map.text(app.as_ref()), "aa…eeeee");
 769        })
 770    }
 771
 772    #[test]
 773    fn test_merging_folds_via_edit() {
 774        App::test((), |app| {
 775            let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
 776            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 777
 778            map.fold(
 779                vec![
 780                    Point::new(0, 2)..Point::new(2, 2),
 781                    Point::new(3, 1)..Point::new(4, 1),
 782                ],
 783                app.as_ref(),
 784            )
 785            .unwrap();
 786            assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee");
 787
 788            buffer.update(app, |buffer, ctx| {
 789                buffer
 790                    .edit(Some(Point::new(2, 2)..Point::new(3, 1)), "", Some(ctx))
 791                    .unwrap();
 792            });
 793            assert_eq!(map.text(app.as_ref()), "aa…eeeee");
 794        });
 795    }
 796
 797    #[test]
 798    fn test_folds_in_range() {
 799        App::test((), |app| {
 800            let buffer = app.add_model(|_| Buffer::new(0, sample_text(5, 6)));
 801            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 802            let buffer = buffer.read(app);
 803
 804            map.fold(
 805                vec![
 806                    Point::new(0, 2)..Point::new(2, 2),
 807                    Point::new(0, 4)..Point::new(1, 0),
 808                    Point::new(1, 2)..Point::new(3, 2),
 809                    Point::new(3, 1)..Point::new(4, 1),
 810                ],
 811                app.as_ref(),
 812            )
 813            .unwrap();
 814            let fold_ranges = map
 815                .folds_in_range(Point::new(1, 0)..Point::new(1, 3), app.as_ref())
 816                .unwrap()
 817                .map(|fold| {
 818                    fold.start.to_point(buffer).unwrap()..fold.end.to_point(buffer).unwrap()
 819                })
 820                .collect::<Vec<_>>();
 821            assert_eq!(
 822                fold_ranges,
 823                vec![
 824                    Point::new(0, 2)..Point::new(2, 2),
 825                    Point::new(0, 4)..Point::new(1, 0),
 826                    Point::new(1, 2)..Point::new(3, 2)
 827                ]
 828            );
 829        });
 830    }
 831
 832    #[test]
 833    fn test_random_folds() {
 834        use crate::editor::ToPoint;
 835        use crate::util::RandomCharIter;
 836        use rand::prelude::*;
 837        use std::env;
 838
 839        let iterations = env::var("ITERATIONS")
 840            .map(|i| i.parse().expect("invalid `ITERATIONS` variable"))
 841            .unwrap_or(100);
 842        let operations = env::var("OPERATIONS")
 843            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 844            .unwrap_or(10);
 845        let seed_range = if let Ok(seed) = env::var("SEED") {
 846            let seed = seed.parse().expect("invalid `SEED` variable");
 847            seed..seed + 1
 848        } else {
 849            0..iterations
 850        };
 851
 852        for seed in seed_range {
 853            dbg!(seed);
 854            let mut rng = StdRng::seed_from_u64(seed);
 855
 856            App::test((), |app| {
 857                let buffer = app.add_model(|_| {
 858                    let len = rng.gen_range(0..10);
 859                    let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
 860                    Buffer::new(0, text)
 861                });
 862                let mut map = FoldMap::new(buffer.clone(), app.as_ref());
 863
 864                for _ in 0..operations {
 865                    log::info!("text: {:?}", buffer.read(app).text());
 866                    if rng.gen() {
 867                        let buffer = buffer.read(app);
 868
 869                        let fold_count = rng.gen_range(1..=5);
 870                        let mut fold_ranges: Vec<Range<usize>> = Vec::new();
 871                        for _ in 0..fold_count {
 872                            let end = rng.gen_range(0..buffer.len() + 1);
 873                            let start = rng.gen_range(0..end + 1);
 874                            fold_ranges.push(start..end);
 875                        }
 876                        log::info!("folding {:?}", fold_ranges);
 877                        map.fold(fold_ranges.clone(), app.as_ref()).unwrap();
 878                    } else {
 879                        let edits = buffer.update(app, |buffer, ctx| {
 880                            let start_version = buffer.version.clone();
 881                            let edit_count = rng.gen_range(1..=5);
 882                            buffer.randomly_edit(&mut rng, edit_count, Some(ctx));
 883                            buffer.edits_since(start_version).collect::<Vec<_>>()
 884                        });
 885                        log::info!("editing {:?}", edits);
 886                    }
 887                    map.check_invariants(app.as_ref());
 888
 889                    let buffer = map.buffer.read(app);
 890                    let mut expected_text = buffer.text();
 891                    let mut expected_buffer_rows = Vec::new();
 892                    let mut next_row = buffer.max_point().row;
 893                    for fold_range in map.merged_fold_ranges(app.as_ref()).into_iter().rev() {
 894                        let fold_start = buffer.point_for_offset(fold_range.start).unwrap();
 895                        let fold_end = buffer.point_for_offset(fold_range.end).unwrap();
 896                        expected_buffer_rows.extend((fold_end.row + 1..=next_row).rev());
 897                        next_row = fold_start.row;
 898
 899                        expected_text.replace_range(fold_range.start..fold_range.end, "");
 900                    }
 901                    expected_buffer_rows.extend((0..=next_row).rev());
 902                    expected_buffer_rows.reverse();
 903
 904                    assert_eq!(map.text(app.as_ref()), expected_text);
 905
 906                    for (display_row, line) in expected_text.lines().enumerate() {
 907                        let line_len = map.line_len(display_row as u32, app.as_ref()).unwrap();
 908                        assert_eq!(line_len, line.chars().count() as u32);
 909                    }
 910
 911                    let mut display_point = DisplayPoint::new(0, 0);
 912                    let mut display_offset = DisplayOffset(0);
 913                    for c in expected_text.chars() {
 914                        let buffer_point = map.to_buffer_point(display_point, app.as_ref());
 915                        let buffer_offset = buffer_point.to_offset(buffer).unwrap();
 916                        assert_eq!(
 917                            map.to_display_point(buffer_point, app.as_ref()),
 918                            display_point
 919                        );
 920                        assert_eq!(
 921                            map.to_buffer_offset(display_point, app.as_ref()).unwrap(),
 922                            buffer_offset
 923                        );
 924                        assert_eq!(
 925                            map.to_display_offset(display_point, app.as_ref()).unwrap(),
 926                            display_offset
 927                        );
 928
 929                        if c == '\n' {
 930                            *display_point.row_mut() += 1;
 931                            *display_point.column_mut() = 0;
 932                        } else {
 933                            *display_point.column_mut() += 1;
 934                        }
 935                        display_offset.0 += 1;
 936                    }
 937
 938                    for _ in 0..5 {
 939                        let row = rng.gen_range(0..=map.max_point(app.as_ref()).row());
 940                        let column = rng.gen_range(0..=map.line_len(row, app.as_ref()).unwrap());
 941                        let point = DisplayPoint::new(row, column);
 942                        let offset = map.to_display_offset(point, app.as_ref()).unwrap().0;
 943                        let len = rng.gen_range(0..=map.len(app.as_ref()) - offset);
 944                        assert_eq!(
 945                            map.snapshot(app.as_ref())
 946                                .chars_at(point, app.as_ref())
 947                                .unwrap()
 948                                .take(len)
 949                                .collect::<String>(),
 950                            expected_text
 951                                .chars()
 952                                .skip(offset)
 953                                .take(len)
 954                                .collect::<String>()
 955                        );
 956                    }
 957
 958                    for (idx, buffer_row) in expected_buffer_rows.iter().enumerate() {
 959                        let display_row = map
 960                            .to_display_point(Point::new(*buffer_row, 0), app.as_ref())
 961                            .row();
 962                        assert_eq!(
 963                            map.snapshot(app.as_ref())
 964                                .buffer_rows(display_row)
 965                                .unwrap()
 966                                .collect::<Vec<_>>(),
 967                            expected_buffer_rows[idx..],
 968                        );
 969                    }
 970
 971                    for fold_range in map.merged_fold_ranges(app.as_ref()) {
 972                        let display_point = map.to_display_point(
 973                            fold_range.start.to_point(buffer).unwrap(),
 974                            app.as_ref(),
 975                        );
 976                        assert!(map.is_line_folded(display_point.row(), app.as_ref()));
 977                    }
 978
 979                    for _ in 0..5 {
 980                        let end = rng.gen_range(0..=buffer.len());
 981                        let start = rng.gen_range(0..=end);
 982                        let expected_folds = map
 983                            .folds
 984                            .items()
 985                            .into_iter()
 986                            .filter(|fold| {
 987                                let fold_start = fold.0.start.to_offset(buffer).unwrap();
 988                                let fold_end = fold.0.end.to_offset(buffer).unwrap();
 989                                start <= fold_end && end >= fold_start
 990                            })
 991                            .map(|fold| fold.0)
 992                            .collect::<Vec<_>>();
 993
 994                        assert_eq!(
 995                            map.folds_in_range(start..end, app.as_ref())
 996                                .unwrap()
 997                                .cloned()
 998                                .collect::<Vec<_>>(),
 999                            expected_folds
1000                        );
1001                    }
1002                }
1003            });
1004        }
1005    }
1006
1007    #[test]
1008    fn test_buffer_rows() {
1009        App::test((), |app| {
1010            let text = sample_text(6, 6) + "\n";
1011            let buffer = app.add_model(|_| Buffer::new(0, text));
1012
1013            let mut map = FoldMap::new(buffer.clone(), app.as_ref());
1014
1015            map.fold(
1016                vec![
1017                    Point::new(0, 2)..Point::new(2, 2),
1018                    Point::new(3, 1)..Point::new(4, 1),
1019                ],
1020                app.as_ref(),
1021            )
1022            .unwrap();
1023
1024            assert_eq!(map.text(app.as_ref()), "aa…cccc\nd…eeeee\nffffff\n");
1025            assert_eq!(
1026                map.snapshot(app.as_ref())
1027                    .buffer_rows(0)
1028                    .unwrap()
1029                    .collect::<Vec<_>>(),
1030                vec![0, 3, 5, 6]
1031            );
1032            assert_eq!(
1033                map.snapshot(app.as_ref())
1034                    .buffer_rows(3)
1035                    .unwrap()
1036                    .collect::<Vec<_>>(),
1037                vec![6]
1038            );
1039        });
1040    }
1041
1042    impl FoldMap {
1043        fn text(&self, app: &AppContext) -> String {
1044            self.snapshot(app)
1045                .chars_at(DisplayPoint(Point::zero()), app)
1046                .unwrap()
1047                .collect()
1048        }
1049
1050        fn merged_fold_ranges(&self, app: &AppContext) -> Vec<Range<usize>> {
1051            let buffer = self.buffer.read(app);
1052            let mut folds = self.folds.items();
1053            // Ensure sorting doesn't change how folds get merged and displayed.
1054            folds.sort_by(|a, b| a.0.cmp(&b.0, buffer).unwrap());
1055            let mut fold_ranges = folds
1056                .iter()
1057                .map(|fold| {
1058                    fold.0.start.to_offset(buffer).unwrap()..fold.0.end.to_offset(buffer).unwrap()
1059                })
1060                .peekable();
1061
1062            let mut merged_ranges = Vec::new();
1063            while let Some(mut fold_range) = fold_ranges.next() {
1064                while let Some(next_range) = fold_ranges.peek() {
1065                    if fold_range.end >= next_range.start {
1066                        if next_range.end > fold_range.end {
1067                            fold_range.end = next_range.end;
1068                        }
1069                        fold_ranges.next();
1070                    } else {
1071                        break;
1072                    }
1073                }
1074                if fold_range.end > fold_range.start {
1075                    merged_ranges.push(fold_range);
1076                }
1077            }
1078            merged_ranges
1079        }
1080
1081        fn check_invariants(&self, ctx: &AppContext) {
1082            let transforms = self.sync(ctx);
1083            let buffer = self.buffer.read(ctx);
1084            assert_eq!(
1085                transforms.summary().buffer.chars,
1086                buffer.len(),
1087                "transform tree does not match buffer's length"
1088            );
1089        }
1090    }
1091}