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