display_map.rs

   1mod block_map;
   2mod fold_map;
   3mod inlay_map;
   4mod tab_map;
   5mod wrap_map;
   6
   7use crate::{Anchor, AnchorRangeExt, InlayId, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
   8pub use block_map::{BlockMap, BlockPoint};
   9use collections::{HashMap, HashSet};
  10use fold_map::FoldMap;
  11use gpui::{
  12    color::Color,
  13    fonts::{FontId, HighlightStyle},
  14    Entity, ModelContext, ModelHandle,
  15};
  16use inlay_map::InlayMap;
  17use language::{
  18    language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,
  19};
  20use std::{any::TypeId, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};
  21use sum_tree::{Bias, TreeMap};
  22use tab_map::TabMap;
  23use wrap_map::WrapMap;
  24
  25pub use block_map::{
  26    BlockBufferRows as DisplayBufferRows, BlockChunks as DisplayChunks, BlockContext,
  27    BlockDisposition, BlockId, BlockProperties, BlockStyle, RenderBlock, TransformBlock,
  28};
  29
  30pub use self::inlay_map::Inlay;
  31
  32#[derive(Copy, Clone, Debug, PartialEq, Eq)]
  33pub enum FoldStatus {
  34    Folded,
  35    Foldable,
  36}
  37
  38#[derive(Copy, Clone, Debug, PartialEq, Eq)]
  39pub enum Clip {
  40    None,
  41    EndOfLine,
  42}
  43
  44pub trait ToDisplayPoint {
  45    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
  46}
  47
  48type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
  49
  50pub struct DisplayMap {
  51    buffer: ModelHandle<MultiBuffer>,
  52    buffer_subscription: BufferSubscription,
  53    fold_map: FoldMap,
  54    inlay_map: InlayMap,
  55    tab_map: TabMap,
  56    wrap_map: ModelHandle<WrapMap>,
  57    block_map: BlockMap,
  58    text_highlights: TextHighlights,
  59    pub default_clip: Clip,
  60}
  61
  62impl Entity for DisplayMap {
  63    type Event = ();
  64}
  65
  66impl DisplayMap {
  67    pub fn new(
  68        buffer: ModelHandle<MultiBuffer>,
  69        font_id: FontId,
  70        font_size: f32,
  71        wrap_width: Option<f32>,
  72        buffer_header_height: u8,
  73        excerpt_header_height: u8,
  74        cx: &mut ModelContext<Self>,
  75    ) -> Self {
  76        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
  77
  78        let tab_size = Self::tab_size(&buffer, cx);
  79        let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
  80        let (fold_map, snapshot) = FoldMap::new(snapshot);
  81        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
  82        let (wrap_map, snapshot) = WrapMap::new(snapshot, font_id, font_size, wrap_width, cx);
  83        let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
  84        cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
  85        DisplayMap {
  86            buffer,
  87            buffer_subscription,
  88            fold_map,
  89            inlay_map,
  90            tab_map,
  91            wrap_map,
  92            block_map,
  93            text_highlights: Default::default(),
  94            default_clip: Clip::None,
  95        }
  96    }
  97
  98    pub fn snapshot(&mut self, cx: &mut ModelContext<Self>) -> DisplaySnapshot {
  99        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 100        let edits = self.buffer_subscription.consume().into_inner();
 101        let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
 102        let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot.clone(), edits);
 103        let tab_size = Self::tab_size(&self.buffer, cx);
 104        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot.clone(), edits, tab_size);
 105        let (wrap_snapshot, edits) = self
 106            .wrap_map
 107            .update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
 108        let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits);
 109
 110        DisplaySnapshot {
 111            buffer_snapshot: self.buffer.read(cx).snapshot(cx),
 112            fold_snapshot,
 113            inlay_snapshot,
 114            tab_snapshot,
 115            wrap_snapshot,
 116            block_snapshot,
 117            text_highlights: self.text_highlights.clone(),
 118            default_clip: self.default_clip,
 119        }
 120    }
 121
 122    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut ModelContext<Self>) {
 123        self.fold(
 124            other
 125                .folds_in_range(0..other.buffer_snapshot.len())
 126                .map(|fold| fold.to_offset(&other.buffer_snapshot)),
 127            cx,
 128        );
 129    }
 130
 131    pub fn fold<T: ToOffset>(
 132        &mut self,
 133        ranges: impl IntoIterator<Item = Range<T>>,
 134        cx: &mut ModelContext<Self>,
 135    ) {
 136        let snapshot = self.buffer.read(cx).snapshot(cx);
 137        let edits = self.buffer_subscription.consume().into_inner();
 138        let tab_size = Self::tab_size(&self.buffer, cx);
 139        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 140        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 141        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 142        let (snapshot, edits) = self
 143            .wrap_map
 144            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 145        self.block_map.read(snapshot, edits);
 146        let (snapshot, edits) = fold_map.fold(ranges);
 147        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 148        let (snapshot, edits) = self
 149            .wrap_map
 150            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 151        self.block_map.read(snapshot, edits);
 152    }
 153
 154    pub fn unfold<T: ToOffset>(
 155        &mut self,
 156        ranges: impl IntoIterator<Item = Range<T>>,
 157        inclusive: bool,
 158        cx: &mut ModelContext<Self>,
 159    ) {
 160        let snapshot = self.buffer.read(cx).snapshot(cx);
 161        let edits = self.buffer_subscription.consume().into_inner();
 162        let tab_size = Self::tab_size(&self.buffer, cx);
 163        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 164        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 165        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 166        let (snapshot, edits) = self
 167            .wrap_map
 168            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 169        self.block_map.read(snapshot, edits);
 170        let (snapshot, edits) = fold_map.unfold(ranges, inclusive);
 171        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 172        let (snapshot, edits) = self
 173            .wrap_map
 174            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 175        self.block_map.read(snapshot, edits);
 176    }
 177
 178    pub fn insert_blocks(
 179        &mut self,
 180        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
 181        cx: &mut ModelContext<Self>,
 182    ) -> Vec<BlockId> {
 183        let snapshot = self.buffer.read(cx).snapshot(cx);
 184        let edits = self.buffer_subscription.consume().into_inner();
 185        let tab_size = Self::tab_size(&self.buffer, cx);
 186        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 187        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 188        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 189        let (snapshot, edits) = self
 190            .wrap_map
 191            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 192        let mut block_map = self.block_map.write(snapshot, edits);
 193        block_map.insert(blocks)
 194    }
 195
 196    pub fn replace_blocks(&mut self, styles: HashMap<BlockId, RenderBlock>) {
 197        self.block_map.replace(styles);
 198    }
 199
 200    pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<Self>) {
 201        let snapshot = self.buffer.read(cx).snapshot(cx);
 202        let edits = self.buffer_subscription.consume().into_inner();
 203        let tab_size = Self::tab_size(&self.buffer, cx);
 204        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 205        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 206        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 207        let (snapshot, edits) = self
 208            .wrap_map
 209            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 210        let mut block_map = self.block_map.write(snapshot, edits);
 211        block_map.remove(ids);
 212    }
 213
 214    pub fn highlight_text(
 215        &mut self,
 216        type_id: TypeId,
 217        ranges: Vec<Range<Anchor>>,
 218        style: HighlightStyle,
 219    ) {
 220        self.text_highlights
 221            .insert(Some(type_id), Arc::new((style, ranges)));
 222    }
 223
 224    pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {
 225        let highlights = self.text_highlights.get(&Some(type_id))?;
 226        Some((highlights.0, &highlights.1))
 227    }
 228
 229    pub fn clear_text_highlights(
 230        &mut self,
 231        type_id: TypeId,
 232    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
 233        self.text_highlights.remove(&Some(type_id))
 234    }
 235
 236    pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) -> bool {
 237        self.wrap_map
 238            .update(cx, |map, cx| map.set_font(font_id, font_size, cx))
 239    }
 240
 241    pub fn set_fold_ellipses_color(&mut self, color: Color) -> bool {
 242        self.fold_map.set_ellipses_color(color)
 243    }
 244
 245    pub fn set_wrap_width(&self, width: Option<f32>, cx: &mut ModelContext<Self>) -> bool {
 246        self.wrap_map
 247            .update(cx, |map, cx| map.set_wrap_width(width, cx))
 248    }
 249
 250    pub fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
 251        self.inlay_map.current_inlays()
 252    }
 253
 254    pub fn splice_inlays(
 255        &mut self,
 256        to_remove: Vec<InlayId>,
 257        to_insert: Vec<Inlay>,
 258        cx: &mut ModelContext<Self>,
 259    ) {
 260        if to_remove.is_empty() && to_insert.is_empty() {
 261            return;
 262        }
 263        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 264        let edits = self.buffer_subscription.consume().into_inner();
 265        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
 266        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 267        let tab_size = Self::tab_size(&self.buffer, cx);
 268        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 269        let (snapshot, edits) = self
 270            .wrap_map
 271            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 272        self.block_map.read(snapshot, edits);
 273
 274        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
 275        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 276        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 277        let (snapshot, edits) = self
 278            .wrap_map
 279            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 280        self.block_map.read(snapshot, edits);
 281    }
 282
 283    fn tab_size(buffer: &ModelHandle<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {
 284        let language = buffer
 285            .read(cx)
 286            .as_singleton()
 287            .and_then(|buffer| buffer.read(cx).language());
 288        language_settings(language.as_deref(), None, cx).tab_size
 289    }
 290
 291    #[cfg(test)]
 292    pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {
 293        self.wrap_map.read(cx).is_rewrapping()
 294    }
 295}
 296
 297pub struct DisplaySnapshot {
 298    pub buffer_snapshot: MultiBufferSnapshot,
 299    fold_snapshot: fold_map::FoldSnapshot,
 300    inlay_snapshot: inlay_map::InlaySnapshot,
 301    tab_snapshot: tab_map::TabSnapshot,
 302    wrap_snapshot: wrap_map::WrapSnapshot,
 303    block_snapshot: block_map::BlockSnapshot,
 304    text_highlights: TextHighlights,
 305    default_clip: Clip,
 306}
 307
 308impl DisplaySnapshot {
 309    #[cfg(test)]
 310    pub fn fold_count(&self) -> usize {
 311        self.fold_snapshot.fold_count()
 312    }
 313
 314    pub fn is_empty(&self) -> bool {
 315        self.buffer_snapshot.len() == 0
 316    }
 317
 318    pub fn buffer_rows(&self, start_row: u32) -> DisplayBufferRows {
 319        self.block_snapshot.buffer_rows(start_row)
 320    }
 321
 322    pub fn max_buffer_row(&self) -> u32 {
 323        self.buffer_snapshot.max_buffer_row()
 324    }
 325
 326    pub fn prev_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
 327        loop {
 328            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
 329            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);
 330            fold_point.0.column = 0;
 331            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
 332            point = self.inlay_snapshot.to_buffer_point(inlay_point);
 333
 334            let mut display_point = self.point_to_display_point(point, Bias::Left);
 335            *display_point.column_mut() = 0;
 336            let next_point = self.display_point_to_point(display_point, Bias::Left);
 337            if next_point == point {
 338                return (point, display_point);
 339            }
 340            point = next_point;
 341        }
 342    }
 343
 344    pub fn next_line_boundary(&self, mut point: Point) -> (Point, DisplayPoint) {
 345        loop {
 346            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
 347            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);
 348            fold_point.0.column = self.fold_snapshot.line_len(fold_point.row());
 349            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
 350            point = self.inlay_snapshot.to_buffer_point(inlay_point);
 351
 352            let mut display_point = self.point_to_display_point(point, Bias::Right);
 353            *display_point.column_mut() = self.line_len(display_point.row());
 354            let next_point = self.display_point_to_point(display_point, Bias::Right);
 355            if next_point == point {
 356                return (point, display_point);
 357            }
 358            point = next_point;
 359        }
 360    }
 361
 362    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
 363        let mut new_start = self.prev_line_boundary(range.start).0;
 364        let mut new_end = self.next_line_boundary(range.end).0;
 365
 366        if new_start.row == range.start.row && new_end.row == range.end.row {
 367            if new_end.row < self.buffer_snapshot.max_point().row {
 368                new_end.row += 1;
 369                new_end.column = 0;
 370            } else if new_start.row > 0 {
 371                new_start.row -= 1;
 372                new_start.column = self.buffer_snapshot.line_len(new_start.row);
 373            }
 374        }
 375
 376        new_start..new_end
 377    }
 378
 379    fn point_to_display_point(&self, point: Point, bias: Bias) -> DisplayPoint {
 380        let inlay_point = self.inlay_snapshot.to_inlay_point(point);
 381        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
 382        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
 383        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
 384        let block_point = self.block_snapshot.to_block_point(wrap_point);
 385        DisplayPoint(block_point)
 386    }
 387
 388    fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
 389        let block_point = point.0;
 390        let wrap_point = self.block_snapshot.to_wrap_point(block_point);
 391        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
 392        let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
 393        let inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
 394        self.inlay_snapshot.to_buffer_point(inlay_point)
 395    }
 396
 397    pub fn max_point(&self) -> DisplayPoint {
 398        DisplayPoint(self.block_snapshot.max_point())
 399    }
 400
 401    /// Returns text chunks starting at the given display row until the end of the file
 402    pub fn text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
 403        self.block_snapshot
 404            .chunks(
 405                display_row..self.max_point().row() + 1,
 406                false,
 407                None,
 408                None,
 409                None,
 410            )
 411            .map(|h| h.text)
 412    }
 413
 414    /// Returns text chunks starting at the end of the given display row in reverse until the start of the file
 415    pub fn reverse_text_chunks(&self, display_row: u32) -> impl Iterator<Item = &str> {
 416        (0..=display_row).into_iter().rev().flat_map(|row| {
 417            self.block_snapshot
 418                .chunks(row..row + 1, false, None, None, None)
 419                .map(|h| h.text)
 420                .collect::<Vec<_>>()
 421                .into_iter()
 422                .rev()
 423        })
 424    }
 425
 426    pub fn chunks(
 427        &self,
 428        display_rows: Range<u32>,
 429        language_aware: bool,
 430        hint_highlights: Option<HighlightStyle>,
 431        suggestion_highlights: Option<HighlightStyle>,
 432    ) -> DisplayChunks<'_> {
 433        self.block_snapshot.chunks(
 434            display_rows,
 435            language_aware,
 436            Some(&self.text_highlights),
 437            hint_highlights,
 438            suggestion_highlights,
 439        )
 440    }
 441
 442    pub fn chars_at(
 443        &self,
 444        mut point: DisplayPoint,
 445    ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
 446        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
 447        self.text_chunks(point.row())
 448            .flat_map(str::chars)
 449            .skip_while({
 450                let mut column = 0;
 451                move |char| {
 452                    let at_point = column >= point.column();
 453                    column += char.len_utf8() as u32;
 454                    !at_point
 455                }
 456            })
 457            .map(move |ch| {
 458                let result = (ch, point);
 459                if ch == '\n' {
 460                    *point.row_mut() += 1;
 461                    *point.column_mut() = 0;
 462                } else {
 463                    *point.column_mut() += ch.len_utf8() as u32;
 464                }
 465                result
 466            })
 467    }
 468
 469    pub fn reverse_chars_at(
 470        &self,
 471        mut point: DisplayPoint,
 472    ) -> impl Iterator<Item = (char, DisplayPoint)> + '_ {
 473        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
 474        self.reverse_text_chunks(point.row())
 475            .flat_map(|chunk| chunk.chars().rev())
 476            .skip_while({
 477                let mut column = self.line_len(point.row());
 478                if self.max_point().row() > point.row() {
 479                    column += 1;
 480                }
 481
 482                move |char| {
 483                    let at_point = column <= point.column();
 484                    column = column.saturating_sub(char.len_utf8() as u32);
 485                    !at_point
 486                }
 487            })
 488            .map(move |ch| {
 489                if ch == '\n' {
 490                    *point.row_mut() -= 1;
 491                    *point.column_mut() = self.line_len(point.row());
 492                } else {
 493                    *point.column_mut() = point.column().saturating_sub(ch.len_utf8() as u32);
 494                }
 495                (ch, point)
 496            })
 497    }
 498
 499    /// Returns an iterator of the start positions of the occurrences of `target` in the `self` after `from`
 500    /// Stops if `condition` returns false for any of the character position pairs observed.
 501    pub fn find_while<'a>(
 502        &'a self,
 503        from: DisplayPoint,
 504        target: &str,
 505        condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
 506    ) -> impl Iterator<Item = DisplayPoint> + 'a {
 507        Self::find_internal(self.chars_at(from), target.chars().collect(), condition)
 508    }
 509
 510    /// Returns an iterator of the end positions of the occurrences of `target` in the `self` before `from`
 511    /// Stops if `condition` returns false for any of the character position pairs observed.
 512    pub fn reverse_find_while<'a>(
 513        &'a self,
 514        from: DisplayPoint,
 515        target: &str,
 516        condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
 517    ) -> impl Iterator<Item = DisplayPoint> + 'a {
 518        Self::find_internal(
 519            self.reverse_chars_at(from),
 520            target.chars().rev().collect(),
 521            condition,
 522        )
 523    }
 524
 525    fn find_internal<'a>(
 526        iterator: impl Iterator<Item = (char, DisplayPoint)> + 'a,
 527        target: Vec<char>,
 528        mut condition: impl FnMut(char, DisplayPoint) -> bool + 'a,
 529    ) -> impl Iterator<Item = DisplayPoint> + 'a {
 530        // List of partial matches with the index of the last seen character in target and the starting point of the match
 531        let mut partial_matches: Vec<(usize, DisplayPoint)> = Vec::new();
 532        iterator
 533            .take_while(move |(ch, point)| condition(*ch, *point))
 534            .filter_map(move |(ch, point)| {
 535                if Some(&ch) == target.get(0) {
 536                    partial_matches.push((0, point));
 537                }
 538
 539                let mut found = None;
 540                // Keep partial matches that have the correct next character
 541                partial_matches.retain_mut(|(match_position, match_start)| {
 542                    if target.get(*match_position) == Some(&ch) {
 543                        *match_position += 1;
 544                        if *match_position == target.len() {
 545                            found = Some(match_start.clone());
 546                            // This match is completed. No need to keep tracking it
 547                            false
 548                        } else {
 549                            true
 550                        }
 551                    } else {
 552                        false
 553                    }
 554                });
 555
 556                found
 557            })
 558    }
 559
 560    pub fn column_to_chars(&self, display_row: u32, target: u32) -> u32 {
 561        let mut count = 0;
 562        let mut column = 0;
 563        for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
 564            if column >= target {
 565                break;
 566            }
 567            count += 1;
 568            column += c.len_utf8() as u32;
 569        }
 570        count
 571    }
 572
 573    pub fn column_from_chars(&self, display_row: u32, char_count: u32) -> u32 {
 574        let mut column = 0;
 575
 576        for (count, (c, _)) in self.chars_at(DisplayPoint::new(display_row, 0)).enumerate() {
 577            if c == '\n' || count >= char_count as usize {
 578                break;
 579            }
 580            column += c.len_utf8() as u32;
 581        }
 582
 583        column
 584    }
 585
 586    pub fn move_left(&self, point: DisplayPoint, clip: Clip) -> DisplayPoint {
 587        self.clip_point_with(
 588            DisplayPoint::new(point.row(), point.column().saturating_sub(1)),
 589            Bias::Left,
 590            clip,
 591        )
 592    }
 593
 594    pub fn move_right(&self, point: DisplayPoint, clip: Clip) -> DisplayPoint {
 595        self.clip_point_with(
 596            DisplayPoint::new(point.row(), point.column() + 1),
 597            Bias::Right,
 598            clip,
 599        )
 600    }
 601
 602    pub fn clip_point_with(&self, point: DisplayPoint, bias: Bias, clip: Clip) -> DisplayPoint {
 603        let new_point = DisplayPoint(self.block_snapshot.clip_point(point.0, bias));
 604        if clip == Clip::EndOfLine && new_point.column() == self.line_len(new_point.row()) {
 605            self.move_left(new_point, Clip::None)
 606        } else {
 607            new_point
 608        }
 609    }
 610
 611    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
 612        self.clip_point_with(point, bias, self.default_clip)
 613    }
 614
 615    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Range<Anchor>>
 616    where
 617        T: ToOffset,
 618    {
 619        self.fold_snapshot.folds_in_range(range)
 620    }
 621
 622    pub fn blocks_in_range(
 623        &self,
 624        rows: Range<u32>,
 625    ) -> impl Iterator<Item = (u32, &TransformBlock)> {
 626        self.block_snapshot.blocks_in_range(rows)
 627    }
 628
 629    pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
 630        self.fold_snapshot.intersects_fold(offset)
 631    }
 632
 633    pub fn is_line_folded(&self, buffer_row: u32) -> bool {
 634        self.fold_snapshot.is_line_folded(buffer_row)
 635    }
 636
 637    pub fn is_block_line(&self, display_row: u32) -> bool {
 638        self.block_snapshot.is_block_line(display_row)
 639    }
 640
 641    pub fn soft_wrap_indent(&self, display_row: u32) -> Option<u32> {
 642        let wrap_row = self
 643            .block_snapshot
 644            .to_wrap_point(BlockPoint::new(display_row, 0))
 645            .row();
 646        self.wrap_snapshot.soft_wrap_indent(wrap_row)
 647    }
 648
 649    pub fn text(&self) -> String {
 650        self.text_chunks(0).collect()
 651    }
 652
 653    pub fn line(&self, display_row: u32) -> String {
 654        let mut result = String::new();
 655        for chunk in self.text_chunks(display_row) {
 656            if let Some(ix) = chunk.find('\n') {
 657                result.push_str(&chunk[0..ix]);
 658                break;
 659            } else {
 660                result.push_str(chunk);
 661            }
 662        }
 663        result
 664    }
 665
 666    pub fn line_indent(&self, display_row: u32) -> (u32, bool) {
 667        let mut indent = 0;
 668        let mut is_blank = true;
 669        for (c, _) in self.chars_at(DisplayPoint::new(display_row, 0)) {
 670            if c == ' ' {
 671                indent += 1;
 672            } else {
 673                is_blank = c == '\n';
 674                break;
 675            }
 676        }
 677        (indent, is_blank)
 678    }
 679
 680    pub fn line_indent_for_buffer_row(&self, buffer_row: u32) -> (u32, bool) {
 681        let (buffer, range) = self
 682            .buffer_snapshot
 683            .buffer_line_for_row(buffer_row)
 684            .unwrap();
 685
 686        let mut indent_size = 0;
 687        let mut is_blank = false;
 688        for c in buffer.chars_at(Point::new(range.start.row, 0)) {
 689            if c == ' ' || c == '\t' {
 690                indent_size += 1;
 691            } else {
 692                if c == '\n' {
 693                    is_blank = true;
 694                }
 695                break;
 696            }
 697        }
 698
 699        (indent_size, is_blank)
 700    }
 701
 702    pub fn line_len(&self, row: u32) -> u32 {
 703        self.block_snapshot.line_len(row)
 704    }
 705
 706    pub fn longest_row(&self) -> u32 {
 707        self.block_snapshot.longest_row()
 708    }
 709
 710    pub fn fold_for_line(self: &Self, buffer_row: u32) -> Option<FoldStatus> {
 711        if self.is_line_folded(buffer_row) {
 712            Some(FoldStatus::Folded)
 713        } else if self.is_foldable(buffer_row) {
 714            Some(FoldStatus::Foldable)
 715        } else {
 716            None
 717        }
 718    }
 719
 720    pub fn is_foldable(self: &Self, buffer_row: u32) -> bool {
 721        let max_row = self.buffer_snapshot.max_buffer_row();
 722        if buffer_row >= max_row {
 723            return false;
 724        }
 725
 726        let (indent_size, is_blank) = self.line_indent_for_buffer_row(buffer_row);
 727        if is_blank {
 728            return false;
 729        }
 730
 731        for next_row in (buffer_row + 1)..=max_row {
 732            let (next_indent_size, next_line_is_blank) = self.line_indent_for_buffer_row(next_row);
 733            if next_indent_size > indent_size {
 734                return true;
 735            } else if !next_line_is_blank {
 736                break;
 737            }
 738        }
 739
 740        false
 741    }
 742
 743    pub fn foldable_range(self: &Self, buffer_row: u32) -> Option<Range<Point>> {
 744        let start = Point::new(buffer_row, self.buffer_snapshot.line_len(buffer_row));
 745        if self.is_foldable(start.row) && !self.is_line_folded(start.row) {
 746            let (start_indent, _) = self.line_indent_for_buffer_row(buffer_row);
 747            let max_point = self.buffer_snapshot.max_point();
 748            let mut end = None;
 749
 750            for row in (buffer_row + 1)..=max_point.row {
 751                let (indent, is_blank) = self.line_indent_for_buffer_row(row);
 752                if !is_blank && indent <= start_indent {
 753                    let prev_row = row - 1;
 754                    end = Some(Point::new(
 755                        prev_row,
 756                        self.buffer_snapshot.line_len(prev_row),
 757                    ));
 758                    break;
 759                }
 760            }
 761            let end = end.unwrap_or(max_point);
 762            Some(start..end)
 763        } else {
 764            None
 765        }
 766    }
 767
 768    #[cfg(any(test, feature = "test-support"))]
 769    pub fn highlight_ranges<Tag: ?Sized + 'static>(
 770        &self,
 771    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
 772        let type_id = TypeId::of::<Tag>();
 773        self.text_highlights.get(&Some(type_id)).cloned()
 774    }
 775}
 776
 777#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
 778pub struct DisplayPoint(BlockPoint);
 779
 780impl Debug for DisplayPoint {
 781    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 782        f.write_fmt(format_args!(
 783            "DisplayPoint({}, {})",
 784            self.row(),
 785            self.column()
 786        ))
 787    }
 788}
 789
 790impl DisplayPoint {
 791    pub fn new(row: u32, column: u32) -> Self {
 792        Self(BlockPoint(Point::new(row, column)))
 793    }
 794
 795    pub fn zero() -> Self {
 796        Self::new(0, 0)
 797    }
 798
 799    pub fn is_zero(&self) -> bool {
 800        self.0.is_zero()
 801    }
 802
 803    pub fn row(self) -> u32 {
 804        self.0.row
 805    }
 806
 807    pub fn column(self) -> u32 {
 808        self.0.column
 809    }
 810
 811    pub fn row_mut(&mut self) -> &mut u32 {
 812        &mut self.0.row
 813    }
 814
 815    pub fn column_mut(&mut self) -> &mut u32 {
 816        &mut self.0.column
 817    }
 818
 819    pub fn to_point(self, map: &DisplaySnapshot) -> Point {
 820        map.display_point_to_point(self, Bias::Left)
 821    }
 822
 823    pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
 824        let wrap_point = map.block_snapshot.to_wrap_point(self.0);
 825        let tab_point = map.wrap_snapshot.to_tab_point(wrap_point);
 826        let fold_point = map.tab_snapshot.to_fold_point(tab_point, bias).0;
 827        let inlay_point = fold_point.to_inlay_point(&map.fold_snapshot);
 828        map.inlay_snapshot
 829            .to_buffer_offset(map.inlay_snapshot.to_offset(inlay_point))
 830    }
 831}
 832
 833impl ToDisplayPoint for usize {
 834    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
 835        map.point_to_display_point(self.to_point(&map.buffer_snapshot), Bias::Left)
 836    }
 837}
 838
 839impl ToDisplayPoint for OffsetUtf16 {
 840    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
 841        self.to_offset(&map.buffer_snapshot).to_display_point(map)
 842    }
 843}
 844
 845impl ToDisplayPoint for Point {
 846    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
 847        map.point_to_display_point(*self, Bias::Left)
 848    }
 849}
 850
 851impl ToDisplayPoint for Anchor {
 852    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
 853        self.to_point(&map.buffer_snapshot).to_display_point(map)
 854    }
 855}
 856
 857pub fn next_rows(display_row: u32, display_map: &DisplaySnapshot) -> impl Iterator<Item = u32> {
 858    let max_row = display_map.max_point().row();
 859    let start_row = display_row + 1;
 860    let mut current = None;
 861    std::iter::from_fn(move || {
 862        if current == None {
 863            current = Some(start_row);
 864        } else {
 865            current = Some(current.unwrap() + 1)
 866        }
 867        if current.unwrap() > max_row {
 868            None
 869        } else {
 870            current
 871        }
 872    })
 873}
 874
 875#[cfg(test)]
 876pub mod tests {
 877    use super::*;
 878    use crate::{movement, test::marked_display_snapshot};
 879    use gpui::{color::Color, elements::*, test::observe, AppContext};
 880    use language::{
 881        language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
 882        Buffer, Language, LanguageConfig, SelectionGoal,
 883    };
 884    use rand::{prelude::*, Rng};
 885    use settings::SettingsStore;
 886    use smol::stream::StreamExt;
 887    use std::{env, sync::Arc};
 888    use theme::SyntaxTheme;
 889    use util::test::{marked_text_offsets, marked_text_ranges, sample_text};
 890    use Bias::*;
 891
 892    #[gpui::test(iterations = 100)]
 893    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
 894        cx.foreground().set_block_on_ticks(0..=50);
 895        cx.foreground().forbid_parking();
 896        let operations = env::var("OPERATIONS")
 897            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
 898            .unwrap_or(10);
 899
 900        let font_cache = cx.font_cache().clone();
 901        let mut tab_size = rng.gen_range(1..=4);
 902        let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
 903        let excerpt_header_height = rng.gen_range(1..=5);
 904        let family_id = font_cache
 905            .load_family(&["Helvetica"], &Default::default())
 906            .unwrap();
 907        let font_id = font_cache
 908            .select_font(family_id, &Default::default())
 909            .unwrap();
 910        let font_size = 14.0;
 911        let max_wrap_width = 300.0;
 912        let mut wrap_width = if rng.gen_bool(0.1) {
 913            None
 914        } else {
 915            Some(rng.gen_range(0.0..=max_wrap_width))
 916        };
 917
 918        log::info!("tab size: {}", tab_size);
 919        log::info!("wrap width: {:?}", wrap_width);
 920
 921        cx.update(|cx| {
 922            init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
 923        });
 924
 925        let buffer = cx.update(|cx| {
 926            if rng.gen() {
 927                let len = rng.gen_range(0..10);
 928                let text = util::RandomCharIter::new(&mut rng)
 929                    .take(len)
 930                    .collect::<String>();
 931                MultiBuffer::build_simple(&text, cx)
 932            } else {
 933                MultiBuffer::build_random(&mut rng, cx)
 934            }
 935        });
 936
 937        let map = cx.add_model(|cx| {
 938            DisplayMap::new(
 939                buffer.clone(),
 940                font_id,
 941                font_size,
 942                wrap_width,
 943                buffer_start_excerpt_header_height,
 944                excerpt_header_height,
 945                cx,
 946            )
 947        });
 948        let mut notifications = observe(&map, cx);
 949        let mut fold_count = 0;
 950        let mut blocks = Vec::new();
 951
 952        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
 953        log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
 954        log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
 955        log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
 956        log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
 957        log::info!("block text: {:?}", snapshot.block_snapshot.text());
 958        log::info!("display text: {:?}", snapshot.text());
 959
 960        for _i in 0..operations {
 961            match rng.gen_range(0..100) {
 962                0..=19 => {
 963                    wrap_width = if rng.gen_bool(0.2) {
 964                        None
 965                    } else {
 966                        Some(rng.gen_range(0.0..=max_wrap_width))
 967                    };
 968                    log::info!("setting wrap width to {:?}", wrap_width);
 969                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
 970                }
 971                20..=29 => {
 972                    let mut tab_sizes = vec![1, 2, 3, 4];
 973                    tab_sizes.remove((tab_size - 1) as usize);
 974                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
 975                    log::info!("setting tab size to {:?}", tab_size);
 976                    cx.update(|cx| {
 977                        cx.update_global::<SettingsStore, _, _>(|store, cx| {
 978                            store.update_user_settings::<AllLanguageSettings>(cx, |s| {
 979                                s.defaults.tab_size = NonZeroU32::new(tab_size);
 980                            });
 981                        });
 982                    });
 983                }
 984                30..=44 => {
 985                    map.update(cx, |map, cx| {
 986                        if rng.gen() || blocks.is_empty() {
 987                            let buffer = map.snapshot(cx).buffer_snapshot;
 988                            let block_properties = (0..rng.gen_range(1..=1))
 989                                .map(|_| {
 990                                    let position =
 991                                        buffer.anchor_after(buffer.clip_offset(
 992                                            rng.gen_range(0..=buffer.len()),
 993                                            Bias::Left,
 994                                        ));
 995
 996                                    let disposition = if rng.gen() {
 997                                        BlockDisposition::Above
 998                                    } else {
 999                                        BlockDisposition::Below
1000                                    };
1001                                    let height = rng.gen_range(1..5);
1002                                    log::info!(
1003                                        "inserting block {:?} {:?} with height {}",
1004                                        disposition,
1005                                        position.to_point(&buffer),
1006                                        height
1007                                    );
1008                                    BlockProperties {
1009                                        style: BlockStyle::Fixed,
1010                                        position,
1011                                        height,
1012                                        disposition,
1013                                        render: Arc::new(|_| Empty::new().into_any()),
1014                                    }
1015                                })
1016                                .collect::<Vec<_>>();
1017                            blocks.extend(map.insert_blocks(block_properties, cx));
1018                        } else {
1019                            blocks.shuffle(&mut rng);
1020                            let remove_count = rng.gen_range(1..=4.min(blocks.len()));
1021                            let block_ids_to_remove = (0..remove_count)
1022                                .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
1023                                .collect();
1024                            log::info!("removing block ids {:?}", block_ids_to_remove);
1025                            map.remove_blocks(block_ids_to_remove, cx);
1026                        }
1027                    });
1028                }
1029                45..=79 => {
1030                    let mut ranges = Vec::new();
1031                    for _ in 0..rng.gen_range(1..=3) {
1032                        buffer.read_with(cx, |buffer, cx| {
1033                            let buffer = buffer.read(cx);
1034                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1035                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1036                            ranges.push(start..end);
1037                        });
1038                    }
1039
1040                    if rng.gen() && fold_count > 0 {
1041                        log::info!("unfolding ranges: {:?}", ranges);
1042                        map.update(cx, |map, cx| {
1043                            map.unfold(ranges, true, cx);
1044                        });
1045                    } else {
1046                        log::info!("folding ranges: {:?}", ranges);
1047                        map.update(cx, |map, cx| {
1048                            map.fold(ranges, cx);
1049                        });
1050                    }
1051                }
1052                _ => {
1053                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
1054                }
1055            }
1056
1057            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
1058                notifications.next().await.unwrap();
1059            }
1060
1061            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1062            fold_count = snapshot.fold_count();
1063            log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
1064            log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
1065            log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
1066            log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
1067            log::info!("block text: {:?}", snapshot.block_snapshot.text());
1068            log::info!("display text: {:?}", snapshot.text());
1069
1070            // Line boundaries
1071            let buffer = &snapshot.buffer_snapshot;
1072            for _ in 0..5 {
1073                let row = rng.gen_range(0..=buffer.max_point().row);
1074                let column = rng.gen_range(0..=buffer.line_len(row));
1075                let point = buffer.clip_point(Point::new(row, column), Left);
1076
1077                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
1078                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
1079
1080                assert!(prev_buffer_bound <= point);
1081                assert!(next_buffer_bound >= point);
1082                assert_eq!(prev_buffer_bound.column, 0);
1083                assert_eq!(prev_display_bound.column(), 0);
1084                if next_buffer_bound < buffer.max_point() {
1085                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
1086                }
1087
1088                assert_eq!(
1089                    prev_display_bound,
1090                    prev_buffer_bound.to_display_point(&snapshot),
1091                    "row boundary before {:?}. reported buffer row boundary: {:?}",
1092                    point,
1093                    prev_buffer_bound
1094                );
1095                assert_eq!(
1096                    next_display_bound,
1097                    next_buffer_bound.to_display_point(&snapshot),
1098                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
1099                    point,
1100                    next_buffer_bound
1101                );
1102                assert_eq!(
1103                    prev_buffer_bound,
1104                    prev_display_bound.to_point(&snapshot),
1105                    "row boundary before {:?}. reported display row boundary: {:?}",
1106                    point,
1107                    prev_display_bound
1108                );
1109                assert_eq!(
1110                    next_buffer_bound,
1111                    next_display_bound.to_point(&snapshot),
1112                    "row boundary after {:?}. reported display row boundary: {:?}",
1113                    point,
1114                    next_display_bound
1115                );
1116            }
1117
1118            // Movement
1119            let min_point = snapshot.clip_point(DisplayPoint::new(0, 0), Left);
1120            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
1121            for _ in 0..5 {
1122                let row = rng.gen_range(0..=snapshot.max_point().row());
1123                let column = rng.gen_range(0..=snapshot.line_len(row));
1124                let point = snapshot.clip_point(DisplayPoint::new(row, column), Left);
1125
1126                log::info!("Moving from point {:?}", point);
1127
1128                let moved_right = movement::right(&snapshot, point);
1129                log::info!("Right {:?}", moved_right);
1130                if point < max_point {
1131                    assert!(moved_right > point);
1132                    if point.column() == snapshot.line_len(point.row())
1133                        || snapshot.soft_wrap_indent(point.row()).is_some()
1134                            && point.column() == snapshot.line_len(point.row()) - 1
1135                    {
1136                        assert!(moved_right.row() > point.row());
1137                    }
1138                } else {
1139                    assert_eq!(moved_right, point);
1140                }
1141
1142                let moved_left = movement::left(&snapshot, point);
1143                log::info!("Left {:?}", moved_left);
1144                if point > min_point {
1145                    assert!(moved_left < point);
1146                    if point.column() == 0 {
1147                        assert!(moved_left.row() < point.row());
1148                    }
1149                } else {
1150                    assert_eq!(moved_left, point);
1151                }
1152            }
1153        }
1154    }
1155
1156    #[gpui::test(retries = 5)]
1157    fn test_soft_wraps(cx: &mut AppContext) {
1158        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
1159        init_test(cx, |_| {});
1160
1161        let font_cache = cx.font_cache();
1162
1163        let family_id = font_cache
1164            .load_family(&["Helvetica"], &Default::default())
1165            .unwrap();
1166        let font_id = font_cache
1167            .select_font(family_id, &Default::default())
1168            .unwrap();
1169        let font_size = 12.0;
1170        let wrap_width = Some(64.);
1171
1172        let text = "one two three four five\nsix seven eight";
1173        let buffer = MultiBuffer::build_simple(text, cx);
1174        let map = cx.add_model(|cx| {
1175            DisplayMap::new(buffer.clone(), font_id, font_size, wrap_width, 1, 1, cx)
1176        });
1177
1178        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1179        assert_eq!(
1180            snapshot.text_chunks(0).collect::<String>(),
1181            "one two \nthree four \nfive\nsix seven \neight"
1182        );
1183        assert_eq!(
1184            snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Left),
1185            DisplayPoint::new(0, 7)
1186        );
1187        assert_eq!(
1188            snapshot.clip_point(DisplayPoint::new(0, 8), Bias::Right),
1189            DisplayPoint::new(1, 0)
1190        );
1191        assert_eq!(
1192            movement::right(&snapshot, DisplayPoint::new(0, 7)),
1193            DisplayPoint::new(1, 0)
1194        );
1195        assert_eq!(
1196            movement::left(&snapshot, DisplayPoint::new(1, 0)),
1197            DisplayPoint::new(0, 7)
1198        );
1199        assert_eq!(
1200            movement::up(
1201                &snapshot,
1202                DisplayPoint::new(1, 10),
1203                SelectionGoal::None,
1204                false
1205            ),
1206            (DisplayPoint::new(0, 7), SelectionGoal::Column(10))
1207        );
1208        assert_eq!(
1209            movement::down(
1210                &snapshot,
1211                DisplayPoint::new(0, 7),
1212                SelectionGoal::Column(10),
1213                false
1214            ),
1215            (DisplayPoint::new(1, 10), SelectionGoal::Column(10))
1216        );
1217        assert_eq!(
1218            movement::down(
1219                &snapshot,
1220                DisplayPoint::new(1, 10),
1221                SelectionGoal::Column(10),
1222                false
1223            ),
1224            (DisplayPoint::new(2, 4), SelectionGoal::Column(10))
1225        );
1226
1227        let ix = snapshot.buffer_snapshot.text().find("seven").unwrap();
1228        buffer.update(cx, |buffer, cx| {
1229            buffer.edit([(ix..ix, "and ")], None, cx);
1230        });
1231
1232        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1233        assert_eq!(
1234            snapshot.text_chunks(1).collect::<String>(),
1235            "three four \nfive\nsix and \nseven eight"
1236        );
1237
1238        // Re-wrap on font size changes
1239        map.update(cx, |map, cx| map.set_font(font_id, font_size + 3., cx));
1240
1241        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1242        assert_eq!(
1243            snapshot.text_chunks(1).collect::<String>(),
1244            "three \nfour five\nsix and \nseven \neight"
1245        )
1246    }
1247
1248    #[gpui::test]
1249    fn test_text_chunks(cx: &mut gpui::AppContext) {
1250        init_test(cx, |_| {});
1251
1252        let text = sample_text(6, 6, 'a');
1253        let buffer = MultiBuffer::build_simple(&text, cx);
1254        let family_id = cx
1255            .font_cache()
1256            .load_family(&["Helvetica"], &Default::default())
1257            .unwrap();
1258        let font_id = cx
1259            .font_cache()
1260            .select_font(family_id, &Default::default())
1261            .unwrap();
1262        let font_size = 14.0;
1263        let map =
1264            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
1265
1266        buffer.update(cx, |buffer, cx| {
1267            buffer.edit(
1268                vec![
1269                    (Point::new(1, 0)..Point::new(1, 0), "\t"),
1270                    (Point::new(1, 1)..Point::new(1, 1), "\t"),
1271                    (Point::new(2, 1)..Point::new(2, 1), "\t"),
1272                ],
1273                None,
1274                cx,
1275            )
1276        });
1277
1278        assert_eq!(
1279            map.update(cx, |map, cx| map.snapshot(cx))
1280                .text_chunks(1)
1281                .collect::<String>()
1282                .lines()
1283                .next(),
1284            Some("    b   bbbbb")
1285        );
1286        assert_eq!(
1287            map.update(cx, |map, cx| map.snapshot(cx))
1288                .text_chunks(2)
1289                .collect::<String>()
1290                .lines()
1291                .next(),
1292            Some("c   ccccc")
1293        );
1294    }
1295
1296    #[gpui::test]
1297    async fn test_chunks(cx: &mut gpui::TestAppContext) {
1298        use unindent::Unindent as _;
1299
1300        let text = r#"
1301            fn outer() {}
1302
1303            mod module {
1304                fn inner() {}
1305            }"#
1306        .unindent();
1307
1308        let theme = SyntaxTheme::new(vec![
1309            ("mod.body".to_string(), Color::red().into()),
1310            ("fn.name".to_string(), Color::blue().into()),
1311        ]);
1312        let language = Arc::new(
1313            Language::new(
1314                LanguageConfig {
1315                    name: "Test".into(),
1316                    path_suffixes: vec![".test".to_string()],
1317                    ..Default::default()
1318                },
1319                Some(tree_sitter_rust::language()),
1320            )
1321            .with_highlights_query(
1322                r#"
1323                (mod_item name: (identifier) body: _ @mod.body)
1324                (function_item name: (identifier) @fn.name)
1325                "#,
1326            )
1327            .unwrap(),
1328        );
1329        language.set_theme(&theme);
1330
1331        cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
1332
1333        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
1334        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
1335        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1336
1337        let font_cache = cx.font_cache();
1338        let family_id = font_cache
1339            .load_family(&["Helvetica"], &Default::default())
1340            .unwrap();
1341        let font_id = font_cache
1342            .select_font(family_id, &Default::default())
1343            .unwrap();
1344        let font_size = 14.0;
1345
1346        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
1347        assert_eq!(
1348            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
1349            vec![
1350                ("fn ".to_string(), None),
1351                ("outer".to_string(), Some(Color::blue())),
1352                ("() {}\n\nmod module ".to_string(), None),
1353                ("{\n    fn ".to_string(), Some(Color::red())),
1354                ("inner".to_string(), Some(Color::blue())),
1355                ("() {}\n}".to_string(), Some(Color::red())),
1356            ]
1357        );
1358        assert_eq!(
1359            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
1360            vec![
1361                ("    fn ".to_string(), Some(Color::red())),
1362                ("inner".to_string(), Some(Color::blue())),
1363                ("() {}\n}".to_string(), Some(Color::red())),
1364            ]
1365        );
1366
1367        map.update(cx, |map, cx| {
1368            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
1369        });
1370        assert_eq!(
1371            cx.update(|cx| syntax_chunks(0..2, &map, &theme, cx)),
1372            vec![
1373                ("fn ".to_string(), None),
1374                ("out".to_string(), Some(Color::blue())),
1375                ("β‹―".to_string(), None),
1376                ("  fn ".to_string(), Some(Color::red())),
1377                ("inner".to_string(), Some(Color::blue())),
1378                ("() {}\n}".to_string(), Some(Color::red())),
1379            ]
1380        );
1381    }
1382
1383    #[gpui::test]
1384    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
1385        use unindent::Unindent as _;
1386
1387        cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX);
1388
1389        let text = r#"
1390            fn outer() {}
1391
1392            mod module {
1393                fn inner() {}
1394            }"#
1395        .unindent();
1396
1397        let theme = SyntaxTheme::new(vec![
1398            ("mod.body".to_string(), Color::red().into()),
1399            ("fn.name".to_string(), Color::blue().into()),
1400        ]);
1401        let language = Arc::new(
1402            Language::new(
1403                LanguageConfig {
1404                    name: "Test".into(),
1405                    path_suffixes: vec![".test".to_string()],
1406                    ..Default::default()
1407                },
1408                Some(tree_sitter_rust::language()),
1409            )
1410            .with_highlights_query(
1411                r#"
1412                (mod_item name: (identifier) body: _ @mod.body)
1413                (function_item name: (identifier) @fn.name)
1414                "#,
1415            )
1416            .unwrap(),
1417        );
1418        language.set_theme(&theme);
1419
1420        cx.update(|cx| init_test(cx, |_| {}));
1421
1422        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
1423        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
1424        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1425
1426        let font_cache = cx.font_cache();
1427
1428        let family_id = font_cache
1429            .load_family(&["Courier"], &Default::default())
1430            .unwrap();
1431        let font_id = font_cache
1432            .select_font(family_id, &Default::default())
1433            .unwrap();
1434        let font_size = 16.0;
1435
1436        let map =
1437            cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, Some(40.0), 1, 1, cx));
1438        assert_eq!(
1439            cx.update(|cx| syntax_chunks(0..5, &map, &theme, cx)),
1440            [
1441                ("fn \n".to_string(), None),
1442                ("oute\nr".to_string(), Some(Color::blue())),
1443                ("() \n{}\n\n".to_string(), None),
1444            ]
1445        );
1446        assert_eq!(
1447            cx.update(|cx| syntax_chunks(3..5, &map, &theme, cx)),
1448            [("{}\n\n".to_string(), None)]
1449        );
1450
1451        map.update(cx, |map, cx| {
1452            map.fold(vec![Point::new(0, 6)..Point::new(3, 2)], cx)
1453        });
1454        assert_eq!(
1455            cx.update(|cx| syntax_chunks(1..4, &map, &theme, cx)),
1456            [
1457                ("out".to_string(), Some(Color::blue())),
1458                ("β‹―\n".to_string(), None),
1459                ("  \nfn ".to_string(), Some(Color::red())),
1460                ("i\n".to_string(), Some(Color::blue()))
1461            ]
1462        );
1463    }
1464
1465    #[gpui::test]
1466    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
1467        cx.update(|cx| init_test(cx, |_| {}));
1468
1469        let theme = SyntaxTheme::new(vec![
1470            ("operator".to_string(), Color::red().into()),
1471            ("string".to_string(), Color::green().into()),
1472        ]);
1473        let language = Arc::new(
1474            Language::new(
1475                LanguageConfig {
1476                    name: "Test".into(),
1477                    path_suffixes: vec![".test".to_string()],
1478                    ..Default::default()
1479                },
1480                Some(tree_sitter_rust::language()),
1481            )
1482            .with_highlights_query(
1483                r#"
1484                ":" @operator
1485                (string_literal) @string
1486                "#,
1487            )
1488            .unwrap(),
1489        );
1490        language.set_theme(&theme);
1491
1492        let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
1493
1494        let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(language, cx));
1495        buffer.condition(cx, |buf, _| !buf.is_parsing()).await;
1496
1497        let buffer = cx.add_model(|cx| MultiBuffer::singleton(buffer, cx));
1498        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
1499
1500        let font_cache = cx.font_cache();
1501        let family_id = font_cache
1502            .load_family(&["Courier"], &Default::default())
1503            .unwrap();
1504        let font_id = font_cache
1505            .select_font(family_id, &Default::default())
1506            .unwrap();
1507        let font_size = 16.0;
1508        let map = cx.add_model(|cx| DisplayMap::new(buffer, font_id, font_size, None, 1, 1, cx));
1509
1510        enum MyType {}
1511
1512        let style = HighlightStyle {
1513            color: Some(Color::blue()),
1514            ..Default::default()
1515        };
1516
1517        map.update(cx, |map, _cx| {
1518            map.highlight_text(
1519                TypeId::of::<MyType>(),
1520                highlighted_ranges
1521                    .into_iter()
1522                    .map(|range| {
1523                        buffer_snapshot.anchor_before(range.start)
1524                            ..buffer_snapshot.anchor_before(range.end)
1525                    })
1526                    .collect(),
1527                style,
1528            );
1529        });
1530
1531        assert_eq!(
1532            cx.update(|cx| chunks(0..10, &map, &theme, cx)),
1533            [
1534                ("const ".to_string(), None, None),
1535                ("a".to_string(), None, Some(Color::blue())),
1536                (":".to_string(), Some(Color::red()), None),
1537                (" B = ".to_string(), None, None),
1538                ("\"c ".to_string(), Some(Color::green()), None),
1539                ("d".to_string(), Some(Color::green()), Some(Color::blue())),
1540                ("\"".to_string(), Some(Color::green()), None),
1541            ]
1542        );
1543    }
1544
1545    #[gpui::test]
1546    fn test_clip_point(cx: &mut gpui::AppContext) {
1547        init_test(cx, |_| {});
1548
1549        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::AppContext) {
1550            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
1551
1552            match bias {
1553                Bias::Left => {
1554                    if shift_right {
1555                        *markers[1].column_mut() += 1;
1556                    }
1557
1558                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
1559                }
1560                Bias::Right => {
1561                    if shift_right {
1562                        *markers[0].column_mut() += 1;
1563                    }
1564
1565                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
1566                }
1567            };
1568        }
1569
1570        use Bias::{Left, Right};
1571        assert("Λ‡Λ‡Ξ±", false, Left, cx);
1572        assert("Λ‡Λ‡Ξ±", true, Left, cx);
1573        assert("Λ‡Λ‡Ξ±", false, Right, cx);
1574        assert("Λ‡Ξ±Λ‡", true, Right, cx);
1575        assert("Λ‡Λ‡βœ‹", false, Left, cx);
1576        assert("Λ‡Λ‡βœ‹", true, Left, cx);
1577        assert("Λ‡Λ‡βœ‹", false, Right, cx);
1578        assert("Λ‡βœ‹Λ‡", true, Right, cx);
1579        assert("Λ‡Λ‡πŸ", false, Left, cx);
1580        assert("Λ‡Λ‡πŸ", true, Left, cx);
1581        assert("Λ‡Λ‡πŸ", false, Right, cx);
1582        assert("Λ‡πŸΛ‡", true, Right, cx);
1583        assert("Λ‡Λ‡\t", false, Left, cx);
1584        assert("Λ‡Λ‡\t", true, Left, cx);
1585        assert("Λ‡Λ‡\t", false, Right, cx);
1586        assert("ˇ\tˇ", true, Right, cx);
1587        assert(" Λ‡Λ‡\t", false, Left, cx);
1588        assert(" Λ‡Λ‡\t", true, Left, cx);
1589        assert(" Λ‡Λ‡\t", false, Right, cx);
1590        assert(" ˇ\tˇ", true, Right, cx);
1591        assert("   Λ‡Λ‡\t", false, Left, cx);
1592        assert("   Λ‡Λ‡\t", false, Right, cx);
1593    }
1594
1595    #[gpui::test]
1596    fn test_clip_at_line_ends(cx: &mut gpui::AppContext) {
1597        init_test(cx, |_| {});
1598
1599        fn assert(text: &str, cx: &mut gpui::AppContext) {
1600            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
1601            unmarked_snapshot.default_clip = Clip::EndOfLine;
1602            assert_eq!(
1603                unmarked_snapshot.clip_point(markers[1], Bias::Left),
1604                markers[0]
1605            );
1606        }
1607
1608        assert("Λ‡Λ‡", cx);
1609        assert("ˇaˇ", cx);
1610        assert("aˇbˇ", cx);
1611        assert("aˇαˇ", cx);
1612    }
1613
1614    #[gpui::test]
1615    fn test_tabs_with_multibyte_chars(cx: &mut gpui::AppContext) {
1616        init_test(cx, |_| {});
1617
1618        let text = "βœ…\t\tΞ±\nΞ²\t\nπŸ€Ξ²\t\tΞ³";
1619        let buffer = MultiBuffer::build_simple(text, cx);
1620        let font_cache = cx.font_cache();
1621        let family_id = font_cache
1622            .load_family(&["Helvetica"], &Default::default())
1623            .unwrap();
1624        let font_id = font_cache
1625            .select_font(family_id, &Default::default())
1626            .unwrap();
1627        let font_size = 14.0;
1628
1629        let map =
1630            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
1631        let map = map.update(cx, |map, cx| map.snapshot(cx));
1632        assert_eq!(map.text(), "βœ…       Ξ±\nΞ²   \nπŸ€Ξ²      Ξ³");
1633        assert_eq!(
1634            map.text_chunks(0).collect::<String>(),
1635            "βœ…       Ξ±\nΞ²   \nπŸ€Ξ²      Ξ³"
1636        );
1637        assert_eq!(map.text_chunks(1).collect::<String>(), "Ξ²   \nπŸ€Ξ²      Ξ³");
1638        assert_eq!(map.text_chunks(2).collect::<String>(), "πŸ€Ξ²      Ξ³");
1639
1640        let point = Point::new(0, "βœ…\t\t".len() as u32);
1641        let display_point = DisplayPoint::new(0, "βœ…       ".len() as u32);
1642        assert_eq!(point.to_display_point(&map), display_point);
1643        assert_eq!(display_point.to_point(&map), point);
1644
1645        let point = Point::new(1, "Ξ²\t".len() as u32);
1646        let display_point = DisplayPoint::new(1, "Ξ²   ".len() as u32);
1647        assert_eq!(point.to_display_point(&map), display_point);
1648        assert_eq!(display_point.to_point(&map), point,);
1649
1650        let point = Point::new(2, "πŸ€Ξ²\t\t".len() as u32);
1651        let display_point = DisplayPoint::new(2, "πŸ€Ξ²      ".len() as u32);
1652        assert_eq!(point.to_display_point(&map), display_point);
1653        assert_eq!(display_point.to_point(&map), point,);
1654
1655        // Display points inside of expanded tabs
1656        assert_eq!(
1657            DisplayPoint::new(0, "βœ…      ".len() as u32).to_point(&map),
1658            Point::new(0, "βœ…\t".len() as u32),
1659        );
1660        assert_eq!(
1661            DisplayPoint::new(0, "βœ… ".len() as u32).to_point(&map),
1662            Point::new(0, "βœ…".len() as u32),
1663        );
1664
1665        // Clipping display points inside of multi-byte characters
1666        assert_eq!(
1667            map.clip_point(DisplayPoint::new(0, "βœ…".len() as u32 - 1), Left),
1668            DisplayPoint::new(0, 0)
1669        );
1670        assert_eq!(
1671            map.clip_point(DisplayPoint::new(0, "βœ…".len() as u32 - 1), Bias::Right),
1672            DisplayPoint::new(0, "βœ…".len() as u32)
1673        );
1674    }
1675
1676    #[gpui::test]
1677    fn test_max_point(cx: &mut gpui::AppContext) {
1678        init_test(cx, |_| {});
1679
1680        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
1681        let font_cache = cx.font_cache();
1682        let family_id = font_cache
1683            .load_family(&["Helvetica"], &Default::default())
1684            .unwrap();
1685        let font_id = font_cache
1686            .select_font(family_id, &Default::default())
1687            .unwrap();
1688        let font_size = 14.0;
1689        let map =
1690            cx.add_model(|cx| DisplayMap::new(buffer.clone(), font_id, font_size, None, 1, 1, cx));
1691        assert_eq!(
1692            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
1693            DisplayPoint::new(1, 11)
1694        )
1695    }
1696
1697    #[test]
1698    fn test_find_internal() {
1699        assert("This is a Λ‡test of find internal", "test");
1700        assert("Some text ˇaˇaˇaa with repeated characters", "aa");
1701
1702        fn assert(marked_text: &str, target: &str) {
1703            let (text, expected_offsets) = marked_text_offsets(marked_text);
1704
1705            let chars = text
1706                .chars()
1707                .enumerate()
1708                .map(|(index, ch)| (ch, DisplayPoint::new(0, index as u32)));
1709            let target = target.chars();
1710
1711            assert_eq!(
1712                expected_offsets
1713                    .into_iter()
1714                    .map(|offset| offset as u32)
1715                    .collect::<Vec<_>>(),
1716                DisplaySnapshot::find_internal(chars, target.collect(), |_, _| true)
1717                    .map(|point| point.column())
1718                    .collect::<Vec<_>>()
1719            )
1720        }
1721    }
1722
1723    fn syntax_chunks<'a>(
1724        rows: Range<u32>,
1725        map: &ModelHandle<DisplayMap>,
1726        theme: &'a SyntaxTheme,
1727        cx: &mut AppContext,
1728    ) -> Vec<(String, Option<Color>)> {
1729        chunks(rows, map, theme, cx)
1730            .into_iter()
1731            .map(|(text, color, _)| (text, color))
1732            .collect()
1733    }
1734
1735    fn chunks<'a>(
1736        rows: Range<u32>,
1737        map: &ModelHandle<DisplayMap>,
1738        theme: &'a SyntaxTheme,
1739        cx: &mut AppContext,
1740    ) -> Vec<(String, Option<Color>, Option<Color>)> {
1741        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1742        let mut chunks: Vec<(String, Option<Color>, Option<Color>)> = Vec::new();
1743        for chunk in snapshot.chunks(rows, true, None, None) {
1744            let syntax_color = chunk
1745                .syntax_highlight_id
1746                .and_then(|id| id.style(theme)?.color);
1747            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
1748            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut() {
1749                if syntax_color == *last_syntax_color && highlight_color == *last_highlight_color {
1750                    last_chunk.push_str(chunk.text);
1751                    continue;
1752                }
1753            }
1754            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
1755        }
1756        chunks
1757    }
1758
1759    fn init_test(cx: &mut AppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
1760        cx.foreground().forbid_parking();
1761        cx.set_global(SettingsStore::test(cx));
1762        language::init(cx);
1763        cx.update_global::<SettingsStore, _, _>(|store, cx| {
1764            store.update_user_settings::<AllLanguageSettings>(cx, f);
1765        });
1766    }
1767}