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