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