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            let mut expected_buffer_rows = Vec::new();
 588            let mut buffer_row = 0;
 589            let mut prev_tab_row = 0;
 590            for display_row in 0..=self.max_point().row() {
 591                let tab_point = self.to_tab_point(WrapPoint::new(display_row, 0));
 592                if tab_point.row() != prev_tab_row {
 593                    let fold_point = self.tab_snapshot.to_fold_point(tab_point, Bias::Left).0;
 594                    let buffer_point = fold_point.to_buffer_point(&self.tab_snapshot.fold_snapshot);
 595                    buffer_row = buffer_point.row;
 596                    prev_tab_row = tab_point.row();
 597                }
 598                expected_buffer_rows.push(buffer_row);
 599            }
 600
 601            for start_display_row in 0..expected_buffer_rows.len() {
 602                assert_eq!(
 603                    self.buffer_rows(start_display_row as u32)
 604                        .map(|(row, _)| row)
 605                        .collect::<Vec<_>>(),
 606                    &expected_buffer_rows[start_display_row..],
 607                    "invalid buffer_rows({}..)",
 608                    start_display_row
 609                );
 610            }
 611        }
 612    }
 613}
 614
 615impl<'a> Iterator for Chunks<'a> {
 616    type Item = &'a str;
 617
 618    fn next(&mut self) -> Option<Self::Item> {
 619        let transform = self.transforms.item()?;
 620        if let Some(display_text) = transform.display_text {
 621            if self.output_position > *self.transforms.seek_start() {
 622                self.output_position.0.column += transform.summary.output.lines.column;
 623                self.transforms.next(&());
 624                return Some(&display_text[1..]);
 625            } else {
 626                self.output_position.0 += transform.summary.output.lines;
 627                self.transforms.next(&());
 628                return Some(display_text);
 629            }
 630        }
 631
 632        if self.input_chunk.is_empty() {
 633            self.input_chunk = self.input_chunks.next().unwrap();
 634        }
 635
 636        let mut input_len = 0;
 637        let transform_end = self.transforms.seek_end(&());
 638        for c in self.input_chunk.chars() {
 639            let char_len = c.len_utf8();
 640            input_len += char_len;
 641            if c == '\n' {
 642                *self.output_position.row_mut() += 1;
 643                *self.output_position.column_mut() = 0;
 644            } else {
 645                *self.output_position.column_mut() += char_len as u32;
 646            }
 647
 648            if self.output_position >= transform_end {
 649                self.transforms.next(&());
 650                break;
 651            }
 652        }
 653
 654        let (prefix, suffix) = self.input_chunk.split_at(input_len);
 655        self.input_chunk = suffix;
 656        Some(prefix)
 657    }
 658}
 659
 660impl<'a> Iterator for HighlightedChunks<'a> {
 661    type Item = (&'a str, StyleId);
 662
 663    fn next(&mut self) -> Option<Self::Item> {
 664        if self.output_position.row() >= self.max_output_row {
 665            return None;
 666        }
 667
 668        let transform = self.transforms.item()?;
 669        if let Some(display_text) = transform.display_text {
 670            let mut start_ix = 0;
 671            let mut end_ix = display_text.len();
 672            let mut summary = transform.summary.output.lines;
 673
 674            if self.output_position > *self.transforms.seek_start() {
 675                // Exclude newline starting prior to the desired row.
 676                start_ix = 1;
 677                summary.row = 0;
 678            } else if self.output_position.row() + 1 >= self.max_output_row {
 679                // Exclude soft indentation ending after the desired row.
 680                end_ix = 1;
 681                summary.column = 0;
 682            }
 683
 684            self.output_position.0 += summary;
 685            self.transforms.next(&());
 686            return Some((&display_text[start_ix..end_ix], self.style_id));
 687        }
 688
 689        if self.input_chunk.is_empty() {
 690            let (chunk, style_id) = self.input_chunks.next().unwrap();
 691            self.input_chunk = chunk;
 692            self.style_id = style_id;
 693        }
 694
 695        let mut input_len = 0;
 696        let transform_end = self.transforms.seek_end(&());
 697        for c in self.input_chunk.chars() {
 698            let char_len = c.len_utf8();
 699            input_len += char_len;
 700            if c == '\n' {
 701                *self.output_position.row_mut() += 1;
 702                *self.output_position.column_mut() = 0;
 703            } else {
 704                *self.output_position.column_mut() += char_len as u32;
 705            }
 706
 707            if self.output_position >= transform_end {
 708                self.transforms.next(&());
 709                break;
 710            }
 711        }
 712
 713        let (prefix, suffix) = self.input_chunk.split_at(input_len);
 714        self.input_chunk = suffix;
 715        Some((prefix, self.style_id))
 716    }
 717}
 718
 719impl<'a> Iterator for BufferRows<'a> {
 720    type Item = (u32, bool);
 721
 722    fn next(&mut self) -> Option<Self::Item> {
 723        if self.output_row > self.max_output_row {
 724            return None;
 725        }
 726
 727        let buffer_row = self.input_buffer_row;
 728        let soft_wrapped = self.soft_wrapped;
 729
 730        self.output_row += 1;
 731        self.transforms
 732            .seek_forward(&WrapPoint::new(self.output_row, 0), Bias::Left, &());
 733        if self.transforms.item().map_or(false, |t| t.is_isomorphic()) {
 734            self.input_buffer_row = self.input_buffer_rows.next().unwrap();
 735            self.soft_wrapped = false;
 736        } else {
 737            self.soft_wrapped = true;
 738        }
 739
 740        Some((buffer_row, soft_wrapped))
 741    }
 742}
 743
 744impl Transform {
 745    fn isomorphic(summary: TextSummary) -> Self {
 746        #[cfg(test)]
 747        assert!(!summary.lines.is_zero());
 748
 749        Self {
 750            summary: TransformSummary {
 751                input: summary.clone(),
 752                output: summary,
 753            },
 754            display_text: None,
 755        }
 756    }
 757
 758    fn wrap(indent: u32) -> Self {
 759        lazy_static! {
 760            static ref WRAP_TEXT: String = {
 761                let mut wrap_text = String::new();
 762                wrap_text.push('\n');
 763                wrap_text.extend((0..LineWrapper::MAX_INDENT as usize).map(|_| ' '));
 764                wrap_text
 765            };
 766        }
 767
 768        Self {
 769            summary: TransformSummary {
 770                input: TextSummary::default(),
 771                output: TextSummary {
 772                    lines: Point::new(1, indent),
 773                    first_line_chars: 0,
 774                    last_line_chars: indent,
 775                    longest_row: 1,
 776                    longest_row_chars: indent,
 777                },
 778            },
 779            display_text: Some(&WRAP_TEXT[..1 + indent as usize]),
 780        }
 781    }
 782
 783    fn is_isomorphic(&self) -> bool {
 784        self.display_text.is_none()
 785    }
 786}
 787
 788impl sum_tree::Item for Transform {
 789    type Summary = TransformSummary;
 790
 791    fn summary(&self) -> Self::Summary {
 792        self.summary.clone()
 793    }
 794}
 795
 796fn push_isomorphic(transforms: &mut Vec<Transform>, summary: TextSummary) {
 797    if let Some(last_transform) = transforms.last_mut() {
 798        if last_transform.is_isomorphic() {
 799            last_transform.summary.input += &summary;
 800            last_transform.summary.output += &summary;
 801            return;
 802        }
 803    }
 804    transforms.push(Transform::isomorphic(summary));
 805}
 806
 807impl SumTree<Transform> {
 808    pub fn push_or_extend(&mut self, transform: Transform) {
 809        let mut transform = Some(transform);
 810        self.update_last(
 811            |last_transform| {
 812                if last_transform.is_isomorphic() && transform.as_ref().unwrap().is_isomorphic() {
 813                    let transform = transform.take().unwrap();
 814                    last_transform.summary.input += &transform.summary.input;
 815                    last_transform.summary.output += &transform.summary.output;
 816                }
 817            },
 818            &(),
 819        );
 820
 821        if let Some(transform) = transform {
 822            self.push(transform, &());
 823        }
 824    }
 825}
 826
 827impl WrapPoint {
 828    pub fn new(row: u32, column: u32) -> Self {
 829        Self(super::Point::new(row, column))
 830    }
 831
 832    pub fn row(self) -> u32 {
 833        self.0.row
 834    }
 835
 836    pub fn column(self) -> u32 {
 837        self.0.column
 838    }
 839
 840    pub fn row_mut(&mut self) -> &mut u32 {
 841        &mut self.0.row
 842    }
 843
 844    pub fn column_mut(&mut self) -> &mut u32 {
 845        &mut self.0.column
 846    }
 847}
 848
 849impl sum_tree::Summary for TransformSummary {
 850    type Context = ();
 851
 852    fn add_summary(&mut self, other: &Self, _: &()) {
 853        self.input += &other.input;
 854        self.output += &other.output;
 855    }
 856}
 857
 858impl<'a> sum_tree::Dimension<'a, TransformSummary> for TabPoint {
 859    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 860        self.0 += summary.input.lines;
 861    }
 862}
 863
 864impl<'a> sum_tree::Dimension<'a, TransformSummary> for WrapPoint {
 865    fn add_summary(&mut self, summary: &'a TransformSummary, _: &()) {
 866        self.0 += summary.output.lines;
 867    }
 868}
 869
 870#[cfg(test)]
 871mod tests {
 872    use super::*;
 873    use crate::{
 874        editor::{
 875            display_map::{fold_map::FoldMap, tab_map::TabMap},
 876            Buffer,
 877        },
 878        test::Observer,
 879        util::RandomCharIter,
 880    };
 881    use rand::prelude::*;
 882    use std::env;
 883
 884    #[gpui::test(iterations = 100)]
 885    async fn test_random_wraps(mut cx: gpui::TestAppContext, mut rng: StdRng) {
 886        cx.foreground().set_block_on_ticks(0..=50);
 887        cx.foreground().forbid_parking();
 888        let operations = env::var("OPERATIONS")
 889            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 890            .unwrap_or(10);
 891
 892        let font_cache = cx.font_cache().clone();
 893        let font_system = cx.platform().fonts();
 894        let mut wrap_width = if rng.gen_bool(0.1) {
 895            None
 896        } else {
 897            Some(rng.gen_range(0.0..=1000.0))
 898        };
 899        let settings = Settings {
 900            tab_size: rng.gen_range(1..=4),
 901            buffer_font_family: font_cache.load_family(&["Helvetica"]).unwrap(),
 902            buffer_font_size: 14.0,
 903            ..Settings::new(&font_cache).unwrap()
 904        };
 905        log::info!("Tab size: {}", settings.tab_size);
 906        log::info!("Wrap width: {:?}", wrap_width);
 907
 908        let buffer = cx.add_model(|cx| {
 909            let len = rng.gen_range(0..10);
 910            let text = RandomCharIter::new(&mut rng).take(len).collect::<String>();
 911            Buffer::new(0, text, cx)
 912        });
 913        let (mut fold_map, folds_snapshot) = cx.read(|cx| FoldMap::new(buffer.clone(), cx));
 914        let (tab_map, tabs_snapshot) = TabMap::new(folds_snapshot.clone(), settings.tab_size);
 915        log::info!(
 916            "Unwrapped text (no folds): {:?}",
 917            buffer.read_with(&cx, |buf, _| buf.text())
 918        );
 919        log::info!(
 920            "Unwrapped text (unexpanded tabs): {:?}",
 921            folds_snapshot.text()
 922        );
 923        log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
 924        let wrap_map = cx
 925            .add_model(|cx| WrapMap::new(tabs_snapshot.clone(), settings.clone(), wrap_width, cx));
 926        let (_observer, notifications) = Observer::new(&wrap_map, &mut cx);
 927
 928        let mut line_wrapper = LineWrapper::new(font_system, &font_cache, settings);
 929        let unwrapped_text = tabs_snapshot.text();
 930        let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
 931
 932        if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) {
 933            notifications.recv().await.unwrap();
 934        }
 935
 936        let snapshot = wrap_map.update(&mut cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
 937        let actual_text = snapshot.text();
 938        assert_eq!(
 939            actual_text, expected_text,
 940            "unwrapped text is: {:?}",
 941            unwrapped_text
 942        );
 943        log::info!("Wrapped text: {:?}", actual_text);
 944
 945        for _i in 0..operations {
 946            match rng.gen_range(0..=100) {
 947                0..=19 => {
 948                    wrap_width = if rng.gen_bool(0.2) {
 949                        None
 950                    } else {
 951                        Some(rng.gen_range(0.0..=1000.0))
 952                    };
 953                    log::info!("Setting wrap width to {:?}", wrap_width);
 954                    wrap_map.update(&mut cx, |map, cx| map.set_wrap_width(wrap_width, cx));
 955                }
 956                20..=39 => {
 957                    for (folds_snapshot, edits) in
 958                        cx.read(|cx| fold_map.randomly_mutate(&mut rng, cx))
 959                    {
 960                        let (tabs_snapshot, edits) = tab_map.sync(folds_snapshot, edits);
 961                        let mut snapshot =
 962                            wrap_map.update(&mut cx, |map, cx| map.sync(tabs_snapshot, edits, cx));
 963                        snapshot.check_invariants();
 964                        snapshot.verify_chunks(&mut rng);
 965                    }
 966                }
 967                _ => {
 968                    buffer.update(&mut cx, |buffer, cx| buffer.randomly_mutate(&mut rng, cx));
 969                }
 970            }
 971
 972            log::info!(
 973                "Unwrapped text (no folds): {:?}",
 974                buffer.read_with(&cx, |buf, _| buf.text())
 975            );
 976            let (folds_snapshot, edits) = cx.read(|cx| fold_map.read(cx));
 977            log::info!(
 978                "Unwrapped text (unexpanded tabs): {:?}",
 979                folds_snapshot.text()
 980            );
 981            let (tabs_snapshot, edits) = tab_map.sync(folds_snapshot, edits);
 982            log::info!("Unwrapped text (expanded tabs): {:?}", tabs_snapshot.text());
 983
 984            let unwrapped_text = tabs_snapshot.text();
 985            let expected_text = wrap_text(&unwrapped_text, wrap_width, &mut line_wrapper);
 986            let mut snapshot = wrap_map.update(&mut cx, |map, cx| {
 987                map.sync(tabs_snapshot.clone(), edits, cx)
 988            });
 989            snapshot.check_invariants();
 990            snapshot.verify_chunks(&mut rng);
 991
 992            if wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) && rng.gen_bool(0.4) {
 993                log::info!("Waiting for wrapping to finish");
 994                while wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) {
 995                    notifications.recv().await.unwrap();
 996                }
 997            }
 998
 999            if !wrap_map.read_with(&cx, |map, _| map.is_rewrapping()) {
1000                let mut wrapped_snapshot =
1001                    wrap_map.update(&mut cx, |map, cx| map.sync(tabs_snapshot, Vec::new(), cx));
1002                let actual_text = wrapped_snapshot.text();
1003                log::info!("Wrapping finished: {:?}", actual_text);
1004                wrapped_snapshot.check_invariants();
1005                wrapped_snapshot.verify_chunks(&mut rng);
1006                assert_eq!(
1007                    actual_text, expected_text,
1008                    "unwrapped text is: {:?}",
1009                    unwrapped_text
1010                );
1011            }
1012        }
1013    }
1014
1015    fn wrap_text(
1016        unwrapped_text: &str,
1017        wrap_width: Option<f32>,
1018        line_wrapper: &mut LineWrapper,
1019    ) -> String {
1020        if let Some(wrap_width) = wrap_width {
1021            let mut wrapped_text = String::new();
1022            for (row, line) in unwrapped_text.split('\n').enumerate() {
1023                if row > 0 {
1024                    wrapped_text.push('\n')
1025                }
1026
1027                let mut prev_ix = 0;
1028                for boundary in line_wrapper.wrap_line(line, wrap_width) {
1029                    wrapped_text.push_str(&line[prev_ix..boundary.ix]);
1030                    wrapped_text.push('\n');
1031                    wrapped_text.push_str(&" ".repeat(boundary.next_indent as usize));
1032                    prev_ix = boundary.ix;
1033                }
1034                wrapped_text.push_str(&line[prev_ix..]);
1035            }
1036            wrapped_text
1037        } else {
1038            unwrapped_text.to_string()
1039        }
1040    }
1041
1042    impl Snapshot {
1043        fn text(&self) -> String {
1044            self.chunks_at(0).collect()
1045        }
1046
1047        fn verify_chunks(&mut self, rng: &mut impl Rng) {
1048            for _ in 0..5 {
1049                let mut end_row = rng.gen_range(0..=self.max_point().row());
1050                let start_row = rng.gen_range(0..=end_row);
1051                end_row += 1;
1052
1053                let mut expected_text = self.chunks_at(start_row).collect::<String>();
1054                if expected_text.ends_with("\n") {
1055                    expected_text.push('\n');
1056                }
1057                let mut expected_text = expected_text
1058                    .lines()
1059                    .take((end_row - start_row) as usize)
1060                    .collect::<Vec<_>>()
1061                    .join("\n");
1062                if end_row <= self.max_point().row() {
1063                    expected_text.push('\n');
1064                }
1065
1066                let actual_text = self
1067                    .highlighted_chunks_for_rows(start_row..end_row)
1068                    .map(|c| c.0)
1069                    .collect::<String>();
1070                assert_eq!(
1071                    expected_text,
1072                    actual_text,
1073                    "chunks != highlighted_chunks for rows {:?}",
1074                    start_row..end_row
1075                );
1076            }
1077        }
1078    }
1079}