display_map.rs

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