wrap_map.rs

   1use super::{
   2    fold_map::FoldBufferRows,
   3    tab_map::{self, TabEdit, TabPoint, TabSnapshot},
   4    Highlights,
   5};
   6use crate::MultiBufferSnapshot;
   7use gpui::{AppContext, Context, Font, LineWrapper, Model, ModelContext, Pixels, Task};
   8use language::{Chunk, Point};
   9use lazy_static::lazy_static;
  10use smol::future::yield_now;
  11use std::{cmp, collections::VecDeque, mem, ops::Range, time::Duration};
  12use sum_tree::{Bias, Cursor, SumTree};
  13use text::Patch;
  14use util::ResultExt;
  15
  16pub use super::tab_map::TextSummary;
  17pub type WrapEdit = text::Edit<u32>;
  18
  19pub struct WrapMap {
  20    snapshot: WrapSnapshot,
  21    pending_edits: VecDeque<(TabSnapshot, Vec<TabEdit>)>,
  22    interpolated_edits: Patch<u32>,
  23    edits_since_sync: Patch<u32>,
  24    wrap_width: Option<Pixels>,
  25    background_task: Option<Task<()>>,
  26    font_with_size: (Font, Pixels),
  27}
  28
  29#[derive(Clone)]
  30pub struct WrapSnapshot {
  31    tab_snapshot: TabSnapshot,
  32    transforms: SumTree<Transform>,
  33    interpolated: bool,
  34}
  35
  36#[derive(Clone, Debug, Default, Eq, PartialEq)]
  37struct Transform {
  38    summary: TransformSummary,
  39    display_text: Option<&'static str>,
  40}
  41
  42#[derive(Clone, Debug, Default, Eq, PartialEq)]
  43struct TransformSummary {
  44    input: TextSummary,
  45    output: TextSummary,
  46}
  47
  48#[derive(Copy, Clone, Debug, Default, Eq, Ord, PartialOrd, PartialEq)]
  49pub struct WrapPoint(pub Point);
  50
  51pub struct WrapChunks<'a> {
  52    input_chunks: tab_map::TabChunks<'a>,
  53    input_chunk: Chunk<'a>,
  54    output_position: WrapPoint,
  55    max_output_row: u32,
  56    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
  57}
  58
  59#[derive(Clone)]
  60pub struct WrapBufferRows<'a> {
  61    input_buffer_rows: FoldBufferRows<'a>,
  62    input_buffer_row: Option<u32>,
  63    output_row: u32,
  64    soft_wrapped: bool,
  65    max_output_row: u32,
  66    transforms: Cursor<'a, Transform, (WrapPoint, TabPoint)>,
  67}
  68
  69impl WrapMap {
  70    pub fn new(
  71        tab_snapshot: TabSnapshot,
  72        font: Font,
  73        font_size: Pixels,
  74        wrap_width: Option<Pixels>,
  75        cx: &mut AppContext,
  76    ) -> (Model<Self>, WrapSnapshot) {
  77        let handle = cx.build_model(|cx| {
  78            let mut this = Self {
  79                font_with_size: (font, font_size),
  80                wrap_width: None,
  81                pending_edits: Default::default(),
  82                interpolated_edits: Default::default(),
  83                edits_since_sync: Default::default(),
  84                snapshot: WrapSnapshot::new(tab_snapshot),
  85                background_task: None,
  86            };
  87            this.set_wrap_width(wrap_width, cx);
  88            mem::take(&mut this.edits_since_sync);
  89            this
  90        });
  91        let snapshot = handle.read(cx).snapshot.clone();
  92        (handle, snapshot)
  93    }
  94
  95    #[cfg(test)]
  96    pub fn is_rewrapping(&self) -> bool {
  97        self.background_task.is_some()
  98    }
  99
 100    pub fn sync(
 101        &mut self,
 102        tab_snapshot: TabSnapshot,
 103        edits: Vec<TabEdit>,
 104        cx: &mut ModelContext<Self>,
 105    ) -> (WrapSnapshot, Patch<u32>) {
 106        if self.wrap_width.is_some() {
 107            self.pending_edits.push_back((tab_snapshot, edits));
 108            self.flush_edits(cx);
 109        } else {
 110            self.edits_since_sync = self
 111                .edits_since_sync
 112                .compose(&self.snapshot.interpolate(tab_snapshot, &edits));
 113            self.snapshot.interpolated = false;
 114        }
 115
 116        (self.snapshot.clone(), mem::take(&mut self.edits_since_sync))
 117    }
 118
 119    pub fn set_font_with_size(
 120        &mut self,
 121        font: Font,
 122        font_size: Pixels,
 123        cx: &mut ModelContext<Self>,
 124    ) -> bool {
 125        let font_with_size = (font, font_size);
 126
 127        if font_with_size != self.font_with_size {
 128            self.font_with_size = font_with_size;
 129            self.rewrap(cx);
 130            true
 131        } else {
 132            false
 133        }
 134    }
 135
 136    pub fn set_wrap_width(
 137        &mut self,
 138        wrap_width: Option<Pixels>,
 139        cx: &mut ModelContext<Self>,
 140    ) -> bool {
 141        if wrap_width == self.wrap_width {
 142            return false;
 143        }
 144
 145        self.wrap_width = wrap_width;
 146        self.rewrap(cx);
 147        true
 148    }
 149
 150    fn rewrap(&mut self, cx: &mut ModelContext<Self>) {
 151        self.background_task.take();
 152        self.interpolated_edits.clear();
 153        self.pending_edits.clear();
 154
 155        if let Some(wrap_width) = self.wrap_width {
 156            let mut new_snapshot = self.snapshot.clone();
 157            let mut edits = Patch::default();
 158            let text_system = cx.text_system().clone();
 159            let (font, font_size) = self.font_with_size.clone();
 160            let task = cx.background_executor().spawn(async move {
 161                if let Some(mut line_wrapper) = text_system.line_wrapper(font, font_size).log_err()
 162                {
 163                    let tab_snapshot = new_snapshot.tab_snapshot.clone();
 164                    let range = TabPoint::zero()..tab_snapshot.max_point();
 165                    edits = new_snapshot
 166                        .update(
 167                            tab_snapshot,
 168                            &[TabEdit {
 169                                old: range.clone(),
 170                                new: range.clone(),
 171                            }],
 172                            wrap_width,
 173                            &mut line_wrapper,
 174                        )
 175                        .await;
 176                }
 177                (new_snapshot, edits)
 178            });
 179
 180            match cx
 181                .background_executor()
 182                .block_with_timeout(Duration::from_millis(5), task)
 183            {
 184                Ok((snapshot, edits)) => {
 185                    self.snapshot = snapshot;
 186                    self.edits_since_sync = self.edits_since_sync.compose(&edits);
 187                }
 188                Err(wrap_task) => {
 189                    self.background_task = Some(cx.spawn(|this, mut cx| async move {
 190                        let (snapshot, edits) = wrap_task.await;
 191                        this.update(&mut cx, |this, cx| {
 192                            this.snapshot = snapshot;
 193                            this.edits_since_sync = this
 194                                .edits_since_sync
 195                                .compose(mem::take(&mut this.interpolated_edits).invert())
 196                                .compose(&edits);
 197                            this.background_task = None;
 198                            this.flush_edits(cx);
 199                            cx.notify();
 200                        });
 201                    }));
 202                }
 203            }
 204        } else {
 205            let old_rows = self.snapshot.transforms.summary().output.lines.row + 1;
 206            self.snapshot.transforms = SumTree::new();
 207            let summary = self.snapshot.tab_snapshot.text_summary();
 208            if !summary.lines.is_zero() {
 209                self.snapshot
 210                    .transforms
 211                    .push(Transform::isomorphic(summary), &());
 212            }
 213            let new_rows = self.snapshot.transforms.summary().output.lines.row + 1;
 214            self.snapshot.interpolated = false;
 215            self.edits_since_sync = self.edits_since_sync.compose(&Patch::new(vec![WrapEdit {
 216                old: 0..old_rows,
 217                new: 0..new_rows,
 218            }]));
 219        }
 220    }
 221
 222    fn flush_edits(&mut self, cx: &mut ModelContext<Self>) {
 223        if !self.snapshot.interpolated {
 224            let mut to_remove_len = 0;
 225            for (tab_snapshot, _) in &self.pending_edits {
 226                if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
 227                    to_remove_len += 1;
 228                } else {
 229                    break;
 230                }
 231            }
 232            self.pending_edits.drain(..to_remove_len);
 233        }
 234
 235        if self.pending_edits.is_empty() {
 236            return;
 237        }
 238
 239        if let Some(wrap_width) = self.wrap_width {
 240            if self.background_task.is_none() {
 241                let pending_edits = self.pending_edits.clone();
 242                let mut snapshot = self.snapshot.clone();
 243                let text_system = cx.text_system().clone();
 244                let (font, font_size) = self.font_with_size.clone();
 245                let update_task = cx.background_executor().spawn(async move {
 246                    let mut edits = Patch::default();
 247                    if let Some(mut line_wrapper) =
 248                        text_system.line_wrapper(font, font_size).log_err()
 249                    {
 250                        for (tab_snapshot, tab_edits) in pending_edits {
 251                            let wrap_edits = snapshot
 252                                .update(tab_snapshot, &tab_edits, wrap_width, &mut line_wrapper)
 253                                .await;
 254                            edits = edits.compose(&wrap_edits);
 255                        }
 256                    }
 257                    (snapshot, edits)
 258                });
 259
 260                match cx
 261                    .background_executor()
 262                    .block_with_timeout(Duration::from_millis(1), update_task)
 263                {
 264                    Ok((snapshot, output_edits)) => {
 265                        self.snapshot = snapshot;
 266                        self.edits_since_sync = self.edits_since_sync.compose(&output_edits);
 267                    }
 268                    Err(update_task) => {
 269                        self.background_task = Some(cx.spawn(|this, mut cx| async move {
 270                            let (snapshot, edits) = update_task.await;
 271                            this.update(&mut cx, |this, cx| {
 272                                this.snapshot = snapshot;
 273                                this.edits_since_sync = this
 274                                    .edits_since_sync
 275                                    .compose(mem::take(&mut this.interpolated_edits).invert())
 276                                    .compose(&edits);
 277                                this.background_task = None;
 278                                this.flush_edits(cx);
 279                                cx.notify();
 280                            });
 281                        }));
 282                    }
 283                }
 284            }
 285        }
 286
 287        let was_interpolated = self.snapshot.interpolated;
 288        let mut to_remove_len = 0;
 289        for (tab_snapshot, edits) in &self.pending_edits {
 290            if tab_snapshot.version <= self.snapshot.tab_snapshot.version {
 291                to_remove_len += 1;
 292            } else {
 293                let interpolated_edits = self.snapshot.interpolate(tab_snapshot.clone(), edits);
 294                self.edits_since_sync = self.edits_since_sync.compose(&interpolated_edits);
 295                self.interpolated_edits = self.interpolated_edits.compose(&interpolated_edits);
 296            }
 297        }
 298
 299        if !was_interpolated {
 300            self.pending_edits.drain(..to_remove_len);
 301        }
 302    }
 303}
 304
 305impl WrapSnapshot {
 306    fn new(tab_snapshot: TabSnapshot) -> Self {
 307        let mut transforms = SumTree::new();
 308        let extent = tab_snapshot.text_summary();
 309        if !extent.lines.is_zero() {
 310            transforms.push(Transform::isomorphic(extent), &());
 311        }
 312        Self {
 313            transforms,
 314            tab_snapshot,
 315            interpolated: true,
 316        }
 317    }
 318
 319    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
 320        self.tab_snapshot.buffer_snapshot()
 321    }
 322
 323    fn interpolate(&mut self, new_tab_snapshot: TabSnapshot, tab_edits: &[TabEdit]) -> Patch<u32> {
 324        let mut new_transforms;
 325        if tab_edits.is_empty() {
 326            new_transforms = self.transforms.clone();
 327        } else {
 328            let mut old_cursor = self.transforms.cursor::<TabPoint>();
 329
 330            let mut tab_edits_iter = tab_edits.iter().peekable();
 331            new_transforms =
 332                old_cursor.slice(&tab_edits_iter.peek().unwrap().old.start, Bias::Right, &());
 333
 334            while let Some(edit) = tab_edits_iter.next() {
 335                if edit.new.start > TabPoint::from(new_transforms.summary().input.lines) {
 336                    let summary = new_tab_snapshot.text_summary_for_range(
 337                        TabPoint::from(new_transforms.summary().input.lines)..edit.new.start,
 338                    );
 339                    new_transforms.push_or_extend(Transform::isomorphic(summary));
 340                }
 341
 342                if !edit.new.is_empty() {
 343                    new_transforms.push_or_extend(Transform::isomorphic(
 344                        new_tab_snapshot.text_summary_for_range(edit.new.clone()),
 345                    ));
 346                }
 347
 348                old_cursor.seek_forward(&edit.old.end, Bias::Right, &());
 349                if let Some(next_edit) = tab_edits_iter.peek() {
 350                    if next_edit.old.start > old_cursor.end(&()) {
 351                        if old_cursor.end(&()) > edit.old.end {
 352                            let summary = self
 353                                .tab_snapshot
 354                                .text_summary_for_range(edit.old.end..old_cursor.end(&()));
 355                            new_transforms.push_or_extend(Transform::isomorphic(summary));
 356                        }
 357
 358                        old_cursor.next(&());
 359                        new_transforms.append(
 360                            old_cursor.slice(&next_edit.old.start, Bias::Right, &()),
 361                            &(),
 362                        );
 363                    }
 364                } else {
 365                    if old_cursor.end(&()) > edit.old.end {
 366                        let summary = self
 367                            .tab_snapshot
 368                            .text_summary_for_range(edit.old.end..old_cursor.end(&()));
 369                        new_transforms.push_or_extend(Transform::isomorphic(summary));
 370                    }
 371                    old_cursor.next(&());
 372                    new_transforms.append(old_cursor.suffix(&()), &());
 373                }
 374            }
 375        }
 376
 377        let old_snapshot = mem::replace(
 378            self,
 379            WrapSnapshot {
 380                tab_snapshot: new_tab_snapshot,
 381                transforms: new_transforms,
 382                interpolated: true,
 383            },
 384        );
 385        self.check_invariants();
 386        old_snapshot.compute_edits(tab_edits, self)
 387    }
 388
 389    async fn update(
 390        &mut self,
 391        new_tab_snapshot: TabSnapshot,
 392        tab_edits: &[TabEdit],
 393        wrap_width: Pixels,
 394        line_wrapper: &mut LineWrapper,
 395    ) -> Patch<u32> {
 396        #[derive(Debug)]
 397        struct RowEdit {
 398            old_rows: Range<u32>,
 399            new_rows: Range<u32>,
 400        }
 401
 402        let mut tab_edits_iter = tab_edits.iter().peekable();
 403        let mut row_edits = Vec::new();
 404        while let Some(edit) = tab_edits_iter.next() {
 405            let mut row_edit = RowEdit {
 406                old_rows: edit.old.start.row()..edit.old.end.row() + 1,
 407                new_rows: edit.new.start.row()..edit.new.end.row() + 1,
 408            };
 409
 410            while let Some(next_edit) = tab_edits_iter.peek() {
 411                if next_edit.old.start.row() <= row_edit.old_rows.end {
 412                    row_edit.old_rows.end = next_edit.old.end.row() + 1;
 413                    row_edit.new_rows.end = next_edit.new.end.row() + 1;
 414                    tab_edits_iter.next();
 415                } else {
 416                    break;
 417                }
 418            }
 419
 420            row_edits.push(row_edit);
 421        }
 422
 423        let mut new_transforms;
 424        if row_edits.is_empty() {
 425            new_transforms = self.transforms.clone();
 426        } else {
 427            let mut row_edits = row_edits.into_iter().peekable();
 428            let mut old_cursor = self.transforms.cursor::<TabPoint>();
 429
 430            new_transforms = old_cursor.slice(
 431                &TabPoint::new(row_edits.peek().unwrap().old_rows.start, 0),
 432                Bias::Right,
 433                &(),
 434            );
 435
 436            while let Some(edit) = row_edits.next() {
 437                if edit.new_rows.start > new_transforms.summary().input.lines.row {
 438                    let summary = new_tab_snapshot.text_summary_for_range(
 439                        TabPoint(new_transforms.summary().input.lines)
 440                            ..TabPoint::new(edit.new_rows.start, 0),
 441                    );
 442                    new_transforms.push_or_extend(Transform::isomorphic(summary));
 443                }
 444
 445                let mut line = String::new();
 446                let mut remaining = None;
 447                let mut chunks = new_tab_snapshot.chunks(
 448                    TabPoint::new(edit.new_rows.start, 0)..new_tab_snapshot.max_point(),
 449                    false,
 450                    Highlights::default(),
 451                );
 452                let mut edit_transforms = Vec::<Transform>::new();
 453                for _ in edit.new_rows.start..edit.new_rows.end {
 454                    while let Some(chunk) =
 455                        remaining.take().or_else(|| chunks.next().map(|c| c.text))
 456                    {
 457                        if let Some(ix) = chunk.find('\n') {
 458                            line.push_str(&chunk[..ix + 1]);
 459                            remaining = Some(&chunk[ix + 1..]);
 460                            break;
 461                        } else {
 462                            line.push_str(chunk)
 463                        }
 464                    }
 465
 466                    if line.is_empty() {
 467                        break;
 468                    }
 469
 470                    let mut prev_boundary_ix = 0;
 471                    for boundary in line_wrapper.wrap_line(&line, wrap_width) {
 472                        let wrapped = &line[prev_boundary_ix..boundary.ix];
 473                        push_isomorphic(&mut edit_transforms, TextSummary::from(wrapped));
 474                        edit_transforms.push(Transform::wrap(boundary.next_indent));
 475                        prev_boundary_ix = boundary.ix;
 476                    }
 477
 478                    if prev_boundary_ix < line.len() {
 479                        push_isomorphic(
 480                            &mut edit_transforms,
 481                            TextSummary::from(&line[prev_boundary_ix..]),
 482                        );
 483                    }
 484
 485                    line.clear();
 486                    yield_now().await;
 487                }
 488
 489                let mut edit_transforms = edit_transforms.into_iter();
 490                if let Some(transform) = edit_transforms.next() {
 491                    new_transforms.push_or_extend(transform);
 492                }
 493                new_transforms.extend(edit_transforms, &());
 494
 495                old_cursor.seek_forward(&TabPoint::new(edit.old_rows.end, 0), Bias::Right, &());
 496                if let Some(next_edit) = row_edits.peek() {
 497                    if next_edit.old_rows.start > old_cursor.end(&()).row() {
 498                        if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
 499                            let summary = self.tab_snapshot.text_summary_for_range(
 500                                TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
 501                            );
 502                            new_transforms.push_or_extend(Transform::isomorphic(summary));
 503                        }
 504                        old_cursor.next(&());
 505                        new_transforms.append(
 506                            old_cursor.slice(
 507                                &TabPoint::new(next_edit.old_rows.start, 0),
 508                                Bias::Right,
 509                                &(),
 510                            ),
 511                            &(),
 512                        );
 513                    }
 514                } else {
 515                    if old_cursor.end(&()) > TabPoint::new(edit.old_rows.end, 0) {
 516                        let summary = self.tab_snapshot.text_summary_for_range(
 517                            TabPoint::new(edit.old_rows.end, 0)..old_cursor.end(&()),
 518                        );
 519                        new_transforms.push_or_extend(Transform::isomorphic(summary));
 520                    }
 521                    old_cursor.next(&());
 522                    new_transforms.append(old_cursor.suffix(&()), &());
 523                }
 524            }
 525        }
 526
 527        let old_snapshot = mem::replace(
 528            self,
 529            WrapSnapshot {
 530                tab_snapshot: new_tab_snapshot,
 531                transforms: new_transforms,
 532                interpolated: false,
 533            },
 534        );
 535        self.check_invariants();
 536        old_snapshot.compute_edits(tab_edits, self)
 537    }
 538
 539    fn compute_edits(&self, tab_edits: &[TabEdit], new_snapshot: &WrapSnapshot) -> Patch<u32> {
 540        let mut wrap_edits = Vec::new();
 541        let mut old_cursor = self.transforms.cursor::<TransformSummary>();
 542        let mut new_cursor = new_snapshot.transforms.cursor::<TransformSummary>();
 543        for mut tab_edit in tab_edits.iter().cloned() {
 544            tab_edit.old.start.0.column = 0;
 545            tab_edit.old.end.0 += Point::new(1, 0);
 546            tab_edit.new.start.0.column = 0;
 547            tab_edit.new.end.0 += Point::new(1, 0);
 548
 549            old_cursor.seek(&tab_edit.old.start, Bias::Right, &());
 550            let mut old_start = old_cursor.start().output.lines;
 551            old_start += tab_edit.old.start.0 - old_cursor.start().input.lines;
 552
 553            old_cursor.seek(&tab_edit.old.end, Bias::Right, &());
 554            let mut old_end = old_cursor.start().output.lines;
 555            old_end += tab_edit.old.end.0 - old_cursor.start().input.lines;
 556
 557            new_cursor.seek(&tab_edit.new.start, Bias::Right, &());
 558            let mut new_start = new_cursor.start().output.lines;
 559            new_start += tab_edit.new.start.0 - new_cursor.start().input.lines;
 560
 561            new_cursor.seek(&tab_edit.new.end, Bias::Right, &());
 562            let mut new_end = new_cursor.start().output.lines;
 563            new_end += tab_edit.new.end.0 - new_cursor.start().input.lines;
 564
 565            wrap_edits.push(WrapEdit {
 566                old: old_start.row..old_end.row,
 567                new: new_start.row..new_end.row,
 568            });
 569        }
 570
 571        consolidate_wrap_edits(&mut wrap_edits);
 572        Patch::new(wrap_edits)
 573    }
 574
 575    pub fn chunks<'a>(
 576        &'a self,
 577        rows: Range<u32>,
 578        language_aware: bool,
 579        highlights: Highlights<'a>,
 580    ) -> WrapChunks<'a> {
 581        let output_start = WrapPoint::new(rows.start, 0);
 582        let output_end = WrapPoint::new(rows.end, 0);
 583        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 584        transforms.seek(&output_start, Bias::Right, &());
 585        let mut input_start = TabPoint(transforms.start().1 .0);
 586        if transforms.item().map_or(false, |t| t.is_isomorphic()) {
 587            input_start.0 += output_start.0 - transforms.start().0 .0;
 588        }
 589        let input_end = self
 590            .to_tab_point(output_end)
 591            .min(self.tab_snapshot.max_point());
 592        WrapChunks {
 593            input_chunks: self.tab_snapshot.chunks(
 594                input_start..input_end,
 595                language_aware,
 596                highlights,
 597            ),
 598            input_chunk: Default::default(),
 599            output_position: output_start,
 600            max_output_row: rows.end,
 601            transforms,
 602        }
 603    }
 604
 605    pub fn max_point(&self) -> WrapPoint {
 606        WrapPoint(self.transforms.summary().output.lines)
 607    }
 608
 609    pub fn line_len(&self, row: u32) -> u32 {
 610        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 611        cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Left, &());
 612        if cursor
 613            .item()
 614            .map_or(false, |transform| transform.is_isomorphic())
 615        {
 616            let overshoot = row - cursor.start().0.row();
 617            let tab_row = cursor.start().1.row() + overshoot;
 618            let tab_line_len = self.tab_snapshot.line_len(tab_row);
 619            if overshoot == 0 {
 620                cursor.start().0.column() + (tab_line_len - cursor.start().1.column())
 621            } else {
 622                tab_line_len
 623            }
 624        } else {
 625            cursor.start().0.column()
 626        }
 627    }
 628
 629    pub fn soft_wrap_indent(&self, row: u32) -> Option<u32> {
 630        let mut cursor = self.transforms.cursor::<WrapPoint>();
 631        cursor.seek(&WrapPoint::new(row + 1, 0), Bias::Right, &());
 632        cursor.item().and_then(|transform| {
 633            if transform.is_isomorphic() {
 634                None
 635            } else {
 636                Some(transform.summary.output.lines.column)
 637            }
 638        })
 639    }
 640
 641    pub fn longest_row(&self) -> u32 {
 642        self.transforms.summary().output.longest_row
 643    }
 644
 645    pub fn buffer_rows(&self, start_row: u32) -> WrapBufferRows {
 646        let mut transforms = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 647        transforms.seek(&WrapPoint::new(start_row, 0), Bias::Left, &());
 648        let mut input_row = transforms.start().1.row();
 649        if transforms.item().map_or(false, |t| t.is_isomorphic()) {
 650            input_row += start_row - transforms.start().0.row();
 651        }
 652        let soft_wrapped = transforms.item().map_or(false, |t| !t.is_isomorphic());
 653        let mut input_buffer_rows = self.tab_snapshot.buffer_rows(input_row);
 654        let input_buffer_row = input_buffer_rows.next().unwrap();
 655        WrapBufferRows {
 656            transforms,
 657            input_buffer_row,
 658            input_buffer_rows,
 659            output_row: start_row,
 660            soft_wrapped,
 661            max_output_row: self.max_point().row(),
 662        }
 663    }
 664
 665    pub fn to_tab_point(&self, point: WrapPoint) -> TabPoint {
 666        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 667        cursor.seek(&point, Bias::Right, &());
 668        let mut tab_point = cursor.start().1 .0;
 669        if cursor.item().map_or(false, |t| t.is_isomorphic()) {
 670            tab_point += point.0 - cursor.start().0 .0;
 671        }
 672        TabPoint(tab_point)
 673    }
 674
 675    pub fn to_point(&self, point: WrapPoint, bias: Bias) -> Point {
 676        self.tab_snapshot.to_point(self.to_tab_point(point), bias)
 677    }
 678
 679    pub fn make_wrap_point(&self, point: Point, bias: Bias) -> WrapPoint {
 680        self.tab_point_to_wrap_point(self.tab_snapshot.make_tab_point(point, bias))
 681    }
 682
 683    pub fn tab_point_to_wrap_point(&self, point: TabPoint) -> WrapPoint {
 684        let mut cursor = self.transforms.cursor::<(TabPoint, WrapPoint)>();
 685        cursor.seek(&point, Bias::Right, &());
 686        WrapPoint(cursor.start().1 .0 + (point.0 - cursor.start().0 .0))
 687    }
 688
 689    pub fn clip_point(&self, mut point: WrapPoint, bias: Bias) -> WrapPoint {
 690        if bias == Bias::Left {
 691            let mut cursor = self.transforms.cursor::<WrapPoint>();
 692            cursor.seek(&point, Bias::Right, &());
 693            if cursor.item().map_or(false, |t| !t.is_isomorphic()) {
 694                point = *cursor.start();
 695                *point.column_mut() -= 1;
 696            }
 697        }
 698
 699        self.tab_point_to_wrap_point(self.tab_snapshot.clip_point(self.to_tab_point(point), bias))
 700    }
 701
 702    pub fn prev_row_boundary(&self, mut point: WrapPoint) -> u32 {
 703        if self.transforms.is_empty() {
 704            return 0;
 705        }
 706
 707        *point.column_mut() = 0;
 708
 709        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 710        cursor.seek(&point, Bias::Right, &());
 711        if cursor.item().is_none() {
 712            cursor.prev(&());
 713        }
 714
 715        while let Some(transform) = cursor.item() {
 716            if transform.is_isomorphic() && cursor.start().1.column() == 0 {
 717                return cmp::min(cursor.end(&()).0.row(), point.row());
 718            } else {
 719                cursor.prev(&());
 720            }
 721        }
 722
 723        unreachable!()
 724    }
 725
 726    pub fn next_row_boundary(&self, mut point: WrapPoint) -> Option<u32> {
 727        point.0 += Point::new(1, 0);
 728
 729        let mut cursor = self.transforms.cursor::<(WrapPoint, TabPoint)>();
 730        cursor.seek(&point, Bias::Right, &());
 731        while let Some(transform) = cursor.item() {
 732            if transform.is_isomorphic() && cursor.start().1.column() == 0 {
 733                return Some(cmp::max(cursor.start().0.row(), point.row()));
 734            } else {
 735                cursor.next(&());
 736            }
 737        }
 738
 739        None
 740    }
 741
 742    fn check_invariants(&self) {
 743        #[cfg(test)]
 744        {
 745            assert_eq!(
 746                TabPoint::from(self.transforms.summary().input.lines),
 747                self.tab_snapshot.max_point()
 748            );
 749
 750            {
 751                let mut transforms = self.transforms.cursor::<()>().peekable();
 752                while let Some(transform) = transforms.next() {
 753                    if let Some(next_transform) = transforms.peek() {
 754                        assert!(transform.is_isomorphic() != next_transform.is_isomorphic());
 755                    }
 756                }
 757            }
 758
 759            let text = language::Rope::from(self.text().as_str());
 760            let mut input_buffer_rows = self.tab_snapshot.buffer_rows(0);
 761            let mut expected_buffer_rows = Vec::new();
 762            let mut prev_tab_row = 0;
 763            for display_row in 0..=self.max_point().row() {
 764                let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
 765                if tab_point.row() == prev_tab_row && display_row != 0 {
 766                    expected_buffer_rows.push(None);
 767                } else {
 768                    expected_buffer_rows.push(input_buffer_rows.next().unwrap());
 769                }
 770
 771                prev_tab_row = tab_point.row();
 772                assert_eq!(self.line_len(display_row), text.line_len(display_row));
 773            }
 774
 775            for start_display_row in 0..expected_buffer_rows.len() {
 776                assert_eq!(
 777                    self.buffer_rows(start_display_row as u32)
 778                        .collect::<Vec<_>>(),
 779                    &expected_buffer_rows[start_display_row..],
 780                    "invalid buffer_rows({}..)",
 781                    start_display_row
 782                );
 783            }
 784        }
 785    }
 786}
 787
 788impl<'a> Iterator for WrapChunks<'a> {
 789    type Item = Chunk<'a>;
 790
 791    fn next(&mut self) -> Option<Self::Item> {
 792        if self.output_position.row() >= self.max_output_row {
 793            return None;
 794        }
 795
 796        let transform = self.transforms.item()?;
 797        if let Some(display_text) = transform.display_text {
 798            let mut start_ix = 0;
 799            let mut end_ix = display_text.len();
 800            let mut summary = transform.summary.output.lines;
 801
 802            if self.output_position > self.transforms.start().0 {
 803                // Exclude newline starting prior to the desired row.
 804                start_ix = 1;
 805                summary.row = 0;
 806            } else if self.output_position.row() + 1 >= self.max_output_row {
 807                // Exclude soft indentation ending after the desired row.
 808                end_ix = 1;
 809                summary.column = 0;
 810            }
 811
 812            self.output_position.0 += summary;
 813            self.transforms.next(&());
 814            return Some(Chunk {
 815                text: &display_text[start_ix..end_ix],
 816                ..self.input_chunk
 817            });
 818        }
 819
 820        if self.input_chunk.text.is_empty() {
 821            self.input_chunk = self.input_chunks.next().unwrap();
 822        }
 823
 824        let mut input_len = 0;
 825        let transform_end = self.transforms.end(&()).0;
 826        for c in self.input_chunk.text.chars() {
 827            let char_len = c.len_utf8();
 828            input_len += char_len;
 829            if c == '\n' {
 830                *self.output_position.row_mut() += 1;
 831                *self.output_position.column_mut() = 0;
 832            } else {
 833                *self.output_position.column_mut() += char_len as u32;
 834            }
 835
 836            if self.output_position >= transform_end {
 837                self.transforms.next(&());
 838                break;
 839            }
 840        }
 841
 842        let (prefix, suffix) = self.input_chunk.text.split_at(input_len);
 843        self.input_chunk.text = suffix;
 844        Some(Chunk {
 845            text: prefix,
 846            ..self.input_chunk
 847        })
 848    }
 849}
 850
 851impl<'a> Iterator for WrapBufferRows<'a> {
 852    type Item = Option<u32>;
 853
 854    fn next(&mut self) -> Option<Self::Item> {
 855        if self.output_row > self.max_output_row {
 856            return None;
 857        }
 858
 859        let buffer_row = self.input_buffer_row;
 860        let soft_wrapped = self.soft_wrapped;
 861
 862        self.output_row += 1;
 863        self.transforms
 864            .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &());
 865        if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
 866            self.input_buffer_row = self.input_buffer_rows.next().unwrap();
 867            self.soft_wrapped = false;
 868        } else {
 869            self.soft_wrapped = true;
 870        }
 871
 872        Some(if soft_wrapped { None } else { buffer_row })
 873    }
 874}
 875
 876impl Transform {
 877    fn isomorphic(summary: TextSummary) -> Self {
 878        #[cfg(test)]
 879        assert!(!summary.lines.is_zero());
 880
 881        Self {
 882            summary: TransformSummary {
 883                input: summary.clone(),
 884                output: summary,
 885            },
 886            display_text: None,
 887        }
 888    }
 889
 890    fn wrap(indent: u32) -> Self {
 891        lazy_static! {
 892            static ref WRAP_TEXT: String = {
 893                let mut wrap_text = String::new();
 894                wrap_text.push('\n');
 895                wrap_text.extend((0..LineWrapper::MAX_INDENT as usize).map(|_| ' '));
 896                wrap_text
 897            };
 898        }
 899
 900        Self {
 901            summary: TransformSummary {
 902                input: TextSummary::default(),
 903                output: TextSummary {
 904                    lines: Point::new(1, indent),
 905                    first_line_chars: 0,
 906                    last_line_chars: indent,
 907                    longest_row: 1,
 908                    longest_row_chars: indent,
 909                },
 910            },
 911            display_text: Some(&WRAP_TEXT[..1 + indent as usize]),
 912        }
 913    }
 914
 915    fn is_isomorphic(&self) -> bool {
 916        self.display_text.is_none()
 917    }
 918}
 919
 920impl sum_tree::Item for Transform {
 921    type Summary = TransformSummary;
 922
 923    fn summary(&self) -> Self::Summary {
 924        self.summary.clone()
 925    }
 926}
 927
 928fn push_isomorphic(transforms: &mut Vec<Transform>, summary: TextSummary) {
 929    if let Some(last_transform) = transforms.last_mut() {
 930        if last_transform.is_isomorphic() {
 931            last_transform.summary.input += &summary;
 932            last_transform.summary.output += &summary;
 933            return;
 934        }
 935    }
 936    transforms.push(Transform::isomorphic(summary));
 937}
 938
 939trait SumTreeExt {
 940    fn push_or_extend(&mut self, transform: Transform);
 941}
 942
 943impl SumTreeExt for SumTree<Transform> {
 944    fn push_or_extend(&mut self, transform: Transform) {
 945        let mut transform = Some(transform);
 946        self.update_last(
 947            |last_transform| {
 948                if last_transform.is_isomorphic() && transform.as_ref().unwrap().is_isomorphic() {
 949                    let transform = transform.take().unwrap();
 950                    last_transform.summary.input += &transform.summary.input;
 951                    last_transform.summary.output += &transform.summary.output;
 952                }
 953            },
 954            &(),
 955        );
 956
 957        if let Some(transform) = transform {
 958            self.push(transform, &());
 959        }
 960    }
 961}
 962
 963impl WrapPoint {
 964    pub fn new(row: u32, column: u32) -> Self {
 965        Self(Point::new(row, column))
 966    }
 967
 968    pub fn row(self) -> u32 {
 969        self.0.row
 970    }
 971
 972    pub fn row_mut(&mut self) -> &mut u32 {
 973        &mut self.0.row
 974    }
 975
 976    pub fn column(self) -> u32 {
 977        self.0.column
 978    }
 979
 980    pub fn column_mut(&mut self) -> &mut u32 {
 981        &mut self.0.column
 982    }
 983}
 984
 985impl sum_tree::Summary for TransformSummary {
 986    type Context = ();
 987
 988    fn add_summary(&mut self, other: &Self, _: &()) {
 989        self.input += &other.input;
 990        self.output += &other.output;
 991    }
 992}
 993
 994impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
 995    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 996        self.0 += summary.input.lines;
 997    }
 998}
 999
1000impl<'a> sum_tree::SeekTarget<'a, TransformSummary, TransformSummary> for TabPoint {
1001    fn cmp(&self, cursor_location: &TransformSummary, _: &()) -> std::cmp::Ordering {
1002        Ord::cmp(&self.0, &cursor_location.input.lines)
1003    }
1004}
1005
1006impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
1007    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
1008        self.0 += summary.output.lines;
1009    }
1010}
1011
1012fn consolidate_wrap_edits(edits: &mut Vec<WrapEdit>) {
1013    let mut i = 1;
1014    while i < edits.len() {
1015        let edit = edits[i].clone();
1016        let prev_edit = &mut edits[i - 1];
1017        if prev_edit.old.end >= edit.old.start {
1018            prev_edit.old.end = edit.old.end;
1019            prev_edit.new.end = edit.new.end;
1020            edits.remove(i);
1021            continue;
1022        }
1023        i += 1;
1024    }
1025}
1026
1027#[cfg(test)]
1028mod tests {
1029    use super::*;
1030    use crate::{
1031        display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap},
1032        MultiBuffer,
1033    };
1034    use gpui::{font, px, test::observe};
1035    use rand::prelude::*;
1036    use settings::SettingsStore;
1037    use smol::stream::StreamExt;
1038    use std::{cmp, env, num::NonZeroU32};
1039    use text::Rope;
1040    use theme::LoadThemes;
1041
1042    #[gpui::test(iterations = 100)]
1043    async fn test_random_wraps(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1044        // todo!() this test is flaky
1045        init_test(cx);
1046
1047        cx.background_executor.set_block_on_ticks(0..=50);
1048        let operations = env::var("OPERATIONS")
1049            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1050            .unwrap_or(10);
1051
1052        let text_system = cx.read(|cx| cx.text_system().clone());
1053        let mut wrap_width = if rng.gen_bool(0.1) {
1054            None
1055        } else {
1056            Some(px(rng.gen_range(0.0..=1000.0)))
1057        };
1058        let tab_size = NonZeroU32::new(rng.gen_range(1..=4)).unwrap();
1059        let font = font("Helvetica");
1060        let font_id = text_system.font_id(&font).unwrap();
1061        let font_size = px(14.0);
1062
1063        log::info!("Tab size: {}", tab_size);
1064        log::info!("Wrap width: {:?}", wrap_width);
1065
1066        let buffer = cx.update(|cx| {
1067            if rng.gen() {
1068                MultiBuffer::build_random(&mut rng, cx)
1069            } else {
1070                let len = rng.gen_range(0..10);
1071                let text = util::RandomCharIter::new(&mut rng)
1072                    .take(len)
1073                    .collect::<String>();
1074                MultiBuffer::build_simple(&text, cx)
1075            }
1076        });
1077        let mut buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
1078        log::info!("Buffer text: {:?}", buffer_snapshot.text());
1079        let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer_snapshot.clone());
1080        log::info!("InlayMap text: {:?}", inlay_snapshot.text());
1081        let (mut fold_map, fold_snapshot) = FoldMap::new(inlay_snapshot.clone());
1082        log::info!("FoldMap text: {:?}", fold_snapshot.text());
1083        let (mut tab_map, _) = TabMap::new(fold_snapshot.clone(), tab_size);
1084        let tabs_snapshot = tab_map.set_max_expansion_column(32);
1085        log::info!("TabMap text: {:?}", tabs_snapshot.text());
1086
1087        let mut line_wrapper = text_system.line_wrapper(font.clone(), font_size).unwrap();
1088        let unwrapped_text = tabs_snapshot.text();
1089        let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1090
1091        let (wrap_map, _) =
1092            cx.update(|cx| WrapMap::new(tabs_snapshot.clone(), font, font_size, wrap_width, cx));
1093        let mut notifications = observe(&wrap_map, cx);
1094
1095        if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1096            notifications.next().await.unwrap();
1097        }
1098
1099        let (initial_snapshot, _) = wrap_map.update(cx, |map, cx| {
1100            assert!(!map.is_rewrapping());
1101            map.sync(tabs_snapshot.clone(), Vec::new(), cx)
1102        });
1103
1104        let actual_text = initial_snapshot.text();
1105        assert_eq!(
1106            actual_text, expected_text,
1107            "unwrapped text is: {:?}",
1108            unwrapped_text
1109        );
1110        log::info!("Wrapped text: {:?}", actual_text);
1111
1112        let mut next_inlay_id = 0;
1113        let mut edits = Vec::new();
1114        for _i in 0..operations {
1115            log::info!("{} ==============================================", _i);
1116
1117            let mut buffer_edits = Vec::new();
1118            match rng.gen_range(0..=100) {
1119                0..=19 => {
1120                    wrap_width = if rng.gen_bool(0.2) {
1121                        None
1122                    } else {
1123                        Some(px(rng.gen_range(0.0..=1000.0)))
1124                    };
1125                    log::info!("Setting wrap width to {:?}", wrap_width);
1126                    wrap_map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1127                }
1128                20..=39 => {
1129                    for (fold_snapshot, fold_edits) in fold_map.randomly_mutate(&mut rng) {
1130                        let (tabs_snapshot, tab_edits) =
1131                            tab_map.sync(fold_snapshot, fold_edits, tab_size);
1132                        let (mut snapshot, wrap_edits) =
1133                            wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1134                        snapshot.check_invariants();
1135                        snapshot.verify_chunks(&mut rng);
1136                        edits.push((snapshot, wrap_edits));
1137                    }
1138                }
1139                40..=59 => {
1140                    let (inlay_snapshot, inlay_edits) =
1141                        inlay_map.randomly_mutate(&mut next_inlay_id, &mut rng);
1142                    let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1143                    let (tabs_snapshot, tab_edits) =
1144                        tab_map.sync(fold_snapshot, fold_edits, tab_size);
1145                    let (mut snapshot, wrap_edits) =
1146                        wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, tab_edits, cx));
1147                    snapshot.check_invariants();
1148                    snapshot.verify_chunks(&mut rng);
1149                    edits.push((snapshot, wrap_edits));
1150                }
1151                _ => {
1152                    buffer.update(cx, |buffer, cx| {
1153                        let subscription = buffer.subscribe();
1154                        let edit_count = rng.gen_range(1..=5);
1155                        buffer.randomly_mutate(&mut rng, edit_count, cx);
1156                        buffer_snapshot = buffer.snapshot(cx);
1157                        buffer_edits.extend(subscription.consume());
1158                    });
1159                }
1160            }
1161
1162            log::info!("Buffer text: {:?}", buffer_snapshot.text());
1163            let (inlay_snapshot, inlay_edits) =
1164                inlay_map.sync(buffer_snapshot.clone(), buffer_edits);
1165            log::info!("InlayMap text: {:?}", inlay_snapshot.text());
1166            let (fold_snapshot, fold_edits) = fold_map.read(inlay_snapshot, inlay_edits);
1167            log::info!("FoldMap text: {:?}", fold_snapshot.text());
1168            let (tabs_snapshot, tab_edits) = tab_map.sync(fold_snapshot, fold_edits, tab_size);
1169            log::info!("TabMap text: {:?}", tabs_snapshot.text());
1170
1171            let unwrapped_text = tabs_snapshot.text();
1172            let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
1173            let (mut snapshot, wrap_edits) =
1174                wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot.clone(), tab_edits, cx));
1175            snapshot.check_invariants();
1176            snapshot.verify_chunks(&mut rng);
1177            edits.push((snapshot, wrap_edits));
1178
1179            if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
1180                log::info!("Waiting for wrapping to finish");
1181                while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1182                    notifications.next().await.unwrap();
1183                }
1184                wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1185            }
1186
1187            if !wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1188                let (mut wrapped_snapshot, wrap_edits) =
1189                    wrap_map.update(cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
1190                let actual_text = wrapped_snapshot.text();
1191                let actual_longest_row = wrapped_snapshot.longest_row();
1192                log::info!("Wrapping finished: {:?}", actual_text);
1193                wrapped_snapshot.check_invariants();
1194                wrapped_snapshot.verify_chunks(&mut rng);
1195                edits.push((wrapped_snapshot.clone(), wrap_edits));
1196                assert_eq!(
1197                    actual_text, expected_text,
1198                    "unwrapped text is: {:?}",
1199                    unwrapped_text
1200                );
1201
1202                let mut summary = TextSummary::default();
1203                for (ix, item) in wrapped_snapshot
1204                    .transforms
1205                    .items(&())
1206                    .into_iter()
1207                    .enumerate()
1208                {
1209                    summary += &item.summary.output;
1210                    log::info!("{} summary: {:?}", ix, item.summary.output,);
1211                }
1212
1213                if tab_size.get() == 1
1214                    || !wrapped_snapshot
1215                        .tab_snapshot
1216                        .fold_snapshot
1217                        .text()
1218                        .contains('\t')
1219                {
1220                    let mut expected_longest_rows = Vec::new();
1221                    let mut longest_line_len = -1;
1222                    for (row, line) in expected_text.split('\n').enumerate() {
1223                        let line_char_count = line.chars().count() as isize;
1224                        if line_char_count > longest_line_len {
1225                            expected_longest_rows.clear();
1226                            longest_line_len = line_char_count;
1227                        }
1228                        if line_char_count >= longest_line_len {
1229                            expected_longest_rows.push(row as u32);
1230                        }
1231                    }
1232
1233                    assert!(
1234                        expected_longest_rows.contains(&actual_longest_row),
1235                        "incorrect longest row {}. expected {:?} with length {}",
1236                        actual_longest_row,
1237                        expected_longest_rows,
1238                        longest_line_len,
1239                    )
1240                }
1241            }
1242        }
1243
1244        let mut initial_text = Rope::from(initial_snapshot.text().as_str());
1245        for (snapshot, patch) in edits {
1246            let snapshot_text = Rope::from(snapshot.text().as_str());
1247            for edit in &patch {
1248                let old_start = initial_text.point_to_offset(Point::new(edit.new.start, 0));
1249                let old_end = initial_text.point_to_offset(cmp::min(
1250                    Point::new(edit.new.start + edit.old.len() as u32, 0),
1251                    initial_text.max_point(),
1252                ));
1253                let new_start = snapshot_text.point_to_offset(Point::new(edit.new.start, 0));
1254                let new_end = snapshot_text.point_to_offset(cmp::min(
1255                    Point::new(edit.new.end, 0),
1256                    snapshot_text.max_point(),
1257                ));
1258                let new_text = snapshot_text
1259                    .chunks_in_range(new_start..new_end)
1260                    .collect::<String>();
1261
1262                initial_text.replace(old_start..old_end, &new_text);
1263            }
1264            assert_eq!(initial_text.to_string(), snapshot_text.to_string());
1265        }
1266
1267        if wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1268            log::info!("Waiting for wrapping to finish");
1269            while wrap_map.read_with(cx, |map, _| map.is_rewrapping()) {
1270                notifications.next().await.unwrap();
1271            }
1272        }
1273        wrap_map.read_with(cx, |map, _| assert!(map.pending_edits.is_empty()));
1274    }
1275
1276    fn init_test(cx: &mut gpui::TestAppContext) {
1277        cx.update(|cx| {
1278            let settings = SettingsStore::test(cx);
1279            cx.set_global(settings);
1280            theme::init(LoadThemes::JustBase, cx);
1281        });
1282    }
1283
1284    fn wrap_text(
1285        unwrapped_text: &str,
1286        wrap_width: Option<Pixels>,
1287        line_wrapper: &mut LineWrapper,
1288    ) -> String {
1289        if let Some(wrap_width) = wrap_width {
1290            let mut wrapped_text = String::new();
1291            for (row, line) in unwrapped_text.split('\n').enumerate() {
1292                if row > 0 {
1293                    wrapped_text.push('\n')
1294                }
1295
1296                let mut prev_ix = 0;
1297                for boundary in line_wrapper.wrap_line(line, wrap_width) {
1298                    wrapped_text.push_str(&line[prev_ix..boundary.ix]);
1299                    wrapped_text.push('\n');
1300                    wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
1301                    prev_ix = boundary.ix;
1302                }
1303                wrapped_text.push_str(&line[prev_ix..]);
1304            }
1305            wrapped_text
1306        } else {
1307            unwrapped_text.to_string()
1308        }
1309    }
1310
1311    impl WrapSnapshot {
1312        pub fn text(&self) -> String {
1313            self.text_chunks(0).collect()
1314        }
1315
1316        pub fn text_chunks(&self, wrap_row: u32) -> impl Iterator<Item = &str> {
1317            self.chunks(
1318                wrap_row..self.max_point().row() + 1,
1319                false,
1320                Highlights::default(),
1321            )
1322            .map(|h| h.text)
1323        }
1324
1325        fn verify_chunks(&mut self, rng: &mut impl Rng) {
1326            for _ in 0..5 {
1327                let mut end_row = rng.gen_range(0..=self.max_point().row());
1328                let start_row = rng.gen_range(0..=end_row);
1329                end_row += 1;
1330
1331                let mut expected_text = self.text_chunks(start_row).collect::<String>();
1332                if expected_text.ends_with('\n') {
1333                    expected_text.push('\n');
1334                }
1335                let mut expected_text = expected_text
1336                    .lines()
1337                    .take((end_row - start_row) as usize)
1338                    .collect::<Vec<_>>()
1339                    .join("\n");
1340                if end_row <= self.max_point().row() {
1341                    expected_text.push('\n');
1342                }
1343
1344                let actual_text = self
1345                    .chunks(start_row..end_row, true, Highlights::default())
1346                    .map(|c| c.text)
1347                    .collect::<String>();
1348                assert_eq!(
1349                    expected_text,
1350                    actual_text,
1351                    "chunks != highlighted_chunks for rows {:?}",
1352                    start_row..end_row
1353                );
1354            }
1355        }
1356    }
1357}