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