wrap_map.rs

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