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::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<Range<text::Anchor>>,
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 placeholder = self.fold_placeholder.clone();
1369        let creases = ranges.into_iter().filter_map(|range| {
1370            let mb_range = excerpt_ids
1371                .iter()
1372                .find_map(|&id| snapshot.anchor_range_in_excerpt(id, range.clone()))?;
1373            Some(Crease::simple(mb_range, placeholder.clone()))
1374        });
1375
1376        let new_ids = self.crease_map.insert(creases, &snapshot);
1377        if !new_ids.is_empty() {
1378            self.lsp_folding_crease_ids.insert(buffer_id, new_ids);
1379        }
1380    }
1381
1382    /// Removes all LSP folding-range creases for a single buffer.
1383    pub(super) fn clear_lsp_folding_ranges(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {
1384        if let hash_map::Entry::Occupied(entry) = self.lsp_folding_crease_ids.entry(buffer_id) {
1385            let old_ids = entry.remove();
1386            let snapshot = self.buffer.read(cx).snapshot(cx);
1387            self.crease_map.remove(old_ids, &snapshot);
1388        }
1389    }
1390
1391    /// Returns `true` when at least one buffer has LSP folding-range creases.
1392    pub(super) fn has_lsp_folding_ranges(&self) -> bool {
1393        !self.lsp_folding_crease_ids.is_empty()
1394    }
1395
1396    #[instrument(skip_all)]
1397    pub fn insert_blocks(
1398        &mut self,
1399        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,
1400        cx: &mut Context<Self>,
1401    ) -> Vec<CustomBlockId> {
1402        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1403
1404        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1405            companion_dm
1406                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1407                .ok()
1408        });
1409
1410        let result = if let Some((companion_dm, companion)) = self.companion.as_ref()
1411            && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
1412        {
1413            companion_dm
1414                .update(cx, |dm, cx| {
1415                    companion.update(cx, |companion, _| {
1416                        self.block_map
1417                            .write(
1418                                self_wrap_snapshot.clone(),
1419                                self_wrap_edits.clone(),
1420                                Some(CompanionViewMut::new(
1421                                    self.entity_id,
1422                                    snapshot,
1423                                    edits,
1424                                    companion,
1425                                    &mut dm.block_map,
1426                                )),
1427                            )
1428                            .insert(blocks)
1429                    })
1430                })
1431                .ok()
1432                .expect("success inserting blocks with companion")
1433        } else {
1434            self.block_map
1435                .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
1436                .insert(blocks)
1437        };
1438
1439        if let Some((companion_dm, _)) = &self.companion {
1440            let _ = companion_dm.update(cx, |dm, cx| {
1441                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1442                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1443                    dm.block_map.read(
1444                        companion_snapshot,
1445                        companion_edits,
1446                        their_companion_ref.map(|c| {
1447                            CompanionView::new(
1448                                dm.entity_id,
1449                                &self_wrap_snapshot,
1450                                &self_wrap_edits,
1451                                c,
1452                            )
1453                        }),
1454                    );
1455                }
1456            });
1457        }
1458
1459        result
1460    }
1461
1462    #[instrument(skip_all)]
1463    pub fn resize_blocks(&mut self, heights: HashMap<CustomBlockId, u32>, cx: &mut Context<Self>) {
1464        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1465
1466        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1467            companion_dm
1468                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1469                .ok()
1470        });
1471
1472        if let Some((companion_dm, companion)) = self.companion.as_ref()
1473            && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
1474        {
1475            companion_dm
1476                .update(cx, |dm, cx| {
1477                    companion.update(cx, |companion, _| {
1478                        self.block_map
1479                            .write(
1480                                self_wrap_snapshot.clone(),
1481                                self_wrap_edits.clone(),
1482                                Some(CompanionViewMut::new(
1483                                    self.entity_id,
1484                                    snapshot,
1485                                    edits,
1486                                    companion,
1487                                    &mut dm.block_map,
1488                                )),
1489                            )
1490                            .resize(heights);
1491                    })
1492                })
1493                .ok();
1494        } else {
1495            self.block_map
1496                .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
1497                .resize(heights);
1498        }
1499
1500        if let Some((companion_dm, _)) = &self.companion {
1501            let _ = companion_dm.update(cx, |dm, cx| {
1502                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1503                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1504                    dm.block_map.read(
1505                        companion_snapshot,
1506                        companion_edits,
1507                        their_companion_ref.map(|c| {
1508                            CompanionView::new(
1509                                dm.entity_id,
1510                                &self_wrap_snapshot,
1511                                &self_wrap_edits,
1512                                c,
1513                            )
1514                        }),
1515                    );
1516                }
1517            });
1518        }
1519    }
1520
1521    #[instrument(skip_all)]
1522    pub fn replace_blocks(&mut self, renderers: HashMap<CustomBlockId, RenderBlock>) {
1523        self.block_map.replace_blocks(renderers);
1524    }
1525
1526    #[instrument(skip_all)]
1527    pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, cx: &mut Context<Self>) {
1528        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1529
1530        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1531            companion_dm
1532                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1533                .ok()
1534        });
1535
1536        if let Some((companion_dm, companion)) = self.companion.as_ref()
1537            && let Some((snapshot, edits)) = companion_wrap_data.as_ref()
1538        {
1539            companion_dm
1540                .update(cx, |dm, cx| {
1541                    companion.update(cx, |companion, _| {
1542                        self.block_map
1543                            .write(
1544                                self_wrap_snapshot.clone(),
1545                                self_wrap_edits.clone(),
1546                                Some(CompanionViewMut::new(
1547                                    self.entity_id,
1548                                    snapshot,
1549                                    edits,
1550                                    companion,
1551                                    &mut dm.block_map,
1552                                )),
1553                            )
1554                            .remove(ids);
1555                    })
1556                })
1557                .ok();
1558        } else {
1559            self.block_map
1560                .write(self_wrap_snapshot.clone(), self_wrap_edits.clone(), None)
1561                .remove(ids);
1562        }
1563
1564        if let Some((companion_dm, _)) = &self.companion {
1565            let _ = companion_dm.update(cx, |dm, cx| {
1566                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1567                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1568                    dm.block_map.read(
1569                        companion_snapshot,
1570                        companion_edits,
1571                        their_companion_ref.map(|c| {
1572                            CompanionView::new(
1573                                dm.entity_id,
1574                                &self_wrap_snapshot,
1575                                &self_wrap_edits,
1576                                c,
1577                            )
1578                        }),
1579                    );
1580                }
1581            });
1582        }
1583    }
1584
1585    #[instrument(skip_all)]
1586    pub fn row_for_block(
1587        &mut self,
1588        block_id: CustomBlockId,
1589        cx: &mut Context<Self>,
1590    ) -> Option<DisplayRow> {
1591        let (self_wrap_snapshot, self_wrap_edits) = self.sync_through_wrap(cx);
1592
1593        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1594            companion_dm
1595                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1596                .ok()
1597        });
1598
1599        let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1600        let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1601            |((snapshot, edits), companion)| {
1602                CompanionView::new(self.entity_id, snapshot, edits, companion)
1603            },
1604        );
1605
1606        let block_map = self.block_map.read(
1607            self_wrap_snapshot.clone(),
1608            self_wrap_edits.clone(),
1609            companion_view,
1610        );
1611        let block_row = block_map.row_for_block(block_id)?;
1612
1613        if let Some((companion_dm, _)) = &self.companion {
1614            let _ = companion_dm.update(cx, |dm, cx| {
1615                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1616                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1617                    dm.block_map.read(
1618                        companion_snapshot,
1619                        companion_edits,
1620                        their_companion_ref.map(|c| {
1621                            CompanionView::new(
1622                                dm.entity_id,
1623                                &self_wrap_snapshot,
1624                                &self_wrap_edits,
1625                                c,
1626                            )
1627                        }),
1628                    );
1629                }
1630            });
1631        }
1632
1633        Some(DisplayRow(block_row.0))
1634    }
1635
1636    #[instrument(skip_all)]
1637    pub fn highlight_text(
1638        &mut self,
1639        key: HighlightKey,
1640        ranges: Vec<Range<Anchor>>,
1641        style: HighlightStyle,
1642        merge: bool,
1643        cx: &App,
1644    ) {
1645        let multi_buffer_snapshot = self.buffer.read(cx).snapshot(cx);
1646        let to_insert = match self.text_highlights.remove(&key).filter(|_| merge) {
1647            Some(previous) => {
1648                let mut merged_ranges = previous.1.clone();
1649                for new_range in ranges {
1650                    let i = merged_ranges
1651                        .binary_search_by(|probe| {
1652                            probe.start.cmp(&new_range.start, &multi_buffer_snapshot)
1653                        })
1654                        .unwrap_or_else(|i| i);
1655                    merged_ranges.insert(i, new_range);
1656                }
1657                Arc::new((style, merged_ranges))
1658            }
1659            None => Arc::new((style, ranges)),
1660        };
1661        self.text_highlights.insert(key, to_insert);
1662    }
1663
1664    #[instrument(skip_all)]
1665    pub(crate) fn highlight_inlays(
1666        &mut self,
1667        key: HighlightKey,
1668        highlights: Vec<InlayHighlight>,
1669        style: HighlightStyle,
1670    ) {
1671        for highlight in highlights {
1672            let update = self.inlay_highlights.update(&key, |highlights| {
1673                highlights.insert(highlight.inlay, (style, highlight.clone()))
1674            });
1675            if update.is_none() {
1676                self.inlay_highlights.insert(
1677                    key,
1678                    TreeMap::from_ordered_entries([(highlight.inlay, (style, highlight))]),
1679                );
1680            }
1681        }
1682    }
1683
1684    #[instrument(skip_all)]
1685    pub fn text_highlights(&self, key: HighlightKey) -> Option<(HighlightStyle, &[Range<Anchor>])> {
1686        let highlights = self.text_highlights.get(&key)?;
1687        Some((highlights.0, &highlights.1))
1688    }
1689
1690    pub fn all_text_highlights(
1691        &self,
1692    ) -> impl Iterator<Item = (&HighlightKey, &Arc<(HighlightStyle, Vec<Range<Anchor>>)>)> {
1693        self.text_highlights.iter()
1694    }
1695
1696    pub fn all_semantic_token_highlights(
1697        &self,
1698    ) -> impl Iterator<
1699        Item = (
1700            &BufferId,
1701            &(Arc<[SemanticTokenHighlight]>, Arc<HighlightStyleInterner>),
1702        ),
1703    > {
1704        self.semantic_token_highlights.iter()
1705    }
1706
1707    pub fn clear_highlights(&mut self, key: HighlightKey) -> bool {
1708        let mut cleared = self.text_highlights.remove(&key).is_some();
1709        cleared |= self.inlay_highlights.remove(&key).is_some();
1710        cleared
1711    }
1712
1713    pub fn clear_highlights_with(&mut self, mut f: impl FnMut(&HighlightKey) -> bool) -> bool {
1714        let mut cleared = false;
1715        self.text_highlights.retain(|k, _| {
1716            let b = !f(k);
1717            cleared |= b;
1718            b
1719        });
1720        self.inlay_highlights.retain(|k, _| {
1721            let b = !f(k);
1722            cleared |= b;
1723            b
1724        });
1725        cleared
1726    }
1727
1728    pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut Context<Self>) -> bool {
1729        self.wrap_map
1730            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))
1731    }
1732
1733    pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut Context<Self>) -> bool {
1734        self.wrap_map
1735            .update(cx, |map, cx| map.set_wrap_width(width, cx))
1736    }
1737
1738    #[instrument(skip_all)]
1739    pub fn update_fold_widths(
1740        &mut self,
1741        widths: impl IntoIterator<Item = (ChunkRendererId, Pixels)>,
1742        cx: &mut Context<Self>,
1743    ) -> bool {
1744        let snapshot = self.buffer.read(cx).snapshot(cx);
1745        let edits = self.buffer_subscription.consume().into_inner();
1746        let tab_size = Self::tab_size(&self.buffer, cx);
1747
1748        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1749            companion_dm
1750                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1751                .ok()
1752        });
1753
1754        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);
1755        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);
1756        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1757        let (snapshot, edits) = self
1758            .wrap_map
1759            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1760
1761        {
1762            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1763            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1764                |((snapshot, edits), companion)| {
1765                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1766                },
1767            );
1768            self.block_map.read(snapshot, edits, companion_view);
1769        }
1770
1771        let (snapshot, edits) = fold_map.update_fold_widths(widths);
1772        let widths_changed = !edits.is_empty();
1773        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1774        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
1775            .wrap_map
1776            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1777
1778        let (self_wrap_snapshot, self_wrap_edits) =
1779            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
1780
1781        {
1782            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1783            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1784                |((snapshot, edits), companion)| {
1785                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1786                },
1787            );
1788            self.block_map
1789                .read(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
1790        }
1791
1792        if let Some((companion_dm, _)) = &self.companion {
1793            let _ = companion_dm.update(cx, |dm, cx| {
1794                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1795                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1796                    dm.block_map.read(
1797                        companion_snapshot,
1798                        companion_edits,
1799                        their_companion_ref.map(|c| {
1800                            CompanionView::new(
1801                                dm.entity_id,
1802                                &self_wrap_snapshot,
1803                                &self_wrap_edits,
1804                                c,
1805                            )
1806                        }),
1807                    );
1808                }
1809            });
1810        }
1811
1812        widths_changed
1813    }
1814
1815    pub(crate) fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {
1816        self.inlay_map.current_inlays()
1817    }
1818
1819    #[instrument(skip_all)]
1820    pub(crate) fn splice_inlays(
1821        &mut self,
1822        to_remove: &[InlayId],
1823        to_insert: Vec<Inlay>,
1824        cx: &mut Context<Self>,
1825    ) {
1826        if to_remove.is_empty() && to_insert.is_empty() {
1827            return;
1828        }
1829        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);
1830        let edits = self.buffer_subscription.consume().into_inner();
1831        let tab_size = Self::tab_size(&self.buffer, cx);
1832
1833        let companion_wrap_data = self.companion.as_ref().and_then(|(companion_dm, _)| {
1834            companion_dm
1835                .update(cx, |dm, cx| dm.sync_through_wrap(cx))
1836                .ok()
1837        });
1838
1839        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);
1840        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
1841        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1842        let (snapshot, edits) = self
1843            .wrap_map
1844            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1845
1846        {
1847            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1848            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1849                |((snapshot, edits), companion)| {
1850                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1851                },
1852            );
1853            self.block_map.read(snapshot, edits, companion_view);
1854        }
1855
1856        let (snapshot, edits) = self.inlay_map.splice(to_remove, to_insert);
1857        let (snapshot, edits) = self.fold_map.read(snapshot, edits);
1858        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);
1859        let (self_new_wrap_snapshot, self_new_wrap_edits) = self
1860            .wrap_map
1861            .update(cx, |map, cx| map.sync(snapshot, edits, cx));
1862
1863        let (self_wrap_snapshot, self_wrap_edits) =
1864            (self_new_wrap_snapshot.clone(), self_new_wrap_edits.clone());
1865
1866        {
1867            let companion_ref = self.companion.as_ref().map(|(_, c)| c.read(cx));
1868            let companion_view = companion_wrap_data.as_ref().zip(companion_ref).map(
1869                |((snapshot, edits), companion)| {
1870                    CompanionView::new(self.entity_id, snapshot, edits, companion)
1871                },
1872            );
1873            self.block_map
1874                .read(self_new_wrap_snapshot, self_new_wrap_edits, companion_view);
1875        }
1876
1877        if let Some((companion_dm, _)) = &self.companion {
1878            let _ = companion_dm.update(cx, |dm, cx| {
1879                if let Some((companion_snapshot, companion_edits)) = companion_wrap_data {
1880                    let their_companion_ref = dm.companion.as_ref().map(|(_, c)| c.read(cx));
1881                    dm.block_map.read(
1882                        companion_snapshot,
1883                        companion_edits,
1884                        their_companion_ref.map(|c| {
1885                            CompanionView::new(
1886                                dm.entity_id,
1887                                &self_wrap_snapshot,
1888                                &self_wrap_edits,
1889                                c,
1890                            )
1891                        }),
1892                    );
1893                }
1894            });
1895        }
1896    }
1897
1898    #[instrument(skip_all)]
1899    fn tab_size(buffer: &Entity<MultiBuffer>, cx: &App) -> NonZeroU32 {
1900        let buffer = buffer.read(cx).as_singleton().map(|buffer| buffer.read(cx));
1901        let language = buffer
1902            .and_then(|buffer| buffer.language())
1903            .map(|l| l.name());
1904        let file = buffer.and_then(|buffer| buffer.file());
1905        language_settings(language, file, cx).tab_size
1906    }
1907
1908    #[cfg(test)]
1909    pub fn is_rewrapping(&self, cx: &gpui::App) -> bool {
1910        self.wrap_map.read(cx).is_rewrapping()
1911    }
1912
1913    pub fn invalidate_semantic_highlights(&mut self, buffer_id: BufferId) {
1914        self.semantic_token_highlights.remove(&buffer_id);
1915    }
1916}
1917
1918#[derive(Debug, Default)]
1919pub(crate) struct Highlights<'a> {
1920    pub text_highlights: Option<&'a TextHighlights>,
1921    pub inlay_highlights: Option<&'a InlayHighlights>,
1922    pub semantic_token_highlights: Option<&'a SemanticTokensHighlights>,
1923    pub styles: HighlightStyles,
1924}
1925
1926#[derive(Clone, Copy, Debug)]
1927pub struct EditPredictionStyles {
1928    pub insertion: HighlightStyle,
1929    pub whitespace: HighlightStyle,
1930}
1931
1932#[derive(Default, Debug, Clone, Copy)]
1933pub struct HighlightStyles {
1934    pub inlay_hint: Option<HighlightStyle>,
1935    pub edit_prediction: Option<EditPredictionStyles>,
1936}
1937
1938#[derive(Clone)]
1939pub enum ChunkReplacement {
1940    Renderer(ChunkRenderer),
1941    Str(SharedString),
1942}
1943
1944pub struct HighlightedChunk<'a> {
1945    pub text: &'a str,
1946    pub style: Option<HighlightStyle>,
1947    pub is_tab: bool,
1948    pub is_inlay: bool,
1949    pub replacement: Option<ChunkReplacement>,
1950}
1951
1952impl<'a> HighlightedChunk<'a> {
1953    #[instrument(skip_all)]
1954    fn highlight_invisibles(
1955        self,
1956        editor_style: &'a EditorStyle,
1957    ) -> impl Iterator<Item = Self> + 'a {
1958        let mut chars = self.text.chars().peekable();
1959        let mut text = self.text;
1960        let style = self.style;
1961        let is_tab = self.is_tab;
1962        let renderer = self.replacement;
1963        let is_inlay = self.is_inlay;
1964        iter::from_fn(move || {
1965            let mut prefix_len = 0;
1966            while let Some(&ch) = chars.peek() {
1967                if !is_invisible(ch) {
1968                    prefix_len += ch.len_utf8();
1969                    chars.next();
1970                    continue;
1971                }
1972                if prefix_len > 0 {
1973                    let (prefix, suffix) = text.split_at(prefix_len);
1974                    text = suffix;
1975                    return Some(HighlightedChunk {
1976                        text: prefix,
1977                        style,
1978                        is_tab,
1979                        is_inlay,
1980                        replacement: renderer.clone(),
1981                    });
1982                }
1983                chars.next();
1984                let (prefix, suffix) = text.split_at(ch.len_utf8());
1985                text = suffix;
1986                if let Some(replacement) = replacement(ch) {
1987                    let invisible_highlight = HighlightStyle {
1988                        background_color: Some(editor_style.status.hint_background),
1989                        underline: Some(UnderlineStyle {
1990                            color: Some(editor_style.status.hint),
1991                            thickness: px(1.),
1992                            wavy: false,
1993                        }),
1994                        ..Default::default()
1995                    };
1996                    let invisible_style = if let Some(style) = style {
1997                        style.highlight(invisible_highlight)
1998                    } else {
1999                        invisible_highlight
2000                    };
2001                    return Some(HighlightedChunk {
2002                        text: prefix,
2003                        style: Some(invisible_style),
2004                        is_tab: false,
2005                        is_inlay,
2006                        replacement: Some(ChunkReplacement::Str(replacement.into())),
2007                    });
2008                } else {
2009                    let invisible_highlight = HighlightStyle {
2010                        background_color: Some(editor_style.status.hint_background),
2011                        underline: Some(UnderlineStyle {
2012                            color: Some(editor_style.status.hint),
2013                            thickness: px(1.),
2014                            wavy: false,
2015                        }),
2016                        ..Default::default()
2017                    };
2018                    let invisible_style = if let Some(style) = style {
2019                        style.highlight(invisible_highlight)
2020                    } else {
2021                        invisible_highlight
2022                    };
2023
2024                    return Some(HighlightedChunk {
2025                        text: prefix,
2026                        style: Some(invisible_style),
2027                        is_tab: false,
2028                        is_inlay,
2029                        replacement: renderer.clone(),
2030                    });
2031                }
2032            }
2033
2034            if !text.is_empty() {
2035                let remainder = text;
2036                text = "";
2037                Some(HighlightedChunk {
2038                    text: remainder,
2039                    style,
2040                    is_tab,
2041                    is_inlay,
2042                    replacement: renderer.clone(),
2043                })
2044            } else {
2045                None
2046            }
2047        })
2048    }
2049}
2050
2051#[derive(Clone)]
2052pub struct DisplaySnapshot {
2053    pub display_map_id: EntityId,
2054    pub companion_display_snapshot: Option<Arc<DisplaySnapshot>>,
2055    pub crease_snapshot: CreaseSnapshot,
2056    block_snapshot: BlockSnapshot,
2057    text_highlights: TextHighlights,
2058    inlay_highlights: InlayHighlights,
2059    semantic_token_highlights: SemanticTokensHighlights,
2060    clip_at_line_ends: bool,
2061    masked: bool,
2062    diagnostics_max_severity: DiagnosticSeverity,
2063    pub(crate) fold_placeholder: FoldPlaceholder,
2064    /// When true, LSP folding ranges are used via the crease map and the
2065    /// indent-based fallback in `crease_for_buffer_row` is skipped.
2066    pub(crate) use_lsp_folding_ranges: bool,
2067}
2068
2069impl DisplaySnapshot {
2070    pub fn companion_snapshot(&self) -> Option<&DisplaySnapshot> {
2071        self.companion_display_snapshot.as_deref()
2072    }
2073
2074    pub fn wrap_snapshot(&self) -> &WrapSnapshot {
2075        &self.block_snapshot.wrap_snapshot
2076    }
2077    pub fn tab_snapshot(&self) -> &TabSnapshot {
2078        &self.block_snapshot.wrap_snapshot.tab_snapshot
2079    }
2080
2081    pub fn fold_snapshot(&self) -> &FoldSnapshot {
2082        &self.block_snapshot.wrap_snapshot.tab_snapshot.fold_snapshot
2083    }
2084
2085    pub fn inlay_snapshot(&self) -> &InlaySnapshot {
2086        &self
2087            .block_snapshot
2088            .wrap_snapshot
2089            .tab_snapshot
2090            .fold_snapshot
2091            .inlay_snapshot
2092    }
2093
2094    pub fn buffer_snapshot(&self) -> &MultiBufferSnapshot {
2095        &self
2096            .block_snapshot
2097            .wrap_snapshot
2098            .tab_snapshot
2099            .fold_snapshot
2100            .inlay_snapshot
2101            .buffer
2102    }
2103
2104    #[cfg(test)]
2105    pub fn fold_count(&self) -> usize {
2106        self.fold_snapshot().fold_count()
2107    }
2108
2109    pub fn is_empty(&self) -> bool {
2110        self.buffer_snapshot().len() == MultiBufferOffset(0)
2111    }
2112
2113    /// Returns whether tree-sitter syntax highlighting should be used.
2114    /// Returns `false` if any buffer with semantic token highlights has the "full" mode setting,
2115    /// meaning LSP semantic tokens should replace tree-sitter highlighting.
2116    pub fn use_tree_sitter_for_syntax(&self, position: DisplayRow, cx: &App) -> bool {
2117        let position = DisplayPoint::new(position, 0);
2118        let Some((buffer_snapshot, ..)) = self.point_to_buffer_point(position.to_point(self))
2119        else {
2120            return false;
2121        };
2122        let settings = language_settings(
2123            buffer_snapshot.language().map(|l| l.name()),
2124            buffer_snapshot.file(),
2125            cx,
2126        );
2127        settings.semantic_tokens.use_tree_sitter()
2128    }
2129
2130    pub fn row_infos(&self, start_row: DisplayRow) -> impl Iterator<Item = RowInfo> + '_ {
2131        self.block_snapshot.row_infos(BlockRow(start_row.0))
2132    }
2133
2134    pub fn widest_line_number(&self) -> u32 {
2135        self.buffer_snapshot().widest_line_number()
2136    }
2137
2138    #[instrument(skip_all)]
2139    pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {
2140        loop {
2141            let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
2142            let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Left);
2143            fold_point.0.column = 0;
2144            inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
2145            point = self.inlay_snapshot().to_buffer_point(inlay_point);
2146
2147            let mut display_point = self.point_to_display_point(point, Bias::Left);
2148            *display_point.column_mut() = 0;
2149            let next_point = self.display_point_to_point(display_point, Bias::Left);
2150            if next_point == point {
2151                return (point, display_point);
2152            }
2153            point = next_point;
2154        }
2155    }
2156
2157    #[instrument(skip_all)]
2158    pub fn next_line_boundary(
2159        &self,
2160        mut point: MultiBufferPoint,
2161    ) -> (MultiBufferPoint, DisplayPoint) {
2162        let original_point = point;
2163        loop {
2164            let mut inlay_point = self.inlay_snapshot().to_inlay_point(point);
2165            let mut fold_point = self.fold_snapshot().to_fold_point(inlay_point, Bias::Right);
2166            fold_point.0.column = self.fold_snapshot().line_len(fold_point.row());
2167            inlay_point = fold_point.to_inlay_point(self.fold_snapshot());
2168            point = self.inlay_snapshot().to_buffer_point(inlay_point);
2169
2170            let mut display_point = self.point_to_display_point(point, Bias::Right);
2171            *display_point.column_mut() = self.line_len(display_point.row());
2172            let next_point = self.display_point_to_point(display_point, Bias::Right);
2173            if next_point == point || original_point == point || original_point == next_point {
2174                return (point, display_point);
2175            }
2176            point = next_point;
2177        }
2178    }
2179
2180    // used by line_mode selections and tries to match vim behavior
2181    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {
2182        let new_start = MultiBufferPoint::new(range.start.row, 0);
2183        let new_end = if range.end.column > 0 {
2184            MultiBufferPoint::new(
2185                range.end.row,
2186                self.buffer_snapshot()
2187                    .line_len(MultiBufferRow(range.end.row)),
2188            )
2189        } else {
2190            range.end
2191        };
2192
2193        new_start..new_end
2194    }
2195
2196    #[instrument(skip_all)]
2197    pub fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {
2198        let inlay_point = self.inlay_snapshot().to_inlay_point(point);
2199        let fold_point = self.fold_snapshot().to_fold_point(inlay_point, bias);
2200        let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
2201        let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
2202        let block_point = self.block_snapshot.to_block_point(wrap_point);
2203        DisplayPoint(block_point)
2204    }
2205
2206    pub fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {
2207        self.inlay_snapshot()
2208            .to_buffer_point(self.display_point_to_inlay_point(point, bias))
2209    }
2210
2211    pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {
2212        self.inlay_snapshot()
2213            .to_offset(self.display_point_to_inlay_point(point, bias))
2214    }
2215
2216    pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {
2217        self.inlay_snapshot()
2218            .to_inlay_offset(anchor.to_offset(self.buffer_snapshot()))
2219    }
2220
2221    pub fn display_point_to_anchor(&self, point: DisplayPoint, bias: Bias) -> Anchor {
2222        self.buffer_snapshot()
2223            .anchor_at(point.to_offset(self, bias), bias)
2224    }
2225
2226    #[instrument(skip_all)]
2227    fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {
2228        let block_point = point.0;
2229        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
2230        let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
2231        let fold_point = self
2232            .tab_snapshot()
2233            .tab_point_to_fold_point(tab_point, bias)
2234            .0;
2235        fold_point.to_inlay_point(self.fold_snapshot())
2236    }
2237
2238    #[instrument(skip_all)]
2239    pub fn display_point_to_fold_point(&self, point: DisplayPoint, bias: Bias) -> FoldPoint {
2240        let block_point = point.0;
2241        let wrap_point = self.block_snapshot.to_wrap_point(block_point, bias);
2242        let tab_point = self.wrap_snapshot().to_tab_point(wrap_point);
2243        self.tab_snapshot()
2244            .tab_point_to_fold_point(tab_point, bias)
2245            .0
2246    }
2247
2248    #[instrument(skip_all)]
2249    pub fn fold_point_to_display_point(&self, fold_point: FoldPoint) -> DisplayPoint {
2250        let tab_point = self.tab_snapshot().fold_point_to_tab_point(fold_point);
2251        let wrap_point = self.wrap_snapshot().tab_point_to_wrap_point(tab_point);
2252        let block_point = self.block_snapshot.to_block_point(wrap_point);
2253        DisplayPoint(block_point)
2254    }
2255
2256    pub fn max_point(&self) -> DisplayPoint {
2257        DisplayPoint(self.block_snapshot.max_point())
2258    }
2259
2260    /// Returns text chunks starting at the given display row until the end of the file
2261    #[instrument(skip_all)]
2262    pub fn text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
2263        self.block_snapshot
2264            .chunks(
2265                BlockRow(display_row.0)..BlockRow(self.max_point().row().next_row().0),
2266                false,
2267                self.masked,
2268                Highlights::default(),
2269            )
2270            .map(|h| h.text)
2271    }
2272
2273    /// Returns text chunks starting at the end of the given display row in reverse until the start of the file
2274    #[instrument(skip_all)]
2275    pub fn reverse_text_chunks(&self, display_row: DisplayRow) -> impl Iterator<Item = &str> {
2276        (0..=display_row.0).rev().flat_map(move |row| {
2277            self.block_snapshot
2278                .chunks(
2279                    BlockRow(row)..BlockRow(row + 1),
2280                    false,
2281                    self.masked,
2282                    Highlights::default(),
2283                )
2284                .map(|h| h.text)
2285                .collect::<Vec<_>>()
2286                .into_iter()
2287                .rev()
2288        })
2289    }
2290
2291    #[instrument(skip_all)]
2292    pub fn chunks(
2293        &self,
2294        display_rows: Range<DisplayRow>,
2295        language_aware: bool,
2296        highlight_styles: HighlightStyles,
2297    ) -> DisplayChunks<'_> {
2298        self.block_snapshot.chunks(
2299            BlockRow(display_rows.start.0)..BlockRow(display_rows.end.0),
2300            language_aware,
2301            self.masked,
2302            Highlights {
2303                text_highlights: Some(&self.text_highlights),
2304                inlay_highlights: Some(&self.inlay_highlights),
2305                semantic_token_highlights: Some(&self.semantic_token_highlights),
2306                styles: highlight_styles,
2307            },
2308        )
2309    }
2310
2311    #[instrument(skip_all)]
2312    pub fn highlighted_chunks<'a>(
2313        &'a self,
2314        display_rows: Range<DisplayRow>,
2315        language_aware: bool,
2316        editor_style: &'a EditorStyle,
2317    ) -> impl Iterator<Item = HighlightedChunk<'a>> {
2318        self.chunks(
2319            display_rows,
2320            language_aware,
2321            HighlightStyles {
2322                inlay_hint: Some(editor_style.inlay_hints_style),
2323                edit_prediction: Some(editor_style.edit_prediction_styles),
2324            },
2325        )
2326        .flat_map(|chunk| {
2327            let syntax_highlight_style = chunk
2328                .syntax_highlight_id
2329                .and_then(|id| id.style(&editor_style.syntax));
2330
2331            let chunk_highlight = chunk.highlight_style.map(|chunk_highlight| {
2332                HighlightStyle {
2333                    // For color inlays, blend the color with the editor background
2334                    // if the color has transparency (alpha < 1.0)
2335                    color: chunk_highlight.color.map(|color| {
2336                        if chunk.is_inlay && !color.is_opaque() {
2337                            editor_style.background.blend(color)
2338                        } else {
2339                            color
2340                        }
2341                    }),
2342                    ..chunk_highlight
2343                }
2344            });
2345
2346            let diagnostic_highlight = chunk
2347                .diagnostic_severity
2348                .filter(|severity| {
2349                    self.diagnostics_max_severity
2350                        .into_lsp()
2351                        .is_some_and(|max_severity| severity <= &max_severity)
2352                })
2353                .map(|severity| HighlightStyle {
2354                    fade_out: chunk
2355                        .is_unnecessary
2356                        .then_some(editor_style.unnecessary_code_fade),
2357                    underline: (chunk.underline
2358                        && editor_style.show_underlines
2359                        && !(chunk.is_unnecessary && severity > lsp::DiagnosticSeverity::WARNING))
2360                        .then(|| {
2361                            let diagnostic_color =
2362                                super::diagnostic_style(severity, &editor_style.status);
2363                            UnderlineStyle {
2364                                color: Some(diagnostic_color),
2365                                thickness: 1.0.into(),
2366                                wavy: true,
2367                            }
2368                        }),
2369                    ..Default::default()
2370                });
2371
2372            let style = [
2373                syntax_highlight_style,
2374                chunk_highlight,
2375                diagnostic_highlight,
2376            ]
2377            .into_iter()
2378            .flatten()
2379            .reduce(|acc, highlight| acc.highlight(highlight));
2380
2381            HighlightedChunk {
2382                text: chunk.text,
2383                style,
2384                is_tab: chunk.is_tab,
2385                is_inlay: chunk.is_inlay,
2386                replacement: chunk.renderer.map(ChunkReplacement::Renderer),
2387            }
2388            .highlight_invisibles(editor_style)
2389        })
2390    }
2391
2392    #[instrument(skip_all)]
2393    pub fn layout_row(
2394        &self,
2395        display_row: DisplayRow,
2396        TextLayoutDetails {
2397            text_system,
2398            editor_style,
2399            rem_size,
2400            scroll_anchor: _,
2401            visible_rows: _,
2402            vertical_scroll_margin: _,
2403        }: &TextLayoutDetails,
2404    ) -> Arc<LineLayout> {
2405        let mut runs = Vec::new();
2406        let mut line = String::new();
2407
2408        let range = display_row..display_row.next_row();
2409        for chunk in self.highlighted_chunks(range, false, editor_style) {
2410            line.push_str(chunk.text);
2411
2412            let text_style = if let Some(style) = chunk.style {
2413                Cow::Owned(editor_style.text.clone().highlight(style))
2414            } else {
2415                Cow::Borrowed(&editor_style.text)
2416            };
2417
2418            runs.push(text_style.to_run(chunk.text.len()))
2419        }
2420
2421        if line.ends_with('\n') {
2422            line.pop();
2423            if let Some(last_run) = runs.last_mut() {
2424                last_run.len -= 1;
2425                if last_run.len == 0 {
2426                    runs.pop();
2427                }
2428            }
2429        }
2430
2431        let font_size = editor_style.text.font_size.to_pixels(*rem_size);
2432        text_system.layout_line(&line, font_size, &runs, None)
2433    }
2434
2435    pub fn x_for_display_point(
2436        &self,
2437        display_point: DisplayPoint,
2438        text_layout_details: &TextLayoutDetails,
2439    ) -> Pixels {
2440        let line = self.layout_row(display_point.row(), text_layout_details);
2441        line.x_for_index(display_point.column() as usize)
2442    }
2443
2444    pub fn display_column_for_x(
2445        &self,
2446        display_row: DisplayRow,
2447        x: Pixels,
2448        details: &TextLayoutDetails,
2449    ) -> u32 {
2450        let layout_line = self.layout_row(display_row, details);
2451        layout_line.closest_index_for_x(x) as u32
2452    }
2453
2454    #[instrument(skip_all)]
2455    pub fn grapheme_at(&self, mut point: DisplayPoint) -> Option<SharedString> {
2456        point = DisplayPoint(self.block_snapshot.clip_point(point.0, Bias::Left));
2457        let chars = self
2458            .text_chunks(point.row())
2459            .flat_map(str::chars)
2460            .skip_while({
2461                let mut column = 0;
2462                move |char| {
2463                    let at_point = column >= point.column();
2464                    column += char.len_utf8() as u32;
2465                    !at_point
2466                }
2467            })
2468            .take_while({
2469                let mut prev = false;
2470                move |char| {
2471                    let now = char.is_ascii();
2472                    let end = char.is_ascii() && (char.is_ascii_whitespace() || prev);
2473                    prev = now;
2474                    !end
2475                }
2476            });
2477        chars.collect::<String>().graphemes(true).next().map(|s| {
2478            if let Some(invisible) = s.chars().next().filter(|&c| is_invisible(c)) {
2479                replacement(invisible).unwrap_or(s).to_owned().into()
2480            } else if s == "\n" {
2481                " ".into()
2482            } else {
2483                s.to_owned().into()
2484            }
2485        })
2486    }
2487
2488    pub fn buffer_chars_at(
2489        &self,
2490        mut offset: MultiBufferOffset,
2491    ) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
2492        self.buffer_snapshot().chars_at(offset).map(move |ch| {
2493            let ret = (ch, offset);
2494            offset += ch.len_utf8();
2495            ret
2496        })
2497    }
2498
2499    pub fn reverse_buffer_chars_at(
2500        &self,
2501        mut offset: MultiBufferOffset,
2502    ) -> impl Iterator<Item = (char, MultiBufferOffset)> + '_ {
2503        self.buffer_snapshot()
2504            .reversed_chars_at(offset)
2505            .map(move |ch| {
2506                offset -= ch.len_utf8();
2507                (ch, offset)
2508            })
2509    }
2510
2511    pub fn clip_point(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
2512        let mut clipped = self.block_snapshot.clip_point(point.0, bias);
2513        if self.clip_at_line_ends {
2514            clipped = self.clip_at_line_end(DisplayPoint(clipped)).0
2515        }
2516        DisplayPoint(clipped)
2517    }
2518
2519    pub fn clip_ignoring_line_ends(&self, point: DisplayPoint, bias: Bias) -> DisplayPoint {
2520        DisplayPoint(self.block_snapshot.clip_point(point.0, bias))
2521    }
2522
2523    pub fn clip_at_line_end(&self, display_point: DisplayPoint) -> DisplayPoint {
2524        let mut point = self.display_point_to_point(display_point, Bias::Left);
2525
2526        if point.column != self.buffer_snapshot().line_len(MultiBufferRow(point.row)) {
2527            return display_point;
2528        }
2529        point.column = point.column.saturating_sub(1);
2530        point = self.buffer_snapshot().clip_point(point, Bias::Left);
2531        self.point_to_display_point(point, Bias::Left)
2532    }
2533
2534    pub fn folds_in_range<T>(&self, range: Range<T>) -> impl Iterator<Item = &Fold>
2535    where
2536        T: ToOffset,
2537    {
2538        self.fold_snapshot().folds_in_range(range)
2539    }
2540
2541    pub fn blocks_in_range(
2542        &self,
2543        rows: Range<DisplayRow>,
2544    ) -> impl Iterator<Item = (DisplayRow, &Block)> {
2545        self.block_snapshot
2546            .blocks_in_range(BlockRow(rows.start.0)..BlockRow(rows.end.0))
2547            .map(|(row, block)| (DisplayRow(row.0), block))
2548    }
2549
2550    pub fn sticky_header_excerpt(&self, row: f64) -> Option<StickyHeaderExcerpt<'_>> {
2551        self.block_snapshot.sticky_header_excerpt(row)
2552    }
2553
2554    pub fn block_for_id(&self, id: BlockId) -> Option<Block> {
2555        self.block_snapshot.block_for_id(id)
2556    }
2557
2558    pub fn intersects_fold<T: ToOffset>(&self, offset: T) -> bool {
2559        self.fold_snapshot().intersects_fold(offset)
2560    }
2561
2562    pub fn is_line_folded(&self, buffer_row: MultiBufferRow) -> bool {
2563        self.block_snapshot.is_line_replaced(buffer_row)
2564            || self.fold_snapshot().is_line_folded(buffer_row)
2565    }
2566
2567    pub fn is_block_line(&self, display_row: DisplayRow) -> bool {
2568        self.block_snapshot.is_block_line(BlockRow(display_row.0))
2569    }
2570
2571    pub fn is_folded_buffer_header(&self, display_row: DisplayRow) -> bool {
2572        self.block_snapshot
2573            .is_folded_buffer_header(BlockRow(display_row.0))
2574    }
2575
2576    pub fn soft_wrap_indent(&self, display_row: DisplayRow) -> Option<u32> {
2577        let wrap_row = self
2578            .block_snapshot
2579            .to_wrap_point(BlockPoint::new(BlockRow(display_row.0), 0), Bias::Left)
2580            .row();
2581        self.wrap_snapshot().soft_wrap_indent(wrap_row)
2582    }
2583
2584    pub fn text(&self) -> String {
2585        self.text_chunks(DisplayRow(0)).collect()
2586    }
2587
2588    pub fn line(&self, display_row: DisplayRow) -> String {
2589        let mut result = String::new();
2590        for chunk in self.text_chunks(display_row) {
2591            if let Some(ix) = chunk.find('\n') {
2592                result.push_str(&chunk[0..ix]);
2593                break;
2594            } else {
2595                result.push_str(chunk);
2596            }
2597        }
2598        result
2599    }
2600
2601    pub fn line_indent_for_buffer_row(&self, buffer_row: MultiBufferRow) -> LineIndent {
2602        self.buffer_snapshot().line_indent_for_row(buffer_row)
2603    }
2604
2605    pub fn line_len(&self, row: DisplayRow) -> u32 {
2606        self.block_snapshot.line_len(BlockRow(row.0))
2607    }
2608
2609    pub fn longest_row(&self) -> DisplayRow {
2610        DisplayRow(self.block_snapshot.longest_row().0)
2611    }
2612
2613    pub fn longest_row_in_range(&self, range: Range<DisplayRow>) -> DisplayRow {
2614        let block_range = BlockRow(range.start.0)..BlockRow(range.end.0);
2615        let longest_row = self.block_snapshot.longest_row_in_range(block_range);
2616        DisplayRow(longest_row.0)
2617    }
2618
2619    pub fn starts_indent(&self, buffer_row: MultiBufferRow) -> bool {
2620        let max_row = self.buffer_snapshot().max_row();
2621        if buffer_row >= max_row {
2622            return false;
2623        }
2624
2625        let line_indent = self.line_indent_for_buffer_row(buffer_row);
2626        if line_indent.is_line_blank() {
2627            return false;
2628        }
2629
2630        (buffer_row.0 + 1..=max_row.0)
2631            .find_map(|next_row| {
2632                let next_line_indent = self.line_indent_for_buffer_row(MultiBufferRow(next_row));
2633                if next_line_indent.raw_len() > line_indent.raw_len() {
2634                    Some(true)
2635                } else if !next_line_indent.is_line_blank() {
2636                    Some(false)
2637                } else {
2638                    None
2639                }
2640            })
2641            .unwrap_or(false)
2642    }
2643
2644    #[instrument(skip_all)]
2645    pub fn crease_for_buffer_row(&self, buffer_row: MultiBufferRow) -> Option<Crease<Point>> {
2646        let start =
2647            MultiBufferPoint::new(buffer_row.0, self.buffer_snapshot().line_len(buffer_row));
2648        if let Some(crease) = self
2649            .crease_snapshot
2650            .query_row(buffer_row, self.buffer_snapshot())
2651        {
2652            match crease {
2653                Crease::Inline {
2654                    range,
2655                    placeholder,
2656                    render_toggle,
2657                    render_trailer,
2658                    metadata,
2659                } => Some(Crease::Inline {
2660                    range: range.to_point(self.buffer_snapshot()),
2661                    placeholder: placeholder.clone(),
2662                    render_toggle: render_toggle.clone(),
2663                    render_trailer: render_trailer.clone(),
2664                    metadata: metadata.clone(),
2665                }),
2666                Crease::Block {
2667                    range,
2668                    block_height,
2669                    block_style,
2670                    render_block,
2671                    block_priority,
2672                    render_toggle,
2673                } => Some(Crease::Block {
2674                    range: range.to_point(self.buffer_snapshot()),
2675                    block_height: *block_height,
2676                    block_style: *block_style,
2677                    render_block: render_block.clone(),
2678                    block_priority: *block_priority,
2679                    render_toggle: render_toggle.clone(),
2680                }),
2681            }
2682        } else if !self.use_lsp_folding_ranges
2683            && self.starts_indent(MultiBufferRow(start.row))
2684            && !self.is_line_folded(MultiBufferRow(start.row))
2685        {
2686            let start_line_indent = self.line_indent_for_buffer_row(buffer_row);
2687            let max_point = self.buffer_snapshot().max_point();
2688            let mut end = None;
2689
2690            for row in (buffer_row.0 + 1)..=max_point.row {
2691                let line_indent = self.line_indent_for_buffer_row(MultiBufferRow(row));
2692                if !line_indent.is_line_blank()
2693                    && line_indent.raw_len() <= start_line_indent.raw_len()
2694                {
2695                    let prev_row = row - 1;
2696                    end = Some(Point::new(
2697                        prev_row,
2698                        self.buffer_snapshot().line_len(MultiBufferRow(prev_row)),
2699                    ));
2700                    break;
2701                }
2702            }
2703
2704            let mut row_before_line_breaks = end.unwrap_or(max_point);
2705            while row_before_line_breaks.row > start.row
2706                && self
2707                    .buffer_snapshot()
2708                    .is_line_blank(MultiBufferRow(row_before_line_breaks.row))
2709            {
2710                row_before_line_breaks.row -= 1;
2711            }
2712
2713            row_before_line_breaks = Point::new(
2714                row_before_line_breaks.row,
2715                self.buffer_snapshot()
2716                    .line_len(MultiBufferRow(row_before_line_breaks.row)),
2717            );
2718
2719            Some(Crease::Inline {
2720                range: start..row_before_line_breaks,
2721                placeholder: self.fold_placeholder.clone(),
2722                render_toggle: None,
2723                render_trailer: None,
2724                metadata: None,
2725            })
2726        } else {
2727            None
2728        }
2729    }
2730
2731    #[cfg(any(test, feature = "test-support"))]
2732    #[instrument(skip_all)]
2733    pub fn text_highlight_ranges(
2734        &self,
2735        key: HighlightKey,
2736    ) -> Option<Arc<(HighlightStyle, Vec<Range<Anchor>>)>> {
2737        self.text_highlights.get(&key).cloned()
2738    }
2739
2740    #[cfg(any(test, feature = "test-support"))]
2741    #[instrument(skip_all)]
2742    pub fn all_text_highlight_ranges(
2743        &self,
2744        f: impl Fn(&HighlightKey) -> bool,
2745    ) -> Vec<(gpui::Hsla, Range<Point>)> {
2746        use itertools::Itertools;
2747
2748        self.text_highlights
2749            .iter()
2750            .filter(|(key, _)| f(key))
2751            .map(|(_, value)| value.clone())
2752            .flat_map(|ranges| {
2753                ranges
2754                    .1
2755                    .iter()
2756                    .flat_map(|range| {
2757                        Some((ranges.0.color?, range.to_point(self.buffer_snapshot())))
2758                    })
2759                    .collect::<Vec<_>>()
2760            })
2761            .sorted_by_key(|(_, range)| range.start)
2762            .collect()
2763    }
2764
2765    #[allow(unused)]
2766    #[cfg(any(test, feature = "test-support"))]
2767    pub(crate) fn inlay_highlights(
2768        &self,
2769        key: HighlightKey,
2770    ) -> Option<&TreeMap<InlayId, (HighlightStyle, InlayHighlight)>> {
2771        self.inlay_highlights.get(&key)
2772    }
2773
2774    pub fn buffer_header_height(&self) -> u32 {
2775        self.block_snapshot.buffer_header_height
2776    }
2777
2778    pub fn excerpt_header_height(&self) -> u32 {
2779        self.block_snapshot.excerpt_header_height
2780    }
2781
2782    /// Given a `DisplayPoint`, returns another `DisplayPoint` corresponding to
2783    /// the start of the buffer row that is a given number of buffer rows away
2784    /// from the provided point.
2785    ///
2786    /// This moves by buffer rows instead of display rows, a distinction that is
2787    /// important when soft wrapping is enabled.
2788    #[instrument(skip_all)]
2789    pub fn start_of_relative_buffer_row(&self, point: DisplayPoint, times: isize) -> DisplayPoint {
2790        let start = self.display_point_to_fold_point(point, Bias::Left);
2791        let target = start.row() as isize + times;
2792        let new_row = (target.max(0) as u32).min(self.fold_snapshot().max_point().row());
2793
2794        self.clip_point(
2795            self.fold_point_to_display_point(
2796                self.fold_snapshot()
2797                    .clip_point(FoldPoint::new(new_row, 0), Bias::Right),
2798            ),
2799            Bias::Right,
2800        )
2801    }
2802}
2803
2804impl std::ops::Deref for DisplaySnapshot {
2805    type Target = BlockSnapshot;
2806
2807    fn deref(&self) -> &Self::Target {
2808        &self.block_snapshot
2809    }
2810}
2811
2812/// A zero-indexed point in a text buffer consisting of a row and column adjusted for inserted blocks.
2813#[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)]
2814pub struct DisplayPoint(BlockPoint);
2815
2816impl Debug for DisplayPoint {
2817    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2818        f.write_fmt(format_args!(
2819            "DisplayPoint({}, {})",
2820            self.row().0,
2821            self.column()
2822        ))
2823    }
2824}
2825
2826impl Add for DisplayPoint {
2827    type Output = Self;
2828
2829    fn add(self, other: Self) -> Self::Output {
2830        DisplayPoint(BlockPoint(self.0.0 + other.0.0))
2831    }
2832}
2833
2834impl Sub for DisplayPoint {
2835    type Output = Self;
2836
2837    fn sub(self, other: Self) -> Self::Output {
2838        DisplayPoint(BlockPoint(self.0.0 - other.0.0))
2839    }
2840}
2841
2842#[derive(Debug, Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Deserialize, Hash)]
2843#[serde(transparent)]
2844pub struct DisplayRow(pub u32);
2845
2846impl DisplayRow {
2847    pub(crate) fn as_display_point(&self) -> DisplayPoint {
2848        DisplayPoint::new(*self, 0)
2849    }
2850}
2851
2852impl Add<DisplayRow> for DisplayRow {
2853    type Output = Self;
2854
2855    fn add(self, other: Self) -> Self::Output {
2856        DisplayRow(self.0 + other.0)
2857    }
2858}
2859
2860impl Add<u32> for DisplayRow {
2861    type Output = Self;
2862
2863    fn add(self, other: u32) -> Self::Output {
2864        DisplayRow(self.0 + other)
2865    }
2866}
2867
2868impl Sub<DisplayRow> for DisplayRow {
2869    type Output = Self;
2870
2871    fn sub(self, other: Self) -> Self::Output {
2872        DisplayRow(self.0 - other.0)
2873    }
2874}
2875
2876impl Sub<u32> for DisplayRow {
2877    type Output = Self;
2878
2879    fn sub(self, other: u32) -> Self::Output {
2880        DisplayRow(self.0 - other)
2881    }
2882}
2883
2884impl DisplayPoint {
2885    pub fn new(row: DisplayRow, column: u32) -> Self {
2886        Self(BlockPoint(Point::new(row.0, column)))
2887    }
2888
2889    pub fn zero() -> Self {
2890        Self::new(DisplayRow(0), 0)
2891    }
2892
2893    pub fn is_zero(&self) -> bool {
2894        self.0.is_zero()
2895    }
2896
2897    pub fn row(self) -> DisplayRow {
2898        DisplayRow(self.0.row)
2899    }
2900
2901    pub fn column(self) -> u32 {
2902        self.0.column
2903    }
2904
2905    pub fn row_mut(&mut self) -> &mut u32 {
2906        &mut self.0.row
2907    }
2908
2909    pub fn column_mut(&mut self) -> &mut u32 {
2910        &mut self.0.column
2911    }
2912
2913    pub fn to_point(self, map: &DisplaySnapshot) -> Point {
2914        map.display_point_to_point(self, Bias::Left)
2915    }
2916
2917    pub fn to_offset(self, map: &DisplaySnapshot, bias: Bias) -> MultiBufferOffset {
2918        let wrap_point = map.block_snapshot.to_wrap_point(self.0, bias);
2919        let tab_point = map.wrap_snapshot().to_tab_point(wrap_point);
2920        let fold_point = map
2921            .tab_snapshot()
2922            .tab_point_to_fold_point(tab_point, bias)
2923            .0;
2924        let inlay_point = fold_point.to_inlay_point(map.fold_snapshot());
2925        map.inlay_snapshot()
2926            .to_buffer_offset(map.inlay_snapshot().to_offset(inlay_point))
2927    }
2928}
2929
2930impl ToDisplayPoint for MultiBufferOffset {
2931    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2932        map.point_to_display_point(self.to_point(map.buffer_snapshot()), Bias::Left)
2933    }
2934}
2935
2936impl ToDisplayPoint for MultiBufferOffsetUtf16 {
2937    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2938        self.to_offset(map.buffer_snapshot()).to_display_point(map)
2939    }
2940}
2941
2942impl ToDisplayPoint for Point {
2943    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2944        map.point_to_display_point(*self, Bias::Left)
2945    }
2946}
2947
2948impl ToDisplayPoint for Anchor {
2949    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint {
2950        self.to_point(map.buffer_snapshot()).to_display_point(map)
2951    }
2952}
2953
2954#[cfg(test)]
2955pub mod tests {
2956    use super::*;
2957    use crate::{
2958        movement,
2959        test::{marked_display_snapshot, test_font},
2960    };
2961    use Bias::*;
2962    use block_map::BlockPlacement;
2963    use gpui::{
2964        App, AppContext as _, BorrowAppContext, Element, Hsla, Rgba, div, font, observe, px,
2965    };
2966    use language::{
2967        Buffer, Diagnostic, DiagnosticEntry, DiagnosticSet, Language, LanguageConfig,
2968        LanguageMatcher,
2969    };
2970    use lsp::LanguageServerId;
2971
2972    use rand::{Rng, prelude::*};
2973    use settings::{SettingsContent, SettingsStore};
2974    use smol::stream::StreamExt;
2975    use std::{env, sync::Arc};
2976    use text::PointUtf16;
2977    use theme::{LoadThemes, SyntaxTheme};
2978    use unindent::Unindent as _;
2979    use util::test::{marked_text_ranges, sample_text};
2980
2981    #[gpui::test(iterations = 100)]
2982    async fn test_random_display_map(cx: &mut gpui::TestAppContext, mut rng: StdRng) {
2983        cx.background_executor.set_block_on_ticks(0..=50);
2984        let operations = env::var("OPERATIONS")
2985            .map(|i| i.parse().expect("invalid `OPERATIONS` variable"))
2986            .unwrap_or(10);
2987
2988        let mut tab_size = rng.random_range(1..=4);
2989        let buffer_start_excerpt_header_height = rng.random_range(1..=5);
2990        let excerpt_header_height = rng.random_range(1..=5);
2991        let font_size = px(14.0);
2992        let max_wrap_width = 300.0;
2993        let mut wrap_width = if rng.random_bool(0.1) {
2994            None
2995        } else {
2996            Some(px(rng.random_range(0.0..=max_wrap_width)))
2997        };
2998
2999        log::info!("tab size: {}", tab_size);
3000        log::info!("wrap width: {:?}", wrap_width);
3001
3002        cx.update(|cx| {
3003            init_test(cx, |s| {
3004                s.project.all_languages.defaults.tab_size = NonZeroU32::new(tab_size)
3005            });
3006        });
3007
3008        let buffer = cx.update(|cx| {
3009            if rng.random() {
3010                let len = rng.random_range(0..10);
3011                let text = util::RandomCharIter::new(&mut rng)
3012                    .take(len)
3013                    .collect::<String>();
3014                MultiBuffer::build_simple(&text, cx)
3015            } else {
3016                MultiBuffer::build_random(&mut rng, cx)
3017            }
3018        });
3019
3020        let font = test_font();
3021        let map = cx.new(|cx| {
3022            DisplayMap::new(
3023                buffer.clone(),
3024                font,
3025                font_size,
3026                wrap_width,
3027                buffer_start_excerpt_header_height,
3028                excerpt_header_height,
3029                FoldPlaceholder::test(),
3030                DiagnosticSeverity::Warning,
3031                cx,
3032            )
3033        });
3034        let mut notifications = observe(&map, cx);
3035        let mut fold_count = 0;
3036        let mut blocks = Vec::new();
3037
3038        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3039        log::info!("buffer text: {:?}", snapshot.buffer_snapshot().text());
3040        log::info!("fold text: {:?}", snapshot.fold_snapshot().text());
3041        log::info!("tab text: {:?}", snapshot.tab_snapshot().text());
3042        log::info!("wrap text: {:?}", snapshot.wrap_snapshot().text());
3043        log::info!("block text: {:?}", snapshot.block_snapshot.text());
3044        log::info!("display text: {:?}", snapshot.text());
3045
3046        for _i in 0..operations {
3047            match rng.random_range(0..100) {
3048                0..=19 => {
3049                    wrap_width = if rng.random_bool(0.2) {
3050                        None
3051                    } else {
3052                        Some(px(rng.random_range(0.0..=max_wrap_width)))
3053                    };
3054                    log::info!("setting wrap width to {:?}", wrap_width);
3055                    map.update(cx, |map, cx| map.set_wrap_width(wrap_width, cx));
3056                }
3057                20..=29 => {
3058                    let mut tab_sizes = vec![1, 2, 3, 4];
3059                    tab_sizes.remove((tab_size - 1) as usize);
3060                    tab_size = *tab_sizes.choose(&mut rng).unwrap();
3061                    log::info!("setting tab size to {:?}", tab_size);
3062                    cx.update(|cx| {
3063                        cx.update_global::<SettingsStore, _>(|store, cx| {
3064                            store.update_user_settings(cx, |s| {
3065                                s.project.all_languages.defaults.tab_size =
3066                                    NonZeroU32::new(tab_size);
3067                            });
3068                        });
3069                    });
3070                }
3071                30..=44 => {
3072                    map.update(cx, |map, cx| {
3073                        if rng.random() || blocks.is_empty() {
3074                            let snapshot = map.snapshot(cx);
3075                            let buffer = snapshot.buffer_snapshot();
3076                            let block_properties = (0..rng.random_range(1..=1))
3077                                .map(|_| {
3078                                    let position = buffer.anchor_after(buffer.clip_offset(
3079                                        rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3080                                        Bias::Left,
3081                                    ));
3082
3083                                    let placement = if rng.random() {
3084                                        BlockPlacement::Above(position)
3085                                    } else {
3086                                        BlockPlacement::Below(position)
3087                                    };
3088                                    let height = rng.random_range(1..5);
3089                                    log::info!(
3090                                        "inserting block {:?} with height {}",
3091                                        placement.as_ref().map(|p| p.to_point(&buffer)),
3092                                        height
3093                                    );
3094                                    let priority = rng.random_range(1..100);
3095                                    BlockProperties {
3096                                        placement,
3097                                        style: BlockStyle::Fixed,
3098                                        height: Some(height),
3099                                        render: Arc::new(|_| div().into_any()),
3100                                        priority,
3101                                    }
3102                                })
3103                                .collect::<Vec<_>>();
3104                            blocks.extend(map.insert_blocks(block_properties, cx));
3105                        } else {
3106                            blocks.shuffle(&mut rng);
3107                            let remove_count = rng.random_range(1..=4.min(blocks.len()));
3108                            let block_ids_to_remove = (0..remove_count)
3109                                .map(|_| blocks.remove(rng.random_range(0..blocks.len())))
3110                                .collect();
3111                            log::info!("removing block ids {:?}", block_ids_to_remove);
3112                            map.remove_blocks(block_ids_to_remove, cx);
3113                        }
3114                    });
3115                }
3116                45..=79 => {
3117                    let mut ranges = Vec::new();
3118                    for _ in 0..rng.random_range(1..=3) {
3119                        buffer.read_with(cx, |buffer, cx| {
3120                            let buffer = buffer.read(cx);
3121                            let end = buffer.clip_offset(
3122                                rng.random_range(MultiBufferOffset(0)..=buffer.len()),
3123                                Right,
3124                            );
3125                            let start = buffer
3126                                .clip_offset(rng.random_range(MultiBufferOffset(0)..=end), Left);
3127                            ranges.push(start..end);
3128                        });
3129                    }
3130
3131                    if rng.random() && fold_count > 0 {
3132                        log::info!("unfolding ranges: {:?}", ranges);
3133                        map.update(cx, |map, cx| {
3134                            map.unfold_intersecting(ranges, true, cx);
3135                        });
3136                    } else {
3137                        log::info!("folding ranges: {:?}", ranges);
3138                        map.update(cx, |map, cx| {
3139                            map.fold(
3140                                ranges
3141                                    .into_iter()
3142                                    .map(|range| Crease::simple(range, FoldPlaceholder::test()))
3143                                    .collect(),
3144                                cx,
3145                            );
3146                        });
3147                    }
3148                }
3149                _ => {
3150                    buffer.update(cx, |buffer, cx| buffer.randomly_mutate(&mut rng, 5, cx));
3151                }
3152            }
3153
3154            if map.read_with(cx, |map, cx| map.is_rewrapping(cx)) {
3155                notifications.next().await.unwrap();
3156            }
3157
3158            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3159            fold_count = snapshot.fold_count();
3160            log::info!("buffer text: {:?}", snapshot.buffer_snapshot().text());
3161            log::info!("fold text: {:?}", snapshot.fold_snapshot().text());
3162            log::info!("tab text: {:?}", snapshot.tab_snapshot().text());
3163            log::info!("wrap text: {:?}", snapshot.wrap_snapshot().text());
3164            log::info!("block text: {:?}", snapshot.block_snapshot.text());
3165            log::info!("display text: {:?}", snapshot.text());
3166
3167            // Line boundaries
3168            let buffer = snapshot.buffer_snapshot();
3169            for _ in 0..5 {
3170                let row = rng.random_range(0..=buffer.max_point().row);
3171                let column = rng.random_range(0..=buffer.line_len(MultiBufferRow(row)));
3172                let point = buffer.clip_point(Point::new(row, column), Left);
3173
3174                let (prev_buffer_bound, prev_display_bound) = snapshot.prev_line_boundary(point);
3175                let (next_buffer_bound, next_display_bound) = snapshot.next_line_boundary(point);
3176
3177                assert!(prev_buffer_bound <= point);
3178                assert!(next_buffer_bound >= point);
3179                assert_eq!(prev_buffer_bound.column, 0);
3180                assert_eq!(prev_display_bound.column(), 0);
3181                if next_buffer_bound < buffer.max_point() {
3182                    assert_eq!(buffer.chars_at(next_buffer_bound).next(), Some('\n'));
3183                }
3184
3185                assert_eq!(
3186                    prev_display_bound,
3187                    prev_buffer_bound.to_display_point(&snapshot),
3188                    "row boundary before {:?}. reported buffer row boundary: {:?}",
3189                    point,
3190                    prev_buffer_bound
3191                );
3192                assert_eq!(
3193                    next_display_bound,
3194                    next_buffer_bound.to_display_point(&snapshot),
3195                    "display row boundary after {:?}. reported buffer row boundary: {:?}",
3196                    point,
3197                    next_buffer_bound
3198                );
3199                assert_eq!(
3200                    prev_buffer_bound,
3201                    prev_display_bound.to_point(&snapshot),
3202                    "row boundary before {:?}. reported display row boundary: {:?}",
3203                    point,
3204                    prev_display_bound
3205                );
3206                assert_eq!(
3207                    next_buffer_bound,
3208                    next_display_bound.to_point(&snapshot),
3209                    "row boundary after {:?}. reported display row boundary: {:?}",
3210                    point,
3211                    next_display_bound
3212                );
3213            }
3214
3215            // Movement
3216            let min_point = snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 0), Left);
3217            let max_point = snapshot.clip_point(snapshot.max_point(), Right);
3218            for _ in 0..5 {
3219                let row = rng.random_range(0..=snapshot.max_point().row().0);
3220                let column = rng.random_range(0..=snapshot.line_len(DisplayRow(row)));
3221                let point = snapshot.clip_point(DisplayPoint::new(DisplayRow(row), column), Left);
3222
3223                log::info!("Moving from point {:?}", point);
3224
3225                let moved_right = movement::right(&snapshot, point);
3226                log::info!("Right {:?}", moved_right);
3227                if point < max_point {
3228                    assert!(moved_right > point);
3229                    if point.column() == snapshot.line_len(point.row())
3230                        || snapshot.soft_wrap_indent(point.row()).is_some()
3231                            && point.column() == snapshot.line_len(point.row()) - 1
3232                    {
3233                        assert!(moved_right.row() > point.row());
3234                    }
3235                } else {
3236                    assert_eq!(moved_right, point);
3237                }
3238
3239                let moved_left = movement::left(&snapshot, point);
3240                log::info!("Left {:?}", moved_left);
3241                if point > min_point {
3242                    assert!(moved_left < point);
3243                    if point.column() == 0 {
3244                        assert!(moved_left.row() < point.row());
3245                    }
3246                } else {
3247                    assert_eq!(moved_left, point);
3248                }
3249            }
3250        }
3251    }
3252
3253    #[gpui::test(retries = 5)]
3254    async fn test_soft_wraps(cx: &mut gpui::TestAppContext) {
3255        cx.background_executor
3256            .set_block_on_ticks(usize::MAX..=usize::MAX);
3257        cx.update(|cx| {
3258            init_test(cx, |_| {});
3259        });
3260
3261        let mut cx = crate::test::editor_test_context::EditorTestContext::new(cx).await;
3262        let editor = cx.editor.clone();
3263        let window = cx.window;
3264
3265        _ = cx.update_window(window, |_, window, cx| {
3266            let text_layout_details =
3267                editor.update(cx, |editor, cx| editor.text_layout_details(window, cx));
3268
3269            let font_size = px(12.0);
3270            let wrap_width = Some(px(96.));
3271
3272            let text = "one two three four five\nsix seven eight";
3273            let buffer = MultiBuffer::build_simple(text, cx);
3274            let map = cx.new(|cx| {
3275                DisplayMap::new(
3276                    buffer.clone(),
3277                    font("Helvetica"),
3278                    font_size,
3279                    wrap_width,
3280                    1,
3281                    1,
3282                    FoldPlaceholder::test(),
3283                    DiagnosticSeverity::Warning,
3284                    cx,
3285                )
3286            });
3287
3288            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3289            assert_eq!(
3290                snapshot.text_chunks(DisplayRow(0)).collect::<String>(),
3291                "one two \nthree four \nfive\nsix seven \neight"
3292            );
3293            assert_eq!(
3294                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Left),
3295                DisplayPoint::new(DisplayRow(0), 7)
3296            );
3297            assert_eq!(
3298                snapshot.clip_point(DisplayPoint::new(DisplayRow(0), 8), Bias::Right),
3299                DisplayPoint::new(DisplayRow(1), 0)
3300            );
3301            assert_eq!(
3302                movement::right(&snapshot, DisplayPoint::new(DisplayRow(0), 7)),
3303                DisplayPoint::new(DisplayRow(1), 0)
3304            );
3305            assert_eq!(
3306                movement::left(&snapshot, DisplayPoint::new(DisplayRow(1), 0)),
3307                DisplayPoint::new(DisplayRow(0), 7)
3308            );
3309
3310            let x = snapshot
3311                .x_for_display_point(DisplayPoint::new(DisplayRow(1), 10), &text_layout_details);
3312            assert_eq!(
3313                movement::up(
3314                    &snapshot,
3315                    DisplayPoint::new(DisplayRow(1), 10),
3316                    language::SelectionGoal::None,
3317                    false,
3318                    &text_layout_details,
3319                ),
3320                (
3321                    DisplayPoint::new(DisplayRow(0), 7),
3322                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3323                )
3324            );
3325            assert_eq!(
3326                movement::down(
3327                    &snapshot,
3328                    DisplayPoint::new(DisplayRow(0), 7),
3329                    language::SelectionGoal::HorizontalPosition(f64::from(x)),
3330                    false,
3331                    &text_layout_details
3332                ),
3333                (
3334                    DisplayPoint::new(DisplayRow(1), 10),
3335                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3336                )
3337            );
3338            assert_eq!(
3339                movement::down(
3340                    &snapshot,
3341                    DisplayPoint::new(DisplayRow(1), 10),
3342                    language::SelectionGoal::HorizontalPosition(f64::from(x)),
3343                    false,
3344                    &text_layout_details
3345                ),
3346                (
3347                    DisplayPoint::new(DisplayRow(2), 4),
3348                    language::SelectionGoal::HorizontalPosition(f64::from(x))
3349                )
3350            );
3351
3352            let ix = MultiBufferOffset(snapshot.buffer_snapshot().text().find("seven").unwrap());
3353            buffer.update(cx, |buffer, cx| {
3354                buffer.edit([(ix..ix, "and ")], None, cx);
3355            });
3356
3357            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3358            assert_eq!(
3359                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
3360                "three four \nfive\nsix and \nseven eight"
3361            );
3362
3363            // Re-wrap on font size changes
3364            map.update(cx, |map, cx| {
3365                map.set_font(font("Helvetica"), font_size + Pixels::from(3.), cx)
3366            });
3367
3368            let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3369            assert_eq!(
3370                snapshot.text_chunks(DisplayRow(1)).collect::<String>(),
3371                "three \nfour five\nsix and \nseven \neight"
3372            )
3373        });
3374    }
3375
3376    #[gpui::test]
3377    fn test_text_chunks(cx: &mut gpui::App) {
3378        init_test(cx, |_| {});
3379
3380        let text = sample_text(6, 6, 'a');
3381        let buffer = MultiBuffer::build_simple(&text, cx);
3382
3383        let font_size = px(14.0);
3384        let map = cx.new(|cx| {
3385            DisplayMap::new(
3386                buffer.clone(),
3387                font("Helvetica"),
3388                font_size,
3389                None,
3390                1,
3391                1,
3392                FoldPlaceholder::test(),
3393                DiagnosticSeverity::Warning,
3394                cx,
3395            )
3396        });
3397
3398        buffer.update(cx, |buffer, cx| {
3399            buffer.edit(
3400                vec![
3401                    (
3402                        MultiBufferPoint::new(1, 0)..MultiBufferPoint::new(1, 0),
3403                        "\t",
3404                    ),
3405                    (
3406                        MultiBufferPoint::new(1, 1)..MultiBufferPoint::new(1, 1),
3407                        "\t",
3408                    ),
3409                    (
3410                        MultiBufferPoint::new(2, 1)..MultiBufferPoint::new(2, 1),
3411                        "\t",
3412                    ),
3413                ],
3414                None,
3415                cx,
3416            )
3417        });
3418
3419        assert_eq!(
3420            map.update(cx, |map, cx| map.snapshot(cx))
3421                .text_chunks(DisplayRow(1))
3422                .collect::<String>()
3423                .lines()
3424                .next(),
3425            Some("    b   bbbbb")
3426        );
3427        assert_eq!(
3428            map.update(cx, |map, cx| map.snapshot(cx))
3429                .text_chunks(DisplayRow(2))
3430                .collect::<String>()
3431                .lines()
3432                .next(),
3433            Some("c   ccccc")
3434        );
3435    }
3436
3437    #[gpui::test]
3438    fn test_inlays_with_newlines_after_blocks(cx: &mut gpui::TestAppContext) {
3439        cx.update(|cx| init_test(cx, |_| {}));
3440
3441        let buffer = cx.new(|cx| Buffer::local("a", cx));
3442        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3443        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3444
3445        let font_size = px(14.0);
3446        let map = cx.new(|cx| {
3447            DisplayMap::new(
3448                buffer.clone(),
3449                font("Helvetica"),
3450                font_size,
3451                None,
3452                1,
3453                1,
3454                FoldPlaceholder::test(),
3455                DiagnosticSeverity::Warning,
3456                cx,
3457            )
3458        });
3459
3460        map.update(cx, |map, cx| {
3461            map.insert_blocks(
3462                [BlockProperties {
3463                    placement: BlockPlacement::Above(
3464                        buffer_snapshot.anchor_before(Point::new(0, 0)),
3465                    ),
3466                    height: Some(2),
3467                    style: BlockStyle::Sticky,
3468                    render: Arc::new(|_| div().into_any()),
3469                    priority: 0,
3470                }],
3471                cx,
3472            );
3473        });
3474        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\na"));
3475
3476        map.update(cx, |map, cx| {
3477            map.splice_inlays(
3478                &[],
3479                vec![Inlay::edit_prediction(
3480                    0,
3481                    buffer_snapshot.anchor_after(MultiBufferOffset(0)),
3482                    "\n",
3483                )],
3484                cx,
3485            );
3486        });
3487        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\na"));
3488
3489        // Regression test: updating the display map does not crash when a
3490        // block is immediately followed by a multi-line inlay.
3491        buffer.update(cx, |buffer, cx| {
3492            buffer.edit(
3493                [(MultiBufferOffset(1)..MultiBufferOffset(1), "b")],
3494                None,
3495                cx,
3496            );
3497        });
3498        map.update(cx, |m, cx| assert_eq!(m.snapshot(cx).text(), "\n\n\nab"));
3499    }
3500
3501    #[gpui::test]
3502    async fn test_chunks(cx: &mut gpui::TestAppContext) {
3503        let text = r#"
3504            fn outer() {}
3505
3506            mod module {
3507                fn inner() {}
3508            }"#
3509        .unindent();
3510
3511        let theme =
3512            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
3513        let language = Arc::new(
3514            Language::new(
3515                LanguageConfig {
3516                    name: "Test".into(),
3517                    matcher: LanguageMatcher {
3518                        path_suffixes: vec![".test".to_string()],
3519                        ..Default::default()
3520                    },
3521                    ..Default::default()
3522                },
3523                Some(tree_sitter_rust::LANGUAGE.into()),
3524            )
3525            .with_highlights_query(
3526                r#"
3527                (mod_item name: (identifier) body: _ @mod.body)
3528                (function_item name: (identifier) @fn.name)
3529                "#,
3530            )
3531            .unwrap(),
3532        );
3533        language.set_theme(&theme);
3534
3535        cx.update(|cx| {
3536            init_test(cx, |s| {
3537                s.project.all_languages.defaults.tab_size = Some(2.try_into().unwrap())
3538            })
3539        });
3540
3541        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3542        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3543        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3544
3545        let font_size = px(14.0);
3546
3547        let map = cx.new(|cx| {
3548            DisplayMap::new(
3549                buffer,
3550                font("Helvetica"),
3551                font_size,
3552                None,
3553                1,
3554                1,
3555                FoldPlaceholder::test(),
3556                DiagnosticSeverity::Warning,
3557                cx,
3558            )
3559        });
3560        assert_eq!(
3561            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
3562            vec![
3563                ("fn ".to_string(), None),
3564                ("outer".to_string(), Some(Hsla::blue())),
3565                ("() {}\n\nmod module ".to_string(), None),
3566                ("{\n    fn ".to_string(), Some(Hsla::red())),
3567                ("inner".to_string(), Some(Hsla::blue())),
3568                ("() {}\n}".to_string(), Some(Hsla::red())),
3569            ]
3570        );
3571        assert_eq!(
3572            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
3573            vec![
3574                ("    fn ".to_string(), Some(Hsla::red())),
3575                ("inner".to_string(), Some(Hsla::blue())),
3576                ("() {}\n}".to_string(), Some(Hsla::red())),
3577            ]
3578        );
3579
3580        map.update(cx, |map, cx| {
3581            map.fold(
3582                vec![Crease::simple(
3583                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
3584                    FoldPlaceholder::test(),
3585                )],
3586                cx,
3587            )
3588        });
3589        assert_eq!(
3590            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(2), &map, &theme, cx)),
3591            vec![
3592                ("fn ".to_string(), None),
3593                ("out".to_string(), Some(Hsla::blue())),
3594                ("".to_string(), None),
3595                ("  fn ".to_string(), Some(Hsla::red())),
3596                ("inner".to_string(), Some(Hsla::blue())),
3597                ("() {}\n}".to_string(), Some(Hsla::red())),
3598            ]
3599        );
3600    }
3601
3602    #[gpui::test]
3603    async fn test_chunks_with_syntax_highlighting_across_blocks(cx: &mut gpui::TestAppContext) {
3604        cx.background_executor
3605            .set_block_on_ticks(usize::MAX..=usize::MAX);
3606
3607        let text = r#"
3608            const A: &str = "
3609                one
3610                two
3611                three
3612            ";
3613            const B: &str = "four";
3614        "#
3615        .unindent();
3616
3617        let theme = SyntaxTheme::new_test(vec![
3618            ("string", Hsla::red()),
3619            ("punctuation", Hsla::blue()),
3620            ("keyword", Hsla::green()),
3621        ]);
3622        let language = Arc::new(
3623            Language::new(
3624                LanguageConfig {
3625                    name: "Rust".into(),
3626                    ..Default::default()
3627                },
3628                Some(tree_sitter_rust::LANGUAGE.into()),
3629            )
3630            .with_highlights_query(
3631                r#"
3632                (string_literal) @string
3633                "const" @keyword
3634                [":" ";"] @punctuation
3635                "#,
3636            )
3637            .unwrap(),
3638        );
3639        language.set_theme(&theme);
3640
3641        cx.update(|cx| init_test(cx, |_| {}));
3642
3643        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3644        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3645        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3646        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3647
3648        let map = cx.new(|cx| {
3649            DisplayMap::new(
3650                buffer,
3651                font("Courier"),
3652                px(16.0),
3653                None,
3654                1,
3655                1,
3656                FoldPlaceholder::test(),
3657                DiagnosticSeverity::Warning,
3658                cx,
3659            )
3660        });
3661
3662        // Insert two blocks in the middle of a multi-line string literal.
3663        // The second block has zero height.
3664        map.update(cx, |map, cx| {
3665            map.insert_blocks(
3666                [
3667                    BlockProperties {
3668                        placement: BlockPlacement::Below(
3669                            buffer_snapshot.anchor_before(Point::new(1, 0)),
3670                        ),
3671                        height: Some(1),
3672                        style: BlockStyle::Sticky,
3673                        render: Arc::new(|_| div().into_any()),
3674                        priority: 0,
3675                    },
3676                    BlockProperties {
3677                        placement: BlockPlacement::Below(
3678                            buffer_snapshot.anchor_before(Point::new(2, 0)),
3679                        ),
3680                        height: None,
3681                        style: BlockStyle::Sticky,
3682                        render: Arc::new(|_| div().into_any()),
3683                        priority: 0,
3684                    },
3685                ],
3686                cx,
3687            )
3688        });
3689
3690        pretty_assertions::assert_eq!(
3691            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(7), &map, &theme, cx)),
3692            [
3693                ("const".into(), Some(Hsla::green())),
3694                (" A".into(), None),
3695                (":".into(), Some(Hsla::blue())),
3696                (" &str = ".into(), None),
3697                ("\"\n    one\n".into(), Some(Hsla::red())),
3698                ("\n".into(), None),
3699                ("    two\n    three\n\"".into(), Some(Hsla::red())),
3700                (";".into(), Some(Hsla::blue())),
3701                ("\n".into(), None),
3702                ("const".into(), Some(Hsla::green())),
3703                (" B".into(), None),
3704                (":".into(), Some(Hsla::blue())),
3705                (" &str = ".into(), None),
3706                ("\"four\"".into(), Some(Hsla::red())),
3707                (";".into(), Some(Hsla::blue())),
3708                ("\n".into(), None),
3709            ]
3710        );
3711    }
3712
3713    #[gpui::test]
3714    async fn test_chunks_with_diagnostics_across_blocks(cx: &mut gpui::TestAppContext) {
3715        cx.background_executor
3716            .set_block_on_ticks(usize::MAX..=usize::MAX);
3717
3718        let text = r#"
3719            struct A {
3720                b: usize;
3721            }
3722            const c: usize = 1;
3723        "#
3724        .unindent();
3725
3726        cx.update(|cx| init_test(cx, |_| {}));
3727
3728        let buffer = cx.new(|cx| Buffer::local(text, cx));
3729
3730        buffer.update(cx, |buffer, cx| {
3731            buffer.update_diagnostics(
3732                LanguageServerId(0),
3733                DiagnosticSet::new(
3734                    [DiagnosticEntry {
3735                        range: PointUtf16::new(0, 0)..PointUtf16::new(2, 1),
3736                        diagnostic: Diagnostic {
3737                            severity: lsp::DiagnosticSeverity::ERROR,
3738                            group_id: 1,
3739                            message: "hi".into(),
3740                            ..Default::default()
3741                        },
3742                    }],
3743                    buffer,
3744                ),
3745                cx,
3746            )
3747        });
3748
3749        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3750        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3751
3752        let map = cx.new(|cx| {
3753            DisplayMap::new(
3754                buffer,
3755                font("Courier"),
3756                px(16.0),
3757                None,
3758                1,
3759                1,
3760                FoldPlaceholder::test(),
3761                DiagnosticSeverity::Warning,
3762                cx,
3763            )
3764        });
3765
3766        let black = gpui::black().to_rgb();
3767        let red = gpui::red().to_rgb();
3768
3769        // Insert a block in the middle of a multi-line diagnostic.
3770        map.update(cx, |map, cx| {
3771            map.highlight_text(
3772                HighlightKey::Editor,
3773                vec![
3774                    buffer_snapshot.anchor_before(Point::new(3, 9))
3775                        ..buffer_snapshot.anchor_after(Point::new(3, 14)),
3776                    buffer_snapshot.anchor_before(Point::new(3, 17))
3777                        ..buffer_snapshot.anchor_after(Point::new(3, 18)),
3778                ],
3779                red.into(),
3780                false,
3781                cx,
3782            );
3783            map.insert_blocks(
3784                [BlockProperties {
3785                    placement: BlockPlacement::Below(
3786                        buffer_snapshot.anchor_before(Point::new(1, 0)),
3787                    ),
3788                    height: Some(1),
3789                    style: BlockStyle::Sticky,
3790                    render: Arc::new(|_| div().into_any()),
3791                    priority: 0,
3792                }],
3793                cx,
3794            )
3795        });
3796
3797        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
3798        let mut chunks = Vec::<(String, Option<lsp::DiagnosticSeverity>, Rgba)>::new();
3799        for chunk in snapshot.chunks(DisplayRow(0)..DisplayRow(5), true, Default::default()) {
3800            let color = chunk
3801                .highlight_style
3802                .and_then(|style| style.color)
3803                .map_or(black, |color| color.to_rgb());
3804            if let Some((last_chunk, last_severity, last_color)) = chunks.last_mut()
3805                && *last_severity == chunk.diagnostic_severity
3806                && *last_color == color
3807            {
3808                last_chunk.push_str(chunk.text);
3809                continue;
3810            }
3811
3812            chunks.push((chunk.text.to_string(), chunk.diagnostic_severity, color));
3813        }
3814
3815        assert_eq!(
3816            chunks,
3817            [
3818                (
3819                    "struct A {\n    b: usize;\n".into(),
3820                    Some(lsp::DiagnosticSeverity::ERROR),
3821                    black
3822                ),
3823                ("\n".into(), None, black),
3824                ("}".into(), Some(lsp::DiagnosticSeverity::ERROR), black),
3825                ("\nconst c: ".into(), None, black),
3826                ("usize".into(), None, red),
3827                (" = ".into(), None, black),
3828                ("1".into(), None, red),
3829                (";\n".into(), None, black),
3830            ]
3831        );
3832    }
3833
3834    #[gpui::test]
3835    async fn test_point_translation_with_replace_blocks(cx: &mut gpui::TestAppContext) {
3836        cx.background_executor
3837            .set_block_on_ticks(usize::MAX..=usize::MAX);
3838
3839        cx.update(|cx| init_test(cx, |_| {}));
3840
3841        let buffer = cx.update(|cx| MultiBuffer::build_simple("abcde\nfghij\nklmno\npqrst", cx));
3842        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
3843        let map = cx.new(|cx| {
3844            DisplayMap::new(
3845                buffer.clone(),
3846                font("Courier"),
3847                px(16.0),
3848                None,
3849                1,
3850                1,
3851                FoldPlaceholder::test(),
3852                DiagnosticSeverity::Warning,
3853                cx,
3854            )
3855        });
3856
3857        let snapshot = map.update(cx, |map, cx| {
3858            map.insert_blocks(
3859                [BlockProperties {
3860                    placement: BlockPlacement::Replace(
3861                        buffer_snapshot.anchor_before(Point::new(1, 2))
3862                            ..=buffer_snapshot.anchor_after(Point::new(2, 3)),
3863                    ),
3864                    height: Some(4),
3865                    style: BlockStyle::Fixed,
3866                    render: Arc::new(|_| div().into_any()),
3867                    priority: 0,
3868                }],
3869                cx,
3870            );
3871            map.snapshot(cx)
3872        });
3873
3874        assert_eq!(snapshot.text(), "abcde\n\n\n\n\npqrst");
3875
3876        let point_to_display_points = [
3877            (Point::new(1, 0), DisplayPoint::new(DisplayRow(1), 0)),
3878            (Point::new(2, 0), DisplayPoint::new(DisplayRow(1), 0)),
3879            (Point::new(3, 0), DisplayPoint::new(DisplayRow(5), 0)),
3880        ];
3881        for (buffer_point, display_point) in point_to_display_points {
3882            assert_eq!(
3883                snapshot.point_to_display_point(buffer_point, Bias::Left),
3884                display_point,
3885                "point_to_display_point({:?}, Bias::Left)",
3886                buffer_point
3887            );
3888            assert_eq!(
3889                snapshot.point_to_display_point(buffer_point, Bias::Right),
3890                display_point,
3891                "point_to_display_point({:?}, Bias::Right)",
3892                buffer_point
3893            );
3894        }
3895
3896        let display_points_to_points = [
3897            (
3898                DisplayPoint::new(DisplayRow(1), 0),
3899                Point::new(1, 0),
3900                Point::new(2, 5),
3901            ),
3902            (
3903                DisplayPoint::new(DisplayRow(2), 0),
3904                Point::new(1, 0),
3905                Point::new(2, 5),
3906            ),
3907            (
3908                DisplayPoint::new(DisplayRow(3), 0),
3909                Point::new(1, 0),
3910                Point::new(2, 5),
3911            ),
3912            (
3913                DisplayPoint::new(DisplayRow(4), 0),
3914                Point::new(1, 0),
3915                Point::new(2, 5),
3916            ),
3917            (
3918                DisplayPoint::new(DisplayRow(5), 0),
3919                Point::new(3, 0),
3920                Point::new(3, 0),
3921            ),
3922        ];
3923        for (display_point, left_buffer_point, right_buffer_point) in display_points_to_points {
3924            assert_eq!(
3925                snapshot.display_point_to_point(display_point, Bias::Left),
3926                left_buffer_point,
3927                "display_point_to_point({:?}, Bias::Left)",
3928                display_point
3929            );
3930            assert_eq!(
3931                snapshot.display_point_to_point(display_point, Bias::Right),
3932                right_buffer_point,
3933                "display_point_to_point({:?}, Bias::Right)",
3934                display_point
3935            );
3936        }
3937    }
3938
3939    #[gpui::test]
3940    async fn test_chunks_with_soft_wrapping(cx: &mut gpui::TestAppContext) {
3941        cx.background_executor
3942            .set_block_on_ticks(usize::MAX..=usize::MAX);
3943
3944        let text = r#"
3945            fn outer() {}
3946
3947            mod module {
3948                fn inner() {}
3949            }"#
3950        .unindent();
3951
3952        let theme =
3953            SyntaxTheme::new_test(vec![("mod.body", Hsla::red()), ("fn.name", Hsla::blue())]);
3954        let language = Arc::new(
3955            Language::new(
3956                LanguageConfig {
3957                    name: "Test".into(),
3958                    matcher: LanguageMatcher {
3959                        path_suffixes: vec![".test".to_string()],
3960                        ..Default::default()
3961                    },
3962                    ..Default::default()
3963                },
3964                Some(tree_sitter_rust::LANGUAGE.into()),
3965            )
3966            .with_highlights_query(
3967                r#"
3968                (mod_item name: (identifier) body: _ @mod.body)
3969                (function_item name: (identifier) @fn.name)
3970                "#,
3971            )
3972            .unwrap(),
3973        );
3974        language.set_theme(&theme);
3975
3976        cx.update(|cx| init_test(cx, |_| {}));
3977
3978        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
3979        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
3980        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
3981
3982        let font_size = px(16.0);
3983
3984        let map = cx.new(|cx| {
3985            DisplayMap::new(
3986                buffer,
3987                font("Courier"),
3988                font_size,
3989                Some(px(40.0)),
3990                1,
3991                1,
3992                FoldPlaceholder::test(),
3993                DiagnosticSeverity::Warning,
3994                cx,
3995            )
3996        });
3997        assert_eq!(
3998            cx.update(|cx| syntax_chunks(DisplayRow(0)..DisplayRow(5), &map, &theme, cx)),
3999            [
4000                ("fn \n".to_string(), None),
4001                ("oute".to_string(), Some(Hsla::blue())),
4002                ("\n".to_string(), None),
4003                ("r".to_string(), Some(Hsla::blue())),
4004                ("() \n{}\n\n".to_string(), None),
4005            ]
4006        );
4007        assert_eq!(
4008            cx.update(|cx| syntax_chunks(DisplayRow(3)..DisplayRow(5), &map, &theme, cx)),
4009            [("{}\n\n".to_string(), None)]
4010        );
4011
4012        map.update(cx, |map, cx| {
4013            map.fold(
4014                vec![Crease::simple(
4015                    MultiBufferPoint::new(0, 6)..MultiBufferPoint::new(3, 2),
4016                    FoldPlaceholder::test(),
4017                )],
4018                cx,
4019            )
4020        });
4021        assert_eq!(
4022            cx.update(|cx| syntax_chunks(DisplayRow(1)..DisplayRow(4), &map, &theme, cx)),
4023            [
4024                ("out".to_string(), Some(Hsla::blue())),
4025                ("\n".to_string(), None),
4026                ("  ".to_string(), Some(Hsla::red())),
4027                ("\n".to_string(), None),
4028                ("fn ".to_string(), Some(Hsla::red())),
4029                ("i".to_string(), Some(Hsla::blue())),
4030                ("\n".to_string(), None)
4031            ]
4032        );
4033    }
4034
4035    #[gpui::test]
4036    async fn test_chunks_with_text_highlights(cx: &mut gpui::TestAppContext) {
4037        cx.update(|cx| init_test(cx, |_| {}));
4038
4039        let theme =
4040            SyntaxTheme::new_test(vec![("operator", Hsla::red()), ("string", Hsla::green())]);
4041        let language = Arc::new(
4042            Language::new(
4043                LanguageConfig {
4044                    name: "Test".into(),
4045                    matcher: LanguageMatcher {
4046                        path_suffixes: vec![".test".to_string()],
4047                        ..Default::default()
4048                    },
4049                    ..Default::default()
4050                },
4051                Some(tree_sitter_rust::LANGUAGE.into()),
4052            )
4053            .with_highlights_query(
4054                r#"
4055                ":" @operator
4056                (string_literal) @string
4057                "#,
4058            )
4059            .unwrap(),
4060        );
4061        language.set_theme(&theme);
4062
4063        let (text, highlighted_ranges) = marked_text_ranges(r#"constˇ «a»«:» B = "c «d»""#, false);
4064
4065        let buffer = cx.new(|cx| Buffer::local(text, cx).with_language(language, cx));
4066        cx.condition(&buffer, |buf, _| !buf.is_parsing()).await;
4067
4068        let buffer = cx.new(|cx| MultiBuffer::singleton(buffer, cx));
4069        let buffer_snapshot = buffer.read_with(cx, |buffer, cx| buffer.snapshot(cx));
4070
4071        let font_size = px(16.0);
4072        let map = cx.new(|cx| {
4073            DisplayMap::new(
4074                buffer,
4075                font("Courier"),
4076                font_size,
4077                None,
4078                1,
4079                1,
4080                FoldPlaceholder::test(),
4081                DiagnosticSeverity::Warning,
4082                cx,
4083            )
4084        });
4085
4086        let style = HighlightStyle {
4087            color: Some(Hsla::blue()),
4088            ..Default::default()
4089        };
4090
4091        map.update(cx, |map, cx| {
4092            map.highlight_text(
4093                HighlightKey::Editor,
4094                highlighted_ranges
4095                    .into_iter()
4096                    .map(|range| MultiBufferOffset(range.start)..MultiBufferOffset(range.end))
4097                    .map(|range| {
4098                        buffer_snapshot.anchor_before(range.start)
4099                            ..buffer_snapshot.anchor_before(range.end)
4100                    })
4101                    .collect(),
4102                style,
4103                false,
4104                cx,
4105            );
4106        });
4107
4108        assert_eq!(
4109            cx.update(|cx| chunks(DisplayRow(0)..DisplayRow(10), &map, &theme, cx)),
4110            [
4111                ("const ".to_string(), None, None),
4112                ("a".to_string(), None, Some(Hsla::blue())),
4113                (":".to_string(), Some(Hsla::red()), Some(Hsla::blue())),
4114                (" B = ".to_string(), None, None),
4115                ("\"c ".to_string(), Some(Hsla::green()), None),
4116                ("d".to_string(), Some(Hsla::green()), Some(Hsla::blue())),
4117                ("\"".to_string(), Some(Hsla::green()), None),
4118            ]
4119        );
4120    }
4121
4122    #[gpui::test]
4123    fn test_clip_point(cx: &mut gpui::App) {
4124        init_test(cx, |_| {});
4125
4126        fn assert(text: &str, shift_right: bool, bias: Bias, cx: &mut gpui::App) {
4127            let (unmarked_snapshot, mut markers) = marked_display_snapshot(text, cx);
4128
4129            match bias {
4130                Bias::Left => {
4131                    if shift_right {
4132                        *markers[1].column_mut() += 1;
4133                    }
4134
4135                    assert_eq!(unmarked_snapshot.clip_point(markers[1], bias), markers[0])
4136                }
4137                Bias::Right => {
4138                    if shift_right {
4139                        *markers[0].column_mut() += 1;
4140                    }
4141
4142                    assert_eq!(unmarked_snapshot.clip_point(markers[0], bias), markers[1])
4143                }
4144            };
4145        }
4146
4147        use Bias::{Left, Right};
4148        assert("ˇˇα", false, Left, cx);
4149        assert("ˇˇα", true, Left, cx);
4150        assert("ˇˇα", false, Right, cx);
4151        assert("ˇαˇ", true, Right, cx);
4152        assert("ˇˇ✋", false, Left, cx);
4153        assert("ˇˇ✋", true, Left, cx);
4154        assert("ˇˇ✋", false, Right, cx);
4155        assert("ˇ✋ˇ", true, Right, cx);
4156        assert("ˇˇ🍐", false, Left, cx);
4157        assert("ˇˇ🍐", true, Left, cx);
4158        assert("ˇˇ🍐", false, Right, cx);
4159        assert("ˇ🍐ˇ", true, Right, cx);
4160        assert("ˇˇ\t", false, Left, cx);
4161        assert("ˇˇ\t", true, Left, cx);
4162        assert("ˇˇ\t", false, Right, cx);
4163        assert("ˇ\tˇ", true, Right, cx);
4164        assert(" ˇˇ\t", false, Left, cx);
4165        assert(" ˇˇ\t", true, Left, cx);
4166        assert(" ˇˇ\t", false, Right, cx);
4167        assert(" ˇ\tˇ", true, Right, cx);
4168        assert("   ˇˇ\t", false, Left, cx);
4169        assert("   ˇˇ\t", false, Right, cx);
4170    }
4171
4172    #[gpui::test]
4173    fn test_clip_at_line_ends(cx: &mut gpui::App) {
4174        init_test(cx, |_| {});
4175
4176        fn assert(text: &str, cx: &mut gpui::App) {
4177            let (mut unmarked_snapshot, markers) = marked_display_snapshot(text, cx);
4178            unmarked_snapshot.clip_at_line_ends = true;
4179            assert_eq!(
4180                unmarked_snapshot.clip_point(markers[1], Bias::Left),
4181                markers[0]
4182            );
4183        }
4184
4185        assert("ˇˇ", cx);
4186        assert("ˇaˇ", cx);
4187        assert("aˇbˇ", cx);
4188        assert("aˇαˇ", cx);
4189    }
4190
4191    #[gpui::test]
4192    fn test_creases(cx: &mut gpui::App) {
4193        init_test(cx, |_| {});
4194
4195        let text = "aaa\nbbb\nccc\nddd\neee\nfff\nggg\nhhh\niii\njjj\nkkk\nlll";
4196        let buffer = MultiBuffer::build_simple(text, cx);
4197        let font_size = px(14.0);
4198        cx.new(|cx| {
4199            let mut map = DisplayMap::new(
4200                buffer.clone(),
4201                font("Helvetica"),
4202                font_size,
4203                None,
4204                1,
4205                1,
4206                FoldPlaceholder::test(),
4207                DiagnosticSeverity::Warning,
4208                cx,
4209            );
4210            let snapshot = map.buffer.read(cx).snapshot(cx);
4211            let range =
4212                snapshot.anchor_before(Point::new(2, 0))..snapshot.anchor_after(Point::new(3, 3));
4213
4214            map.crease_map.insert(
4215                [Crease::inline(
4216                    range,
4217                    FoldPlaceholder::test(),
4218                    |_row, _status, _toggle, _window, _cx| div(),
4219                    |_row, _status, _window, _cx| div(),
4220                )],
4221                &map.buffer.read(cx).snapshot(cx),
4222            );
4223
4224            map
4225        });
4226    }
4227
4228    #[gpui::test]
4229    fn test_tabs_with_multibyte_chars(cx: &mut gpui::App) {
4230        init_test(cx, |_| {});
4231
4232        let text = "\t\tα\nβ\t\n🏀β\t\tγ";
4233        let buffer = MultiBuffer::build_simple(text, cx);
4234        let font_size = px(14.0);
4235
4236        let map = cx.new(|cx| {
4237            DisplayMap::new(
4238                buffer.clone(),
4239                font("Helvetica"),
4240                font_size,
4241                None,
4242                1,
4243                1,
4244                FoldPlaceholder::test(),
4245                DiagnosticSeverity::Warning,
4246                cx,
4247            )
4248        });
4249        let map = map.update(cx, |map, cx| map.snapshot(cx));
4250        assert_eq!(map.text(), "✅       α\nβ   \n🏀β      γ");
4251        assert_eq!(
4252            map.text_chunks(DisplayRow(0)).collect::<String>(),
4253            "✅       α\nβ   \n🏀β      γ"
4254        );
4255        assert_eq!(
4256            map.text_chunks(DisplayRow(1)).collect::<String>(),
4257            "β   \n🏀β      γ"
4258        );
4259        assert_eq!(
4260            map.text_chunks(DisplayRow(2)).collect::<String>(),
4261            "🏀β      γ"
4262        );
4263
4264        let point = MultiBufferPoint::new(0, "\t\t".len() as u32);
4265        let display_point = DisplayPoint::new(DisplayRow(0), "".len() as u32);
4266        assert_eq!(point.to_display_point(&map), display_point);
4267        assert_eq!(display_point.to_point(&map), point);
4268
4269        let point = MultiBufferPoint::new(1, "β\t".len() as u32);
4270        let display_point = DisplayPoint::new(DisplayRow(1), "β   ".len() as u32);
4271        assert_eq!(point.to_display_point(&map), display_point);
4272        assert_eq!(display_point.to_point(&map), point,);
4273
4274        let point = MultiBufferPoint::new(2, "🏀β\t\t".len() as u32);
4275        let display_point = DisplayPoint::new(DisplayRow(2), "🏀β      ".len() as u32);
4276        assert_eq!(point.to_display_point(&map), display_point);
4277        assert_eq!(display_point.to_point(&map), point,);
4278
4279        // Display points inside of expanded tabs
4280        assert_eq!(
4281            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
4282            MultiBufferPoint::new(0, "\t".len() as u32),
4283        );
4284        assert_eq!(
4285            DisplayPoint::new(DisplayRow(0), "".len() as u32).to_point(&map),
4286            MultiBufferPoint::new(0, "".len() as u32),
4287        );
4288
4289        // Clipping display points inside of multi-byte characters
4290        assert_eq!(
4291            map.clip_point(
4292                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
4293                Left
4294            ),
4295            DisplayPoint::new(DisplayRow(0), 0)
4296        );
4297        assert_eq!(
4298            map.clip_point(
4299                DisplayPoint::new(DisplayRow(0), "".len() as u32 - 1),
4300                Bias::Right
4301            ),
4302            DisplayPoint::new(DisplayRow(0), "".len() as u32)
4303        );
4304    }
4305
4306    #[gpui::test]
4307    fn test_max_point(cx: &mut gpui::App) {
4308        init_test(cx, |_| {});
4309
4310        let buffer = MultiBuffer::build_simple("aaa\n\t\tbbb", cx);
4311        let font_size = px(14.0);
4312        let map = cx.new(|cx| {
4313            DisplayMap::new(
4314                buffer.clone(),
4315                font("Helvetica"),
4316                font_size,
4317                None,
4318                1,
4319                1,
4320                FoldPlaceholder::test(),
4321                DiagnosticSeverity::Warning,
4322                cx,
4323            )
4324        });
4325        assert_eq!(
4326            map.update(cx, |map, cx| map.snapshot(cx)).max_point(),
4327            DisplayPoint::new(DisplayRow(1), 11)
4328        )
4329    }
4330
4331    fn syntax_chunks(
4332        rows: Range<DisplayRow>,
4333        map: &Entity<DisplayMap>,
4334        theme: &SyntaxTheme,
4335        cx: &mut App,
4336    ) -> Vec<(String, Option<Hsla>)> {
4337        chunks(rows, map, theme, cx)
4338            .into_iter()
4339            .map(|(text, color, _)| (text, color))
4340            .collect()
4341    }
4342
4343    fn chunks(
4344        rows: Range<DisplayRow>,
4345        map: &Entity<DisplayMap>,
4346        theme: &SyntaxTheme,
4347        cx: &mut App,
4348    ) -> Vec<(String, Option<Hsla>, Option<Hsla>)> {
4349        let snapshot = map.update(cx, |map, cx| map.snapshot(cx));
4350        let mut chunks: Vec<(String, Option<Hsla>, Option<Hsla>)> = Vec::new();
4351        for chunk in snapshot.chunks(rows, true, HighlightStyles::default()) {
4352            let syntax_color = chunk
4353                .syntax_highlight_id
4354                .and_then(|id| id.style(theme)?.color);
4355            let highlight_color = chunk.highlight_style.and_then(|style| style.color);
4356            if let Some((last_chunk, last_syntax_color, last_highlight_color)) = chunks.last_mut()
4357                && syntax_color == *last_syntax_color
4358                && highlight_color == *last_highlight_color
4359            {
4360                last_chunk.push_str(chunk.text);
4361                continue;
4362            }
4363            chunks.push((chunk.text.to_string(), syntax_color, highlight_color));
4364        }
4365        chunks
4366    }
4367
4368    fn init_test(cx: &mut App, f: impl Fn(&mut SettingsContent)) {
4369        let settings = SettingsStore::test(cx);
4370        cx.set_global(settings);
4371        crate::init(cx);
4372        theme::init(LoadThemes::JustBase, cx);
4373        cx.update_global::<SettingsStore, _>(|store, cx| {
4374            store.update_user_settings(cx, f);
4375        });
4376    }
4377}