wrap_map.rs

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