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