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