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