display_map.rs

   1//! This module defines where the text should be displayed in an [`Editor`][Editor].
   2//!
   3//! Not literally though - rendering, layout and all that jazz is a responsibility of [`EditorElement`][EditorElement].
   4//! Instead, [`DisplayMap`] decides where Inlays/Inlay hints are displayed, when
   5//! to apply a soft wrap, where to add fold indicators, whether there are any tabs in the buffer that
   6//! we display as spaces and where to display custom blocks (like diagnostics).
   7//! Seems like a lot? That's because it is. [`DisplayMap`] is conceptually made up
   8//! of several smaller structures that form a hierarchy (starting at the bottom):
   9//! - [`InlayMap`] that decides where the [`Inlay`]s should be displayed.
  10//! - [`FoldMap`] that decides where the fold indicators should be; it also tracks parts of a source file that are currently folded.
  11//! - [`TabMap`] that keeps track of hard tabs in a buffer.
  12//! - [`WrapMap`] that handles soft wrapping.
  13//! - [`BlockMap`] that tracks custom blocks such as diagnostics that should be displayed within buffer.
  14//! - [`DisplayMap`] that adds background highlights to the regions of text.
  15//!   Each one of those builds on top of preceding map.
  16//!
  17//! [Editor]: crate::Editor
  18//! [EditorElement]: crate::element::EditorElement
  19
  20mod block_map;
  21mod crease_map;
  22mod custom_highlights;
  23mod fold_map;
  24mod inlay_map;
  25pub(crate) mod invisibles;
  26mod tab_map;
  27mod wrap_map;
  28
  29use crate::{
  30    hover_links::InlayHighlight, movement::TextLayoutDetails, EditorStyle, InlayId, RowExt,
  31};
  32pub use block_map::{
  33    Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,
  34    BlockPoint, BlockProperties, BlockRows, BlockStyle, CustomBlockId, RenderBlock,
  35    StickyHeaderExcerpt,
  36};
  37use block_map::{BlockRow, BlockSnapshot};
  38use collections::{HashMap, HashSet};
  39pub use crease_map::*;
  40pub use fold_map::{Fold, FoldId, FoldPlaceholder, FoldPoint};
  41use fold_map::{FoldMap, FoldSnapshot};
  42use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};
  43pub use inlay_map::Inlay;
  44use inlay_map::{InlayMap, InlaySnapshot};
  45pub use inlay_map::{InlayOffset, InlayPoint};
  46use invisibles::{is_invisible, replacement};
  47use language::{
  48    language_settings::language_settings, ChunkRenderer, OffsetUtf16, Point,
  49    Subscription as BufferSubscription,
  50};
  51use lsp::DiagnosticSeverity;
  52use multi_buffer::{
  53    Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,
  54    RowInfo, ToOffset, ToPoint,
  55};
  56use serde::Deserialize;
  57use std::{
  58    any::TypeId,
  59    borrow::Cow,
  60    fmt::Debug,
  61    iter,
  62    num::NonZeroU32,
  63    ops::{Add, Range, Sub},
  64    sync::Arc,
  65};
  66use sum_tree::{Bias, TreeMap};
  67use tab_map::{TabMap, TabSnapshot};
  68use text::{BufferId, LineIndent};
  69use ui::{px, SharedString};
  70use unicode_segmentation::UnicodeSegmentation;
  71use wrap_map::{WrapMap, WrapSnapshot};
  72
  73#[derive(Copy, Clone, Debug, PartialEq, Eq)]
  74pub enum FoldStatus {
  75    Folded,
  76    Foldable,
  77}
  78
  79pub trait ToDisplayPoint {
  80    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
  81}
  82
  83type TextHighlights = TreeMap<TypeId, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
  84type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
  85
  86/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
  87/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.
  88///
  89/// See the [module level documentation](self) for more information.
  90pub struct DisplayMap {
  91    /// The buffer that we are displaying.
  92    buffer: Entity<MultiBuffer>,
  93    buffer_subscription: BufferSubscription,
  94    /// Decides where the [`Inlay`]s should be displayed.
  95    inlay_map: InlayMap,
  96    /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
  97    fold_map: FoldMap,
  98    /// Keeps track of hard tabs in a buffer.
  99    tab_map: TabMap,
 100    /// Handles soft wrapping.
 101    wrap_map: Entity<WrapMap>,
 102    /// Tracks custom blocks such as diagnostics that should be displayed within buffer.
 103    block_map: BlockMap,
 104    /// Regions of text that should be highlighted.
 105    text_highlights: TextHighlights,
 106    /// Regions of inlays that should be highlighted.
 107    inlay_highlights: InlayHighlights,
 108    /// A container for explicitly foldable ranges, which supersede indentation based fold range suggestions.
 109    crease_map: CreaseMap,
 110    pub(crate) fold_placeholder: FoldPlaceholder,
 111    pub clip_at_line_ends: bool,
 112    pub(crate) masked: bool,
 113}
 114
 115impl DisplayMap {
 116    pub fn new(
 117        buffer: Entity<MultiBuffer>,
 118        font: Font,
 119        font_size: Pixels,
 120        wrap_width: Option<Pixels>,
 121        show_excerpt_controls: bool,
 122        buffer_header_height: u32,
 123        excerpt_header_height: u32,
 124        excerpt_footer_height: u32,
 125        fold_placeholder: FoldPlaceholder,
 126        cx: &mut Context<Self>,
 127    ) -> Self {
 128        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
 129
 130        let tab_size = Self::tab_size(&buffer, cx);
 131        let buffer_snapshot = buffer.read(cx).snapshot(cx);
 132        let crease_map = CreaseMap::new(&buffer_snapshot);
 133        let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);
 134        let (fold_map, snapshot) = FoldMap::new(snapshot);
 135        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
 136        let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
 137        let block_map = BlockMap::new(
 138            snapshot,
 139            show_excerpt_controls,
 140            buffer_header_height,
 141            excerpt_header_height,
 142            excerpt_footer_height,
 143        );
 144
 145        cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
 146
 147        DisplayMap {
 148            buffer,
 149            buffer_subscription,
 150            fold_map,
 151            inlay_map,
 152            tab_map,
 153            wrap_map,
 154            block_map,
 155            crease_map,
 156            fold_placeholder,
 157            text_highlights: Default::default(),
 158            inlay_highlights: Default::default(),
 159            clip_at_line_ends: false,
 160            masked: false,
 161        }
 162    }
 163
 164    pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {
 165        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 166        let edits = self.buffer_subscription.consume().into_inner();
 167        let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
 168        let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot.clone(), edits);
 169        let tab_size = Self::tab_size(&self.buffer, cx);
 170        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot.clone(), edits, tab_size);
 171        let (wrap_snapshot, edits) = self
 172            .wrap_map
 173            .update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));
 174        let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits).snapshot;
 175
 176        DisplaySnapshot {
 177            buffer_snapshot: self.buffer.read(cx).snapshot(cx),
 178            fold_snapshot,
 179            inlay_snapshot,
 180            tab_snapshot,
 181            wrap_snapshot,
 182            block_snapshot,
 183            crease_snapshot: self.crease_map.snapshot(),
 184            text_highlights: self.text_highlights.clone(),
 185            inlay_highlights: self.inlay_highlights.clone(),
 186            clip_at_line_ends: self.clip_at_line_ends,
 187            masked: self.masked,
 188            fold_placeholder: self.fold_placeholder.clone(),
 189        }
 190    }
 191
 192    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {
 193        self.fold(
 194            other
 195                .folds_in_range(0..other.buffer_snapshot.len())
 196                .map(|fold| {
 197                    Crease::simple(
 198                        fold.range.to_offset(&other.buffer_snapshot),
 199                        fold.placeholder.clone(),
 200                    )
 201                })
 202                .collect(),
 203            cx,
 204        );
 205    }
 206
 207    /// Creates folds for the given creases.
 208    pub fn fold<T: Clone + ToOffset>(&mut self, creases: Vec<Crease<T>>, cx: &mut Context<Self>) {
 209        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 210        let edits = self.buffer_subscription.consume().into_inner();
 211        let tab_size = Self::tab_size(&self.buffer, cx);
 212        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);
 213        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 214        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 215        let (snapshot, edits) = self
 216            .wrap_map
 217            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 218        self.block_map.read(snapshot, edits);
 219
 220        let inline = creases.iter().filter_map(|crease| {
 221            if let Crease::Inline {
 222                range, placeholder, ..
 223            } = crease
 224            {
 225                Some((range.clone(), placeholder.clone()))
 226            } else {
 227                None
 228            }
 229        });
 230        let (snapshot, edits) = fold_map.fold(inline);
 231
 232        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 233        let (snapshot, edits) = self
 234            .wrap_map
 235            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 236        let mut block_map = self.block_map.write(snapshot, edits);
 237        let blocks = creases.into_iter().filter_map(|crease| {
 238            if let Crease::Block {
 239                range,
 240                block_height,
 241                render_block,
 242                block_style,
 243                block_priority,
 244                ..
 245            } = crease
 246            {
 247                Some((
 248                    range,
 249                    render_block,
 250                    block_height,
 251                    block_style,
 252                    block_priority,
 253                ))
 254            } else {
 255                None
 256            }
 257        });
 258        block_map.insert(
 259            blocks
 260                .into_iter()
 261                .map(|(range, render, height, style, priority)| {
 262                    let start = buffer_snapshot.anchor_before(range.start);
 263                    let end = buffer_snapshot.anchor_after(range.end);
 264                    BlockProperties {
 265                        placement: BlockPlacement::Replace(start..=end),
 266                        render,
 267                        height,
 268                        style,
 269                        priority,
 270                    }
 271                }),
 272        );
 273    }
 274
 275    /// Removes any folds with the given ranges.
 276    pub fn remove_folds_with_type<T: ToOffset>(
 277        &mut self,
 278        ranges: impl IntoIterator<Item = Range<T>>,
 279        type_id: TypeId,
 280        cx: &mut Context<Self>,
 281    ) {
 282        let snapshot = self.buffer.read(cx).snapshot(cx);
 283        let edits = self.buffer_subscription.consume().into_inner();
 284        let tab_size = Self::tab_size(&self.buffer, cx);
 285        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 286        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 287        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 288        let (snapshot, edits) = self
 289            .wrap_map
 290            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 291        self.block_map.read(snapshot, edits);
 292        let (snapshot, edits) = fold_map.remove_folds(ranges, type_id);
 293        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 294        let (snapshot, edits) = self
 295            .wrap_map
 296            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 297        self.block_map.write(snapshot, edits);
 298    }
 299
 300    /// Removes any folds whose ranges intersect any of the given ranges.
 301    pub fn unfold_intersecting<T: ToOffset>(
 302        &mut self,
 303        ranges: impl IntoIterator<Item = Range<T>>,
 304        inclusive: bool,
 305        cx: &mut Context<Self>,
 306    ) {
 307        let snapshot = self.buffer.read(cx).snapshot(cx);
 308        let offset_ranges = ranges
 309            .into_iter()
 310            .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot))
 311            .collect::<Vec<_>>();
 312        let edits = self.buffer_subscription.consume().into_inner();
 313        let tab_size = Self::tab_size(&self.buffer, cx);
 314        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 315        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 316        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 317        let (snapshot, edits) = self
 318            .wrap_map
 319            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 320        self.block_map.read(snapshot, edits);
 321
 322        let (snapshot, edits) =
 323            fold_map.unfold_intersecting(offset_ranges.iter().cloned(), inclusive);
 324        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 325        let (snapshot, edits) = self
 326            .wrap_map
 327            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 328        let mut block_map = self.block_map.write(snapshot, edits);
 329        block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
 330    }
 331
 332    pub fn fold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
 333        let snapshot = self.buffer.read(cx).snapshot(cx);
 334        let edits = self.buffer_subscription.consume().into_inner();
 335        let tab_size = Self::tab_size(&self.buffer, cx);
 336        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 337        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 338        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 339        let (snapshot, edits) = self
 340            .wrap_map
 341            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 342        let mut block_map = self.block_map.write(snapshot, edits);
 343        block_map.fold_buffer(buffer_id, self.buffer.read(cx), cx)
 344    }
 345
 346    pub fn unfold_buffer(&mut self, buffer_id: language::BufferId, cx: &mut Context<Self>) {
 347        let snapshot = self.buffer.read(cx).snapshot(cx);
 348        let edits = self.buffer_subscription.consume().into_inner();
 349        let tab_size = Self::tab_size(&self.buffer, cx);
 350        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 351        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 352        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 353        let (snapshot, edits) = self
 354            .wrap_map
 355            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 356        let mut block_map = self.block_map.write(snapshot, edits);
 357        block_map.unfold_buffer(buffer_id, self.buffer.read(cx), cx)
 358    }
 359
 360    pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {
 361        self.block_map.folded_buffers.contains(&buffer_id)
 362    }
 363
 364    pub(crate) fn folded_buffers(&self) -> &HashSet<BufferId> {
 365        &self.block_map.folded_buffers
 366    }
 367
 368    pub fn insert_creases(
 369        &mut self,
 370        creases: impl IntoIterator<Item = Crease<Anchor>>,
 371        cx: &mut Context<Self>,
 372    ) -> Vec<CreaseId> {
 373        let snapshot = self.buffer.read(cx).snapshot(cx);
 374        self.crease_map.insert(creases, &snapshot)
 375    }
 376
 377    pub fn remove_creases(
 378        &mut self,
 379        crease_ids: impl IntoIterator<Item = CreaseId>,
 380        cx: &mut Context<Self>,
 381    ) {
 382        let snapshot = self.buffer.read(cx).snapshot(cx);
 383        self.crease_map.remove(crease_ids, &snapshot)
 384    }
 385
 386    pub fn insert_blocks(
 387        &mut self,
 388        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
 389        cx: &mut Context<Self>,
 390    ) -> Vec<CustomBlockId> {
 391        let snapshot = self.buffer.read(cx).snapshot(cx);
 392        let edits = self.buffer_subscription.consume().into_inner();
 393        let tab_size = Self::tab_size(&self.buffer, cx);
 394        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 395        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 396        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 397        let (snapshot, edits) = self
 398            .wrap_map
 399            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 400        let mut block_map = self.block_map.write(snapshot, edits);
 401        block_map.insert(blocks)
 402    }
 403
 404    pub fn resize_blocks(&mut self, heights: HashMap<CustomBlockId, u32>, cx: &mut Context<Self>) {
 405        let snapshot = self.buffer.read(cx).snapshot(cx);
 406        let edits = self.buffer_subscription.consume().into_inner();
 407        let tab_size = Self::tab_size(&self.buffer, cx);
 408        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 409        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 410        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 411        let (snapshot, edits) = self
 412            .wrap_map
 413            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 414        let mut block_map = self.block_map.write(snapshot, edits);
 415        block_map.resize(heights);
 416    }
 417
 418    pub fn replace_blocks(&mut self, renderers: HashMap<CustomBlockId, RenderBlock>) {
 419        self.block_map.replace_blocks(renderers);
 420    }
 421
 422    pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut Context<Self>) {
 423        let snapshot = self.buffer.read(cx).snapshot(cx);
 424        let edits = self.buffer_subscription.consume().into_inner();
 425        let tab_size = Self::tab_size(&self.buffer, cx);
 426        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 427        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 428        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 429        let (snapshot, edits) = self
 430            .wrap_map
 431            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 432        let mut block_map = self.block_map.write(snapshot, edits);
 433        block_map.remove(ids);
 434    }
 435
 436    pub fn row_for_block(
 437        &mut self,
 438        block_id: CustomBlockId,
 439        cx: &mut Context<Self>,
 440    ) -> Option<DisplayRow> {
 441        let snapshot = self.buffer.read(cx).snapshot(cx);
 442        let edits = self.buffer_subscription.consume().into_inner();
 443        let tab_size = Self::tab_size(&self.buffer, cx);
 444        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 445        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 446        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 447        let (snapshot, edits) = self
 448            .wrap_map
 449            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 450        let block_map = self.block_map.read(snapshot, edits);
 451        let block_row = block_map.row_for_block(block_id)?;
 452        Some(DisplayRow(block_row.0))
 453    }
 454
 455    pub fn highlight_text(
 456        &mut self,
 457        type_id: TypeId,
 458        ranges: Vec<Range<Anchor>>,
 459        style: HighlightStyle,
 460    ) {
 461        self.text_highlights
 462            .insert(type_id, Arc::new((style, ranges)));
 463    }
 464
 465    pub(crate) fn highlight_inlays(
 466        &mut self,
 467        type_id: TypeId,
 468        highlights: Vec<InlayHighlight>,
 469        style: HighlightStyle,
 470    ) {
 471        for highlight in highlights {
 472            let update = self.inlay_highlights.update(&type_id, |highlights| {
 473                highlights.insert(highlight.inlay, (style, highlight.clone()))
 474            });
 475            if update.is_none() {
 476                self.inlay_highlights.insert(
 477                    type_id,
 478                    TreeMap::from_ordered_entries([(highlight.inlay, (style, highlight))]),
 479                );
 480            }
 481        }
 482    }
 483
 484    pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {
 485        let highlights = self.text_highlights.get(&type_id)?;
 486        Some((highlights.0, &highlights.1))
 487    }
 488    pub fn clear_highlights(&mut self, type_id: TypeId) -> bool {
 489        let mut cleared = self.text_highlights.remove(&type_id).is_some();
 490        cleared |= self.inlay_highlights.remove(&type_id).is_some();
 491        cleared
 492    }
 493
 494    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> bool {
 495        self.wrap_map
 496            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
 497    }
 498
 499    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut Context<Self>) -> bool {
 500        self.wrap_map
 501            .update(cx, |map, cx| map.set_wrap_width(width, cx))
 502    }
 503
 504    pub(crate) fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
 505        self.inlay_map.current_inlays()
 506    }
 507
 508    pub(crate) fn splice_inlays(
 509        &mut self,
 510        to_remove: &[InlayId],
 511        to_insert: Vec<Inlay>,
 512        cx: &mut Context<Self>,
 513    ) {
 514        if to_remove.is_empty() && to_insert.is_empty() {
 515            return;
 516        }
 517        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 518        let edits = self.buffer_subscription.consume().into_inner();
 519        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
 520        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 521        let tab_size = Self::tab_size(&self.buffer, cx);
 522        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 523        let (snapshot, edits) = self
 524            .wrap_map
 525            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 526        self.block_map.read(snapshot, edits);
 527
 528        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
 529        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 530        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 531        let (snapshot, edits) = self
 532            .wrap_map
 533            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 534        self.block_map.read(snapshot, edits);
 535    }
 536
 537    fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
 538        let buffer = buffer.read(cx).as_singleton().map(|buffer| buffer.read(cx));
 539        let language = buffer
 540            .and_then(|buffer| buffer.language())
 541            .map(|l| l.name());
 542        let file = buffer.and_then(|buffer| buffer.file());
 543        language_settings(language, file, cx).tab_size
 544    }
 545
 546    #[cfg(test)]
 547    pub fn is_rewrapping(&self, cx: &gpui::App) -> bool {
 548        self.wrap_map.read(cx).is_rewrapping()
 549    }
 550
 551    pub fn show_excerpt_controls(&self) -> bool {
 552        self.block_map.show_excerpt_controls()
 553    }
 554}
 555
 556#[derive(Debug, Default)]
 557pub(crate) struct Highlights<'a> {
 558    pub text_highlights: Option<&'a TextHighlights>,
 559    pub inlay_highlights: Option<&'a InlayHighlights>,
 560    pub styles: HighlightStyles,
 561}
 562
 563#[derive(Clone, Copy, Debug)]
 564pub struct InlineCompletionStyles {
 565    pub insertion: HighlightStyle,
 566    pub whitespace: HighlightStyle,
 567}
 568
 569#[derive(Default, Debug, Clone, Copy)]
 570pub struct HighlightStyles {
 571    pub inlay_hint: Option<HighlightStyle>,
 572    pub inline_completion: Option<InlineCompletionStyles>,
 573}
 574
 575#[derive(Clone)]
 576pub enum ChunkReplacement {
 577    Renderer(ChunkRenderer),
 578    Str(SharedString),
 579}
 580
 581pub struct HighlightedChunk<'a> {
 582    pub text: &'a str,
 583    pub style: Option<HighlightStyle>,
 584    pub is_tab: bool,
 585    pub replacement: Option<ChunkReplacement>,
 586}
 587
 588impl<'a> HighlightedChunk<'a> {
 589    fn highlight_invisibles(
 590        self,
 591        editor_style: &'a EditorStyle,
 592    ) -> impl Iterator<Item = Self> + 'a {
 593        let mut chars = self.text.chars().peekable();
 594        let mut text = self.text;
 595        let style = self.style;
 596        let is_tab = self.is_tab;
 597        let renderer = self.replacement;
 598        iter::from_fn(move || {
 599            let mut prefix_len = 0;
 600            while let Some(&ch) = chars.peek() {
 601                if !is_invisible(ch) {
 602                    prefix_len += ch.len_utf8();
 603                    chars.next();
 604                    continue;
 605                }
 606                if prefix_len > 0 {
 607                    let (prefix, suffix) = text.split_at(prefix_len);
 608                    text = suffix;
 609                    return Some(HighlightedChunk {
 610                        text: prefix,
 611                        style,
 612                        is_tab,
 613                        replacement: renderer.clone(),
 614                    });
 615                }
 616                chars.next();
 617                let (prefix, suffix) = text.split_at(ch.len_utf8());
 618                text = suffix;
 619                if let Some(replacement) = replacement(ch) {
 620                    let invisible_highlight = HighlightStyle {
 621                        background_color: Some(editor_style.status.hint_background),
 622                        underline: Some(UnderlineStyle {
 623                            color: Some(editor_style.status.hint),
 624                            thickness: px(1.),
 625                            wavy: false,
 626                        }),
 627                        ..Default::default()
 628                    };
 629                    let invisible_style = if let Some(mut style) = style {
 630                        style.highlight(invisible_highlight);
 631                        style
 632                    } else {
 633                        invisible_highlight
 634                    };
 635                    return Some(HighlightedChunk {
 636                        text: prefix,
 637                        style: Some(invisible_style),
 638                        is_tab: false,
 639                        replacement: Some(ChunkReplacement::Str(replacement.into())),
 640                    });
 641                } else {
 642                    let invisible_highlight = HighlightStyle {
 643                        background_color: Some(editor_style.status.hint_background),
 644                        underline: Some(UnderlineStyle {
 645                            color: Some(editor_style.status.hint),
 646                            thickness: px(1.),
 647                            wavy: false,
 648                        }),
 649                        ..Default::default()
 650                    };
 651                    let invisible_style = if let Some(mut style) = style {
 652                        style.highlight(invisible_highlight);
 653                        style
 654                    } else {
 655                        invisible_highlight
 656                    };
 657
 658                    return Some(HighlightedChunk {
 659                        text: prefix,
 660                        style: Some(invisible_style),
 661                        is_tab: false,
 662                        replacement: renderer.clone(),
 663                    });
 664                }
 665            }
 666
 667            if !text.is_empty() {
 668                let remainder = text;
 669                text = "";
 670                Some(HighlightedChunk {
 671                    text: remainder,
 672                    style,
 673                    is_tab,
 674                    replacement: renderer.clone(),
 675                })
 676            } else {
 677                None
 678            }
 679        })
 680    }
 681}
 682
 683#[derive(Clone)]
 684pub struct DisplaySnapshot {
 685    pub buffer_snapshot: MultiBufferSnapshot,
 686    pub fold_snapshot: FoldSnapshot,
 687    pub crease_snapshot: CreaseSnapshot,
 688    inlay_snapshot: InlaySnapshot,
 689    tab_snapshot: TabSnapshot,
 690    wrap_snapshot: WrapSnapshot,
 691    block_snapshot: BlockSnapshot,
 692    text_highlights: TextHighlights,
 693    inlay_highlights: InlayHighlights,
 694    clip_at_line_ends: bool,
 695    masked: bool,
 696    pub(crate) fold_placeholder: FoldPlaceholder,
 697}
 698
 699impl DisplaySnapshot {
 700    #[cfg(test)]
 701    pub fn fold_count(&self) -> usize {
 702        self.fold_snapshot.fold_count()
 703    }
 704
 705    pub fn is_empty(&self) -> bool {
 706        self.buffer_snapshot.len() == 0
 707    }
 708
 709    pub fn row_infos(&self, start_row: DisplayRow) -> impl Iterator<Item = RowInfo> + '_ {
 710        self.block_snapshot.row_infos(BlockRow(start_row.0))
 711    }
 712
 713    pub fn widest_line_number(&self) -> u32 {
 714        self.buffer_snapshot.widest_line_number()
 715    }
 716
 717    pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
 718        loop {
 719            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
 720            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);
 721            fold_point.0.column = 0;
 722            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
 723            point = self.inlay_snapshot.to_buffer_point(inlay_point);
 724
 725            let mut display_point = self.point_to_display_point(point, Bias::Left);
 726            *display_point.column_mut() = 0;
 727            let next_point = self.display_point_to_point(display_point, Bias::Left);
 728            if next_point == point {
 729                return (point, display_point);
 730            }
 731            point = next_point;
 732        }
 733    }
 734
 735    pub fn next_line_boundary(
 736        &self,
 737        mut point: MultiBufferPoint,
 738    ) -> (MultiBufferPoint, DisplayPoint) {
 739        let original_point = point;
 740        loop {
 741            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);
 742            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);
 743            fold_point.0.column = self.fold_snapshot.line_len(fold_point.row());
 744            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);
 745            point = self.inlay_snapshot.to_buffer_point(inlay_point);
 746
 747            let mut display_point = self.point_to_display_point(point, Bias::Right);
 748            *display_point.column_mut() = self.line_len(display_point.row());
 749            let next_point = self.display_point_to_point(display_point, Bias::Right);
 750            if next_point == point || original_point == point || original_point == next_point {
 751                return (point, display_point);
 752            }
 753            point = next_point;
 754        }
 755    }
 756
 757    // used by line_mode selections and tries to match vim behavior
 758    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
 759        let max_row = self.buffer_snapshot.max_row().0;
 760        let new_start = if range.start.row == 0 {
 761            MultiBufferPoint::new(0, 0)
 762        } else if range.start.row == max_row || (range.end.column > 0 && range.end.row == max_row) {
 763            MultiBufferPoint::new(
 764                range.start.row - 1,
 765                self.buffer_snapshot
 766                    .line_len(MultiBufferRow(range.start.row - 1)),
 767            )
 768        } else {
 769            self.prev_line_boundary(range.start).0
 770        };
 771
 772        let new_end = if range.end.column == 0 {
 773            range.end
 774        } else if range.end.row < max_row {
 775            self.buffer_snapshot
 776                .clip_point(MultiBufferPoint::new(range.end.row + 1, 0), Bias::Left)
 777        } else {
 778            self.buffer_snapshot.max_point()
 779        };
 780
 781        new_start..new_end
 782    }
 783
 784    pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
 785        let inlay_point = self.inlay_snapshot.to_inlay_point(point);
 786        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);
 787        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
 788        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
 789        let block_point = self.block_snapshot.to_block_point(wrap_point);
 790        DisplayPoint(block_point)
 791    }
 792
 793    pub fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
 794        self.inlay_snapshot
 795            .to_buffer_point(self.display_point_to_inlay_point(point, bias))
 796    }
 797
 798    pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
 799        self.inlay_snapshot
 800            .to_offset(self.display_point_to_inlay_point(point, bias))
 801    }
 802
 803    pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {
 804        self.inlay_snapshot
 805            .to_inlay_offset(anchor.to_offset(&self.buffer_snapshot))
 806    }
 807
 808    pub fn display_point_to_anchor(&self, point: DisplayPoint, bias: Bias) -> Anchor {
 809        self.buffer_snapshot
 810            .anchor_at(point.to_offset(self, bias), bias)
 811    }
 812
 813    fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
 814        let block_point = point.0;
 815        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
 816        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
 817        let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;
 818        fold_point.to_inlay_point(&self.fold_snapshot)
 819    }
 820
 821    pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
 822        let block_point = point.0;
 823        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
 824        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);
 825        self.tab_snapshot.to_fold_point(tab_point, bias).0
 826    }
 827
 828    pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
 829        let tab_point = self.tab_snapshot.to_tab_point(fold_point);
 830        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);
 831        let block_point = self.block_snapshot.to_block_point(wrap_point);
 832        DisplayPoint(block_point)
 833    }
 834
 835    pub fn max_point(&self) -> DisplayPoint {
 836        DisplayPoint(self.block_snapshot.max_point())
 837    }
 838
 839    /// Returns text chunks starting at the given display row until the end of the file
 840    pub fn text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
 841        self.block_snapshot
 842            .chunks(
 843                display_row.0..self.max_point().row().next_row().0,
 844                false,
 845                self.masked,
 846                Highlights::default(),
 847            )
 848            .map(|h| h.text)
 849    }
 850
 851    /// Returns text chunks starting at the end of the given display row in reverse until the start of the file
 852    pub fn reverse_text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
 853        (0..=display_row.0).rev().flat_map(move |row| {
 854            self.block_snapshot
 855                .chunks(row..row + 1, false, self.masked, Highlights::default())
 856                .map(|h| h.text)
 857                .collect::<Vec<_>>()
 858                .into_iter()
 859                .rev()
 860        })
 861    }
 862
 863    pub fn chunks(
 864        &self,
 865        display_rows: Range<DisplayRow>,
 866        language_aware: bool,
 867        highlight_styles: HighlightStyles,
 868    ) -> DisplayChunks<'_> {
 869        self.block_snapshot.chunks(
 870            display_rows.start.0..display_rows.end.0,
 871            language_aware,
 872            self.masked,
 873            Highlights {
 874                text_highlights: Some(&self.text_highlights),
 875                inlay_highlights: Some(&self.inlay_highlights),
 876                styles: highlight_styles,
 877            },
 878        )
 879    }
 880
 881    pub fn highlighted_chunks<'a>(
 882        &'a self,
 883        display_rows: Range<DisplayRow>,
 884        language_aware: bool,
 885        editor_style: &'a EditorStyle,
 886    ) -> impl Iterator<Item = HighlightedChunk<'a>> {
 887        self.chunks(
 888            display_rows,
 889            language_aware,
 890            HighlightStyles {
 891                inlay_hint: Some(editor_style.inlay_hints_style),
 892                inline_completion: Some(editor_style.inline_completion_styles),
 893            },
 894        )
 895        .flat_map(|chunk| {
 896            let mut highlight_style = chunk
 897                .syntax_highlight_id
 898                .and_then(|id| id.style(&editor_style.syntax));
 899
 900            if let Some(chunk_highlight) = chunk.highlight_style {
 901                if let Some(highlight_style) = highlight_style.as_mut() {
 902                    highlight_style.highlight(chunk_highlight);
 903                } else {
 904                    highlight_style = Some(chunk_highlight);
 905                }
 906            }
 907
 908            let mut diagnostic_highlight = HighlightStyle::default();
 909
 910            if chunk.is_unnecessary {
 911                diagnostic_highlight.fade_out = Some(editor_style.unnecessary_code_fade);
 912            }
 913
 914            if let Some(severity) = chunk.diagnostic_severity {
 915                // Omit underlines for HINT/INFO diagnostics on 'unnecessary' code.
 916                if severity <= DiagnosticSeverity::WARNING || !chunk.is_unnecessary {
 917                    let diagnostic_color = super::diagnostic_style(severity, &editor_style.status);
 918                    diagnostic_highlight.underline = Some(UnderlineStyle {
 919                        color: Some(diagnostic_color),
 920                        thickness: 1.0.into(),
 921                        wavy: true,
 922                    });
 923                }
 924            }
 925
 926            if let Some(highlight_style) = highlight_style.as_mut() {
 927                highlight_style.highlight(diagnostic_highlight);
 928            } else {
 929                highlight_style = Some(diagnostic_highlight);
 930            }
 931
 932            HighlightedChunk {
 933                text: chunk.text,
 934                style: highlight_style,
 935                is_tab: chunk.is_tab,
 936                replacement: chunk.renderer.map(ChunkReplacement::Renderer),
 937            }
 938            .highlight_invisibles(editor_style)
 939        })
 940    }
 941
 942    pub fn layout_row(
 943        &self,
 944        display_row: DisplayRow,
 945        TextLayoutDetails {
 946            text_system,
 947            editor_style,
 948            rem_size,
 949            scroll_anchor: _,
 950            visible_rows: _,
 951            vertical_scroll_margin: _,
 952        }: &TextLayoutDetails,
 953    ) -> Arc<LineLayout> {
 954        let mut runs = Vec::new();
 955        let mut line = String::new();
 956
 957        let range = display_row..display_row.next_row();
 958        for chunk in self.highlighted_chunks(range, false, editor_style) {
 959            line.push_str(chunk.text);
 960
 961            let text_style = if let Some(style) = chunk.style {
 962                Cow::Owned(editor_style.text.clone().highlight(style))
 963            } else {
 964                Cow::Borrowed(&editor_style.text)
 965            };
 966
 967            runs.push(text_style.to_run(chunk.text.len()))
 968        }
 969
 970        if line.ends_with('\n') {
 971            line.pop();
 972            if let Some(last_run) = runs.last_mut() {
 973                last_run.len -= 1;
 974                if last_run.len == 0 {
 975                    runs.pop();
 976                }
 977            }
 978        }
 979
 980        let font_size = editor_style.text.font_size.to_pixels(*rem_size);
 981        text_system
 982            .layout_line(&line, font_size, &runs)
 983            .expect("we expect the font to be loaded because it's rendered by the editor")
 984    }
 985
 986    pub fn x_for_display_point(
 987        &self,
 988        display_point: DisplayPoint,
 989        text_layout_details: &TextLayoutDetails,
 990    ) -> Pixels {
 991        let line = self.layout_row(display_point.row(), text_layout_details);
 992        line.x_for_index(display_point.column() as usize)
 993    }
 994
 995    pub fn display_column_for_x(
 996        &self,
 997        display_row: DisplayRow,
 998        x: Pixels,
 999        details: &TextLayoutDetails,
1000    ) -> u32 {
1001        let layout_line = self.layout_row(display_row, details);
1002        layout_line.closest_index_for_x(x) as u32
1003    }
1004
1005    pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<SharedString> {
1006        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
1007        let chars = self
1008            .text_chunks(point.row())
1009            .flat_map(str::chars)
1010            .skip_while({
1011                let mut column = 0;
1012                move |char| {
1013                    let at_point = column >= point.column();
1014                    column += char.len_utf8() as u32;
1015                    !at_point
1016                }
1017            })
1018            .take_while({
1019                let mut prev = false;
1020                move |char| {
1021                    let now = char.is_ascii();
1022                    let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
1023                    prev = now;
1024                    !end
1025                }
1026            });
1027        chars.collect::<String>().graphemes(true).next().map(|s| {
1028            if let Some(invisible) = s.chars().next().filter(|&c| is_invisible(c)) {
1029                replacement(invisible).unwrap_or(s).to_owned().into()
1030            } else if s == "\n" {
1031                " ".into()
1032            } else {
1033                s.to_owned().into()
1034            }
1035        })
1036    }
1037
1038    pub fn buffer_chars_at(&self, mut offset: usize) -> impl Iterator<Item = (char, usize)> + '_ {
1039        self.buffer_snapshot.chars_at(offset).map(move |ch| {
1040            let ret = (ch, offset);
1041            offset += ch.len_utf8();
1042            ret
1043        })
1044    }
1045
1046    pub fn reverse_buffer_chars_at(
1047        &self,
1048        mut offset: usize,
1049    ) -> impl Iterator<Item = (char, usize)> + '_ {
1050        self.buffer_snapshot
1051            .reversed_chars_at(offset)
1052            .map(move |ch| {
1053                offset -= ch.len_utf8();
1054                (ch, offset)
1055            })
1056    }
1057
1058    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
1059        let mut clipped = self.block_snapshot.clip_point(point.0, bias);
1060        if self.clip_at_line_ends {
1061            clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
1062        }
1063        DisplayPoint(clipped)
1064    }
1065
1066    pub fn clip_ignoring_line_ends(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
1067        DisplayPoint(self.block_snapshot.clip_point(point.0, bias))
1068    }
1069
1070    pub fn clip_at_line_end(&self, display_point: DisplayPoint) -> DisplayPoint {
1071        let mut point = self.display_point_to_point(display_point, Bias::Left);
1072
1073        if point.column != self.buffer_snapshot.line_len(MultiBufferRow(point.row)) {
1074            return display_point;
1075        }
1076        point.column = point.column.saturating_sub(1);
1077        point = self.buffer_snapshot.clip_point(point, Bias::Left);
1078        self.point_to_display_point(point, Bias::Left)
1079    }
1080
1081    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
1082    where
1083        T: ToOffset,
1084    {
1085        self.fold_snapshot.folds_in_range(range)
1086    }
1087
1088    pub fn blocks_in_range(
1089        &self,
1090        rows: Range<DisplayRow>,
1091    ) -> impl Iterator<Item = (DisplayRow, &Block)> {
1092        self.block_snapshot
1093            .blocks_in_range(rows.start.0..rows.end.0)
1094            .map(|(row, block)| (DisplayRow(row), block))
1095    }
1096
1097    pub fn sticky_header_excerpt(&self, row: DisplayRow) -> Option<StickyHeaderExcerpt<'_>> {
1098        self.block_snapshot.sticky_header_excerpt(row.0)
1099    }
1100
1101    pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
1102        self.block_snapshot.block_for_id(id)
1103    }
1104
1105    pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
1106        self.fold_snapshot.intersects_fold(offset)
1107    }
1108
1109    pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
1110        self.block_snapshot.is_line_replaced(buffer_row)
1111            || self.fold_snapshot.is_line_folded(buffer_row)
1112    }
1113
1114    pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
1115        self.block_snapshot.is_block_line(BlockRow(display_row.0))
1116    }
1117
1118    pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
1119        let wrap_row = self
1120            .block_snapshot
1121            .to_wrap_point(BlockPoint::new(display_row.0, 0), Bias::Left)
1122            .row();
1123        self.wrap_snapshot.soft_wrap_indent(wrap_row)
1124    }
1125
1126    pub fn text(&self) -> String {
1127        self.text_chunks(DisplayRow(0)).collect()
1128    }
1129
1130    pub fn line(&self, display_row: DisplayRow) -> String {
1131        let mut result = String::new();
1132        for chunk in self.text_chunks(display_row) {
1133            if let Some(ix) = chunk.find('\n') {
1134                result.push_str(&chunk[0..ix]);
1135                break;
1136            } else {
1137                result.push_str(chunk);
1138            }
1139        }
1140        result
1141    }
1142
1143    pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> LineIndent {
1144        self.buffer_snapshot.line_indent_for_row(buffer_row)
1145    }
1146
1147    pub fn line_len(&self, row: DisplayRow) -> u32 {
1148        self.block_snapshot.line_len(BlockRow(row.0))
1149    }
1150
1151    pub fn longest_row(&self) -> DisplayRow {
1152        DisplayRow(self.block_snapshot.longest_row())
1153    }
1154
1155    pub fn longest_row_in_range(&self, range: Range<DisplayRow>) -> DisplayRow {
1156        let block_range = BlockRow(range.start.0)..BlockRow(range.end.0);
1157        let longest_row = self.block_snapshot.longest_row_in_range(block_range);
1158        DisplayRow(longest_row.0)
1159    }
1160
1161    pub fn starts_indent(&self, buffer_row: MultiBufferRow) -> bool {
1162        let max_row = self.buffer_snapshot.max_row();
1163        if buffer_row >= max_row {
1164            return false;
1165        }
1166
1167        let line_indent = self.line_indent_for_buffer_row(buffer_row);
1168        if line_indent.is_line_blank() {
1169            return false;
1170        }
1171
1172        (buffer_row.0 + 1..=max_row.0)
1173            .find_map(|next_row| {
1174                let next_line_indent = self.line_indent_for_buffer_row(MultiBufferRow(next_row));
1175                if next_line_indent.raw_len() > line_indent.raw_len() {
1176                    Some(true)
1177                } else if !next_line_indent.is_line_blank() {
1178                    Some(false)
1179                } else {
1180                    None
1181                }
1182            })
1183            .unwrap_or(false)
1184    }
1185
1186    pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
1187        let start = MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot.line_len(buffer_row));
1188        if let Some(crease) = self
1189            .crease_snapshot
1190            .query_row(buffer_row, &self.buffer_snapshot)
1191        {
1192            match crease {
1193                Crease::Inline {
1194                    range,
1195                    placeholder,
1196                    render_toggle,
1197                    render_trailer,
1198                    metadata,
1199                } => Some(Crease::Inline {
1200                    range: range.to_point(&self.buffer_snapshot),
1201                    placeholder: placeholder.clone(),
1202                    render_toggle: render_toggle.clone(),
1203                    render_trailer: render_trailer.clone(),
1204                    metadata: metadata.clone(),
1205                }),
1206                Crease::Block {
1207                    range,
1208                    block_height,
1209                    block_style,
1210                    render_block,
1211                    block_priority,
1212                    render_toggle,
1213                } => Some(Crease::Block {
1214                    range: range.to_point(&self.buffer_snapshot),
1215                    block_height: *block_height,
1216                    block_style: *block_style,
1217                    render_block: render_block.clone(),
1218                    block_priority: *block_priority,
1219                    render_toggle: render_toggle.clone(),
1220                }),
1221            }
1222        } else if self.starts_indent(MultiBufferRow(start.row))
1223            && !self.is_line_folded(MultiBufferRow(start.row))
1224        {
1225            let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
1226            let max_point = self.buffer_snapshot.max_point();
1227            let mut end = None;
1228
1229            for row in (buffer_row.0 + 1)..=max_point.row {
1230                let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
1231                if !line_indent.is_line_blank()
1232                    && line_indent.raw_len() <= start_line_indent.raw_len()
1233                {
1234                    let prev_row = row - 1;
1235                    end = Some(Point::new(
1236                        prev_row,
1237                        self.buffer_snapshot.line_len(MultiBufferRow(prev_row)),
1238                    ));
1239                    break;
1240                }
1241            }
1242
1243            let mut row_before_line_breaks = end.unwrap_or(max_point);
1244            while row_before_line_breaks.row > start.row
1245                && self
1246                    .buffer_snapshot
1247                    .is_line_blank(MultiBufferRow(row_before_line_breaks.row))
1248            {
1249                row_before_line_breaks.row -= 1;
1250            }
1251
1252            row_before_line_breaks = Point::new(
1253                row_before_line_breaks.row,
1254                self.buffer_snapshot
1255                    .line_len(MultiBufferRow(row_before_line_breaks.row)),
1256            );
1257
1258            Some(Crease::Inline {
1259                range: start..row_before_line_breaks,
1260                placeholder: self.fold_placeholder.clone(),
1261                render_toggle: None,
1262                render_trailer: None,
1263                metadata: None,
1264            })
1265        } else {
1266            None
1267        }
1268    }
1269
1270    #[cfg(any(test, feature = "test-support"))]
1271    pub fn text_highlight_ranges<Tag: ?Sized + 'static>(
1272        &self,
1273    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
1274        let type_id = TypeId::of::<Tag>();
1275        self.text_highlights.get(&type_id).cloned()
1276    }
1277
1278    #[allow(unused)]
1279    #[cfg(any(test, feature = "test-support"))]
1280    pub(crate) fn inlay_highlights<Tag: ?Sized + 'static>(
1281        &self,
1282    ) -> Option<&TreeMap<InlayId, (HighlightStyle, InlayHighlight)>> {
1283        let type_id = TypeId::of::<Tag>();
1284        self.inlay_highlights.get(&type_id)
1285    }
1286
1287    pub fn buffer_header_height(&self) -> u32 {
1288        self.block_snapshot.buffer_header_height
1289    }
1290
1291    pub fn excerpt_footer_height(&self) -> u32 {
1292        self.block_snapshot.excerpt_footer_height
1293    }
1294
1295    pub fn excerpt_header_height(&self) -> u32 {
1296        self.block_snapshot.excerpt_header_height
1297    }
1298}
1299
1300#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
1301pub struct DisplayPoint(BlockPoint);
1302
1303impl Debug for DisplayPoint {
1304    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1305        f.write_fmt(format_args!(
1306            "DisplayPoint({}, {})",
1307            self.row().0,
1308            self.column()
1309        ))
1310    }
1311}
1312
1313impl Add for DisplayPoint {
1314    type Output = Self;
1315
1316    fn add(self, other: Self) -> Self::Output {
1317        DisplayPoint(BlockPoint(self.0 .0 + other.0 .0))
1318    }
1319}
1320
1321impl Sub for DisplayPoint {
1322    type Output = Self;
1323
1324    fn sub(self, other: Self) -> Self::Output {
1325        DisplayPoint(BlockPoint(self.0 .0 - other.0 .0))
1326    }
1327}
1328
1329#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
1330#[serde(transparent)]
1331pub struct DisplayRow(pub u32);
1332
1333impl Add<DisplayRow> for DisplayRow {
1334    type Output = Self;
1335
1336    fn add(self, other: Self) -> Self::Output {
1337        DisplayRow(self.0 + other.0)
1338    }
1339}
1340
1341impl Add<u32> for DisplayRow {
1342    type Output = Self;
1343
1344    fn add(self, other: u32) -> Self::Output {
1345        DisplayRow(self.0 + other)
1346    }
1347}
1348
1349impl Sub<DisplayRow> for DisplayRow {
1350    type Output = Self;
1351
1352    fn sub(self, other: Self) -> Self::Output {
1353        DisplayRow(self.0 - other.0)
1354    }
1355}
1356
1357impl Sub<u32> for DisplayRow {
1358    type Output = Self;
1359
1360    fn sub(self, other: u32) -> Self::Output {
1361        DisplayRow(self.0 - other)
1362    }
1363}
1364
1365impl DisplayPoint {
1366    pub fn new(row: DisplayRow, column: u32) -> Self {
1367        Self(BlockPoint(Point::new(row.0, column)))
1368    }
1369
1370    pub fn zero() -> Self {
1371        Self::new(DisplayRow(0), 0)
1372    }
1373
1374    pub fn is_zero(&self) -> bool {
1375        self.0.is_zero()
1376    }
1377
1378    pub fn row(self) -> DisplayRow {
1379        DisplayRow(self.0.row)
1380    }
1381
1382    pub fn column(self) -> u32 {
1383        self.0.column
1384    }
1385
1386    pub fn row_mut(&mut self) -> &mut u32 {
1387        &mut self.0.row
1388    }
1389
1390    pub fn column_mut(&mut self) -> &mut u32 {
1391        &mut self.0.column
1392    }
1393
1394    pub fn to_point(self, map: &DisplaySnapshot) -> Point {
1395        map.display_point_to_point(self, Bias::Left)
1396    }
1397
1398    pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> usize {
1399        let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
1400        let tab_point = map.wrap_snapshot.to_tab_point(wrap_point);
1401        let fold_point = map.tab_snapshot.to_fold_point(tab_point, bias).0;
1402        let inlay_point = fold_point.to_inlay_point(&map.fold_snapshot);
1403        map.inlay_snapshot
1404            .to_buffer_offset(map.inlay_snapshot.to_offset(inlay_point))
1405    }
1406}
1407
1408impl ToDisplayPoint for usize {
1409    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
1410        map.point_to_display_point(self.to_point(&map.buffer_snapshot), Bias::Left)
1411    }
1412}
1413
1414impl ToDisplayPoint for OffsetUtf16 {
1415    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
1416        self.to_offset(&map.buffer_snapshot).to_display_point(map)
1417    }
1418}
1419
1420impl ToDisplayPoint for Point {
1421    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
1422        map.point_to_display_point(*self, Bias::Left)
1423    }
1424}
1425
1426impl ToDisplayPoint for Anchor {
1427    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
1428        self.to_point(&map.buffer_snapshot).to_display_point(map)
1429    }
1430}
1431
1432#[cfg(test)]
1433pub mod tests {
1434    use super::*;
1435    use crate::{
1436        movement,
1437        test::{marked_display_snapshot, test_font},
1438    };
1439    use block_map::BlockPlacement;
1440    use gpui::{
1441        div, font, observe, px, App, AppContext as _, BorrowAppContext, Element, Hsla, Rgba,
1442    };
1443    use language::{
1444        language_settings::{AllLanguageSettings, AllLanguageSettingsContent},
1445        Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig,
1446        LanguageMatcher,
1447    };
1448    use lsp::LanguageServerId;
1449    use project::Project;
1450    use rand::{prelude::*, Rng};
1451    use settings::SettingsStore;
1452    use smol::stream::StreamExt;
1453    use std::{env, sync::Arc};
1454    use text::PointUtf16;
1455    use theme::{LoadThemes, SyntaxTheme};
1456    use unindent::Unindent as _;
1457    use util::test::{marked_text_ranges, sample_text};
1458    use Bias::*;
1459
1460    #[gpui::test(iterations = 100)]
1461    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
1462        cx.background_executor.set_block_on_ticks(0..=50);
1463        let operations = env::var("OPERATIONS")
1464            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
1465            .unwrap_or(10);
1466
1467        let mut tab_size = rng.gen_range(1..=4);
1468        let buffer_start_excerpt_header_height = rng.gen_range(1..=5);
1469        let excerpt_header_height = rng.gen_range(1..=5);
1470        let font_size = px(14.0);
1471        let max_wrap_width = 300.0;
1472        let mut wrap_width = if rng.gen_bool(0.1) {
1473            None
1474        } else {
1475            Some(px(rng.gen_range(0.0..=max_wrap_width)))
1476        };
1477
1478        log::info!("tab size: {}", tab_size);
1479        log::info!("wrap width: {:?}", wrap_width);
1480
1481        cx.update(|cx| {
1482            init_test(cx, |s| s.defaults.tab_size = NonZeroU32::new(tab_size));
1483        });
1484
1485        let buffer = cx.update(|cx| {
1486            if rng.gen() {
1487                let len = rng.gen_range(0..10);
1488                let text = util::RandomCharIter::new(&mut rng)
1489                    .take(len)
1490                    .collect::<String>();
1491                MultiBuffer::build_simple(&text, cx)
1492            } else {
1493                MultiBuffer::build_random(&mut rng, cx)
1494            }
1495        });
1496
1497        let font = test_font();
1498        let map = cx.new(|cx| {
1499            DisplayMap::new(
1500                buffer.clone(),
1501                font,
1502                font_size,
1503                wrap_width,
1504                true,
1505                buffer_start_excerpt_header_height,
1506                excerpt_header_height,
1507                0,
1508                FoldPlaceholder::test(),
1509                cx,
1510            )
1511        });
1512        let mut notifications = observe(&map, cx);
1513        let mut fold_count = 0;
1514        let mut blocks = Vec::new();
1515
1516        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1517        log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
1518        log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
1519        log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
1520        log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
1521        log::info!("block text: {:?}", snapshot.block_snapshot.text());
1522        log::info!("display text: {:?}", snapshot.text());
1523
1524        for _i in 0..operations {
1525            match rng.gen_range(0..100) {
1526                0..=19 => {
1527                    wrap_width = if rng.gen_bool(0.2) {
1528                        None
1529                    } else {
1530                        Some(px(rng.gen_range(0.0..=max_wrap_width)))
1531                    };
1532                    log::info!("setting wrap width to {:?}", wrap_width);
1533                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
1534                }
1535                20..=29 => {
1536                    let mut tab_sizes = vec![1, 2, 3, 4];
1537                    tab_sizes.remove((tab_size - 1) as usize);
1538                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
1539                    log::info!("setting tab size to {:?}", tab_size);
1540                    cx.update(|cx| {
1541                        cx.update_global::<SettingsStore, _>(|store, cx| {
1542                            store.update_user_settings::<AllLanguageSettings>(cx, |s| {
1543                                s.defaults.tab_size = NonZeroU32::new(tab_size);
1544                            });
1545                        });
1546                    });
1547                }
1548                30..=44 => {
1549                    map.update(cx, |map, cx| {
1550                        if rng.gen() || blocks.is_empty() {
1551                            let buffer = map.snapshot(cx).buffer_snapshot;
1552                            let block_properties = (0..rng.gen_range(1..=1))
1553                                .map(|_| {
1554                                    let position =
1555                                        buffer.anchor_after(buffer.clip_offset(
1556                                            rng.gen_range(0..=buffer.len()),
1557                                            Bias::Left,
1558                                        ));
1559
1560                                    let placement = if rng.gen() {
1561                                        BlockPlacement::Above(position)
1562                                    } else {
1563                                        BlockPlacement::Below(position)
1564                                    };
1565                                    let height = rng.gen_range(1..5);
1566                                    log::info!(
1567                                        "inserting block {:?} with height {}",
1568                                        placement.as_ref().map(|p| p.to_point(&buffer)),
1569                                        height
1570                                    );
1571                                    let priority = rng.gen_range(1..100);
1572                                    BlockProperties {
1573                                        placement,
1574                                        style: BlockStyle::Fixed,
1575                                        height,
1576                                        render: Arc::new(|_| div().into_any()),
1577                                        priority,
1578                                    }
1579                                })
1580                                .collect::<Vec<_>>();
1581                            blocks.extend(map.insert_blocks(block_properties, cx));
1582                        } else {
1583                            blocks.shuffle(&mut rng);
1584                            let remove_count = rng.gen_range(1..=4.min(blocks.len()));
1585                            let block_ids_to_remove = (0..remove_count)
1586                                .map(|_| blocks.remove(rng.gen_range(0..blocks.len())))
1587                                .collect();
1588                            log::info!("removing block ids {:?}", block_ids_to_remove);
1589                            map.remove_blocks(block_ids_to_remove, cx);
1590                        }
1591                    });
1592                }
1593                45..=79 => {
1594                    let mut ranges = Vec::new();
1595                    for _ in 0..rng.gen_range(1..=3) {
1596                        buffer.read_with(cx, |buffer, cx| {
1597                            let buffer = buffer.read(cx);
1598                            let end = buffer.clip_offset(rng.gen_range(0..=buffer.len()), Right);
1599                            let start = buffer.clip_offset(rng.gen_range(0..=end), Left);
1600                            ranges.push(start..end);
1601                        });
1602                    }
1603
1604                    if rng.gen() && fold_count > 0 {
1605                        log::info!("unfolding ranges: {:?}", ranges);
1606                        map.update(cx, |map, cx| {
1607                            map.unfold_intersecting(ranges, true, cx);
1608                        });
1609                    } else {
1610                        log::info!("folding ranges: {:?}", ranges);
1611                        map.update(cx, |map, cx| {
1612                            map.fold(
1613                                ranges
1614                                    .into_iter()
1615                                    .map(|range| Crease::simple(range, FoldPlaceholder::test()))
1616                                    .collect(),
1617                                cx,
1618                            );
1619                        });
1620                    }
1621                }
1622                _ => {
1623                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
1624                }
1625            }
1626
1627            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
1628                notifications.next().await.unwrap();
1629            }
1630
1631            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1632            fold_count = snapshot.fold_count();
1633            log::info!("buffer text: {:?}", snapshot.buffer_snapshot.text());
1634            log::info!("fold text: {:?}", snapshot.fold_snapshot.text());
1635            log::info!("tab text: {:?}", snapshot.tab_snapshot.text());
1636            log::info!("wrap text: {:?}", snapshot.wrap_snapshot.text());
1637            log::info!("block text: {:?}", snapshot.block_snapshot.text());
1638            log::info!("display text: {:?}", snapshot.text());
1639
1640            // Line boundaries
1641            let buffer = &snapshot.buffer_snapshot;
1642            for _ in 0..5 {
1643                let row = rng.gen_range(0..=buffer.max_point().row);
1644                let column = rng.gen_range(0..=buffer.line_len(MultiBufferRow(row)));
1645                let point = buffer.clip_point(Point::new(row, column), Left);
1646
1647                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
1648                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
1649
1650                assert!(prev_buffer_bound <= point);
1651                assert!(next_buffer_bound >= point);
1652                assert_eq!(prev_buffer_bound.column, 0);
1653                assert_eq!(prev_display_bound.column(), 0);
1654                if next_buffer_bound < buffer.max_point() {
1655                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
1656                }
1657
1658                assert_eq!(
1659                    prev_display_bound,
1660                    prev_buffer_bound.to_display_point(&snapshot),
1661                    "row boundary before {:?}. reported buffer row boundary: {:?}",
1662                    point,
1663                    prev_buffer_bound
1664                );
1665                assert_eq!(
1666                    next_display_bound,
1667                    next_buffer_bound.to_display_point(&snapshot),
1668                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
1669                    point,
1670                    next_buffer_bound
1671                );
1672                assert_eq!(
1673                    prev_buffer_bound,
1674                    prev_display_bound.to_point(&snapshot),
1675                    "row boundary before {:?}. reported display row boundary: {:?}",
1676                    point,
1677                    prev_display_bound
1678                );
1679                assert_eq!(
1680                    next_buffer_bound,
1681                    next_display_bound.to_point(&snapshot),
1682                    "row boundary after {:?}. reported display row boundary: {:?}",
1683                    point,
1684                    next_display_bound
1685                );
1686            }
1687
1688            // Movement
1689            let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
1690            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
1691            for _ in 0..5 {
1692                let row = rng.gen_range(0..=snapshot.max_point().row().0);
1693                let column = rng.gen_range(0..=snapshot.line_len(DisplayRow(row)));
1694                let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
1695
1696                log::info!("Moving from point {:?}", point);
1697
1698                let moved_right = movement::right(&snapshot, point);
1699                log::info!("Right {:?}", moved_right);
1700                if point < max_point {
1701                    assert!(moved_right > point);
1702                    if point.column() == snapshot.line_len(point.row())
1703                        || snapshot.soft_wrap_indent(point.row()).is_some()
1704                            && point.column() == snapshot.line_len(point.row()) - 1
1705                    {
1706                        assert!(moved_right.row() > point.row());
1707                    }
1708                } else {
1709                    assert_eq!(moved_right, point);
1710                }
1711
1712                let moved_left = movement::left(&snapshot, point);
1713                log::info!("Left {:?}", moved_left);
1714                if point > min_point {
1715                    assert!(moved_left < point);
1716                    if point.column() == 0 {
1717                        assert!(moved_left.row() < point.row());
1718                    }
1719                } else {
1720                    assert_eq!(moved_left, point);
1721                }
1722            }
1723        }
1724    }
1725
1726    #[cfg(target_os = "macos")]
1727    #[gpui::test(retries = 5)]
1728    async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
1729        cx.background_executor
1730            .set_block_on_ticks(usize::MAX..=usize::MAX);
1731        cx.update(|cx| {
1732            init_test(cx, |_| {});
1733        });
1734
1735        let mut cx = crate::test::editor_test_context::EditorTestContext::new(cx).await;
1736        let editor = cx.editor.clone();
1737        let window = cx.window;
1738
1739        _ = cx.update_window(window, |_, window, cx| {
1740            let text_layout_details =
1741                editor.update(cx, |editor, _cx| editor.text_layout_details(window));
1742
1743            let font_size = px(12.0);
1744            let wrap_width = Some(px(64.));
1745
1746            let text = "one two three four five\nsix seven eight";
1747            let buffer = MultiBuffer::build_simple(text, cx);
1748            let map = cx.new(|cx| {
1749                DisplayMap::new(
1750                    buffer.clone(),
1751                    font("Helvetica"),
1752                    font_size,
1753                    wrap_width,
1754                    true,
1755                    1,
1756                    1,
1757                    0,
1758                    FoldPlaceholder::test(),
1759                    cx,
1760                )
1761            });
1762
1763            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1764            assert_eq!(
1765                snapshot.text_chunks(DisplayRow(0)).collect::<String>(),
1766                "one two \nthree four \nfive\nsix seven \neight"
1767            );
1768            assert_eq!(
1769                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Left),
1770                DisplayPoint::new(DisplayRow(0), 7)
1771            );
1772            assert_eq!(
1773                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Right),
1774                DisplayPoint::new(DisplayRow(1), 0)
1775            );
1776            assert_eq!(
1777                movement::right(&snapshot, DisplayPoint::new(DisplayRow(0), 7)),
1778                DisplayPoint::new(DisplayRow(1), 0)
1779            );
1780            assert_eq!(
1781                movement::left(&snapshot, DisplayPoint::new(DisplayRow(1), 0)),
1782                DisplayPoint::new(DisplayRow(0), 7)
1783            );
1784
1785            let x = snapshot
1786                .x_for_display_point(DisplayPoint::new(DisplayRow(1), 10), &text_layout_details);
1787            assert_eq!(
1788                movement::up(
1789                    &snapshot,
1790                    DisplayPoint::new(DisplayRow(1), 10),
1791                    language::SelectionGoal::None,
1792                    false,
1793                    &text_layout_details,
1794                ),
1795                (
1796                    DisplayPoint::new(DisplayRow(0), 7),
1797                    language::SelectionGoal::HorizontalPosition(x.0)
1798                )
1799            );
1800            assert_eq!(
1801                movement::down(
1802                    &snapshot,
1803                    DisplayPoint::new(DisplayRow(0), 7),
1804                    language::SelectionGoal::HorizontalPosition(x.0),
1805                    false,
1806                    &text_layout_details
1807                ),
1808                (
1809                    DisplayPoint::new(DisplayRow(1), 10),
1810                    language::SelectionGoal::HorizontalPosition(x.0)
1811                )
1812            );
1813            assert_eq!(
1814                movement::down(
1815                    &snapshot,
1816                    DisplayPoint::new(DisplayRow(1), 10),
1817                    language::SelectionGoal::HorizontalPosition(x.0),
1818                    false,
1819                    &text_layout_details
1820                ),
1821                (
1822                    DisplayPoint::new(DisplayRow(2), 4),
1823                    language::SelectionGoal::HorizontalPosition(x.0)
1824                )
1825            );
1826
1827            let ix = snapshot.buffer_snapshot.text().find("seven").unwrap();
1828            buffer.update(cx, |buffer, cx| {
1829                buffer.edit([(ix..ix, "and ")], None, cx);
1830            });
1831
1832            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1833            assert_eq!(
1834                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
1835                "three four \nfive\nsix and \nseven eight"
1836            );
1837
1838            // Re-wrap on font size changes
1839            map.update(cx, |map, cx| {
1840                map.set_font(font("Helvetica"), px(font_size.0 + 3.), cx)
1841            });
1842
1843            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
1844            assert_eq!(
1845                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
1846                "three \nfour five\nsix and \nseven \neight"
1847            )
1848        });
1849    }
1850
1851    #[gpui::test]
1852    fn test_text_chunks(cx: &mut gpui::App) {
1853        init_test(cx, |_| {});
1854
1855        let text = sample_text(6, 6, 'a');
1856        let buffer = MultiBuffer::build_simple(&text, cx);
1857
1858        let font_size = px(14.0);
1859        let map = cx.new(|cx| {
1860            DisplayMap::new(
1861                buffer.clone(),
1862                font("Helvetica"),
1863                font_size,
1864                None,
1865                true,
1866                1,
1867                1,
1868                0,
1869                FoldPlaceholder::test(),
1870                cx,
1871            )
1872        });
1873
1874        buffer.update(cx, |buffer, cx| {
1875            buffer.edit(
1876                vec![
1877                    (
1878                        MultiBufferPoint::new(1, 0)..MultiBufferPoint::new(1, 0),
1879                        "\t",
1880                    ),
1881                    (
1882                        MultiBufferPoint::new(1, 1)..MultiBufferPoint::new(1, 1),
1883                        "\t",
1884                    ),
1885                    (
1886                        MultiBufferPoint::new(2, 1)..MultiBufferPoint::new(2, 1),
1887                        "\t",
1888                    ),
1889                ],
1890                None,
1891                cx,
1892            )
1893        });
1894
1895        assert_eq!(
1896            map.update(cx, |map, cx| map.snapshot(cx))
1897                .text_chunks(DisplayRow(1))
1898                .collect::<String>()
1899                .lines()
1900                .next(),
1901            Some("    b   bbbbb")
1902        );
1903        assert_eq!(
1904            map.update(cx, |map, cx| map.snapshot(cx))
1905                .text_chunks(DisplayRow(2))
1906                .collect::<String>()
1907                .lines()
1908                .next(),
1909            Some("c   ccccc")
1910        );
1911    }
1912
1913    #[gpui::test]
1914    async fn test_chunks(cx: &mut gpui::TestAppContext) {
1915        let text = r#"
1916            fn outer() {}
1917
1918            mod module {
1919                fn inner() {}
1920            }"#
1921        .unindent();
1922
1923        let theme =
1924            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
1925        let language = Arc::new(
1926            Language::new(
1927                LanguageConfig {
1928                    name: "Test".into(),
1929                    matcher: LanguageMatcher {
1930                        path_suffixes: vec![".test".to_string()],
1931                        ..Default::default()
1932                    },
1933                    ..Default::default()
1934                },
1935                Some(tree_sitter_rust::LANGUAGE.into()),
1936            )
1937            .with_highlights_query(
1938                r#"
1939                (mod_item name: (identifier) body: _ @mod.body)
1940                (function_item name: (identifier) @fn.name)
1941                "#,
1942            )
1943            .unwrap(),
1944        );
1945        language.set_theme(&theme);
1946
1947        cx.update(|cx| init_test(cx, |s| s.defaults.tab_size = Some(2.try_into().unwrap())));
1948
1949        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
1950        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
1951        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
1952
1953        let font_size = px(14.0);
1954
1955        let map = cx.new(|cx| {
1956            DisplayMap::new(
1957                buffer,
1958                font("Helvetica"),
1959                font_size,
1960                None,
1961                true,
1962                1,
1963                1,
1964                1,
1965                FoldPlaceholder::test(),
1966                cx,
1967            )
1968        });
1969        assert_eq!(
1970            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
1971            vec![
1972                ("fn ".to_string(), None),
1973                ("outer".to_string(), Some(Hsla::blue())),
1974                ("() {}\n\nmod module ".to_string(), None),
1975                ("{\n    fn ".to_string(), Some(Hsla::red())),
1976                ("inner".to_string(), Some(Hsla::blue())),
1977                ("() {}\n}".to_string(), Some(Hsla::red())),
1978            ]
1979        );
1980        assert_eq!(
1981            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
1982            vec![
1983                ("    fn ".to_string(), Some(Hsla::red())),
1984                ("inner".to_string(), Some(Hsla::blue())),
1985                ("() {}\n}".to_string(), Some(Hsla::red())),
1986            ]
1987        );
1988
1989        map.update(cx, |map, cx| {
1990            map.fold(
1991                vec![Crease::simple(
1992                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
1993                    FoldPlaceholder::test(),
1994                )],
1995                cx,
1996            )
1997        });
1998        assert_eq!(
1999            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(2), &map, &theme, cx)),
2000            vec![
2001                ("fn ".to_string(), None),
2002                ("out".to_string(), Some(Hsla::blue())),
2003                ("".to_string(), None),
2004                ("  fn ".to_string(), Some(Hsla::red())),
2005                ("inner".to_string(), Some(Hsla::blue())),
2006                ("() {}\n}".to_string(), Some(Hsla::red())),
2007            ]
2008        );
2009    }
2010
2011    #[gpui::test]
2012    async fn test_chunks_with_syntax_highlighting_across_blocks(cx: &mut gpui::TestAppContext) {
2013        cx.background_executor
2014            .set_block_on_ticks(usize::MAX..=usize::MAX);
2015
2016        let text = r#"
2017            const A: &str = "
2018                one
2019                two
2020                three
2021            ";
2022            const B: &str = "four";
2023        "#
2024        .unindent();
2025
2026        let theme = SyntaxTheme::new_test(vec![
2027            ("string", Hsla::red()),
2028            ("punctuation", Hsla::blue()),
2029            ("keyword", Hsla::green()),
2030        ]);
2031        let language = Arc::new(
2032            Language::new(
2033                LanguageConfig {
2034                    name: "Rust".into(),
2035                    ..Default::default()
2036                },
2037                Some(tree_sitter_rust::LANGUAGE.into()),
2038            )
2039            .with_highlights_query(
2040                r#"
2041                (string_literal) @string
2042                "const" @keyword
2043                [":" ";"] @punctuation
2044                "#,
2045            )
2046            .unwrap(),
2047        );
2048        language.set_theme(&theme);
2049
2050        cx.update(|cx| init_test(cx, |_| {}));
2051
2052        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
2053        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
2054        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
2055        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
2056
2057        let map = cx.new(|cx| {
2058            DisplayMap::new(
2059                buffer,
2060                font("Courier"),
2061                px(16.0),
2062                None,
2063                true,
2064                1,
2065                1,
2066                0,
2067                FoldPlaceholder::test(),
2068                cx,
2069            )
2070        });
2071
2072        // Insert two blocks in the middle of a multi-line string literal.
2073        // The second block has zero height.
2074        map.update(cx, |map, cx| {
2075            map.insert_blocks(
2076                [
2077                    BlockProperties {
2078                        placement: BlockPlacement::Below(
2079                            buffer_snapshot.anchor_before(Point::new(1, 0)),
2080                        ),
2081                        height: 1,
2082                        style: BlockStyle::Sticky,
2083                        render: Arc::new(|_| div().into_any()),
2084                        priority: 0,
2085                    },
2086                    BlockProperties {
2087                        placement: BlockPlacement::Below(
2088                            buffer_snapshot.anchor_before(Point::new(2, 0)),
2089                        ),
2090                        height: 0,
2091                        style: BlockStyle::Sticky,
2092                        render: Arc::new(|_| div().into_any()),
2093                        priority: 0,
2094                    },
2095                ],
2096                cx,
2097            )
2098        });
2099
2100        pretty_assertions::assert_eq!(
2101            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(7), &map, &theme, cx)),
2102            [
2103                ("const".into(), Some(Hsla::green())),
2104                (" A".into(), None),
2105                (":".into(), Some(Hsla::blue())),
2106                (" &str = ".into(), None),
2107                ("\"\n    one\n".into(), Some(Hsla::red())),
2108                ("\n".into(), None),
2109                ("    two\n    three\n\"".into(), Some(Hsla::red())),
2110                (";".into(), Some(Hsla::blue())),
2111                ("\n".into(), None),
2112                ("const".into(), Some(Hsla::green())),
2113                (" B".into(), None),
2114                (":".into(), Some(Hsla::blue())),
2115                (" &str = ".into(), None),
2116                ("\"four\"".into(), Some(Hsla::red())),
2117                (";".into(), Some(Hsla::blue())),
2118                ("\n".into(), None),
2119            ]
2120        );
2121    }
2122
2123    #[gpui::test]
2124    async fn test_chunks_with_diagnostics_across_blocks(cx: &mut gpui::TestAppContext) {
2125        cx.background_executor
2126            .set_block_on_ticks(usize::MAX..=usize::MAX);
2127
2128        let text = r#"
2129            struct A {
2130                b: usize;
2131            }
2132            const c: usize = 1;
2133        "#
2134        .unindent();
2135
2136        cx.update(|cx| init_test(cx, |_| {}));
2137
2138        let buffer = cx.new(|cx| Buffer::local(text, cx));
2139
2140        buffer.update(cx, |buffer, cx| {
2141            buffer.update_diagnostics(
2142                LanguageServerId(0),
2143                DiagnosticSet::new(
2144                    [DiagnosticEntry {
2145                        range: PointUtf16::new(0, 0)..PointUtf16::new(2, 1),
2146                        diagnostic: Diagnostic {
2147                            severity: DiagnosticSeverity::ERROR,
2148                            group_id: 1,
2149                            message: "hi".into(),
2150                            ..Default::default()
2151                        },
2152                    }],
2153                    buffer,
2154                ),
2155                cx,
2156            )
2157        });
2158
2159        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
2160        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
2161
2162        let map = cx.new(|cx| {
2163            DisplayMap::new(
2164                buffer,
2165                font("Courier"),
2166                px(16.0),
2167                None,
2168                true,
2169                1,
2170                1,
2171                0,
2172                FoldPlaceholder::test(),
2173                cx,
2174            )
2175        });
2176
2177        let black = gpui::black().to_rgb();
2178        let red = gpui::red().to_rgb();
2179
2180        // Insert a block in the middle of a multi-line diagnostic.
2181        map.update(cx, |map, cx| {
2182            map.highlight_text(
2183                TypeId::of::<usize>(),
2184                vec![
2185                    buffer_snapshot.anchor_before(Point::new(3, 9))
2186                        ..buffer_snapshot.anchor_after(Point::new(3, 14)),
2187                    buffer_snapshot.anchor_before(Point::new(3, 17))
2188                        ..buffer_snapshot.anchor_after(Point::new(3, 18)),
2189                ],
2190                red.into(),
2191            );
2192            map.insert_blocks(
2193                [BlockProperties {
2194                    placement: BlockPlacement::Below(
2195                        buffer_snapshot.anchor_before(Point::new(1, 0)),
2196                    ),
2197                    height: 1,
2198                    style: BlockStyle::Sticky,
2199                    render: Arc::new(|_| div().into_any()),
2200                    priority: 0,
2201                }],
2202                cx,
2203            )
2204        });
2205
2206        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
2207        let mut chunks = Vec::<(String, Option<DiagnosticSeverity>, Rgba)>::new();
2208        for chunk in snapshot.chunks(DisplayRow(0)..DisplayRow(5), true, Default::default()) {
2209            let color = chunk
2210                .highlight_style
2211                .and_then(|style| style.color)
2212                .map_or(black, |color| color.to_rgb());
2213            if let Some((last_chunk, last_severity, last_color)) = chunks.last_mut() {
2214                if *last_severity == chunk.diagnostic_severity && *last_color == color {
2215                    last_chunk.push_str(chunk.text);
2216                    continue;
2217                }
2218            }
2219
2220            chunks.push((chunk.text.to_string(), chunk.diagnostic_severity, color));
2221        }
2222
2223        assert_eq!(
2224            chunks,
2225            [
2226                (
2227                    "struct A {\n    b: usize;\n".into(),
2228                    Some(DiagnosticSeverity::ERROR),
2229                    black
2230                ),
2231                ("\n".into(), None, black),
2232                ("}".into(), Some(DiagnosticSeverity::ERROR), black),
2233                ("\nconst c: ".into(), None, black),
2234                ("usize".into(), None, red),
2235                (" = ".into(), None, black),
2236                ("1".into(), None, red),
2237                (";\n".into(), None, black),
2238            ]
2239        );
2240    }
2241
2242    #[gpui::test]
2243    async fn test_point_translation_with_replace_blocks(cx: &mut gpui::TestAppContext) {
2244        cx.background_executor
2245            .set_block_on_ticks(usize::MAX..=usize::MAX);
2246
2247        cx.update(|cx| init_test(cx, |_| {}));
2248
2249        let buffer = cx.update(|cx| MultiBuffer::build_simple("abcde\nfghij\nklmno\npqrst", cx));
2250        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
2251        let map = cx.new(|cx| {
2252            DisplayMap::new(
2253                buffer.clone(),
2254                font("Courier"),
2255                px(16.0),
2256                None,
2257                true,
2258                1,
2259                1,
2260                0,
2261                FoldPlaceholder::test(),
2262                cx,
2263            )
2264        });
2265
2266        let snapshot = map.update(cx, |map, cx| {
2267            map.insert_blocks(
2268                [BlockProperties {
2269                    placement: BlockPlacement::Replace(
2270                        buffer_snapshot.anchor_before(Point::new(1, 2))
2271                            ..=buffer_snapshot.anchor_after(Point::new(2, 3)),
2272                    ),
2273                    height: 4,
2274                    style: BlockStyle::Fixed,
2275                    render: Arc::new(|_| div().into_any()),
2276                    priority: 0,
2277                }],
2278                cx,
2279            );
2280            map.snapshot(cx)
2281        });
2282
2283        assert_eq!(snapshot.text(), "abcde\n\n\n\n\npqrst");
2284
2285        let point_to_display_points = [
2286            (Point::new(1, 0), DisplayPoint::new(DisplayRow(1), 0)),
2287            (Point::new(2, 0), DisplayPoint::new(DisplayRow(1), 0)),
2288            (Point::new(3, 0), DisplayPoint::new(DisplayRow(5), 0)),
2289        ];
2290        for (buffer_point, display_point) in point_to_display_points {
2291            assert_eq!(
2292                snapshot.point_to_display_point(buffer_point, Bias::Left),
2293                display_point,
2294                "point_to_display_point({:?}, Bias::Left)",
2295                buffer_point
2296            );
2297            assert_eq!(
2298                snapshot.point_to_display_point(buffer_point, Bias::Right),
2299                display_point,
2300                "point_to_display_point({:?}, Bias::Right)",
2301                buffer_point
2302            );
2303        }
2304
2305        let display_points_to_points = [
2306            (
2307                DisplayPoint::new(DisplayRow(1), 0),
2308                Point::new(1, 0),
2309                Point::new(2, 5),
2310            ),
2311            (
2312                DisplayPoint::new(DisplayRow(2), 0),
2313                Point::new(1, 0),
2314                Point::new(2, 5),
2315            ),
2316            (
2317                DisplayPoint::new(DisplayRow(3), 0),
2318                Point::new(1, 0),
2319                Point::new(2, 5),
2320            ),
2321            (
2322                DisplayPoint::new(DisplayRow(4), 0),
2323                Point::new(1, 0),
2324                Point::new(2, 5),
2325            ),
2326            (
2327                DisplayPoint::new(DisplayRow(5), 0),
2328                Point::new(3, 0),
2329                Point::new(3, 0),
2330            ),
2331        ];
2332        for (display_point, left_buffer_point, right_buffer_point) in display_points_to_points {
2333            assert_eq!(
2334                snapshot.display_point_to_point(display_point, Bias::Left),
2335                left_buffer_point,
2336                "display_point_to_point({:?}, Bias::Left)",
2337                display_point
2338            );
2339            assert_eq!(
2340                snapshot.display_point_to_point(display_point, Bias::Right),
2341                right_buffer_point,
2342                "display_point_to_point({:?}, Bias::Right)",
2343                display_point
2344            );
2345        }
2346    }
2347
2348    // todo(linux) fails due to pixel differences in text rendering
2349    #[cfg(target_os = "macos")]
2350    #[gpui::test]
2351    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
2352        cx.background_executor
2353            .set_block_on_ticks(usize::MAX..=usize::MAX);
2354
2355        let text = r#"
2356            fn outer() {}
2357
2358            mod module {
2359                fn inner() {}
2360            }"#
2361        .unindent();
2362
2363        let theme =
2364            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
2365        let language = Arc::new(
2366            Language::new(
2367                LanguageConfig {
2368                    name: "Test".into(),
2369                    matcher: LanguageMatcher {
2370                        path_suffixes: vec![".test".to_string()],
2371                        ..Default::default()
2372                    },
2373                    ..Default::default()
2374                },
2375                Some(tree_sitter_rust::LANGUAGE.into()),
2376            )
2377            .with_highlights_query(
2378                r#"
2379                (mod_item name: (identifier) body: _ @mod.body)
2380                (function_item name: (identifier) @fn.name)
2381                "#,
2382            )
2383            .unwrap(),
2384        );
2385        language.set_theme(&theme);
2386
2387        cx.update(|cx| init_test(cx, |_| {}));
2388
2389        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
2390        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
2391        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
2392
2393        let font_size = px(16.0);
2394
2395        let map = cx.new(|cx| {
2396            DisplayMap::new(
2397                buffer,
2398                font("Courier"),
2399                font_size,
2400                Some(px(40.0)),
2401                true,
2402                1,
2403                1,
2404                0,
2405                FoldPlaceholder::test(),
2406                cx,
2407            )
2408        });
2409        assert_eq!(
2410            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
2411            [
2412                ("fn \n".to_string(), None),
2413                ("oute\nr".to_string(), Some(Hsla::blue())),
2414                ("() \n{}\n\n".to_string(), None),
2415            ]
2416        );
2417        assert_eq!(
2418            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
2419            [("{}\n\n".to_string(), None)]
2420        );
2421
2422        map.update(cx, |map, cx| {
2423            map.fold(
2424                vec![Crease::simple(
2425                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
2426                    FoldPlaceholder::test(),
2427                )],
2428                cx,
2429            )
2430        });
2431        assert_eq!(
2432            cx.update(|cx| syntax_chunks(DisplayRow(1)..DisplayRow(4), &map, &theme, cx)),
2433            [
2434                ("out".to_string(), Some(Hsla::blue())),
2435                ("\n".to_string(), None),
2436                ("  \nfn ".to_string(), Some(Hsla::red())),
2437                ("i\n".to_string(), Some(Hsla::blue()))
2438            ]
2439        );
2440    }
2441
2442    #[gpui::test]
2443    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
2444        cx.update(|cx| init_test(cx, |_| {}));
2445
2446        let theme =
2447            SyntaxTheme::new_test(vec![("operator", Hsla::red()), ("string", Hsla::green())]);
2448        let language = Arc::new(
2449            Language::new(
2450                LanguageConfig {
2451                    name: "Test".into(),
2452                    matcher: LanguageMatcher {
2453                        path_suffixes: vec![".test".to_string()],
2454                        ..Default::default()
2455                    },
2456                    ..Default::default()
2457                },
2458                Some(tree_sitter_rust::LANGUAGE.into()),
2459            )
2460            .with_highlights_query(
2461                r#"
2462                ":" @operator
2463                (string_literal) @string
2464                "#,
2465            )
2466            .unwrap(),
2467        );
2468        language.set_theme(&theme);
2469
2470        let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»: B = "c «d»""#, false);
2471
2472        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
2473        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
2474
2475        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
2476        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
2477
2478        let font_size = px(16.0);
2479        let map = cx.new(|cx| {
2480            DisplayMap::new(
2481                buffer,
2482                font("Courier"),
2483                font_size,
2484                None,
2485                true,
2486                1,
2487                1,
2488                1,
2489                FoldPlaceholder::test(),
2490                cx,
2491            )
2492        });
2493
2494        enum MyType {}
2495
2496        let style = HighlightStyle {
2497            color: Some(Hsla::blue()),
2498            ..Default::default()
2499        };
2500
2501        map.update(cx, |map, _cx| {
2502            map.highlight_text(
2503                TypeId::of::<MyType>(),
2504                highlighted_ranges
2505                    .into_iter()
2506                    .map(|range| {
2507                        buffer_snapshot.anchor_before(range.start)
2508                            ..buffer_snapshot.anchor_before(range.end)
2509                    })
2510                    .collect(),
2511                style,
2512            );
2513        });
2514
2515        assert_eq!(
2516            cx.update(|cx| chunks(DisplayRow(0)..DisplayRow(10), &map, &theme, cx)),
2517            [
2518                ("const ".to_string(), None, None),
2519                ("a".to_string(), None, Some(Hsla::blue())),
2520                (":".to_string(), Some(Hsla::red()), None),
2521                (" B = ".to_string(), None, None),
2522                ("\"c ".to_string(), Some(Hsla::green()), None),
2523                ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
2524                ("\"".to_string(), Some(Hsla::green()), None),
2525            ]
2526        );
2527    }
2528
2529    #[gpui::test]
2530    fn test_clip_point(cx: &mut gpui::App) {
2531        init_test(cx, |_| {});
2532
2533        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::App) {
2534            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
2535
2536            match bias {
2537                Bias::Left => {
2538                    if shift_right {
2539                        *markers[1].column_mut() += 1;
2540                    }
2541
2542                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
2543                }
2544                Bias::Right => {
2545                    if shift_right {
2546                        *markers[0].column_mut() += 1;
2547                    }
2548
2549                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
2550                }
2551            };
2552        }
2553
2554        use Bias::{Left, Right};
2555        assert("ˇˇα", false, Left, cx);
2556        assert("ˇˇα", true, Left, cx);
2557        assert("ˇˇα", false, Right, cx);
2558        assert("ˇαˇ", true, Right, cx);
2559        assert("ˇˇ✋", false, Left, cx);
2560        assert("ˇˇ✋", true, Left, cx);
2561        assert("ˇˇ✋", false, Right, cx);
2562        assert("ˇ✋ˇ", true, Right, cx);
2563        assert("ˇˇ🍐", false, Left, cx);
2564        assert("ˇˇ🍐", true, Left, cx);
2565        assert("ˇˇ🍐", false, Right, cx);
2566        assert("ˇ🍐ˇ", true, Right, cx);
2567        assert("ˇˇ\t", false, Left, cx);
2568        assert("ˇˇ\t", true, Left, cx);
2569        assert("ˇˇ\t", false, Right, cx);
2570        assert("ˇ\tˇ", true, Right, cx);
2571        assert(" ˇˇ\t", false, Left, cx);
2572        assert(" ˇˇ\t", true, Left, cx);
2573        assert(" ˇˇ\t", false, Right, cx);
2574        assert(" ˇ\tˇ", true, Right, cx);
2575        assert("   ˇˇ\t", false, Left, cx);
2576        assert("   ˇˇ\t", false, Right, cx);
2577    }
2578
2579    #[gpui::test]
2580    fn test_clip_at_line_ends(cx: &mut gpui::App) {
2581        init_test(cx, |_| {});
2582
2583        fn assert(text: &str, cx: &mut gpui::App) {
2584            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
2585            unmarked_snapshot.clip_at_line_ends = true;
2586            assert_eq!(
2587                unmarked_snapshot.clip_point(markers[1], Bias::Left),
2588                markers[0]
2589            );
2590        }
2591
2592        assert("ˇˇ", cx);
2593        assert("ˇaˇ", cx);
2594        assert("aˇbˇ", cx);
2595        assert("aˇαˇ", cx);
2596    }
2597
2598    #[gpui::test]
2599    fn test_creases(cx: &mut gpui::App) {
2600        init_test(cx, |_| {});
2601
2602        let text = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll";
2603        let buffer = MultiBuffer::build_simple(text, cx);
2604        let font_size = px(14.0);
2605        cx.new(|cx| {
2606            let mut map = DisplayMap::new(
2607                buffer.clone(),
2608                font("Helvetica"),
2609                font_size,
2610                None,
2611                true,
2612                1,
2613                1,
2614                0,
2615                FoldPlaceholder::test(),
2616                cx,
2617            );
2618            let snapshot = map.buffer.read(cx).snapshot(cx);
2619            let range =
2620                snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
2621
2622            map.crease_map.insert(
2623                [Crease::inline(
2624                    range,
2625                    FoldPlaceholder::test(),
2626                    |_row, _status, _toggle, _window, _cx| div(),
2627                    |_row, _status, _window, _cx| div(),
2628                )],
2629                &map.buffer.read(cx).snapshot(cx),
2630            );
2631
2632            map
2633        });
2634    }
2635
2636    #[gpui::test]
2637    fn test_tabs_with_multibyte_chars(cx: &mut gpui::App) {
2638        init_test(cx, |_| {});
2639
2640        let text = "\t\tα\nβ\t\n🏀β\t\tγ";
2641        let buffer = MultiBuffer::build_simple(text, cx);
2642        let font_size = px(14.0);
2643
2644        let map = cx.new(|cx| {
2645            DisplayMap::new(
2646                buffer.clone(),
2647                font("Helvetica"),
2648                font_size,
2649                None,
2650                true,
2651                1,
2652                1,
2653                0,
2654                FoldPlaceholder::test(),
2655                cx,
2656            )
2657        });
2658        let map = map.update(cx, |map, cx| map.snapshot(cx));
2659        assert_eq!(map.text(), "✅       α\nβ   \n🏀β      γ");
2660        assert_eq!(
2661            map.text_chunks(DisplayRow(0)).collect::<String>(),
2662            "✅       α\nβ   \n🏀β      γ"
2663        );
2664        assert_eq!(
2665            map.text_chunks(DisplayRow(1)).collect::<String>(),
2666            "β   \n🏀β      γ"
2667        );
2668        assert_eq!(
2669            map.text_chunks(DisplayRow(2)).collect::<String>(),
2670            "🏀β      γ"
2671        );
2672
2673        let point = MultiBufferPoint::new(0, "\t\t".len() as u32);
2674        let display_point = DisplayPoint::new(DisplayRow(0), "".len() as u32);
2675        assert_eq!(point.to_display_point(&map), display_point);
2676        assert_eq!(display_point.to_point(&map), point);
2677
2678        let point = MultiBufferPoint::new(1, "β\t".len() as u32);
2679        let display_point = DisplayPoint::new(DisplayRow(1), "β   ".len() as u32);
2680        assert_eq!(point.to_display_point(&map), display_point);
2681        assert_eq!(display_point.to_point(&map), point,);
2682
2683        let point = MultiBufferPoint::new(2, "🏀β\t\t".len() as u32);
2684        let display_point = DisplayPoint::new(DisplayRow(2), "🏀β      ".len() as u32);
2685        assert_eq!(point.to_display_point(&map), display_point);
2686        assert_eq!(display_point.to_point(&map), point,);
2687
2688        // Display points inside of expanded tabs
2689        assert_eq!(
2690            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
2691            MultiBufferPoint::new(0, "\t".len() as u32),
2692        );
2693        assert_eq!(
2694            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
2695            MultiBufferPoint::new(0, "".len() as u32),
2696        );
2697
2698        // Clipping display points inside of multi-byte characters
2699        assert_eq!(
2700            map.clip_point(
2701                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
2702                Left
2703            ),
2704            DisplayPoint::new(DisplayRow(0), 0)
2705        );
2706        assert_eq!(
2707            map.clip_point(
2708                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
2709                Bias::Right
2710            ),
2711            DisplayPoint::new(DisplayRow(0), "".len() as u32)
2712        );
2713    }
2714
2715    #[gpui::test]
2716    fn test_max_point(cx: &mut gpui::App) {
2717        init_test(cx, |_| {});
2718
2719        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
2720        let font_size = px(14.0);
2721        let map = cx.new(|cx| {
2722            DisplayMap::new(
2723                buffer.clone(),
2724                font("Helvetica"),
2725                font_size,
2726                None,
2727                true,
2728                1,
2729                1,
2730                0,
2731                FoldPlaceholder::test(),
2732                cx,
2733            )
2734        });
2735        assert_eq!(
2736            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
2737            DisplayPoint::new(DisplayRow(1), 11)
2738        )
2739    }
2740
2741    fn syntax_chunks(
2742        rows: Range<DisplayRow>,
2743        map: &Entity<DisplayMap>,
2744        theme: &SyntaxTheme,
2745        cx: &mut App,
2746    ) -> Vec<(String, Option<Hsla>)> {
2747        chunks(rows, map, theme, cx)
2748            .into_iter()
2749            .map(|(text, color, _)| (text, color))
2750            .collect()
2751    }
2752
2753    fn chunks(
2754        rows: Range<DisplayRow>,
2755        map: &Entity<DisplayMap>,
2756        theme: &SyntaxTheme,
2757        cx: &mut App,
2758    ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
2759        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
2760        let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
2761        for chunk in snapshot.chunks(rows, true, HighlightStyles::default()) {
2762            let syntax_color = chunk
2763                .syntax_highlight_id
2764                .and_then(|id| id.style(theme)?.color);
2765            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
2766            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut() {
2767                if syntax_color == *last_syntax_color && highlight_color == *last_highlight_color {
2768                    last_chunk.push_str(chunk.text);
2769                    continue;
2770                }
2771            }
2772            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
2773        }
2774        chunks
2775    }
2776
2777    fn init_test(cx: &mut App, f: impl Fn(&mut AllLanguageSettingsContent)) {
2778        let settings = SettingsStore::test(cx);
2779        cx.set_global(settings);
2780        workspace::init_settings(cx);
2781        language::init(cx);
2782        crate::init(cx);
2783        Project::init_settings(cx);
2784        theme::init(LoadThemes::JustBase, cx);
2785        cx.update_global::<SettingsStore, _>(|store, cx| {
2786            store.update_user_settings::<AllLanguageSettings>(cx, f);
2787        });
2788    }
2789}