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