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//! ## Structure of the display map layers
  18//!
  19//! Each layer in the map (and the multibuffer itself to some extent) has a few
  20//! structures that are used to implement the public API available to the layer
  21//! above:
  22//! - a `Transform` type - this represents a region of text that the layer in
  23//!   question is "managing", that it transforms into a more "processed" text
  24//!   for the layer above. For example, the inlay map has an `enum Transform`
  25//!   that has two variants:
  26//!     - `Isomorphic`, representing a region of text that has no inlay hints (i.e.
  27//!       is passed through the map transparently)
  28//!     - `Inlay`, representing a location where an inlay hint is to be inserted.
  29//! - a `TransformSummary` type, which is usually a struct with two fields:
  30//!   [`input: TextSummary`][`TextSummary`] and [`output: TextSummary`][`TextSummary`]. Here,
  31//!   `input` corresponds to "text in the layer below", and `output` corresponds to the text
  32//!   exposed to the layer above. So in the inlay map case, a `Transform::Isomorphic`'s summary is
  33//!   just `input = output = summary`, where `summary` is the [`TextSummary`] stored in that
  34//!   variant. Conversely, a `Transform::Inlay` always has an empty `input` summary, because it's
  35//!   not "replacing" any text that exists on disk. The `output` is the summary of the inlay text
  36//!   to be injected. - Various newtype wrappers for co-ordinate spaces (e.g. [`WrapRow`]
  37//!   represents a row index, after soft-wrapping (and all lower layers)).
  38//! - A `Snapshot` type (e.g. [`InlaySnapshot`]) that captures the state of a layer at a specific
  39//!   point in time.
  40//! - various APIs which drill through the layers below to work with the underlying text. Notably:
  41//!   - `fn text_summary_for_offset()` returns a [`TextSummary`] for the range in the co-ordinate
  42//!     space that the map in question is responsible for.
  43//!   - `fn <A>_point_to_<B>_point()` converts a point in co-ordinate space `A` into co-ordinate
  44//!     space `B`.
  45//!   - A [`RowInfo`] iterator (e.g. [`InlayBufferRows`]) and a [`Chunk`] iterator
  46//!     (e.g. [`InlayChunks`])
  47//!   - A `sync` function (e.g. [`InlayMap::sync`]) that takes a snapshot and list of [`Edit<T>`]s,
  48//!     and returns a new snapshot and a list of transformed [`Edit<S>`]s. Note that the generic
  49//!     parameter on `Edit` changes, since these methods take in edits in the co-ordinate space of
  50//!     the lower layer, and return edits in their own co-ordinate space. The term "edit" is
  51//!     slightly misleading, since an [`Edit<T>`] doesn't tell you what changed - rather it can be
  52//!     thought of as a "region to invalidate". In theory, it would be correct to always use a
  53//!     single edit that covers the entire range. However, this would lead to lots of unnecessary
  54//!     recalculation.
  55//!
  56//! See the docs for the [`inlay_map`] module for a more in-depth explanation of how a single layer
  57//! works.
  58//!
  59//! [Editor]: crate::Editor
  60//! [EditorElement]: crate::element::EditorElement
  61//! [`TextSummary`]: multi_buffer::MBTextSummary
  62//! [`WrapRow`]: wrap_map::WrapRow
  63//! [`InlayBufferRows`]: inlay_map::InlayBufferRows
  64//! [`InlayChunks`]: inlay_map::InlayChunks
  65//! [`Edit<T>`]: text::Edit
  66//! [`Edit<S>`]: text::Edit
  67//! [`Chunk`]: language::Chunk
  68
  69#[macro_use]
  70mod dimensions;
  71
  72mod block_map;
  73mod crease_map;
  74mod custom_highlights;
  75mod fold_map;
  76mod inlay_map;
  77mod invisibles;
  78mod tab_map;
  79mod wrap_map;
  80
  81pub use crate::display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap};
  82pub use block_map::{
  83    Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,
  84    BlockPoint, BlockProperties, BlockRows, BlockStyle, CompanionView, CustomBlockId,
  85    EditorMargins, RenderBlock, StickyHeaderExcerpt,
  86};
  87pub use crease_map::*;
  88pub use fold_map::{
  89    ChunkRenderer, ChunkRendererContext, ChunkRendererId, Fold, FoldId, FoldPlaceholder, FoldPoint,
  90};
  91pub use inlay_map::{InlayOffset, InlayPoint};
  92pub use invisibles::{is_invisible, replacement};
  93pub use wrap_map::{WrapPoint, WrapRow, WrapSnapshot};
  94
  95use collections::{HashMap, HashSet, IndexSet};
  96use gpui::{
  97    App, Context, Entity, EntityId, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle,
  98    WeakEntity,
  99};
 100use language::{Point, Subscription as BufferSubscription, language_settings::language_settings};
 101use multi_buffer::{
 102    Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16,
 103    MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint,
 104};
 105use project::project_settings::DiagnosticSeverity;
 106use project::{InlayId, lsp_store::TokenType};
 107use serde::Deserialize;
 108use sum_tree::{Bias, TreeMap};
 109use text::{BufferId, LineIndent, Patch};
 110use ui::{SharedString, px};
 111use unicode_segmentation::UnicodeSegmentation;
 112use ztracing::instrument;
 113
 114use std::{
 115    any::TypeId,
 116    borrow::Cow,
 117    fmt::Debug,
 118    iter,
 119    num::NonZeroU32,
 120    ops::{self, Add, Bound, Range, Sub},
 121    sync::Arc,
 122};
 123
 124use crate::{
 125    EditorStyle, RowExt, hover_links::InlayHighlight, inlays::Inlay, movement::TextLayoutDetails,
 126};
 127use block_map::{BlockRow, BlockSnapshot};
 128use fold_map::FoldSnapshot;
 129use inlay_map::InlaySnapshot;
 130use tab_map::TabSnapshot;
 131use wrap_map::{WrapMap, WrapPatch};
 132
 133#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 134pub enum FoldStatus {
 135    Folded,
 136    Foldable,
 137}
 138
 139/// Keys for tagging text highlights.
 140///
 141/// Note the order is important as it determines the priority of the highlights, lower means higher priority
 142#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
 143pub enum HighlightKey {
 144    // Note we want semantic tokens > colorized brackets
 145    // to allow language server highlights to work over brackets.
 146    ColorizeBracket(usize),
 147    SemanticToken,
 148    // below is sorted lexicographically, as there is no relevant ordering for these aside from coming after the above
 149    BufferSearchHighlights,
 150    ConsoleAnsiHighlight(usize),
 151    DebugStackFrameLine,
 152    DocumentHighlightRead,
 153    DocumentHighlightWrite,
 154    EditPredictionHighlight,
 155    Editor,
 156    HighlightOnYank,
 157    HighlightsTreeView(usize),
 158    HoverState,
 159    HoveredLinkState,
 160    InlineAssist,
 161    InputComposition,
 162    MatchingBracket,
 163    PendingInput,
 164    ProjectSearchView,
 165    Rename,
 166    SearchWithinRange,
 167    SelectedTextHighlight,
 168    SyntaxTreeView(usize),
 169    VimExchange,
 170}
 171
 172pub trait ToDisplayPoint {
 173    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
 174}
 175
 176type TextHighlights = TreeMap<HighlightKey, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
 177type SemanticTokensHighlights =
 178    TreeMap<BufferId, (Arc<[SemanticTokenHighlight]>, Arc<HighlightStyleInterner>)>;
 179type InlayHighlights = TreeMap<HighlightKey, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>;
 180
 181#[derive(Debug)]
 182pub struct CompanionExcerptPatch {
 183    pub patch: Patch<MultiBufferPoint>,
 184    pub edited_range: Range<MultiBufferPoint>,
 185    pub source_excerpt_range: Range<MultiBufferPoint>,
 186    pub target_excerpt_range: Range<MultiBufferPoint>,
 187}
 188
 189pub type ConvertMultiBufferRows = fn(
 190    &HashMap<ExcerptId, ExcerptId>,
 191    &MultiBufferSnapshot,
 192    &MultiBufferSnapshot,
 193    (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
 194) -> Vec<CompanionExcerptPatch>;
 195
 196/// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,
 197/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.
 198///
 199/// See the [module level documentation](self) for more information.
 200pub struct DisplayMap {
 201    entity_id: EntityId,
 202    /// The buffer that we are displaying.
 203    buffer: Entity<MultiBuffer>,
 204    buffer_subscription: BufferSubscription<MultiBufferOffset>,
 205    /// Decides where the [`Inlay`]s should be displayed.
 206    inlay_map: InlayMap,
 207    /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.
 208    fold_map: FoldMap,
 209    /// Keeps track of hard tabs in a buffer.
 210    tab_map: TabMap,
 211    /// Handles soft wrapping.
 212    wrap_map: Entity<WrapMap>,
 213    /// Tracks custom blocks such as diagnostics that should be displayed within buffer.
 214    block_map: BlockMap,
 215    /// Regions of text that should be highlighted.
 216    text_highlights: TextHighlights,
 217    /// Regions of inlays that should be highlighted.
 218    inlay_highlights: InlayHighlights,
 219    /// The semantic tokens from the language server.
 220    pub semantic_token_highlights: SemanticTokensHighlights,
 221    /// A container for explicitly foldable ranges, which supersede indentation based fold range suggestions.
 222    crease_map: CreaseMap,
 223    pub(crate) fold_placeholder: FoldPlaceholder,
 224    pub clip_at_line_ends: bool,
 225    pub(crate) masked: bool,
 226    pub(crate) diagnostics_max_severity: DiagnosticSeverity,
 227    pub(crate) companion: Option<(WeakEntity<DisplayMap>, Entity<Companion>)>,
 228}
 229
 230// test change
 231
 232pub(crate) struct Companion {
 233    rhs_display_map_id: EntityId,
 234    rhs_folded_buffers: HashSet<BufferId>,
 235    rhs_buffer_to_lhs_buffer: HashMap<BufferId, BufferId>,
 236    lhs_buffer_to_rhs_buffer: HashMap<BufferId, BufferId>,
 237    rhs_excerpt_to_lhs_excerpt: HashMap<ExcerptId, ExcerptId>,
 238    lhs_excerpt_to_rhs_excerpt: HashMap<ExcerptId, ExcerptId>,
 239    rhs_rows_to_lhs_rows: ConvertMultiBufferRows,
 240    lhs_rows_to_rhs_rows: ConvertMultiBufferRows,
 241}
 242
 243impl Companion {
 244    pub(crate) fn new(
 245        rhs_display_map_id: EntityId,
 246        rhs_folded_buffers: HashSet<BufferId>,
 247        rhs_rows_to_lhs_rows: ConvertMultiBufferRows,
 248        lhs_rows_to_rhs_rows: ConvertMultiBufferRows,
 249    ) -> Self {
 250        Self {
 251            rhs_display_map_id,
 252            rhs_folded_buffers,
 253            rhs_buffer_to_lhs_buffer: Default::default(),
 254            lhs_buffer_to_rhs_buffer: Default::default(),
 255            rhs_excerpt_to_lhs_excerpt: Default::default(),
 256            lhs_excerpt_to_rhs_excerpt: Default::default(),
 257            rhs_rows_to_lhs_rows,
 258            lhs_rows_to_rhs_rows,
 259        }
 260    }
 261
 262    pub(crate) fn convert_rows_to_companion(
 263        &self,
 264        display_map_id: EntityId,
 265        companion_snapshot: &MultiBufferSnapshot,
 266        our_snapshot: &MultiBufferSnapshot,
 267        bounds: (Bound<MultiBufferPoint>, Bound<MultiBufferPoint>),
 268    ) -> Vec<CompanionExcerptPatch> {
 269        let (excerpt_map, convert_fn) = if display_map_id == self.rhs_display_map_id {
 270            (&self.rhs_excerpt_to_lhs_excerpt, self.rhs_rows_to_lhs_rows)
 271        } else {
 272            (&self.lhs_excerpt_to_rhs_excerpt, self.lhs_rows_to_rhs_rows)
 273        };
 274        convert_fn(excerpt_map, companion_snapshot, our_snapshot, bounds)
 275    }
 276
 277    pub(crate) fn convert_point_from_companion(
 278        &self,
 279        display_map_id: EntityId,
 280        our_snapshot: &MultiBufferSnapshot,
 281        companion_snapshot: &MultiBufferSnapshot,
 282        point: MultiBufferPoint,
 283    ) -> Range<MultiBufferPoint> {
 284        let (excerpt_map, convert_fn) = if display_map_id == self.rhs_display_map_id {
 285            (&self.lhs_excerpt_to_rhs_excerpt, self.lhs_rows_to_rhs_rows)
 286        } else {
 287            (&self.rhs_excerpt_to_lhs_excerpt, self.rhs_rows_to_lhs_rows)
 288        };
 289
 290        let excerpt = convert_fn(
 291            excerpt_map,
 292            our_snapshot,
 293            companion_snapshot,
 294            (Bound::Included(point), Bound::Included(point)),
 295        )
 296        .into_iter()
 297        .next();
 298
 299        let Some(excerpt) = excerpt else {
 300            return Point::zero()..our_snapshot.max_point();
 301        };
 302        excerpt.patch.edit_for_old_position(point).new
 303    }
 304
 305    pub(crate) fn companion_excerpt_to_excerpt(
 306        &self,
 307        display_map_id: EntityId,
 308    ) -> &HashMap<ExcerptId, ExcerptId> {
 309        if display_map_id == self.rhs_display_map_id {
 310            &self.lhs_excerpt_to_rhs_excerpt
 311        } else {
 312            &self.rhs_excerpt_to_lhs_excerpt
 313        }
 314    }
 315
 316    fn buffer_to_companion_buffer(&self, display_map_id: EntityId) -> &HashMap<BufferId, BufferId> {
 317        if display_map_id == self.rhs_display_map_id {
 318            &self.rhs_buffer_to_lhs_buffer
 319        } else {
 320            &self.lhs_buffer_to_rhs_buffer
 321        }
 322    }
 323
 324    pub(crate) fn add_excerpt_mapping(&mut self, lhs_id: ExcerptId, rhs_id: ExcerptId) {
 325        self.lhs_excerpt_to_rhs_excerpt.insert(lhs_id, rhs_id);
 326        self.rhs_excerpt_to_lhs_excerpt.insert(rhs_id, lhs_id);
 327    }
 328
 329    pub(crate) fn remove_excerpt_mappings(
 330        &mut self,
 331        lhs_ids: impl IntoIterator<Item = ExcerptId>,
 332        rhs_ids: impl IntoIterator<Item = ExcerptId>,
 333    ) {
 334        for id in lhs_ids {
 335            self.lhs_excerpt_to_rhs_excerpt.remove(&id);
 336        }
 337        for id in rhs_ids {
 338            self.rhs_excerpt_to_lhs_excerpt.remove(&id);
 339        }
 340    }
 341
 342    pub(crate) fn lhs_to_rhs_buffer(&self, lhs_buffer_id: BufferId) -> Option<BufferId> {
 343        self.lhs_buffer_to_rhs_buffer.get(&lhs_buffer_id).copied()
 344    }
 345
 346    pub(crate) fn add_buffer_mapping(&mut self, lhs_buffer: BufferId, rhs_buffer: BufferId) {
 347        self.lhs_buffer_to_rhs_buffer.insert(lhs_buffer, rhs_buffer);
 348        self.rhs_buffer_to_lhs_buffer.insert(rhs_buffer, lhs_buffer);
 349    }
 350}
 351
 352#[derive(Default, Debug)]
 353pub struct HighlightStyleInterner {
 354    styles: IndexSet<HighlightStyle>,
 355}
 356
 357impl HighlightStyleInterner {
 358    pub(crate) fn intern(&mut self, style: HighlightStyle) -> HighlightStyleId {
 359        HighlightStyleId(self.styles.insert_full(style).0 as u32)
 360    }
 361}
 362
 363impl ops::Index<HighlightStyleId> for HighlightStyleInterner {
 364    type Output = HighlightStyle;
 365
 366    fn index(&self, index: HighlightStyleId) -> &Self::Output {
 367        &self.styles[index.0 as usize]
 368    }
 369}
 370
 371#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 372pub struct HighlightStyleId(u32);
 373
 374/// A `SemanticToken`, but positioned to an offset in a buffer, and stylized.
 375#[derive(Debug, Clone)]
 376pub struct SemanticTokenHighlight {
 377    pub range: Range<Anchor>,
 378    pub style: HighlightStyleId,
 379    pub token_type: TokenType,
 380    pub token_modifiers: u32,
 381    pub server_id: lsp::LanguageServerId,
 382}
 383
 384impl DisplayMap {
 385    pub fn new(
 386        buffer: Entity<MultiBuffer>,
 387        font: Font,
 388        font_size: Pixels,
 389        wrap_width: Option<Pixels>,
 390        buffer_header_height: u32,
 391        excerpt_header_height: u32,
 392        fold_placeholder: FoldPlaceholder,
 393        diagnostics_max_severity: DiagnosticSeverity,
 394        cx: &mut Context<Self>,
 395    ) -> Self {
 396        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());
 397
 398        let tab_size = Self::tab_size(&buffer, cx);
 399        let buffer_snapshot = buffer.read(cx).snapshot(cx);
 400        let crease_map = CreaseMap::new(&buffer_snapshot);
 401        let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);
 402        let (fold_map, snapshot) = FoldMap::new(snapshot);
 403        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);
 404        let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);
 405        let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);
 406
 407        cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();
 408
 409        DisplayMap {
 410            entity_id: cx.entity_id(),
 411            buffer,
 412            buffer_subscription,
 413            fold_map,
 414            inlay_map,
 415            tab_map,
 416            wrap_map,
 417            block_map,
 418            crease_map,
 419            fold_placeholder,
 420            diagnostics_max_severity,
 421            text_highlights: Default::default(),
 422            inlay_highlights: Default::default(),
 423            semantic_token_highlights: TreeMap::default(),
 424            clip_at_line_ends: false,
 425            masked: false,
 426            companion: None,
 427        }
 428    }
 429
 430    pub(crate) fn set_companion(
 431        &mut self,
 432        companion: Option<(WeakEntity<DisplayMap>, Entity<Companion>)>,
 433        cx: &mut Context<Self>,
 434    ) {
 435        let Some((companion_display_map, companion)) = companion else {
 436            self.companion = None;
 437            let (snapshot, edits) = self.sync_through_wrap(cx);
 438            let edits = edits.compose([text::Edit {
 439                old: WrapRow(0)..snapshot.max_point().row(),
 440                new: WrapRow(0)..snapshot.max_point().row(),
 441            }]);
 442            self.block_map.read(snapshot, edits, None);
 443            return;
 444        };
 445
 446        // Second call to set_companion doesn't need to do anything
 447        if companion_display_map
 448            .update(cx, |companion_dm, _| companion_dm.companion.is_none())
 449            .unwrap_or(true)
 450        {
 451            self.companion = Some((companion_display_map, companion));
 452            return;
 453        }
 454
 455        let rhs_display_map_id = companion.read(cx).rhs_display_map_id;
 456        if self.entity_id != rhs_display_map_id {
 457            let buffer_mapping = companion
 458                .read(cx)
 459                .buffer_to_companion_buffer(rhs_display_map_id);
 460            self.block_map.folded_buffers = companion
 461                .read(cx)
 462                .rhs_folded_buffers
 463                .iter()
 464                .filter_map(|id| buffer_mapping.get(id).copied())
 465                .collect();
 466        }
 467
 468        let snapshot = self.unfold_intersecting([Anchor::min()..Anchor::max()], true, cx);
 469
 470        self.companion = Some((companion_display_map.clone(), companion));
 471
 472        let companion_wrap_data = companion_display_map
 473            .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 474            .ok();
 475        let companion = self.companion.as_ref().map(|(_, c)| c.read(cx));
 476        let companion_view =
 477            companion_wrap_data
 478                .as_ref()
 479                .zip(companion)
 480                .map(|((snapshot, edits), companion)| {
 481                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 482                });
 483
 484        let edits = Patch::new(
 485            [text::Edit {
 486                old: WrapRow(0)..snapshot.max_point().row(),
 487                new: WrapRow(0)..snapshot.max_point().row(),
 488            }]
 489            .into_iter()
 490            .collect(),
 491        );
 492        self.block_map
 493            .read(snapshot.clone(), edits.clone(), companion_view);
 494
 495        if let Some((companion_dm, _)) = &self.companion {
 496            let _ = companion_dm.update(cx, |dm, _cx| {
 497                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 498                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(_cx));
 499                    dm.block_map.read(
 500                        companion_snapshot,
 501                        companion_edits,
 502                        their_companion_ref
 503                            .map(|c| CompanionView::new(dm.entity_id, &snapshot, &edits, c)),
 504                    );
 505                }
 506            });
 507        }
 508    }
 509
 510    pub(crate) fn companion(&self) -> Option<&Entity<Companion>> {
 511        self.companion.as_ref().map(|(_, c)| c)
 512    }
 513
 514    pub(crate) fn companion_excerpt_to_my_excerpt(
 515        &self,
 516        their_id: ExcerptId,
 517        cx: &App,
 518    ) -> Option<ExcerptId> {
 519        let (_, companion) = self.companion.as_ref()?;
 520        let c = companion.read(cx);
 521        c.companion_excerpt_to_excerpt(self.entity_id)
 522            .get(&their_id)
 523            .copied()
 524    }
 525
 526    fn sync_through_wrap(&mut self, cx: &mut App) -> (WrapSnapshot, WrapPatch) {
 527        let tab_size = Self::tab_size(&self.buffer, cx);
 528        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 529        let edits = self.buffer_subscription.consume().into_inner();
 530
 531        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
 532        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
 533        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 534        self.wrap_map
 535            .update(cx, |map, cx| map.sync(snapshot, edits, cx))
 536    }
 537
 538    #[instrument(skip_all)]
 539    pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {
 540        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
 541        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 542            companion_dm
 543                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 544                .ok()
 545        });
 546        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 547        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 548            |((snapshot, edits), companion)| {
 549                CompanionView::new(self.entity_id, snapshot, edits, companion)
 550            },
 551        );
 552
 553        let block_snapshot = self
 554            .block_map
 555            .read(
 556                self_wrap_snapshot.clone(),
 557                self_wrap_edits.clone(),
 558                companion_view,
 559            )
 560            .snapshot;
 561
 562        if let Some((companion_dm, _)) = &self.companion {
 563            let _ = companion_dm.update(cx, |dm, _cx| {
 564                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 565                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(_cx));
 566                    dm.block_map.read(
 567                        companion_snapshot,
 568                        companion_edits,
 569                        their_companion_ref.map(|c| {
 570                            CompanionView::new(
 571                                dm.entity_id,
 572                                &self_wrap_snapshot,
 573                                &self_wrap_edits,
 574                                c,
 575                            )
 576                        }),
 577                    );
 578                }
 579            });
 580        }
 581
 582        let companion_display_snapshot = self.companion.as_ref().and_then(|(companion_dm, _)| {
 583            companion_dm
 584                .update(cx, |dm, cx| Arc::new(dm.snapshot_simple(cx)))
 585                .ok()
 586        });
 587
 588        DisplaySnapshot {
 589            display_map_id: self.entity_id,
 590            companion_display_snapshot,
 591            block_snapshot,
 592            diagnostics_max_severity: self.diagnostics_max_severity,
 593            crease_snapshot: self.crease_map.snapshot(),
 594            text_highlights: self.text_highlights.clone(),
 595            inlay_highlights: self.inlay_highlights.clone(),
 596            semantic_token_highlights: self.semantic_token_highlights.clone(),
 597            clip_at_line_ends: self.clip_at_line_ends,
 598            masked: self.masked,
 599            fold_placeholder: self.fold_placeholder.clone(),
 600        }
 601    }
 602
 603    fn snapshot_simple(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {
 604        let (wrap_snapshot, wrap_edits) = self.sync_through_wrap(cx);
 605
 606        let block_snapshot = self
 607            .block_map
 608            .read(wrap_snapshot, wrap_edits, None)
 609            .snapshot;
 610
 611        DisplaySnapshot {
 612            display_map_id: self.entity_id,
 613            companion_display_snapshot: None,
 614            block_snapshot,
 615            diagnostics_max_severity: self.diagnostics_max_severity,
 616            crease_snapshot: self.crease_map.snapshot(),
 617            text_highlights: self.text_highlights.clone(),
 618            inlay_highlights: self.inlay_highlights.clone(),
 619            semantic_token_highlights: self.semantic_token_highlights.clone(),
 620            clip_at_line_ends: self.clip_at_line_ends,
 621            masked: self.masked,
 622            fold_placeholder: self.fold_placeholder.clone(),
 623        }
 624    }
 625
 626    #[instrument(skip_all)]
 627    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {
 628        self.fold(
 629            other
 630                .folds_in_range(MultiBufferOffset(0)..other.buffer_snapshot().len())
 631                .map(|fold| {
 632                    Crease::simple(
 633                        fold.range.to_offset(other.buffer_snapshot()),
 634                        fold.placeholder.clone(),
 635                    )
 636                })
 637                .collect(),
 638            cx,
 639        );
 640    }
 641
 642    /// Creates folds for the given creases.
 643    #[instrument(skip_all)]
 644    pub fn fold<T: Clone + ToOffset>(&mut self, creases: Vec<Crease<T>>, cx: &mut Context<Self>) {
 645        if self.companion().is_some() {
 646            return;
 647        }
 648
 649        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
 650        let edits = self.buffer_subscription.consume().into_inner();
 651        let tab_size = Self::tab_size(&self.buffer, cx);
 652
 653        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 654            companion_dm
 655                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 656                .ok()
 657        });
 658
 659        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);
 660        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 661        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 662        let (snapshot, edits) = self
 663            .wrap_map
 664            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 665
 666        {
 667            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 668            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 669                |((snapshot, edits), companion)| {
 670                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 671                },
 672            );
 673            self.block_map.read(snapshot, edits, companion_view);
 674        }
 675
 676        let inline = creases.iter().filter_map(|crease| {
 677            if let Crease::Inline {
 678                range, placeholder, ..
 679            } = crease
 680            {
 681                Some((range.clone(), placeholder.clone()))
 682            } else {
 683                None
 684            }
 685        });
 686        let (snapshot, edits) = fold_map.fold(inline);
 687
 688        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 689        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
 690            .wrap_map
 691            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 692
 693        let (self_wrap_snapshot, self_wrap_edits) =
 694            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
 695
 696        let mut block_map = {
 697            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 698            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 699                |((snapshot, edits), companion)| {
 700                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 701                },
 702            );
 703            self.block_map
 704                .write(self_new_wrap_snapshot, self_new_wrap_edits, companion_view)
 705        };
 706        let blocks = creases.into_iter().filter_map(|crease| {
 707            if let Crease::Block {
 708                range,
 709                block_height,
 710                render_block,
 711                block_style,
 712                block_priority,
 713                ..
 714            } = crease
 715            {
 716                Some((
 717                    range,
 718                    render_block,
 719                    block_height,
 720                    block_style,
 721                    block_priority,
 722                ))
 723            } else {
 724                None
 725            }
 726        });
 727        block_map.insert(
 728            blocks
 729                .into_iter()
 730                .map(|(range, render, height, style, priority)| {
 731                    let start = buffer_snapshot.anchor_before(range.start);
 732                    let end = buffer_snapshot.anchor_after(range.end);
 733                    BlockProperties {
 734                        placement: BlockPlacement::Replace(start..=end),
 735                        render,
 736                        height: Some(height),
 737                        style,
 738                        priority,
 739                    }
 740                }),
 741        );
 742
 743        if let Some((companion_dm, _)) = &self.companion {
 744            let _ = companion_dm.update(cx, |dm, cx| {
 745                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 746                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
 747                    dm.block_map.read(
 748                        companion_snapshot,
 749                        companion_edits,
 750                        their_companion_ref.map(|c| {
 751                            CompanionView::new(
 752                                dm.entity_id,
 753                                &self_wrap_snapshot,
 754                                &self_wrap_edits,
 755                                c,
 756                            )
 757                        }),
 758                    );
 759                }
 760            });
 761        }
 762    }
 763
 764    /// Removes any folds with the given ranges.
 765    #[instrument(skip_all)]
 766    pub fn remove_folds_with_type<T: ToOffset>(
 767        &mut self,
 768        ranges: impl IntoIterator<Item = Range<T>>,
 769        type_id: TypeId,
 770        cx: &mut Context<Self>,
 771    ) {
 772        let snapshot = self.buffer.read(cx).snapshot(cx);
 773        let edits = self.buffer_subscription.consume().into_inner();
 774        let tab_size = Self::tab_size(&self.buffer, cx);
 775
 776        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 777            companion_dm
 778                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 779                .ok()
 780        });
 781
 782        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 783        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 784        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 785        let (snapshot, edits) = self
 786            .wrap_map
 787            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 788
 789        {
 790            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 791            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 792                |((snapshot, edits), companion)| {
 793                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 794                },
 795            );
 796            self.block_map.read(snapshot, edits, companion_view);
 797        }
 798
 799        let (snapshot, edits) = fold_map.remove_folds(ranges, type_id);
 800        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 801        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
 802            .wrap_map
 803            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 804
 805        let (self_wrap_snapshot, self_wrap_edits) =
 806            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
 807
 808        {
 809            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 810            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 811                |((snapshot, edits), companion)| {
 812                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 813                },
 814            );
 815            self.block_map
 816                .write(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
 817        }
 818
 819        if let Some((companion_dm, _)) = &self.companion {
 820            let _ = companion_dm.update(cx, |dm, cx| {
 821                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 822                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
 823                    dm.block_map.read(
 824                        companion_snapshot,
 825                        companion_edits,
 826                        their_companion_ref.map(|c| {
 827                            CompanionView::new(
 828                                dm.entity_id,
 829                                &self_wrap_snapshot,
 830                                &self_wrap_edits,
 831                                c,
 832                            )
 833                        }),
 834                    );
 835                }
 836            });
 837        }
 838    }
 839
 840    /// Removes any folds whose ranges intersect any of the given ranges.
 841    #[instrument(skip_all)]
 842    pub fn unfold_intersecting<T: ToOffset>(
 843        &mut self,
 844        ranges: impl IntoIterator<Item = Range<T>>,
 845        inclusive: bool,
 846        cx: &mut Context<Self>,
 847    ) -> WrapSnapshot {
 848        let snapshot = self.buffer.read(cx).snapshot(cx);
 849        let offset_ranges = ranges
 850            .into_iter()
 851            .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot))
 852            .collect::<Vec<_>>();
 853        let edits = self.buffer_subscription.consume().into_inner();
 854        let tab_size = Self::tab_size(&self.buffer, cx);
 855
 856        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 857            companion_dm
 858                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 859                .ok()
 860        });
 861
 862        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
 863        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
 864        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 865        let (snapshot, edits) = self
 866            .wrap_map
 867            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 868
 869        {
 870            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 871            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 872                |((snapshot, edits), companion)| {
 873                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 874                },
 875            );
 876            self.block_map.read(snapshot, edits, companion_view);
 877        }
 878
 879        let (snapshot, edits) =
 880            fold_map.unfold_intersecting(offset_ranges.iter().cloned(), inclusive);
 881        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
 882        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
 883            .wrap_map
 884            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
 885
 886        let (self_wrap_snapshot, self_wrap_edits) =
 887            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
 888
 889        let mut block_map = {
 890            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 891            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 892                |((snapshot, edits), companion)| {
 893                    CompanionView::new(self.entity_id, snapshot, edits, companion)
 894                },
 895            );
 896            self.block_map.write(
 897                self_new_wrap_snapshot.clone(),
 898                self_new_wrap_edits,
 899                companion_view,
 900            )
 901        };
 902        block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);
 903
 904        if let Some((companion_dm, _)) = &self.companion {
 905            let _ = companion_dm.update(cx, |dm, cx| {
 906                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 907                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
 908                    dm.block_map.read(
 909                        companion_snapshot,
 910                        companion_edits,
 911                        their_companion_ref.map(|c| {
 912                            CompanionView::new(
 913                                dm.entity_id,
 914                                &self_wrap_snapshot,
 915                                &self_wrap_edits,
 916                                c,
 917                            )
 918                        }),
 919                    );
 920                }
 921            });
 922        }
 923
 924        self_new_wrap_snapshot
 925    }
 926
 927    #[instrument(skip_all)]
 928    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
 929        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
 930
 931        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 932            companion_dm
 933                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
 934                .ok()
 935        });
 936
 937        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
 938        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
 939            |((snapshot, edits), companion)| {
 940                CompanionView::new(self.entity_id, snapshot, edits, companion)
 941            },
 942        );
 943
 944        let mut block_map = self.block_map.write(
 945            self_wrap_snapshot.clone(),
 946            self_wrap_edits.clone(),
 947            companion_view,
 948        );
 949        block_map.disable_header_for_buffer(buffer_id);
 950
 951        if let Some((companion_dm, _)) = &self.companion {
 952            let _ = companion_dm.update(cx, |dm, cx| {
 953                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
 954                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
 955                    dm.block_map.read(
 956                        companion_snapshot,
 957                        companion_edits,
 958                        their_companion_ref.map(|c| {
 959                            CompanionView::new(
 960                                dm.entity_id,
 961                                &self_wrap_snapshot,
 962                                &self_wrap_edits,
 963                                c,
 964                            )
 965                        }),
 966                    );
 967                }
 968            });
 969        }
 970    }
 971
 972    #[instrument(skip_all)]
 973    pub fn fold_buffers(
 974        &mut self,
 975        buffer_ids: impl IntoIterator<Item = language::BufferId>,
 976        cx: &mut App,
 977    ) {
 978        let buffer_ids: Vec<_> = buffer_ids.into_iter().collect();
 979
 980        if let Some((_, companion_entity)) = &self.companion {
 981            companion_entity.update(cx, |companion, _| {
 982                if self.entity_id == companion.rhs_display_map_id {
 983                    companion
 984                        .rhs_folded_buffers
 985                        .extend(buffer_ids.iter().copied());
 986                } else {
 987                    let rhs_ids = buffer_ids
 988                        .iter()
 989                        .filter_map(|id| companion.lhs_buffer_to_rhs_buffer.get(id).copied());
 990                    companion.rhs_folded_buffers.extend(rhs_ids);
 991                }
 992            });
 993        }
 994
 995        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
 996
 997        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
 998            companion_dm
 999                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1000                .ok()
1001        });
1002
1003        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1004        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1005            |((snapshot, edits), companion)| {
1006                CompanionView::new(self.entity_id, snapshot, edits, companion)
1007            },
1008        );
1009
1010        let mut block_map = self.block_map.write(
1011            self_wrap_snapshot.clone(),
1012            self_wrap_edits.clone(),
1013            companion_view,
1014        );
1015        block_map.fold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
1016
1017        if let Some((companion_dm, companion_entity)) = &self.companion {
1018            let buffer_mapping = companion_entity
1019                .read(cx)
1020                .buffer_to_companion_buffer(self.entity_id);
1021            let their_buffer_ids: Vec<_> = buffer_ids
1022                .iter()
1023                .filter_map(|id| buffer_mapping.get(id).copied())
1024                .collect();
1025
1026            let _ = companion_dm.update(cx, |dm, cx| {
1027                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1028                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1029                    let mut block_map = dm.block_map.write(
1030                        companion_snapshot,
1031                        companion_edits,
1032                        their_companion_ref.map(|c| {
1033                            CompanionView::new(
1034                                dm.entity_id,
1035                                &self_wrap_snapshot,
1036                                &self_wrap_edits,
1037                                c,
1038                            )
1039                        }),
1040                    );
1041                    if !their_buffer_ids.is_empty() {
1042                        block_map.fold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
1043                    }
1044                }
1045            });
1046        }
1047    }
1048
1049    #[instrument(skip_all)]
1050    pub fn unfold_buffers(
1051        &mut self,
1052        buffer_ids: impl IntoIterator<Item = language::BufferId>,
1053        cx: &mut Context<Self>,
1054    ) {
1055        let buffer_ids: Vec<_> = buffer_ids.into_iter().collect();
1056
1057        if let Some((_, companion_entity)) = &self.companion {
1058            companion_entity.update(cx, |companion, _| {
1059                if self.entity_id == companion.rhs_display_map_id {
1060                    for id in &buffer_ids {
1061                        companion.rhs_folded_buffers.remove(id);
1062                    }
1063                } else {
1064                    for id in &buffer_ids {
1065                        if let Some(rhs_id) = companion.lhs_buffer_to_rhs_buffer.get(id) {
1066                            companion.rhs_folded_buffers.remove(rhs_id);
1067                        }
1068                    }
1069                }
1070            });
1071        }
1072
1073        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1074
1075        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1076            companion_dm
1077                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1078                .ok()
1079        });
1080
1081        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1082        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1083            |((snapshot, edits), companion)| {
1084                CompanionView::new(self.entity_id, snapshot, edits, companion)
1085            },
1086        );
1087
1088        let mut block_map = self.block_map.write(
1089            self_wrap_snapshot.clone(),
1090            self_wrap_edits.clone(),
1091            companion_view,
1092        );
1093        block_map.unfold_buffers(buffer_ids.iter().copied(), self.buffer.read(cx), cx);
1094
1095        if let Some((companion_dm, companion_entity)) = &self.companion {
1096            let buffer_mapping = companion_entity
1097                .read(cx)
1098                .buffer_to_companion_buffer(self.entity_id);
1099            let their_buffer_ids: Vec<_> = buffer_ids
1100                .iter()
1101                .filter_map(|id| buffer_mapping.get(id).copied())
1102                .collect();
1103
1104            let _ = companion_dm.update(cx, |dm, cx| {
1105                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1106                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1107                    let mut block_map = dm.block_map.write(
1108                        companion_snapshot,
1109                        companion_edits,
1110                        their_companion_ref.map(|c| {
1111                            CompanionView::new(
1112                                dm.entity_id,
1113                                &self_wrap_snapshot,
1114                                &self_wrap_edits,
1115                                c,
1116                            )
1117                        }),
1118                    );
1119                    if !their_buffer_ids.is_empty() {
1120                        block_map.unfold_buffers(their_buffer_ids, dm.buffer.read(cx), cx);
1121                    }
1122                }
1123            });
1124        }
1125    }
1126
1127    #[instrument(skip_all)]
1128    pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {
1129        self.block_map.folded_buffers.contains(&buffer_id)
1130    }
1131
1132    #[instrument(skip_all)]
1133    pub(crate) fn folded_buffers(&self) -> &HashSet<BufferId> {
1134        &self.block_map.folded_buffers
1135    }
1136
1137    #[instrument(skip_all)]
1138    pub fn insert_creases(
1139        &mut self,
1140        creases: impl IntoIterator<Item = Crease<Anchor>>,
1141        cx: &mut Context<Self>,
1142    ) -> Vec<CreaseId> {
1143        let snapshot = self.buffer.read(cx).snapshot(cx);
1144        self.crease_map.insert(creases, &snapshot)
1145    }
1146
1147    #[instrument(skip_all)]
1148    pub fn remove_creases(
1149        &mut self,
1150        crease_ids: impl IntoIterator<Item = CreaseId>,
1151        cx: &mut Context<Self>,
1152    ) -> Vec<(CreaseId, Range<Anchor>)> {
1153        let snapshot = self.buffer.read(cx).snapshot(cx);
1154        self.crease_map.remove(crease_ids, &snapshot)
1155    }
1156
1157    #[instrument(skip_all)]
1158    pub fn insert_blocks(
1159        &mut self,
1160        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
1161        cx: &mut Context<Self>,
1162    ) -> Vec<CustomBlockId> {
1163        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1164
1165        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1166            companion_dm
1167                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1168                .ok()
1169        });
1170
1171        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1172        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1173            |((snapshot, edits), companion)| {
1174                CompanionView::new(self.entity_id, snapshot, edits, companion)
1175            },
1176        );
1177
1178        let mut block_map = self.block_map.write(
1179            self_wrap_snapshot.clone(),
1180            self_wrap_edits.clone(),
1181            companion_view,
1182        );
1183        let result = block_map.insert(blocks);
1184
1185        if let Some((companion_dm, _)) = &self.companion {
1186            let _ = companion_dm.update(cx, |dm, cx| {
1187                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1188                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1189                    dm.block_map.read(
1190                        companion_snapshot,
1191                        companion_edits,
1192                        their_companion_ref.map(|c| {
1193                            CompanionView::new(
1194                                dm.entity_id,
1195                                &self_wrap_snapshot,
1196                                &self_wrap_edits,
1197                                c,
1198                            )
1199                        }),
1200                    );
1201                }
1202            });
1203        }
1204
1205        result
1206    }
1207
1208    #[instrument(skip_all)]
1209    pub fn resize_blocks(&mut self, heights: HashMap<CustomBlockId, u32>, cx: &mut Context<Self>) {
1210        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1211
1212        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1213            companion_dm
1214                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1215                .ok()
1216        });
1217
1218        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1219        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1220            |((snapshot, edits), companion)| {
1221                CompanionView::new(self.entity_id, snapshot, edits, companion)
1222            },
1223        );
1224
1225        let mut block_map = self.block_map.write(
1226            self_wrap_snapshot.clone(),
1227            self_wrap_edits.clone(),
1228            companion_view,
1229        );
1230        block_map.resize(heights);
1231
1232        if let Some((companion_dm, _)) = &self.companion {
1233            let _ = companion_dm.update(cx, |dm, cx| {
1234                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1235                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1236                    dm.block_map.read(
1237                        companion_snapshot,
1238                        companion_edits,
1239                        their_companion_ref.map(|c| {
1240                            CompanionView::new(
1241                                dm.entity_id,
1242                                &self_wrap_snapshot,
1243                                &self_wrap_edits,
1244                                c,
1245                            )
1246                        }),
1247                    );
1248                }
1249            });
1250        }
1251    }
1252
1253    #[instrument(skip_all)]
1254    pub fn replace_blocks(&mut self, renderers: HashMap<CustomBlockId, RenderBlock>) {
1255        self.block_map.replace_blocks(renderers);
1256    }
1257
1258    #[instrument(skip_all)]
1259    pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut Context<Self>) {
1260        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1261
1262        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1263            companion_dm
1264                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1265                .ok()
1266        });
1267
1268        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1269        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1270            |((snapshot, edits), companion)| {
1271                CompanionView::new(self.entity_id, snapshot, edits, companion)
1272            },
1273        );
1274
1275        let mut block_map = self.block_map.write(
1276            self_wrap_snapshot.clone(),
1277            self_wrap_edits.clone(),
1278            companion_view,
1279        );
1280        block_map.remove(ids);
1281
1282        if let Some((companion_dm, _)) = &self.companion {
1283            let _ = companion_dm.update(cx, |dm, cx| {
1284                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1285                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1286                    dm.block_map.read(
1287                        companion_snapshot,
1288                        companion_edits,
1289                        their_companion_ref.map(|c| {
1290                            CompanionView::new(
1291                                dm.entity_id,
1292                                &self_wrap_snapshot,
1293                                &self_wrap_edits,
1294                                c,
1295                            )
1296                        }),
1297                    );
1298                }
1299            });
1300        }
1301    }
1302
1303    #[instrument(skip_all)]
1304    pub fn row_for_block(
1305        &mut self,
1306        block_id: CustomBlockId,
1307        cx: &mut Context<Self>,
1308    ) -> Option<DisplayRow> {
1309        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1310
1311        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1312            companion_dm
1313                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1314                .ok()
1315        });
1316
1317        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1318        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1319            |((snapshot, edits), companion)| {
1320                CompanionView::new(self.entity_id, snapshot, edits, companion)
1321            },
1322        );
1323
1324        let block_map = self.block_map.read(
1325            self_wrap_snapshot.clone(),
1326            self_wrap_edits.clone(),
1327            companion_view,
1328        );
1329        let block_row = block_map.row_for_block(block_id)?;
1330
1331        if let Some((companion_dm, _)) = &self.companion {
1332            let _ = companion_dm.update(cx, |dm, cx| {
1333                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1334                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1335                    dm.block_map.read(
1336                        companion_snapshot,
1337                        companion_edits,
1338                        their_companion_ref.map(|c| {
1339                            CompanionView::new(
1340                                dm.entity_id,
1341                                &self_wrap_snapshot,
1342                                &self_wrap_edits,
1343                                c,
1344                            )
1345                        }),
1346                    );
1347                }
1348            });
1349        }
1350
1351        Some(DisplayRow(block_row.0))
1352    }
1353
1354    #[instrument(skip_all)]
1355    pub fn highlight_text(
1356        &mut self,
1357        key: HighlightKey,
1358        ranges: Vec<Range<Anchor>>,
1359        style: HighlightStyle,
1360        merge: bool,
1361        cx: &App,
1362    ) {
1363        let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
1364        let to_insert = match self.text_highlights.remove(&key).filter(|_| merge) {
1365            Some(previous) => {
1366                let mut merged_ranges = previous.1.clone();
1367                for new_range in ranges {
1368                    let i = merged_ranges
1369                        .binary_search_by(|probe| {
1370                            probe.start.cmp(&new_range.start, &multi_buffer_snapshot)
1371                        })
1372                        .unwrap_or_else(|i| i);
1373                    merged_ranges.insert(i, new_range);
1374                }
1375                Arc::new((style, merged_ranges))
1376            }
1377            None => Arc::new((style, ranges)),
1378        };
1379        self.text_highlights.insert(key, to_insert);
1380    }
1381
1382    #[instrument(skip_all)]
1383    pub(crate) fn highlight_inlays(
1384        &mut self,
1385        key: HighlightKey,
1386        highlights: Vec<InlayHighlight>,
1387        style: HighlightStyle,
1388    ) {
1389        for highlight in highlights {
1390            let update = self.inlay_highlights.update(&key, |highlights| {
1391                highlights.insert(highlight.inlay, (style, highlight.clone()))
1392            });
1393            if update.is_none() {
1394                self.inlay_highlights.insert(
1395                    key,
1396                    TreeMap::from_ordered_entries([(highlight.inlay, (style, highlight))]),
1397                );
1398            }
1399        }
1400    }
1401
1402    #[instrument(skip_all)]
1403    pub fn text_highlights(&self, key: HighlightKey) -> Option<(HighlightStyle, &[Range<Anchor>])> {
1404        let highlights = self.text_highlights.get(&key)?;
1405        Some((highlights.0, &highlights.1))
1406    }
1407
1408    pub fn all_text_highlights(
1409        &self,
1410    ) -> impl Iterator<Item = (&HighlightKey, &Arc<(HighlightStyle, Vec<Range<Anchor>>)>)> {
1411        self.text_highlights.iter()
1412    }
1413
1414    pub fn all_semantic_token_highlights(
1415        &self,
1416    ) -> impl Iterator<
1417        Item = (
1418            &BufferId,
1419            &(Arc<[SemanticTokenHighlight]>, Arc<HighlightStyleInterner>),
1420        ),
1421    > {
1422        self.semantic_token_highlights.iter()
1423    }
1424
1425    pub fn clear_highlights(&mut self, key: HighlightKey) -> bool {
1426        let mut cleared = self.text_highlights.remove(&key).is_some();
1427        cleared |= self.inlay_highlights.remove(&key).is_some();
1428        cleared
1429    }
1430
1431    pub fn clear_highlights_with(&mut self, mut f: impl FnMut(&HighlightKey) -> bool) -> bool {
1432        let mut cleared = false;
1433        self.text_highlights.retain(|k, _| {
1434            let b = !f(k);
1435            cleared |= b;
1436            b
1437        });
1438        self.inlay_highlights.retain(|k, _| {
1439            let b = !f(k);
1440            cleared |= b;
1441            b
1442        });
1443        cleared
1444    }
1445
1446    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> bool {
1447        self.wrap_map
1448            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
1449    }
1450
1451    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut Context<Self>) -> bool {
1452        self.wrap_map
1453            .update(cx, |map, cx| map.set_wrap_width(width, cx))
1454    }
1455
1456    #[instrument(skip_all)]
1457    pub fn update_fold_widths(
1458        &mut self,
1459        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
1460        cx: &mut Context<Self>,
1461    ) -> bool {
1462        let snapshot = self.buffer.read(cx).snapshot(cx);
1463        let edits = self.buffer_subscription.consume().into_inner();
1464        let tab_size = Self::tab_size(&self.buffer, cx);
1465
1466        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1467            companion_dm
1468                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1469                .ok()
1470        });
1471
1472        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
1473        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
1474        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1475        let (snapshot, edits) = self
1476            .wrap_map
1477            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1478
1479        {
1480            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1481            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1482                |((snapshot, edits), companion)| {
1483                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1484                },
1485            );
1486            self.block_map.read(snapshot, edits, companion_view);
1487        }
1488
1489        let (snapshot, edits) = fold_map.update_fold_widths(widths);
1490        let widths_changed = !edits.is_empty();
1491        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1492        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
1493            .wrap_map
1494            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1495
1496        let (self_wrap_snapshot, self_wrap_edits) =
1497            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
1498
1499        {
1500            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1501            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1502                |((snapshot, edits), companion)| {
1503                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1504                },
1505            );
1506            self.block_map
1507                .read(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
1508        }
1509
1510        if let Some((companion_dm, _)) = &self.companion {
1511            let _ = companion_dm.update(cx, |dm, cx| {
1512                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1513                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1514                    dm.block_map.read(
1515                        companion_snapshot,
1516                        companion_edits,
1517                        their_companion_ref.map(|c| {
1518                            CompanionView::new(
1519                                dm.entity_id,
1520                                &self_wrap_snapshot,
1521                                &self_wrap_edits,
1522                                c,
1523                            )
1524                        }),
1525                    );
1526                }
1527            });
1528        }
1529
1530        widths_changed
1531    }
1532
1533    pub(crate) fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
1534        self.inlay_map.current_inlays()
1535    }
1536
1537    #[instrument(skip_all)]
1538    pub(crate) fn splice_inlays(
1539        &mut self,
1540        to_remove: &[InlayId],
1541        to_insert: Vec<Inlay>,
1542        cx: &mut Context<Self>,
1543    ) {
1544        if to_remove.is_empty() && to_insert.is_empty() {
1545            return;
1546        }
1547        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
1548        let edits = self.buffer_subscription.consume().into_inner();
1549        let tab_size = Self::tab_size(&self.buffer, cx);
1550
1551        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1552            companion_dm
1553                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1554                .ok()
1555        });
1556
1557        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
1558        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
1559        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1560        let (snapshot, edits) = self
1561            .wrap_map
1562            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1563
1564        {
1565            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1566            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1567                |((snapshot, edits), companion)| {
1568                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1569                },
1570            );
1571            self.block_map.read(snapshot, edits, companion_view);
1572        }
1573
1574        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
1575        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
1576        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1577        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
1578            .wrap_map
1579            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1580
1581        let (self_wrap_snapshot, self_wrap_edits) =
1582            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
1583
1584        {
1585            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1586            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1587                |((snapshot, edits), companion)| {
1588                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1589                },
1590            );
1591            self.block_map
1592                .read(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
1593        }
1594
1595        if let Some((companion_dm, _)) = &self.companion {
1596            let _ = companion_dm.update(cx, |dm, cx| {
1597                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1598                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1599                    dm.block_map.read(
1600                        companion_snapshot,
1601                        companion_edits,
1602                        their_companion_ref.map(|c| {
1603                            CompanionView::new(
1604                                dm.entity_id,
1605                                &self_wrap_snapshot,
1606                                &self_wrap_edits,
1607                                c,
1608                            )
1609                        }),
1610                    );
1611                }
1612            });
1613        }
1614    }
1615
1616    #[instrument(skip_all)]
1617    fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
1618        let buffer = buffer.read(cx).as_singleton().map(|buffer| buffer.read(cx));
1619        let language = buffer
1620            .and_then(|buffer| buffer.language())
1621            .map(|l| l.name());
1622        let file = buffer.and_then(|buffer| buffer.file());
1623        language_settings(language, file, cx).tab_size
1624    }
1625
1626    #[cfg(test)]
1627    pub fn is_rewrapping(&self, cx: &gpui::App) -> bool {
1628        self.wrap_map.read(cx).is_rewrapping()
1629    }
1630
1631    pub fn invalidate_semantic_highlights(&mut self, buffer_id: BufferId) {
1632        self.semantic_token_highlights.remove(&buffer_id);
1633    }
1634}
1635
1636#[derive(Debug, Default)]
1637pub(crate) struct Highlights<'a> {
1638    pub text_highlights: Option<&'a TextHighlights>,
1639    pub inlay_highlights: Option<&'a InlayHighlights>,
1640    pub semantic_token_highlights: Option<&'a SemanticTokensHighlights>,
1641    pub styles: HighlightStyles,
1642}
1643
1644#[derive(Clone, Copy, Debug)]
1645pub struct EditPredictionStyles {
1646    pub insertion: HighlightStyle,
1647    pub whitespace: HighlightStyle,
1648}
1649
1650#[derive(Default, Debug, Clone, Copy)]
1651pub struct HighlightStyles {
1652    pub inlay_hint: Option<HighlightStyle>,
1653    pub edit_prediction: Option<EditPredictionStyles>,
1654}
1655
1656#[derive(Clone)]
1657pub enum ChunkReplacement {
1658    Renderer(ChunkRenderer),
1659    Str(SharedString),
1660}
1661
1662pub struct HighlightedChunk<'a> {
1663    pub text: &'a str,
1664    pub style: Option<HighlightStyle>,
1665    pub is_tab: bool,
1666    pub is_inlay: bool,
1667    pub replacement: Option<ChunkReplacement>,
1668}
1669
1670impl<'a> HighlightedChunk<'a> {
1671    #[instrument(skip_all)]
1672    fn highlight_invisibles(
1673        self,
1674        editor_style: &'a EditorStyle,
1675    ) -> impl Iterator<Item = Self> + 'a {
1676        let mut chars = self.text.chars().peekable();
1677        let mut text = self.text;
1678        let style = self.style;
1679        let is_tab = self.is_tab;
1680        let renderer = self.replacement;
1681        let is_inlay = self.is_inlay;
1682        iter::from_fn(move || {
1683            let mut prefix_len = 0;
1684            while let Some(&ch) = chars.peek() {
1685                if !is_invisible(ch) {
1686                    prefix_len += ch.len_utf8();
1687                    chars.next();
1688                    continue;
1689                }
1690                if prefix_len > 0 {
1691                    let (prefix, suffix) = text.split_at(prefix_len);
1692                    text = suffix;
1693                    return Some(HighlightedChunk {
1694                        text: prefix,
1695                        style,
1696                        is_tab,
1697                        is_inlay,
1698                        replacement: renderer.clone(),
1699                    });
1700                }
1701                chars.next();
1702                let (prefix, suffix) = text.split_at(ch.len_utf8());
1703                text = suffix;
1704                if let Some(replacement) = replacement(ch) {
1705                    let invisible_highlight = HighlightStyle {
1706                        background_color: Some(editor_style.status.hint_background),
1707                        underline: Some(UnderlineStyle {
1708                            color: Some(editor_style.status.hint),
1709                            thickness: px(1.),
1710                            wavy: false,
1711                        }),
1712                        ..Default::default()
1713                    };
1714                    let invisible_style = if let Some(style) = style {
1715                        style.highlight(invisible_highlight)
1716                    } else {
1717                        invisible_highlight
1718                    };
1719                    return Some(HighlightedChunk {
1720                        text: prefix,
1721                        style: Some(invisible_style),
1722                        is_tab: false,
1723                        is_inlay,
1724                        replacement: Some(ChunkReplacement::Str(replacement.into())),
1725                    });
1726                } else {
1727                    let invisible_highlight = HighlightStyle {
1728                        background_color: Some(editor_style.status.hint_background),
1729                        underline: Some(UnderlineStyle {
1730                            color: Some(editor_style.status.hint),
1731                            thickness: px(1.),
1732                            wavy: false,
1733                        }),
1734                        ..Default::default()
1735                    };
1736                    let invisible_style = if let Some(style) = style {
1737                        style.highlight(invisible_highlight)
1738                    } else {
1739                        invisible_highlight
1740                    };
1741
1742                    return Some(HighlightedChunk {
1743                        text: prefix,
1744                        style: Some(invisible_style),
1745                        is_tab: false,
1746                        is_inlay,
1747                        replacement: renderer.clone(),
1748                    });
1749                }
1750            }
1751
1752            if !text.is_empty() {
1753                let remainder = text;
1754                text = "";
1755                Some(HighlightedChunk {
1756                    text: remainder,
1757                    style,
1758                    is_tab,
1759                    is_inlay,
1760                    replacement: renderer.clone(),
1761                })
1762            } else {
1763                None
1764            }
1765        })
1766    }
1767}
1768
1769#[derive(Clone)]
1770pub struct DisplaySnapshot {
1771    pub display_map_id: EntityId,
1772    pub companion_display_snapshot: Option<Arc<DisplaySnapshot>>,
1773    pub crease_snapshot: CreaseSnapshot,
1774    block_snapshot: BlockSnapshot,
1775    text_highlights: TextHighlights,
1776    inlay_highlights: InlayHighlights,
1777    semantic_token_highlights: SemanticTokensHighlights,
1778    clip_at_line_ends: bool,
1779    masked: bool,
1780    diagnostics_max_severity: DiagnosticSeverity,
1781    pub(crate) fold_placeholder: FoldPlaceholder,
1782}
1783
1784impl DisplaySnapshot {
1785    pub fn companion_snapshot(&self) -> Option<&DisplaySnapshot> {
1786        self.companion_display_snapshot.as_deref()
1787    }
1788
1789    pub fn wrap_snapshot(&self) -> &WrapSnapshot {
1790        &self.block_snapshot.wrap_snapshot
1791    }
1792    pub fn tab_snapshot(&self) -> &TabSnapshot {
1793        &self.block_snapshot.wrap_snapshot.tab_snapshot
1794    }
1795
1796    pub fn fold_snapshot(&self) -> &FoldSnapshot {
1797        &self.block_snapshot.wrap_snapshot.tab_snapshot.fold_snapshot
1798    }
1799
1800    pub fn inlay_snapshot(&self) -> &InlaySnapshot {
1801        &self
1802            .block_snapshot
1803            .wrap_snapshot
1804            .tab_snapshot
1805            .fold_snapshot
1806            .inlay_snapshot
1807    }
1808
1809    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
1810        &self
1811            .block_snapshot
1812            .wrap_snapshot
1813            .tab_snapshot
1814            .fold_snapshot
1815            .inlay_snapshot
1816            .buffer
1817    }
1818
1819    #[cfg(test)]
1820    pub fn fold_count(&self) -> usize {
1821        self.fold_snapshot().fold_count()
1822    }
1823
1824    pub fn is_empty(&self) -> bool {
1825        self.buffer_snapshot().len() == MultiBufferOffset(0)
1826    }
1827
1828    /// Returns whether tree-sitter syntax highlighting should be used.
1829    /// Returns `false` if any buffer with semantic token highlights has the "full" mode setting,
1830    /// meaning LSP semantic tokens should replace tree-sitter highlighting.
1831    pub fn use_tree_sitter_for_syntax(&self, position: DisplayRow, cx: &App) -> bool {
1832        let position = DisplayPoint::new(position, 0);
1833        let Some((buffer_snapshot, ..)) = self.point_to_buffer_point(position.to_point(self))
1834        else {
1835            return false;
1836        };
1837        let settings = language_settings(
1838            buffer_snapshot.language().map(|l| l.name()),
1839            buffer_snapshot.file(),
1840            cx,
1841        );
1842        settings.semantic_tokens.use_tree_sitter()
1843    }
1844
1845    pub fn row_infos(&self, start_row: DisplayRow) -> impl Iterator<Item = RowInfo> + '_ {
1846        self.block_snapshot.row_infos(BlockRow(start_row.0))
1847    }
1848
1849    pub fn widest_line_number(&self) -> u32 {
1850        self.buffer_snapshot().widest_line_number()
1851    }
1852
1853    #[instrument(skip_all)]
1854    pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
1855        loop {
1856            let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
1857            let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Left);
1858            fold_point.0.column = 0;
1859            inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
1860            point = self.inlay_snapshot().to_buffer_point(inlay_point);
1861
1862            let mut display_point = self.point_to_display_point(point, Bias::Left);
1863            *display_point.column_mut() = 0;
1864            let next_point = self.display_point_to_point(display_point, Bias::Left);
1865            if next_point == point {
1866                return (point, display_point);
1867            }
1868            point = next_point;
1869        }
1870    }
1871
1872    #[instrument(skip_all)]
1873    pub fn next_line_boundary(
1874        &self,
1875        mut point: MultiBufferPoint,
1876    ) -> (MultiBufferPoint, DisplayPoint) {
1877        let original_point = point;
1878        loop {
1879            let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
1880            let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Right);
1881            fold_point.0.column = self.fold_snapshot().line_len(fold_point.row());
1882            inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
1883            point = self.inlay_snapshot().to_buffer_point(inlay_point);
1884
1885            let mut display_point = self.point_to_display_point(point, Bias::Right);
1886            *display_point.column_mut() = self.line_len(display_point.row());
1887            let next_point = self.display_point_to_point(display_point, Bias::Right);
1888            if next_point == point || original_point == point || original_point == next_point {
1889                return (point, display_point);
1890            }
1891            point = next_point;
1892        }
1893    }
1894
1895    // used by line_mode selections and tries to match vim behavior
1896    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
1897        let new_start = MultiBufferPoint::new(range.start.row, 0);
1898        let new_end = if range.end.column > 0 {
1899            MultiBufferPoint::new(
1900                range.end.row,
1901                self.buffer_snapshot()
1902                    .line_len(MultiBufferRow(range.end.row)),
1903            )
1904        } else {
1905            range.end
1906        };
1907
1908        new_start..new_end
1909    }
1910
1911    #[instrument(skip_all)]
1912    pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
1913        let inlay_point = self.inlay_snapshot().to_inlay_point(point);
1914        let fold_point = self.fold_snapshot().to_fold_point(inlay_point, bias);
1915        let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
1916        let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
1917        let block_point = self.block_snapshot.to_block_point(wrap_point);
1918        DisplayPoint(block_point)
1919    }
1920
1921    pub fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
1922        self.inlay_snapshot()
1923            .to_buffer_point(self.display_point_to_inlay_point(point, bias))
1924    }
1925
1926    pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
1927        self.inlay_snapshot()
1928            .to_offset(self.display_point_to_inlay_point(point, bias))
1929    }
1930
1931    pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {
1932        self.inlay_snapshot()
1933            .to_inlay_offset(anchor.to_offset(self.buffer_snapshot()))
1934    }
1935
1936    pub fn display_point_to_anchor(&self, point: DisplayPoint, bias: Bias) -> Anchor {
1937        self.buffer_snapshot()
1938            .anchor_at(point.to_offset(self, bias), bias)
1939    }
1940
1941    #[instrument(skip_all)]
1942    fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
1943        let block_point = point.0;
1944        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
1945        let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
1946        let fold_point = self
1947            .tab_snapshot()
1948            .tab_point_to_fold_point(tab_point, bias)
1949            .0;
1950        fold_point.to_inlay_point(self.fold_snapshot())
1951    }
1952
1953    #[instrument(skip_all)]
1954    pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
1955        let block_point = point.0;
1956        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
1957        let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
1958        self.tab_snapshot()
1959            .tab_point_to_fold_point(tab_point, bias)
1960            .0
1961    }
1962
1963    #[instrument(skip_all)]
1964    pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
1965        let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
1966        let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
1967        let block_point = self.block_snapshot.to_block_point(wrap_point);
1968        DisplayPoint(block_point)
1969    }
1970
1971    pub fn max_point(&self) -> DisplayPoint {
1972        DisplayPoint(self.block_snapshot.max_point())
1973    }
1974
1975    /// Returns text chunks starting at the given display row until the end of the file
1976    #[instrument(skip_all)]
1977    pub fn text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
1978        self.block_snapshot
1979            .chunks(
1980                BlockRow(display_row.0)..BlockRow(self.max_point().row().next_row().0),
1981                false,
1982                self.masked,
1983                Highlights::default(),
1984            )
1985            .map(|h| h.text)
1986    }
1987
1988    /// Returns text chunks starting at the end of the given display row in reverse until the start of the file
1989    #[instrument(skip_all)]
1990    pub fn reverse_text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
1991        (0..=display_row.0).rev().flat_map(move |row| {
1992            self.block_snapshot
1993                .chunks(
1994                    BlockRow(row)..BlockRow(row + 1),
1995                    false,
1996                    self.masked,
1997                    Highlights::default(),
1998                )
1999                .map(|h| h.text)
2000                .collect::<Vec<_>>()
2001                .into_iter()
2002                .rev()
2003        })
2004    }
2005
2006    #[instrument(skip_all)]
2007    pub fn chunks(
2008        &self,
2009        display_rows: Range<DisplayRow>,
2010        language_aware: bool,
2011        highlight_styles: HighlightStyles,
2012    ) -> DisplayChunks<'_> {
2013        self.block_snapshot.chunks(
2014            BlockRow(display_rows.start.0)..BlockRow(display_rows.end.0),
2015            language_aware,
2016            self.masked,
2017            Highlights {
2018                text_highlights: Some(&self.text_highlights),
2019                inlay_highlights: Some(&self.inlay_highlights),
2020                semantic_token_highlights: Some(&self.semantic_token_highlights),
2021                styles: highlight_styles,
2022            },
2023        )
2024    }
2025
2026    #[instrument(skip_all)]
2027    pub fn highlighted_chunks<'a>(
2028        &'a self,
2029        display_rows: Range<DisplayRow>,
2030        language_aware: bool,
2031        editor_style: &'a EditorStyle,
2032    ) -> impl Iterator<Item = HighlightedChunk<'a>> {
2033        self.chunks(
2034            display_rows,
2035            language_aware,
2036            HighlightStyles {
2037                inlay_hint: Some(editor_style.inlay_hints_style),
2038                edit_prediction: Some(editor_style.edit_prediction_styles),
2039            },
2040        )
2041        .flat_map(|chunk| {
2042            let syntax_highlight_style = chunk
2043                .syntax_highlight_id
2044                .and_then(|id| id.style(&editor_style.syntax));
2045
2046            let chunk_highlight = chunk.highlight_style.map(|chunk_highlight| {
2047                HighlightStyle {
2048                    // For color inlays, blend the color with the editor background
2049                    // if the color has transparency (alpha < 1.0)
2050                    color: chunk_highlight.color.map(|color| {
2051                        if chunk.is_inlay && !color.is_opaque() {
2052                            editor_style.background.blend(color)
2053                        } else {
2054                            color
2055                        }
2056                    }),
2057                    ..chunk_highlight
2058                }
2059            });
2060
2061            let diagnostic_highlight = chunk
2062                .diagnostic_severity
2063                .filter(|severity| {
2064                    self.diagnostics_max_severity
2065                        .into_lsp()
2066                        .is_some_and(|max_severity| severity <= &max_severity)
2067                })
2068                .map(|severity| HighlightStyle {
2069                    fade_out: chunk
2070                        .is_unnecessary
2071                        .then_some(editor_style.unnecessary_code_fade),
2072                    underline: (chunk.underline
2073                        && editor_style.show_underlines
2074                        && !(chunk.is_unnecessary && severity > lsp::DiagnosticSeverity::WARNING))
2075                        .then(|| {
2076                            let diagnostic_color =
2077                                super::diagnostic_style(severity, &editor_style.status);
2078                            UnderlineStyle {
2079                                color: Some(diagnostic_color),
2080                                thickness: 1.0.into(),
2081                                wavy: true,
2082                            }
2083                        }),
2084                    ..Default::default()
2085                });
2086
2087            let style = [
2088                syntax_highlight_style,
2089                chunk_highlight,
2090                diagnostic_highlight,
2091            ]
2092            .into_iter()
2093            .flatten()
2094            .reduce(|acc, highlight| acc.highlight(highlight));
2095
2096            HighlightedChunk {
2097                text: chunk.text,
2098                style,
2099                is_tab: chunk.is_tab,
2100                is_inlay: chunk.is_inlay,
2101                replacement: chunk.renderer.map(ChunkReplacement::Renderer),
2102            }
2103            .highlight_invisibles(editor_style)
2104        })
2105    }
2106
2107    #[instrument(skip_all)]
2108    pub fn layout_row(
2109        &self,
2110        display_row: DisplayRow,
2111        TextLayoutDetails {
2112            text_system,
2113            editor_style,
2114            rem_size,
2115            scroll_anchor: _,
2116            visible_rows: _,
2117            vertical_scroll_margin: _,
2118        }: &TextLayoutDetails,
2119    ) -> Arc<LineLayout> {
2120        let mut runs = Vec::new();
2121        let mut line = String::new();
2122
2123        let range = display_row..display_row.next_row();
2124        for chunk in self.highlighted_chunks(range, false, editor_style) {
2125            line.push_str(chunk.text);
2126
2127            let text_style = if let Some(style) = chunk.style {
2128                Cow::Owned(editor_style.text.clone().highlight(style))
2129            } else {
2130                Cow::Borrowed(&editor_style.text)
2131            };
2132
2133            runs.push(text_style.to_run(chunk.text.len()))
2134        }
2135
2136        if line.ends_with('\n') {
2137            line.pop();
2138            if let Some(last_run) = runs.last_mut() {
2139                last_run.len -= 1;
2140                if last_run.len == 0 {
2141                    runs.pop();
2142                }
2143            }
2144        }
2145
2146        let font_size = editor_style.text.font_size.to_pixels(*rem_size);
2147        text_system.layout_line(&line, font_size, &runs, None)
2148    }
2149
2150    pub fn x_for_display_point(
2151        &self,
2152        display_point: DisplayPoint,
2153        text_layout_details: &TextLayoutDetails,
2154    ) -> Pixels {
2155        let line = self.layout_row(display_point.row(), text_layout_details);
2156        line.x_for_index(display_point.column() as usize)
2157    }
2158
2159    pub fn display_column_for_x(
2160        &self,
2161        display_row: DisplayRow,
2162        x: Pixels,
2163        details: &TextLayoutDetails,
2164    ) -> u32 {
2165        let layout_line = self.layout_row(display_row, details);
2166        layout_line.closest_index_for_x(x) as u32
2167    }
2168
2169    #[instrument(skip_all)]
2170    pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<SharedString> {
2171        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
2172        let chars = self
2173            .text_chunks(point.row())
2174            .flat_map(str::chars)
2175            .skip_while({
2176                let mut column = 0;
2177                move |char| {
2178                    let at_point = column >= point.column();
2179                    column += char.len_utf8() as u32;
2180                    !at_point
2181                }
2182            })
2183            .take_while({
2184                let mut prev = false;
2185                move |char| {
2186                    let now = char.is_ascii();
2187                    let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
2188                    prev = now;
2189                    !end
2190                }
2191            });
2192        chars.collect::<String>().graphemes(true).next().map(|s| {
2193            if let Some(invisible) = s.chars().next().filter(|&c| is_invisible(c)) {
2194                replacement(invisible).unwrap_or(s).to_owned().into()
2195            } else if s == "\n" {
2196                " ".into()
2197            } else {
2198                s.to_owned().into()
2199            }
2200        })
2201    }
2202
2203    pub fn buffer_chars_at(
2204        &self,
2205        mut offset: MultiBufferOffset,
2206    ) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
2207        self.buffer_snapshot().chars_at(offset).map(move |ch| {
2208            let ret = (ch, offset);
2209            offset += ch.len_utf8();
2210            ret
2211        })
2212    }
2213
2214    pub fn reverse_buffer_chars_at(
2215        &self,
2216        mut offset: MultiBufferOffset,
2217    ) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
2218        self.buffer_snapshot()
2219            .reversed_chars_at(offset)
2220            .map(move |ch| {
2221                offset -= ch.len_utf8();
2222                (ch, offset)
2223            })
2224    }
2225
2226    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
2227        let mut clipped = self.block_snapshot.clip_point(point.0, bias);
2228        if self.clip_at_line_ends {
2229            clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
2230        }
2231        DisplayPoint(clipped)
2232    }
2233
2234    pub fn clip_ignoring_line_ends(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
2235        DisplayPoint(self.block_snapshot.clip_point(point.0, bias))
2236    }
2237
2238    pub fn clip_at_line_end(&self, display_point: DisplayPoint) -> DisplayPoint {
2239        let mut point = self.display_point_to_point(display_point, Bias::Left);
2240
2241        if point.column != self.buffer_snapshot().line_len(MultiBufferRow(point.row)) {
2242            return display_point;
2243        }
2244        point.column = point.column.saturating_sub(1);
2245        point = self.buffer_snapshot().clip_point(point, Bias::Left);
2246        self.point_to_display_point(point, Bias::Left)
2247    }
2248
2249    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
2250    where
2251        T: ToOffset,
2252    {
2253        self.fold_snapshot().folds_in_range(range)
2254    }
2255
2256    pub fn blocks_in_range(
2257        &self,
2258        rows: Range<DisplayRow>,
2259    ) -> impl Iterator<Item = (DisplayRow, &Block)> {
2260        self.block_snapshot
2261            .blocks_in_range(BlockRow(rows.start.0)..BlockRow(rows.end.0))
2262            .map(|(row, block)| (DisplayRow(row.0), block))
2263    }
2264
2265    pub fn sticky_header_excerpt(&self, row: f64) -> Option<StickyHeaderExcerpt<'_>> {
2266        self.block_snapshot.sticky_header_excerpt(row)
2267    }
2268
2269    pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
2270        self.block_snapshot.block_for_id(id)
2271    }
2272
2273    pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
2274        self.fold_snapshot().intersects_fold(offset)
2275    }
2276
2277    pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
2278        self.block_snapshot.is_line_replaced(buffer_row)
2279            || self.fold_snapshot().is_line_folded(buffer_row)
2280    }
2281
2282    pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
2283        self.block_snapshot.is_block_line(BlockRow(display_row.0))
2284    }
2285
2286    pub fn is_folded_buffer_header(&self, display_row: DisplayRow) -> bool {
2287        self.block_snapshot
2288            .is_folded_buffer_header(BlockRow(display_row.0))
2289    }
2290
2291    pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
2292        let wrap_row = self
2293            .block_snapshot
2294            .to_wrap_point(BlockPoint::new(BlockRow(display_row.0), 0), Bias::Left)
2295            .row();
2296        self.wrap_snapshot().soft_wrap_indent(wrap_row)
2297    }
2298
2299    pub fn text(&self) -> String {
2300        self.text_chunks(DisplayRow(0)).collect()
2301    }
2302
2303    pub fn line(&self, display_row: DisplayRow) -> String {
2304        let mut result = String::new();
2305        for chunk in self.text_chunks(display_row) {
2306            if let Some(ix) = chunk.find('\n') {
2307                result.push_str(&chunk[0..ix]);
2308                break;
2309            } else {
2310                result.push_str(chunk);
2311            }
2312        }
2313        result
2314    }
2315
2316    pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> LineIndent {
2317        self.buffer_snapshot().line_indent_for_row(buffer_row)
2318    }
2319
2320    pub fn line_len(&self, row: DisplayRow) -> u32 {
2321        self.block_snapshot.line_len(BlockRow(row.0))
2322    }
2323
2324    pub fn longest_row(&self) -> DisplayRow {
2325        DisplayRow(self.block_snapshot.longest_row().0)
2326    }
2327
2328    pub fn longest_row_in_range(&self, range: Range<DisplayRow>) -> DisplayRow {
2329        let block_range = BlockRow(range.start.0)..BlockRow(range.end.0);
2330        let longest_row = self.block_snapshot.longest_row_in_range(block_range);
2331        DisplayRow(longest_row.0)
2332    }
2333
2334    pub fn starts_indent(&self, buffer_row: MultiBufferRow) -> bool {
2335        let max_row = self.buffer_snapshot().max_row();
2336        if buffer_row >= max_row {
2337            return false;
2338        }
2339
2340        let line_indent = self.line_indent_for_buffer_row(buffer_row);
2341        if line_indent.is_line_blank() {
2342            return false;
2343        }
2344
2345        (buffer_row.0 + 1..=max_row.0)
2346            .find_map(|next_row| {
2347                let next_line_indent = self.line_indent_for_buffer_row(MultiBufferRow(next_row));
2348                if next_line_indent.raw_len() > line_indent.raw_len() {
2349                    Some(true)
2350                } else if !next_line_indent.is_line_blank() {
2351                    Some(false)
2352                } else {
2353                    None
2354                }
2355            })
2356            .unwrap_or(false)
2357    }
2358
2359    #[instrument(skip_all)]
2360    pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
2361        let start =
2362            MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot().line_len(buffer_row));
2363        if let Some(crease) = self
2364            .crease_snapshot
2365            .query_row(buffer_row, self.buffer_snapshot())
2366        {
2367            match crease {
2368                Crease::Inline {
2369                    range,
2370                    placeholder,
2371                    render_toggle,
2372                    render_trailer,
2373                    metadata,
2374                } => Some(Crease::Inline {
2375                    range: range.to_point(self.buffer_snapshot()),
2376                    placeholder: placeholder.clone(),
2377                    render_toggle: render_toggle.clone(),
2378                    render_trailer: render_trailer.clone(),
2379                    metadata: metadata.clone(),
2380                }),
2381                Crease::Block {
2382                    range,
2383                    block_height,
2384                    block_style,
2385                    render_block,
2386                    block_priority,
2387                    render_toggle,
2388                } => Some(Crease::Block {
2389                    range: range.to_point(self.buffer_snapshot()),
2390                    block_height: *block_height,
2391                    block_style: *block_style,
2392                    render_block: render_block.clone(),
2393                    block_priority: *block_priority,
2394                    render_toggle: render_toggle.clone(),
2395                }),
2396            }
2397        } else if self.starts_indent(MultiBufferRow(start.row))
2398            && !self.is_line_folded(MultiBufferRow(start.row))
2399        {
2400            let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
2401            let max_point = self.buffer_snapshot().max_point();
2402            let mut end = None;
2403
2404            for row in (buffer_row.0 + 1)..=max_point.row {
2405                let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
2406                if !line_indent.is_line_blank()
2407                    && line_indent.raw_len() <= start_line_indent.raw_len()
2408                {
2409                    let prev_row = row - 1;
2410                    end = Some(Point::new(
2411                        prev_row,
2412                        self.buffer_snapshot().line_len(MultiBufferRow(prev_row)),
2413                    ));
2414                    break;
2415                }
2416            }
2417
2418            let mut row_before_line_breaks = end.unwrap_or(max_point);
2419            while row_before_line_breaks.row > start.row
2420                && self
2421                    .buffer_snapshot()
2422                    .is_line_blank(MultiBufferRow(row_before_line_breaks.row))
2423            {
2424                row_before_line_breaks.row -= 1;
2425            }
2426
2427            row_before_line_breaks = Point::new(
2428                row_before_line_breaks.row,
2429                self.buffer_snapshot()
2430                    .line_len(MultiBufferRow(row_before_line_breaks.row)),
2431            );
2432
2433            Some(Crease::Inline {
2434                range: start..row_before_line_breaks,
2435                placeholder: self.fold_placeholder.clone(),
2436                render_toggle: None,
2437                render_trailer: None,
2438                metadata: None,
2439            })
2440        } else {
2441            None
2442        }
2443    }
2444
2445    #[cfg(any(test, feature = "test-support"))]
2446    #[instrument(skip_all)]
2447    pub fn text_highlight_ranges(
2448        &self,
2449        key: HighlightKey,
2450    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
2451        self.text_highlights.get(&key).cloned()
2452    }
2453
2454    #[cfg(any(test, feature = "test-support"))]
2455    #[instrument(skip_all)]
2456    pub fn all_text_highlight_ranges(
2457        &self,
2458        f: impl Fn(&HighlightKey) -> bool,
2459    ) -> Vec<(gpui::Hsla, Range<Point>)> {
2460        use itertools::Itertools;
2461
2462        self.text_highlights
2463            .iter()
2464            .filter(|(key, _)| f(key))
2465            .map(|(_, value)| value.clone())
2466            .flat_map(|ranges| {
2467                ranges
2468                    .1
2469                    .iter()
2470                    .flat_map(|range| {
2471                        Some((ranges.0.color?, range.to_point(self.buffer_snapshot())))
2472                    })
2473                    .collect::<Vec<_>>()
2474            })
2475            .sorted_by_key(|(_, range)| range.start)
2476            .collect()
2477    }
2478
2479    #[allow(unused)]
2480    #[cfg(any(test, feature = "test-support"))]
2481    pub(crate) fn inlay_highlights(
2482        &self,
2483        key: HighlightKey,
2484    ) -> Option<&TreeMap<InlayId, (HighlightStyle, InlayHighlight)>> {
2485        self.inlay_highlights.get(&key)
2486    }
2487
2488    pub fn buffer_header_height(&self) -> u32 {
2489        self.block_snapshot.buffer_header_height
2490    }
2491
2492    pub fn excerpt_header_height(&self) -> u32 {
2493        self.block_snapshot.excerpt_header_height
2494    }
2495
2496    /// Given a `DisplayPoint`, returns another `DisplayPoint` corresponding to
2497    /// the start of the buffer row that is a given number of buffer rows away
2498    /// from the provided point.
2499    ///
2500    /// This moves by buffer rows instead of display rows, a distinction that is
2501    /// important when soft wrapping is enabled.
2502    #[instrument(skip_all)]
2503    pub fn start_of_relative_buffer_row(&self, point: DisplayPoint, times: isize) -> DisplayPoint {
2504        let start = self.display_point_to_fold_point(point, Bias::Left);
2505        let target = start.row() as isize + times;
2506        let new_row = (target.max(0) as u32).min(self.fold_snapshot().max_point().row());
2507
2508        self.clip_point(
2509            self.fold_point_to_display_point(
2510                self.fold_snapshot()
2511                    .clip_point(FoldPoint::new(new_row, 0), Bias::Right),
2512            ),
2513            Bias::Right,
2514        )
2515    }
2516}
2517
2518impl std::ops::Deref for DisplaySnapshot {
2519    type Target = BlockSnapshot;
2520
2521    fn deref(&self) -> &Self::Target {
2522        &self.block_snapshot
2523    }
2524}
2525
2526/// A zero-indexed point in a text buffer consisting of a row and column adjusted for inserted blocks.
2527#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
2528pub struct DisplayPoint(BlockPoint);
2529
2530impl Debug for DisplayPoint {
2531    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2532        f.write_fmt(format_args!(
2533            "DisplayPoint({}, {})",
2534            self.row().0,
2535            self.column()
2536        ))
2537    }
2538}
2539
2540impl Add for DisplayPoint {
2541    type Output = Self;
2542
2543    fn add(self, other: Self) -> Self::Output {
2544        DisplayPoint(BlockPoint(self.0.0 + other.0.0))
2545    }
2546}
2547
2548impl Sub for DisplayPoint {
2549    type Output = Self;
2550
2551    fn sub(self, other: Self) -> Self::Output {
2552        DisplayPoint(BlockPoint(self.0.0 - other.0.0))
2553    }
2554}
2555
2556#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
2557#[serde(transparent)]
2558pub struct DisplayRow(pub u32);
2559
2560impl DisplayRow {
2561    pub(crate) fn as_display_point(&self) -> DisplayPoint {
2562        DisplayPoint::new(*self, 0)
2563    }
2564}
2565
2566impl Add<DisplayRow> for DisplayRow {
2567    type Output = Self;
2568
2569    fn add(self, other: Self) -> Self::Output {
2570        DisplayRow(self.0 + other.0)
2571    }
2572}
2573
2574impl Add<u32> for DisplayRow {
2575    type Output = Self;
2576
2577    fn add(self, other: u32) -> Self::Output {
2578        DisplayRow(self.0 + other)
2579    }
2580}
2581
2582impl Sub<DisplayRow> for DisplayRow {
2583    type Output = Self;
2584
2585    fn sub(self, other: Self) -> Self::Output {
2586        DisplayRow(self.0 - other.0)
2587    }
2588}
2589
2590impl Sub<u32> for DisplayRow {
2591    type Output = Self;
2592
2593    fn sub(self, other: u32) -> Self::Output {
2594        DisplayRow(self.0 - other)
2595    }
2596}
2597
2598impl DisplayPoint {
2599    pub fn new(row: DisplayRow, column: u32) -> Self {
2600        Self(BlockPoint(Point::new(row.0, column)))
2601    }
2602
2603    pub fn zero() -> Self {
2604        Self::new(DisplayRow(0), 0)
2605    }
2606
2607    pub fn is_zero(&self) -> bool {
2608        self.0.is_zero()
2609    }
2610
2611    pub fn row(self) -> DisplayRow {
2612        DisplayRow(self.0.row)
2613    }
2614
2615    pub fn column(self) -> u32 {
2616        self.0.column
2617    }
2618
2619    pub fn row_mut(&mut self) -> &mut u32 {
2620        &mut self.0.row
2621    }
2622
2623    pub fn column_mut(&mut self) -> &mut u32 {
2624        &mut self.0.column
2625    }
2626
2627    pub fn to_point(self, map: &DisplaySnapshot) -> Point {
2628        map.display_point_to_point(self, Bias::Left)
2629    }
2630
2631    pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> MultiBufferOffset {
2632        let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
2633        let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
2634        let fold_point = map
2635            .tab_snapshot()
2636            .tab_point_to_fold_point(tab_point, bias)
2637            .0;
2638        let inlay_point = fold_point.to_inlay_point(map.fold_snapshot());
2639        map.inlay_snapshot()
2640            .to_buffer_offset(map.inlay_snapshot().to_offset(inlay_point))
2641    }
2642}
2643
2644impl ToDisplayPoint for MultiBufferOffset {
2645    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2646        map.point_to_display_point(self.to_point(map.buffer_snapshot()), Bias::Left)
2647    }
2648}
2649
2650impl ToDisplayPoint for MultiBufferOffsetUtf16 {
2651    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2652        self.to_offset(map.buffer_snapshot()).to_display_point(map)
2653    }
2654}
2655
2656impl ToDisplayPoint for Point {
2657    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2658        map.point_to_display_point(*self, Bias::Left)
2659    }
2660}
2661
2662impl ToDisplayPoint for Anchor {
2663    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2664        self.to_point(map.buffer_snapshot()).to_display_point(map)
2665    }
2666}
2667
2668#[cfg(test)]
2669pub mod tests {
2670    use super::*;
2671    use crate::{
2672        movement,
2673        test::{marked_display_snapshot, test_font},
2674    };
2675    use Bias::*;
2676    use block_map::BlockPlacement;
2677    use gpui::{
2678        App, AppContext as _, BorrowAppContext, Element, Hsla, Rgba, div, font, observe, px,
2679    };
2680    use language::{
2681        Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig,
2682        LanguageMatcher,
2683    };
2684    use lsp::LanguageServerId;
2685
2686    use rand::{Rng, prelude::*};
2687    use settings::{SettingsContent, SettingsStore};
2688    use smol::stream::StreamExt;
2689    use std::{env, sync::Arc};
2690    use text::PointUtf16;
2691    use theme::{LoadThemes, SyntaxTheme};
2692    use unindent::Unindent as _;
2693    use util::test::{marked_text_ranges, sample_text};
2694
2695    #[gpui::test(iterations = 100)]
2696    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2697        cx.background_executor.set_block_on_ticks(0..=50);
2698        let operations = env::var("OPERATIONS")
2699            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2700            .unwrap_or(10);
2701
2702        let mut tab_size = rng.random_range(1..=4);
2703        let buffer_start_excerpt_header_height = rng.random_range(1..=5);
2704        let excerpt_header_height = rng.random_range(1..=5);
2705        let font_size = px(14.0);
2706        let max_wrap_width = 300.0;
2707        let mut wrap_width = if rng.random_bool(0.1) {
2708            None
2709        } else {
2710            Some(px(rng.random_range(0.0..=max_wrap_width)))
2711        };
2712
2713        log::info!("tab size: {}", tab_size);
2714        log::info!("wrap width: {:?}", wrap_width);
2715
2716        cx.update(|cx| {
2717            init_test(cx, |s| {
2718                s.project.all_languages.defaults.tab_size = NonZeroU32::new(tab_size)
2719            });
2720        });
2721
2722        let buffer = cx.update(|cx| {
2723            if rng.random() {
2724                let len = rng.random_range(0..10);
2725                let text = util::RandomCharIter::new(&mut rng)
2726                    .take(len)
2727                    .collect::<String>();
2728                MultiBuffer::build_simple(&text, cx)
2729            } else {
2730                MultiBuffer::build_random(&mut rng, cx)
2731            }
2732        });
2733
2734        let font = test_font();
2735        let map = cx.new(|cx| {
2736            DisplayMap::new(
2737                buffer.clone(),
2738                font,
2739                font_size,
2740                wrap_width,
2741                buffer_start_excerpt_header_height,
2742                excerpt_header_height,
2743                FoldPlaceholder::test(),
2744                DiagnosticSeverity::Warning,
2745                cx,
2746            )
2747        });
2748        let mut notifications = observe(&map, cx);
2749        let mut fold_count = 0;
2750        let mut blocks = Vec::new();
2751
2752        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
2753        log::info!("buffer text: {:?}", snapshot.buffer_snapshot().text());
2754        log::info!("fold text: {:?}", snapshot.fold_snapshot().text());
2755        log::info!("tab text: {:?}", snapshot.tab_snapshot().text());
2756        log::info!("wrap text: {:?}", snapshot.wrap_snapshot().text());
2757        log::info!("block text: {:?}", snapshot.block_snapshot.text());
2758        log::info!("display text: {:?}", snapshot.text());
2759
2760        for _i in 0..operations {
2761            match rng.random_range(0..100) {
2762                0..=19 => {
2763                    wrap_width = if rng.random_bool(0.2) {
2764                        None
2765                    } else {
2766                        Some(px(rng.random_range(0.0..=max_wrap_width)))
2767                    };
2768                    log::info!("setting wrap width to {:?}", wrap_width);
2769                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
2770                }
2771                20..=29 => {
2772                    let mut tab_sizes = vec![1, 2, 3, 4];
2773                    tab_sizes.remove((tab_size - 1) as usize);
2774                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
2775                    log::info!("setting tab size to {:?}", tab_size);
2776                    cx.update(|cx| {
2777                        cx.update_global::<SettingsStore, _>(|store, cx| {
2778                            store.update_user_settings(cx, |s| {
2779                                s.project.all_languages.defaults.tab_size =
2780                                    NonZeroU32::new(tab_size);
2781                            });
2782                        });
2783                    });
2784                }
2785                30..=44 => {
2786                    map.update(cx, |map, cx| {
2787                        if rng.random() || blocks.is_empty() {
2788                            let snapshot = map.snapshot(cx);
2789                            let buffer = snapshot.buffer_snapshot();
2790                            let block_properties = (0..rng.random_range(1..=1))
2791                                .map(|_| {
2792                                    let position = buffer.anchor_after(buffer.clip_offset(
2793                                        rng.random_range(MultiBufferOffset(0)..=buffer.len()),
2794                                        Bias::Left,
2795                                    ));
2796
2797                                    let placement = if rng.random() {
2798                                        BlockPlacement::Above(position)
2799                                    } else {
2800                                        BlockPlacement::Below(position)
2801                                    };
2802                                    let height = rng.random_range(1..5);
2803                                    log::info!(
2804                                        "inserting block {:?} with height {}",
2805                                        placement.as_ref().map(|p| p.to_point(&buffer)),
2806                                        height
2807                                    );
2808                                    let priority = rng.random_range(1..100);
2809                                    BlockProperties {
2810                                        placement,
2811                                        style: BlockStyle::Fixed,
2812                                        height: Some(height),
2813                                        render: Arc::new(|_| div().into_any()),
2814                                        priority,
2815                                    }
2816                                })
2817                                .collect::<Vec<_>>();
2818                            blocks.extend(map.insert_blocks(block_properties, cx));
2819                        } else {
2820                            blocks.shuffle(&mut rng);
2821                            let remove_count = rng.random_range(1..=4.min(blocks.len()));
2822                            let block_ids_to_remove = (0..remove_count)
2823                                .map(|_| blocks.remove(rng.random_range(0..blocks.len())))
2824                                .collect();
2825                            log::info!("removing block ids {:?}", block_ids_to_remove);
2826                            map.remove_blocks(block_ids_to_remove, cx);
2827                        }
2828                    });
2829                }
2830                45..=79 => {
2831                    let mut ranges = Vec::new();
2832                    for _ in 0..rng.random_range(1..=3) {
2833                        buffer.read_with(cx, |buffer, cx| {
2834                            let buffer = buffer.read(cx);
2835                            let end = buffer.clip_offset(
2836                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
2837                                Right,
2838                            );
2839                            let start = buffer
2840                                .clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
2841                            ranges.push(start..end);
2842                        });
2843                    }
2844
2845                    if rng.random() && fold_count > 0 {
2846                        log::info!("unfolding ranges: {:?}", ranges);
2847                        map.update(cx, |map, cx| {
2848                            map.unfold_intersecting(ranges, true, cx);
2849                        });
2850                    } else {
2851                        log::info!("folding ranges: {:?}", ranges);
2852                        map.update(cx, |map, cx| {
2853                            map.fold(
2854                                ranges
2855                                    .into_iter()
2856                                    .map(|range| Crease::simple(range, FoldPlaceholder::test()))
2857                                    .collect(),
2858                                cx,
2859                            );
2860                        });
2861                    }
2862                }
2863                _ => {
2864                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
2865                }
2866            }
2867
2868            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
2869                notifications.next().await.unwrap();
2870            }
2871
2872            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
2873            fold_count = snapshot.fold_count();
2874            log::info!("buffer text: {:?}", snapshot.buffer_snapshot().text());
2875            log::info!("fold text: {:?}", snapshot.fold_snapshot().text());
2876            log::info!("tab text: {:?}", snapshot.tab_snapshot().text());
2877            log::info!("wrap text: {:?}", snapshot.wrap_snapshot().text());
2878            log::info!("block text: {:?}", snapshot.block_snapshot.text());
2879            log::info!("display text: {:?}", snapshot.text());
2880
2881            // Line boundaries
2882            let buffer = snapshot.buffer_snapshot();
2883            for _ in 0..5 {
2884                let row = rng.random_range(0..=buffer.max_point().row);
2885                let column = rng.random_range(0..=buffer.line_len(MultiBufferRow(row)));
2886                let point = buffer.clip_point(Point::new(row, column), Left);
2887
2888                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
2889                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
2890
2891                assert!(prev_buffer_bound <= point);
2892                assert!(next_buffer_bound >= point);
2893                assert_eq!(prev_buffer_bound.column, 0);
2894                assert_eq!(prev_display_bound.column(), 0);
2895                if next_buffer_bound < buffer.max_point() {
2896                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
2897                }
2898
2899                assert_eq!(
2900                    prev_display_bound,
2901                    prev_buffer_bound.to_display_point(&snapshot),
2902                    "row boundary before {:?}. reported buffer row boundary: {:?}",
2903                    point,
2904                    prev_buffer_bound
2905                );
2906                assert_eq!(
2907                    next_display_bound,
2908                    next_buffer_bound.to_display_point(&snapshot),
2909                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
2910                    point,
2911                    next_buffer_bound
2912                );
2913                assert_eq!(
2914                    prev_buffer_bound,
2915                    prev_display_bound.to_point(&snapshot),
2916                    "row boundary before {:?}. reported display row boundary: {:?}",
2917                    point,
2918                    prev_display_bound
2919                );
2920                assert_eq!(
2921                    next_buffer_bound,
2922                    next_display_bound.to_point(&snapshot),
2923                    "row boundary after {:?}. reported display row boundary: {:?}",
2924                    point,
2925                    next_display_bound
2926                );
2927            }
2928
2929            // Movement
2930            let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
2931            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
2932            for _ in 0..5 {
2933                let row = rng.random_range(0..=snapshot.max_point().row().0);
2934                let column = rng.random_range(0..=snapshot.line_len(DisplayRow(row)));
2935                let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
2936
2937                log::info!("Moving from point {:?}", point);
2938
2939                let moved_right = movement::right(&snapshot, point);
2940                log::info!("Right {:?}", moved_right);
2941                if point < max_point {
2942                    assert!(moved_right > point);
2943                    if point.column() == snapshot.line_len(point.row())
2944                        || snapshot.soft_wrap_indent(point.row()).is_some()
2945                            && point.column() == snapshot.line_len(point.row()) - 1
2946                    {
2947                        assert!(moved_right.row() > point.row());
2948                    }
2949                } else {
2950                    assert_eq!(moved_right, point);
2951                }
2952
2953                let moved_left = movement::left(&snapshot, point);
2954                log::info!("Left {:?}", moved_left);
2955                if point > min_point {
2956                    assert!(moved_left < point);
2957                    if point.column() == 0 {
2958                        assert!(moved_left.row() < point.row());
2959                    }
2960                } else {
2961                    assert_eq!(moved_left, point);
2962                }
2963            }
2964        }
2965    }
2966
2967    #[gpui::test(retries = 5)]
2968    async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
2969        cx.background_executor
2970            .set_block_on_ticks(usize::MAX..=usize::MAX);
2971        cx.update(|cx| {
2972            init_test(cx, |_| {});
2973        });
2974
2975        let mut cx = crate::test::editor_test_context::EditorTestContext::new(cx).await;
2976        let editor = cx.editor.clone();
2977        let window = cx.window;
2978
2979        _ = cx.update_window(window, |_, window, cx| {
2980            let text_layout_details =
2981                editor.update(cx, |editor, cx| editor.text_layout_details(window, cx));
2982
2983            let font_size = px(12.0);
2984            let wrap_width = Some(px(96.));
2985
2986            let text = "one two three four five\nsix seven eight";
2987            let buffer = MultiBuffer::build_simple(text, cx);
2988            let map = cx.new(|cx| {
2989                DisplayMap::new(
2990                    buffer.clone(),
2991                    font("Helvetica"),
2992                    font_size,
2993                    wrap_width,
2994                    1,
2995                    1,
2996                    FoldPlaceholder::test(),
2997                    DiagnosticSeverity::Warning,
2998                    cx,
2999                )
3000            });
3001
3002            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3003            assert_eq!(
3004                snapshot.text_chunks(DisplayRow(0)).collect::<String>(),
3005                "one two \nthree four \nfive\nsix seven \neight"
3006            );
3007            assert_eq!(
3008                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Left),
3009                DisplayPoint::new(DisplayRow(0), 7)
3010            );
3011            assert_eq!(
3012                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Right),
3013                DisplayPoint::new(DisplayRow(1), 0)
3014            );
3015            assert_eq!(
3016                movement::right(&snapshot, DisplayPoint::new(DisplayRow(0), 7)),
3017                DisplayPoint::new(DisplayRow(1), 0)
3018            );
3019            assert_eq!(
3020                movement::left(&snapshot, DisplayPoint::new(DisplayRow(1), 0)),
3021                DisplayPoint::new(DisplayRow(0), 7)
3022            );
3023
3024            let x = snapshot
3025                .x_for_display_point(DisplayPoint::new(DisplayRow(1), 10), &text_layout_details);
3026            assert_eq!(
3027                movement::up(
3028                    &snapshot,
3029                    DisplayPoint::new(DisplayRow(1), 10),
3030                    language::SelectionGoal::None,
3031                    false,
3032                    &text_layout_details,
3033                ),
3034                (
3035                    DisplayPoint::new(DisplayRow(0), 7),
3036                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3037                )
3038            );
3039            assert_eq!(
3040                movement::down(
3041                    &snapshot,
3042                    DisplayPoint::new(DisplayRow(0), 7),
3043                    language::SelectionGoal::HorizontalPosition(f64::from(x)),
3044                    false,
3045                    &text_layout_details
3046                ),
3047                (
3048                    DisplayPoint::new(DisplayRow(1), 10),
3049                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3050                )
3051            );
3052            assert_eq!(
3053                movement::down(
3054                    &snapshot,
3055                    DisplayPoint::new(DisplayRow(1), 10),
3056                    language::SelectionGoal::HorizontalPosition(f64::from(x)),
3057                    false,
3058                    &text_layout_details
3059                ),
3060                (
3061                    DisplayPoint::new(DisplayRow(2), 4),
3062                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3063                )
3064            );
3065
3066            let ix = MultiBufferOffset(snapshot.buffer_snapshot().text().find("seven").unwrap());
3067            buffer.update(cx, |buffer, cx| {
3068                buffer.edit([(ix..ix, "and ")], None, cx);
3069            });
3070
3071            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3072            assert_eq!(
3073                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
3074                "three four \nfive\nsix and \nseven eight"
3075            );
3076
3077            // Re-wrap on font size changes
3078            map.update(cx, |map, cx| {
3079                map.set_font(font("Helvetica"), font_size + Pixels::from(3.), cx)
3080            });
3081
3082            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3083            assert_eq!(
3084                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
3085                "three \nfour five\nsix and \nseven \neight"
3086            )
3087        });
3088    }
3089
3090    #[gpui::test]
3091    fn test_text_chunks(cx: &mut gpui::App) {
3092        init_test(cx, |_| {});
3093
3094        let text = sample_text(6, 6, 'a');
3095        let buffer = MultiBuffer::build_simple(&text, cx);
3096
3097        let font_size = px(14.0);
3098        let map = cx.new(|cx| {
3099            DisplayMap::new(
3100                buffer.clone(),
3101                font("Helvetica"),
3102                font_size,
3103                None,
3104                1,
3105                1,
3106                FoldPlaceholder::test(),
3107                DiagnosticSeverity::Warning,
3108                cx,
3109            )
3110        });
3111
3112        buffer.update(cx, |buffer, cx| {
3113            buffer.edit(
3114                vec![
3115                    (
3116                        MultiBufferPoint::new(1, 0)..MultiBufferPoint::new(1, 0),
3117                        "\t",
3118                    ),
3119                    (
3120                        MultiBufferPoint::new(1, 1)..MultiBufferPoint::new(1, 1),
3121                        "\t",
3122                    ),
3123                    (
3124                        MultiBufferPoint::new(2, 1)..MultiBufferPoint::new(2, 1),
3125                        "\t",
3126                    ),
3127                ],
3128                None,
3129                cx,
3130            )
3131        });
3132
3133        assert_eq!(
3134            map.update(cx, |map, cx| map.snapshot(cx))
3135                .text_chunks(DisplayRow(1))
3136                .collect::<String>()
3137                .lines()
3138                .next(),
3139            Some("    b   bbbbb")
3140        );
3141        assert_eq!(
3142            map.update(cx, |map, cx| map.snapshot(cx))
3143                .text_chunks(DisplayRow(2))
3144                .collect::<String>()
3145                .lines()
3146                .next(),
3147            Some("c   ccccc")
3148        );
3149    }
3150
3151    #[gpui::test]
3152    fn test_inlays_with_newlines_after_blocks(cx: &mut gpui::TestAppContext) {
3153        cx.update(|cx| init_test(cx, |_| {}));
3154
3155        let buffer = cx.new(|cx| Buffer::local("a", cx));
3156        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3157        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3158
3159        let font_size = px(14.0);
3160        let map = cx.new(|cx| {
3161            DisplayMap::new(
3162                buffer.clone(),
3163                font("Helvetica"),
3164                font_size,
3165                None,
3166                1,
3167                1,
3168                FoldPlaceholder::test(),
3169                DiagnosticSeverity::Warning,
3170                cx,
3171            )
3172        });
3173
3174        map.update(cx, |map, cx| {
3175            map.insert_blocks(
3176                [BlockProperties {
3177                    placement: BlockPlacement::Above(
3178                        buffer_snapshot.anchor_before(Point::new(0, 0)),
3179                    ),
3180                    height: Some(2),
3181                    style: BlockStyle::Sticky,
3182                    render: Arc::new(|_| div().into_any()),
3183                    priority: 0,
3184                }],
3185                cx,
3186            );
3187        });
3188        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\na"));
3189
3190        map.update(cx, |map, cx| {
3191            map.splice_inlays(
3192                &[],
3193                vec![Inlay::edit_prediction(
3194                    0,
3195                    buffer_snapshot.anchor_after(MultiBufferOffset(0)),
3196                    "\n",
3197                )],
3198                cx,
3199            );
3200        });
3201        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\na"));
3202
3203        // Regression test: updating the display map does not crash when a
3204        // block is immediately followed by a multi-line inlay.
3205        buffer.update(cx, |buffer, cx| {
3206            buffer.edit(
3207                [(MultiBufferOffset(1)..MultiBufferOffset(1), "b")],
3208                None,
3209                cx,
3210            );
3211        });
3212        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\nab"));
3213    }
3214
3215    #[gpui::test]
3216    async fn test_chunks(cx: &mut gpui::TestAppContext) {
3217        let text = r#"
3218            fn outer() {}
3219
3220            mod module {
3221                fn inner() {}
3222            }"#
3223        .unindent();
3224
3225        let theme =
3226            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
3227        let language = Arc::new(
3228            Language::new(
3229                LanguageConfig {
3230                    name: "Test".into(),
3231                    matcher: LanguageMatcher {
3232                        path_suffixes: vec![".test".to_string()],
3233                        ..Default::default()
3234                    },
3235                    ..Default::default()
3236                },
3237                Some(tree_sitter_rust::LANGUAGE.into()),
3238            )
3239            .with_highlights_query(
3240                r#"
3241                (mod_item name: (identifier) body: _ @mod.body)
3242                (function_item name: (identifier) @fn.name)
3243                "#,
3244            )
3245            .unwrap(),
3246        );
3247        language.set_theme(&theme);
3248
3249        cx.update(|cx| {
3250            init_test(cx, |s| {
3251                s.project.all_languages.defaults.tab_size = Some(2.try_into().unwrap())
3252            })
3253        });
3254
3255        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3256        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3257        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3258
3259        let font_size = px(14.0);
3260
3261        let map = cx.new(|cx| {
3262            DisplayMap::new(
3263                buffer,
3264                font("Helvetica"),
3265                font_size,
3266                None,
3267                1,
3268                1,
3269                FoldPlaceholder::test(),
3270                DiagnosticSeverity::Warning,
3271                cx,
3272            )
3273        });
3274        assert_eq!(
3275            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
3276            vec![
3277                ("fn ".to_string(), None),
3278                ("outer".to_string(), Some(Hsla::blue())),
3279                ("() {}\n\nmod module ".to_string(), None),
3280                ("{\n    fn ".to_string(), Some(Hsla::red())),
3281                ("inner".to_string(), Some(Hsla::blue())),
3282                ("() {}\n}".to_string(), Some(Hsla::red())),
3283            ]
3284        );
3285        assert_eq!(
3286            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
3287            vec![
3288                ("    fn ".to_string(), Some(Hsla::red())),
3289                ("inner".to_string(), Some(Hsla::blue())),
3290                ("() {}\n}".to_string(), Some(Hsla::red())),
3291            ]
3292        );
3293
3294        map.update(cx, |map, cx| {
3295            map.fold(
3296                vec![Crease::simple(
3297                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
3298                    FoldPlaceholder::test(),
3299                )],
3300                cx,
3301            )
3302        });
3303        assert_eq!(
3304            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(2), &map, &theme, cx)),
3305            vec![
3306                ("fn ".to_string(), None),
3307                ("out".to_string(), Some(Hsla::blue())),
3308                ("".to_string(), None),
3309                ("  fn ".to_string(), Some(Hsla::red())),
3310                ("inner".to_string(), Some(Hsla::blue())),
3311                ("() {}\n}".to_string(), Some(Hsla::red())),
3312            ]
3313        );
3314    }
3315
3316    #[gpui::test]
3317    async fn test_chunks_with_syntax_highlighting_across_blocks(cx: &mut gpui::TestAppContext) {
3318        cx.background_executor
3319            .set_block_on_ticks(usize::MAX..=usize::MAX);
3320
3321        let text = r#"
3322            const A: &str = "
3323                one
3324                two
3325                three
3326            ";
3327            const B: &str = "four";
3328        "#
3329        .unindent();
3330
3331        let theme = SyntaxTheme::new_test(vec![
3332            ("string", Hsla::red()),
3333            ("punctuation", Hsla::blue()),
3334            ("keyword", Hsla::green()),
3335        ]);
3336        let language = Arc::new(
3337            Language::new(
3338                LanguageConfig {
3339                    name: "Rust".into(),
3340                    ..Default::default()
3341                },
3342                Some(tree_sitter_rust::LANGUAGE.into()),
3343            )
3344            .with_highlights_query(
3345                r#"
3346                (string_literal) @string
3347                "const" @keyword
3348                [":" ";"] @punctuation
3349                "#,
3350            )
3351            .unwrap(),
3352        );
3353        language.set_theme(&theme);
3354
3355        cx.update(|cx| init_test(cx, |_| {}));
3356
3357        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3358        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3359        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3360        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3361
3362        let map = cx.new(|cx| {
3363            DisplayMap::new(
3364                buffer,
3365                font("Courier"),
3366                px(16.0),
3367                None,
3368                1,
3369                1,
3370                FoldPlaceholder::test(),
3371                DiagnosticSeverity::Warning,
3372                cx,
3373            )
3374        });
3375
3376        // Insert two blocks in the middle of a multi-line string literal.
3377        // The second block has zero height.
3378        map.update(cx, |map, cx| {
3379            map.insert_blocks(
3380                [
3381                    BlockProperties {
3382                        placement: BlockPlacement::Below(
3383                            buffer_snapshot.anchor_before(Point::new(1, 0)),
3384                        ),
3385                        height: Some(1),
3386                        style: BlockStyle::Sticky,
3387                        render: Arc::new(|_| div().into_any()),
3388                        priority: 0,
3389                    },
3390                    BlockProperties {
3391                        placement: BlockPlacement::Below(
3392                            buffer_snapshot.anchor_before(Point::new(2, 0)),
3393                        ),
3394                        height: None,
3395                        style: BlockStyle::Sticky,
3396                        render: Arc::new(|_| div().into_any()),
3397                        priority: 0,
3398                    },
3399                ],
3400                cx,
3401            )
3402        });
3403
3404        pretty_assertions::assert_eq!(
3405            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(7), &map, &theme, cx)),
3406            [
3407                ("const".into(), Some(Hsla::green())),
3408                (" A".into(), None),
3409                (":".into(), Some(Hsla::blue())),
3410                (" &str = ".into(), None),
3411                ("\"\n    one\n".into(), Some(Hsla::red())),
3412                ("\n".into(), None),
3413                ("    two\n    three\n\"".into(), Some(Hsla::red())),
3414                (";".into(), Some(Hsla::blue())),
3415                ("\n".into(), None),
3416                ("const".into(), Some(Hsla::green())),
3417                (" B".into(), None),
3418                (":".into(), Some(Hsla::blue())),
3419                (" &str = ".into(), None),
3420                ("\"four\"".into(), Some(Hsla::red())),
3421                (";".into(), Some(Hsla::blue())),
3422                ("\n".into(), None),
3423            ]
3424        );
3425    }
3426
3427    #[gpui::test]
3428    async fn test_chunks_with_diagnostics_across_blocks(cx: &mut gpui::TestAppContext) {
3429        cx.background_executor
3430            .set_block_on_ticks(usize::MAX..=usize::MAX);
3431
3432        let text = r#"
3433            struct A {
3434                b: usize;
3435            }
3436            const c: usize = 1;
3437        "#
3438        .unindent();
3439
3440        cx.update(|cx| init_test(cx, |_| {}));
3441
3442        let buffer = cx.new(|cx| Buffer::local(text, cx));
3443
3444        buffer.update(cx, |buffer, cx| {
3445            buffer.update_diagnostics(
3446                LanguageServerId(0),
3447                DiagnosticSet::new(
3448                    [DiagnosticEntry {
3449                        range: PointUtf16::new(0, 0)..PointUtf16::new(2, 1),
3450                        diagnostic: Diagnostic {
3451                            severity: lsp::DiagnosticSeverity::ERROR,
3452                            group_id: 1,
3453                            message: "hi".into(),
3454                            ..Default::default()
3455                        },
3456                    }],
3457                    buffer,
3458                ),
3459                cx,
3460            )
3461        });
3462
3463        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3464        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3465
3466        let map = cx.new(|cx| {
3467            DisplayMap::new(
3468                buffer,
3469                font("Courier"),
3470                px(16.0),
3471                None,
3472                1,
3473                1,
3474                FoldPlaceholder::test(),
3475                DiagnosticSeverity::Warning,
3476                cx,
3477            )
3478        });
3479
3480        let black = gpui::black().to_rgb();
3481        let red = gpui::red().to_rgb();
3482
3483        // Insert a block in the middle of a multi-line diagnostic.
3484        map.update(cx, |map, cx| {
3485            map.highlight_text(
3486                HighlightKey::Editor,
3487                vec![
3488                    buffer_snapshot.anchor_before(Point::new(3, 9))
3489                        ..buffer_snapshot.anchor_after(Point::new(3, 14)),
3490                    buffer_snapshot.anchor_before(Point::new(3, 17))
3491                        ..buffer_snapshot.anchor_after(Point::new(3, 18)),
3492                ],
3493                red.into(),
3494                false,
3495                cx,
3496            );
3497            map.insert_blocks(
3498                [BlockProperties {
3499                    placement: BlockPlacement::Below(
3500                        buffer_snapshot.anchor_before(Point::new(1, 0)),
3501                    ),
3502                    height: Some(1),
3503                    style: BlockStyle::Sticky,
3504                    render: Arc::new(|_| div().into_any()),
3505                    priority: 0,
3506                }],
3507                cx,
3508            )
3509        });
3510
3511        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3512        let mut chunks = Vec::<(String, Option<lsp::DiagnosticSeverity>, Rgba)>::new();
3513        for chunk in snapshot.chunks(DisplayRow(0)..DisplayRow(5), true, Default::default()) {
3514            let color = chunk
3515                .highlight_style
3516                .and_then(|style| style.color)
3517                .map_or(black, |color| color.to_rgb());
3518            if let Some((last_chunk, last_severity, last_color)) = chunks.last_mut()
3519                && *last_severity == chunk.diagnostic_severity
3520                && *last_color == color
3521            {
3522                last_chunk.push_str(chunk.text);
3523                continue;
3524            }
3525
3526            chunks.push((chunk.text.to_string(), chunk.diagnostic_severity, color));
3527        }
3528
3529        assert_eq!(
3530            chunks,
3531            [
3532                (
3533                    "struct A {\n    b: usize;\n".into(),
3534                    Some(lsp::DiagnosticSeverity::ERROR),
3535                    black
3536                ),
3537                ("\n".into(), None, black),
3538                ("}".into(), Some(lsp::DiagnosticSeverity::ERROR), black),
3539                ("\nconst c: ".into(), None, black),
3540                ("usize".into(), None, red),
3541                (" = ".into(), None, black),
3542                ("1".into(), None, red),
3543                (";\n".into(), None, black),
3544            ]
3545        );
3546    }
3547
3548    #[gpui::test]
3549    async fn test_point_translation_with_replace_blocks(cx: &mut gpui::TestAppContext) {
3550        cx.background_executor
3551            .set_block_on_ticks(usize::MAX..=usize::MAX);
3552
3553        cx.update(|cx| init_test(cx, |_| {}));
3554
3555        let buffer = cx.update(|cx| MultiBuffer::build_simple("abcde\nfghij\nklmno\npqrst", cx));
3556        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3557        let map = cx.new(|cx| {
3558            DisplayMap::new(
3559                buffer.clone(),
3560                font("Courier"),
3561                px(16.0),
3562                None,
3563                1,
3564                1,
3565                FoldPlaceholder::test(),
3566                DiagnosticSeverity::Warning,
3567                cx,
3568            )
3569        });
3570
3571        let snapshot = map.update(cx, |map, cx| {
3572            map.insert_blocks(
3573                [BlockProperties {
3574                    placement: BlockPlacement::Replace(
3575                        buffer_snapshot.anchor_before(Point::new(1, 2))
3576                            ..=buffer_snapshot.anchor_after(Point::new(2, 3)),
3577                    ),
3578                    height: Some(4),
3579                    style: BlockStyle::Fixed,
3580                    render: Arc::new(|_| div().into_any()),
3581                    priority: 0,
3582                }],
3583                cx,
3584            );
3585            map.snapshot(cx)
3586        });
3587
3588        assert_eq!(snapshot.text(), "abcde\n\n\n\n\npqrst");
3589
3590        let point_to_display_points = [
3591            (Point::new(1, 0), DisplayPoint::new(DisplayRow(1), 0)),
3592            (Point::new(2, 0), DisplayPoint::new(DisplayRow(1), 0)),
3593            (Point::new(3, 0), DisplayPoint::new(DisplayRow(5), 0)),
3594        ];
3595        for (buffer_point, display_point) in point_to_display_points {
3596            assert_eq!(
3597                snapshot.point_to_display_point(buffer_point, Bias::Left),
3598                display_point,
3599                "point_to_display_point({:?}, Bias::Left)",
3600                buffer_point
3601            );
3602            assert_eq!(
3603                snapshot.point_to_display_point(buffer_point, Bias::Right),
3604                display_point,
3605                "point_to_display_point({:?}, Bias::Right)",
3606                buffer_point
3607            );
3608        }
3609
3610        let display_points_to_points = [
3611            (
3612                DisplayPoint::new(DisplayRow(1), 0),
3613                Point::new(1, 0),
3614                Point::new(2, 5),
3615            ),
3616            (
3617                DisplayPoint::new(DisplayRow(2), 0),
3618                Point::new(1, 0),
3619                Point::new(2, 5),
3620            ),
3621            (
3622                DisplayPoint::new(DisplayRow(3), 0),
3623                Point::new(1, 0),
3624                Point::new(2, 5),
3625            ),
3626            (
3627                DisplayPoint::new(DisplayRow(4), 0),
3628                Point::new(1, 0),
3629                Point::new(2, 5),
3630            ),
3631            (
3632                DisplayPoint::new(DisplayRow(5), 0),
3633                Point::new(3, 0),
3634                Point::new(3, 0),
3635            ),
3636        ];
3637        for (display_point, left_buffer_point, right_buffer_point) in display_points_to_points {
3638            assert_eq!(
3639                snapshot.display_point_to_point(display_point, Bias::Left),
3640                left_buffer_point,
3641                "display_point_to_point({:?}, Bias::Left)",
3642                display_point
3643            );
3644            assert_eq!(
3645                snapshot.display_point_to_point(display_point, Bias::Right),
3646                right_buffer_point,
3647                "display_point_to_point({:?}, Bias::Right)",
3648                display_point
3649            );
3650        }
3651    }
3652
3653    #[gpui::test]
3654    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
3655        cx.background_executor
3656            .set_block_on_ticks(usize::MAX..=usize::MAX);
3657
3658        let text = r#"
3659            fn outer() {}
3660
3661            mod module {
3662                fn inner() {}
3663            }"#
3664        .unindent();
3665
3666        let theme =
3667            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
3668        let language = Arc::new(
3669            Language::new(
3670                LanguageConfig {
3671                    name: "Test".into(),
3672                    matcher: LanguageMatcher {
3673                        path_suffixes: vec![".test".to_string()],
3674                        ..Default::default()
3675                    },
3676                    ..Default::default()
3677                },
3678                Some(tree_sitter_rust::LANGUAGE.into()),
3679            )
3680            .with_highlights_query(
3681                r#"
3682                (mod_item name: (identifier) body: _ @mod.body)
3683                (function_item name: (identifier) @fn.name)
3684                "#,
3685            )
3686            .unwrap(),
3687        );
3688        language.set_theme(&theme);
3689
3690        cx.update(|cx| init_test(cx, |_| {}));
3691
3692        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3693        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3694        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3695
3696        let font_size = px(16.0);
3697
3698        let map = cx.new(|cx| {
3699            DisplayMap::new(
3700                buffer,
3701                font("Courier"),
3702                font_size,
3703                Some(px(40.0)),
3704                1,
3705                1,
3706                FoldPlaceholder::test(),
3707                DiagnosticSeverity::Warning,
3708                cx,
3709            )
3710        });
3711        assert_eq!(
3712            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
3713            [
3714                ("fn \n".to_string(), None),
3715                ("oute".to_string(), Some(Hsla::blue())),
3716                ("\n".to_string(), None),
3717                ("r".to_string(), Some(Hsla::blue())),
3718                ("() \n{}\n\n".to_string(), None),
3719            ]
3720        );
3721        assert_eq!(
3722            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
3723            [("{}\n\n".to_string(), None)]
3724        );
3725
3726        map.update(cx, |map, cx| {
3727            map.fold(
3728                vec![Crease::simple(
3729                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
3730                    FoldPlaceholder::test(),
3731                )],
3732                cx,
3733            )
3734        });
3735        assert_eq!(
3736            cx.update(|cx| syntax_chunks(DisplayRow(1)..DisplayRow(4), &map, &theme, cx)),
3737            [
3738                ("out".to_string(), Some(Hsla::blue())),
3739                ("\n".to_string(), None),
3740                ("  ".to_string(), Some(Hsla::red())),
3741                ("\n".to_string(), None),
3742                ("fn ".to_string(), Some(Hsla::red())),
3743                ("i".to_string(), Some(Hsla::blue())),
3744                ("\n".to_string(), None)
3745            ]
3746        );
3747    }
3748
3749    #[gpui::test]
3750    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
3751        cx.update(|cx| init_test(cx, |_| {}));
3752
3753        let theme =
3754            SyntaxTheme::new_test(vec![("operator", Hsla::red()), ("string", Hsla::green())]);
3755        let language = Arc::new(
3756            Language::new(
3757                LanguageConfig {
3758                    name: "Test".into(),
3759                    matcher: LanguageMatcher {
3760                        path_suffixes: vec![".test".to_string()],
3761                        ..Default::default()
3762                    },
3763                    ..Default::default()
3764                },
3765                Some(tree_sitter_rust::LANGUAGE.into()),
3766            )
3767            .with_highlights_query(
3768                r#"
3769                ":" @operator
3770                (string_literal) @string
3771                "#,
3772            )
3773            .unwrap(),
3774        );
3775        language.set_theme(&theme);
3776
3777        let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»«:» B = "c «d»""#, false);
3778
3779        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3780        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3781
3782        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3783        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3784
3785        let font_size = px(16.0);
3786        let map = cx.new(|cx| {
3787            DisplayMap::new(
3788                buffer,
3789                font("Courier"),
3790                font_size,
3791                None,
3792                1,
3793                1,
3794                FoldPlaceholder::test(),
3795                DiagnosticSeverity::Warning,
3796                cx,
3797            )
3798        });
3799
3800        let style = HighlightStyle {
3801            color: Some(Hsla::blue()),
3802            ..Default::default()
3803        };
3804
3805        map.update(cx, |map, cx| {
3806            map.highlight_text(
3807                HighlightKey::Editor,
3808                highlighted_ranges
3809                    .into_iter()
3810                    .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
3811                    .map(|range| {
3812                        buffer_snapshot.anchor_before(range.start)
3813                            ..buffer_snapshot.anchor_before(range.end)
3814                    })
3815                    .collect(),
3816                style,
3817                false,
3818                cx,
3819            );
3820        });
3821
3822        assert_eq!(
3823            cx.update(|cx| chunks(DisplayRow(0)..DisplayRow(10), &map, &theme, cx)),
3824            [
3825                ("const ".to_string(), None, None),
3826                ("a".to_string(), None, Some(Hsla::blue())),
3827                (":".to_string(), Some(Hsla::red()), Some(Hsla::blue())),
3828                (" B = ".to_string(), None, None),
3829                ("\"c ".to_string(), Some(Hsla::green()), None),
3830                ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
3831                ("\"".to_string(), Some(Hsla::green()), None),
3832            ]
3833        );
3834    }
3835
3836    #[gpui::test]
3837    fn test_clip_point(cx: &mut gpui::App) {
3838        init_test(cx, |_| {});
3839
3840        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::App) {
3841            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
3842
3843            match bias {
3844                Bias::Left => {
3845                    if shift_right {
3846                        *markers[1].column_mut() += 1;
3847                    }
3848
3849                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
3850                }
3851                Bias::Right => {
3852                    if shift_right {
3853                        *markers[0].column_mut() += 1;
3854                    }
3855
3856                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
3857                }
3858            };
3859        }
3860
3861        use Bias::{Left, Right};
3862        assert("ˇˇα", false, Left, cx);
3863        assert("ˇˇα", true, Left, cx);
3864        assert("ˇˇα", false, Right, cx);
3865        assert("ˇαˇ", true, Right, cx);
3866        assert("ˇˇ✋", false, Left, cx);
3867        assert("ˇˇ✋", true, Left, cx);
3868        assert("ˇˇ✋", false, Right, cx);
3869        assert("ˇ✋ˇ", true, Right, cx);
3870        assert("ˇˇ🍐", false, Left, cx);
3871        assert("ˇˇ🍐", true, Left, cx);
3872        assert("ˇˇ🍐", false, Right, cx);
3873        assert("ˇ🍐ˇ", true, Right, cx);
3874        assert("ˇˇ\t", false, Left, cx);
3875        assert("ˇˇ\t", true, Left, cx);
3876        assert("ˇˇ\t", false, Right, cx);
3877        assert("ˇ\tˇ", true, Right, cx);
3878        assert(" ˇˇ\t", false, Left, cx);
3879        assert(" ˇˇ\t", true, Left, cx);
3880        assert(" ˇˇ\t", false, Right, cx);
3881        assert(" ˇ\tˇ", true, Right, cx);
3882        assert("   ˇˇ\t", false, Left, cx);
3883        assert("   ˇˇ\t", false, Right, cx);
3884    }
3885
3886    #[gpui::test]
3887    fn test_clip_at_line_ends(cx: &mut gpui::App) {
3888        init_test(cx, |_| {});
3889
3890        fn assert(text: &str, cx: &mut gpui::App) {
3891            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
3892            unmarked_snapshot.clip_at_line_ends = true;
3893            assert_eq!(
3894                unmarked_snapshot.clip_point(markers[1], Bias::Left),
3895                markers[0]
3896            );
3897        }
3898
3899        assert("ˇˇ", cx);
3900        assert("ˇaˇ", cx);
3901        assert("aˇbˇ", cx);
3902        assert("aˇαˇ", cx);
3903    }
3904
3905    #[gpui::test]
3906    fn test_creases(cx: &mut gpui::App) {
3907        init_test(cx, |_| {});
3908
3909        let text = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll";
3910        let buffer = MultiBuffer::build_simple(text, cx);
3911        let font_size = px(14.0);
3912        cx.new(|cx| {
3913            let mut map = DisplayMap::new(
3914                buffer.clone(),
3915                font("Helvetica"),
3916                font_size,
3917                None,
3918                1,
3919                1,
3920                FoldPlaceholder::test(),
3921                DiagnosticSeverity::Warning,
3922                cx,
3923            );
3924            let snapshot = map.buffer.read(cx).snapshot(cx);
3925            let range =
3926                snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
3927
3928            map.crease_map.insert(
3929                [Crease::inline(
3930                    range,
3931                    FoldPlaceholder::test(),
3932                    |_row, _status, _toggle, _window, _cx| div(),
3933                    |_row, _status, _window, _cx| div(),
3934                )],
3935                &map.buffer.read(cx).snapshot(cx),
3936            );
3937
3938            map
3939        });
3940    }
3941
3942    #[gpui::test]
3943    fn test_tabs_with_multibyte_chars(cx: &mut gpui::App) {
3944        init_test(cx, |_| {});
3945
3946        let text = "\t\tα\nβ\t\n🏀β\t\tγ";
3947        let buffer = MultiBuffer::build_simple(text, cx);
3948        let font_size = px(14.0);
3949
3950        let map = cx.new(|cx| {
3951            DisplayMap::new(
3952                buffer.clone(),
3953                font("Helvetica"),
3954                font_size,
3955                None,
3956                1,
3957                1,
3958                FoldPlaceholder::test(),
3959                DiagnosticSeverity::Warning,
3960                cx,
3961            )
3962        });
3963        let map = map.update(cx, |map, cx| map.snapshot(cx));
3964        assert_eq!(map.text(), "✅       α\nβ   \n🏀β      γ");
3965        assert_eq!(
3966            map.text_chunks(DisplayRow(0)).collect::<String>(),
3967            "✅       α\nβ   \n🏀β      γ"
3968        );
3969        assert_eq!(
3970            map.text_chunks(DisplayRow(1)).collect::<String>(),
3971            "β   \n🏀β      γ"
3972        );
3973        assert_eq!(
3974            map.text_chunks(DisplayRow(2)).collect::<String>(),
3975            "🏀β      γ"
3976        );
3977
3978        let point = MultiBufferPoint::new(0, "\t\t".len() as u32);
3979        let display_point = DisplayPoint::new(DisplayRow(0), "".len() as u32);
3980        assert_eq!(point.to_display_point(&map), display_point);
3981        assert_eq!(display_point.to_point(&map), point);
3982
3983        let point = MultiBufferPoint::new(1, "β\t".len() as u32);
3984        let display_point = DisplayPoint::new(DisplayRow(1), "β   ".len() as u32);
3985        assert_eq!(point.to_display_point(&map), display_point);
3986        assert_eq!(display_point.to_point(&map), point,);
3987
3988        let point = MultiBufferPoint::new(2, "🏀β\t\t".len() as u32);
3989        let display_point = DisplayPoint::new(DisplayRow(2), "🏀β      ".len() as u32);
3990        assert_eq!(point.to_display_point(&map), display_point);
3991        assert_eq!(display_point.to_point(&map), point,);
3992
3993        // Display points inside of expanded tabs
3994        assert_eq!(
3995            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
3996            MultiBufferPoint::new(0, "\t".len() as u32),
3997        );
3998        assert_eq!(
3999            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
4000            MultiBufferPoint::new(0, "".len() as u32),
4001        );
4002
4003        // Clipping display points inside of multi-byte characters
4004        assert_eq!(
4005            map.clip_point(
4006                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
4007                Left
4008            ),
4009            DisplayPoint::new(DisplayRow(0), 0)
4010        );
4011        assert_eq!(
4012            map.clip_point(
4013                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
4014                Bias::Right
4015            ),
4016            DisplayPoint::new(DisplayRow(0), "".len() as u32)
4017        );
4018    }
4019
4020    #[gpui::test]
4021    fn test_max_point(cx: &mut gpui::App) {
4022        init_test(cx, |_| {});
4023
4024        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
4025        let font_size = px(14.0);
4026        let map = cx.new(|cx| {
4027            DisplayMap::new(
4028                buffer.clone(),
4029                font("Helvetica"),
4030                font_size,
4031                None,
4032                1,
4033                1,
4034                FoldPlaceholder::test(),
4035                DiagnosticSeverity::Warning,
4036                cx,
4037            )
4038        });
4039        assert_eq!(
4040            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
4041            DisplayPoint::new(DisplayRow(1), 11)
4042        )
4043    }
4044
4045    fn syntax_chunks(
4046        rows: Range<DisplayRow>,
4047        map: &Entity<DisplayMap>,
4048        theme: &SyntaxTheme,
4049        cx: &mut App,
4050    ) -> Vec<(String, Option<Hsla>)> {
4051        chunks(rows, map, theme, cx)
4052            .into_iter()
4053            .map(|(text, color, _)| (text, color))
4054            .collect()
4055    }
4056
4057    fn chunks(
4058        rows: Range<DisplayRow>,
4059        map: &Entity<DisplayMap>,
4060        theme: &SyntaxTheme,
4061        cx: &mut App,
4062    ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
4063        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
4064        let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
4065        for chunk in snapshot.chunks(rows, true, HighlightStyles::default()) {
4066            let syntax_color = chunk
4067                .syntax_highlight_id
4068                .and_then(|id| id.style(theme)?.color);
4069            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
4070            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut()
4071                && syntax_color == *last_syntax_color
4072                && highlight_color == *last_highlight_color
4073            {
4074                last_chunk.push_str(chunk.text);
4075                continue;
4076            }
4077            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
4078        }
4079        chunks
4080    }
4081
4082    fn init_test(cx: &mut App, f: impl Fn(&mut SettingsContent)) {
4083        let settings = SettingsStore::test(cx);
4084        cx.set_global(settings);
4085        crate::init(cx);
4086        theme::init(LoadThemes::JustBase, cx);
4087        cx.update_global::<SettingsStore, _>(|store, cx| {
4088            store.update_user_settings(cx, f);
4089        });
4090    }
4091}