wrap_map.rs

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