3148 lines · 113.7 KB · Rust
RawDownload
//! This module defines where the text should be displayed in an [`Editor`][Editor].//!//! Not literally though - rendering, layout and all that jazz is a responsibility of [`EditorElement`][EditorElement].//! Instead, [`DisplayMap`] decides where Inlays/Inlay hints are displayed, when//! to apply a soft wrap, where to add fold indicators, whether there are any tabs in the buffer that//! we display as spaces and where to display custom blocks (like diagnostics).//! Seems like a lot? That's because it is. [`DisplayMap`] is conceptually made up//! of several smaller structures that form a hierarchy (starting at the bottom)://! - [`InlayMap`] that decides where the [`Inlay`]s should be displayed.//! - [`FoldMap`] that decides where the fold indicators should be; it also tracks parts of a source file that are currently folded.//! - [`TabMap`] that keeps track of hard tabs in a buffer.//! - [`WrapMap`] that handles soft wrapping.//! - [`BlockMap`] that tracks custom blocks such as diagnostics that should be displayed within buffer.//! - [`DisplayMap`] that adds background highlights to the regions of text.//!   Each one of those builds on top of preceding map.//!//! ## Structure of the display map layers//!//! Each layer in the map (and the multibuffer itself to some extent) has a few//! structures that are used to implement the public API available to the layer//! above://! - a `Transform` type - this represents a region of text that the layer in//!   question is "managing", that it transforms into a more "processed" text//!   for the layer above. For example, the inlay map has an `enum Transform`//!   that has two variants://!     - `Isomorphic`, representing a region of text that has no inlay hints (i.e.//!       is passed through the map transparently)//!     - `Inlay`, representing a location where an inlay hint is to be inserted.//! - a `TransformSummary` type, which is usually a struct with two fields://!   [`input: TextSummary`][`TextSummary`] and [`output: TextSummary`][`TextSummary`]. Here,//!   `input` corresponds to "text in the layer below", and `output` corresponds to the text//!   exposed to the layer above. So in the inlay map case, a `Transform::Isomorphic`'s summary is//!   just `input = output = summary`, where `summary` is the [`TextSummary`] stored in that//!   variant. Conversely, a `Transform::Inlay` always has an empty `input` summary, because it's//!   not "replacing" any text that exists on disk. The `output` is the summary of the inlay text//!   to be injected. - Various newtype wrappers for co-ordinate spaces (e.g. [`WrapRow`]//!   represents a row index, after soft-wrapping (and all lower layers)).//! - A `Snapshot` type (e.g. [`InlaySnapshot`]) that captures the state of a layer at a specific//!   point in time.//! - various APIs which drill through the layers below to work with the underlying text. Notably://!   - `fn text_summary_for_offset()` returns a [`TextSummary`] for the range in the co-ordinate//!     space that the map in question is responsible for.//!   - `fn <A>_point_to_<B>_point()` converts a point in co-ordinate space `A` into co-ordinate//!     space `B`.//!   - A [`RowInfo`] iterator (e.g. [`InlayBufferRows`]) and a [`Chunk`] iterator//!     (e.g. [`InlayChunks`])//!   - A `sync` function (e.g. [`InlayMap::sync`]) that takes a snapshot and list of [`Edit<T>`]s,//!     and returns a new snapshot and a list of transformed [`Edit<S>`]s. Note that the generic//!     parameter on `Edit` changes, since these methods take in edits in the co-ordinate space of//!     the lower layer, and return edits in their own co-ordinate space. The term "edit" is//!     slightly misleading, since an [`Edit<T>`] doesn't tell you what changed - rather it can be//!     thought of as a "region to invalidate". In theory, it would be correct to always use a//!     single edit that covers the entire range. However, this would lead to lots of unnecessary//!     recalculation.//!//! See the docs for the [`inlay_map`] module for a more in-depth explanation of how a single layer//! works.//!//! [Editor]: crate::Editor//! [EditorElement]: crate::element::EditorElement//! [`TextSummary`]: multi_buffer::MBTextSummary//! [`WrapRow`]: wrap_map::WrapRow//! [`InlayBufferRows`]: inlay_map::InlayBufferRows//! [`InlayChunks`]: inlay_map::InlayChunks//! [`Edit<T>`]: text::Edit//! [`Edit<S>`]: text::Edit//! [`Chunk`]: language::Chunk #[macro_use]mod dimensions; mod block_map;mod crease_map;mod custom_highlights;mod fold_map;mod inlay_map;mod invisibles;mod tab_map;mod wrap_map; pub use crate::display_map::{fold_map::FoldMap, inlay_map::InlayMap, tab_map::TabMap};pub use block_map::{    Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,    BlockPoint, BlockProperties, BlockRows, BlockStyle, CustomBlockId, EditorMargins, RenderBlock,    StickyHeaderExcerpt,};pub use crease_map::*;pub use fold_map::{    ChunkRenderer, ChunkRendererContext, ChunkRendererId, Fold, FoldId, FoldPlaceholder, FoldPoint,};pub use inlay_map::{InlayOffset, InlayPoint};pub use invisibles::{is_invisible, replacement}; use collections::{HashMap, HashSet};use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};use language::{Point, Subscription as BufferSubscription, language_settings::language_settings};use multi_buffer::{    Anchor, AnchorRangeExt, MultiBuffer, MultiBufferOffset, MultiBufferOffsetUtf16,    MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot, RowInfo, ToOffset, ToPoint,};use project::InlayId;use project::project_settings::DiagnosticSeverity;use serde::Deserialize;use sum_tree::{Bias, TreeMap};use text::{BufferId, LineIndent};use ui::{SharedString, px};use unicode_segmentation::UnicodeSegmentation;use ztracing::instrument; use std::{    any::TypeId,    borrow::Cow,    fmt::Debug,    iter,    num::NonZeroU32,    ops::{Add, Range, Sub},    sync::Arc,}; use crate::{    EditorStyle, RowExt, hover_links::InlayHighlight, inlays::Inlay, movement::TextLayoutDetails,};use block_map::{BlockRow, BlockSnapshot};use fold_map::FoldSnapshot;use inlay_map::InlaySnapshot;use tab_map::TabSnapshot;use wrap_map::{WrapMap, WrapSnapshot}; #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub enum FoldStatus {    Folded,    Foldable,} #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]pub enum HighlightKey {    Type(TypeId),    TypePlus(TypeId, usize),} pub trait ToDisplayPoint {    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;} type TextHighlights = TreeMap<HighlightKey, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;type InlayHighlights = TreeMap<TypeId, TreeMap<InlayId, (HighlightStyle, InlayHighlight)>>; /// Decides how text in a [`MultiBuffer`] should be displayed in a buffer, handling inlay hints,/// folding, hard tabs, soft wrapping, custom blocks (like diagnostics), and highlighting.////// See the [module level documentation](self) for more information.pub struct DisplayMap {    /// The buffer that we are displaying.    buffer: Entity<MultiBuffer>,    buffer_subscription: BufferSubscription<MultiBufferOffset>,    /// Decides where the [`Inlay`]s should be displayed.    inlay_map: InlayMap,    /// Decides where the fold indicators should be and tracks parts of a source file that are currently folded.    fold_map: FoldMap,    /// Keeps track of hard tabs in a buffer.    tab_map: TabMap,    /// Handles soft wrapping.    wrap_map: Entity<WrapMap>,    /// Tracks custom blocks such as diagnostics that should be displayed within buffer.    block_map: BlockMap,    /// Regions of text that should be highlighted.    text_highlights: TextHighlights,    /// Regions of inlays that should be highlighted.    inlay_highlights: InlayHighlights,    /// A container for explicitly foldable ranges, which supersede indentation based fold range suggestions.    crease_map: CreaseMap,    pub(crate) fold_placeholder: FoldPlaceholder,    pub clip_at_line_ends: bool,    pub(crate) masked: bool,    pub(crate) diagnostics_max_severity: DiagnosticSeverity,} impl DisplayMap {    pub fn new(        buffer: Entity<MultiBuffer>,        font: Font,        font_size: Pixels,        wrap_width: Option<Pixels>,        buffer_header_height: u32,        excerpt_header_height: u32,        fold_placeholder: FoldPlaceholder,        diagnostics_max_severity: DiagnosticSeverity,        cx: &mut Context<Self>,    ) -> Self {        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());         let tab_size = Self::tab_size(&buffer, cx);        let buffer_snapshot = buffer.read(cx).snapshot(cx);        let crease_map = CreaseMap::new(&buffer_snapshot);        let (inlay_map, snapshot) = InlayMap::new(buffer_snapshot);        let (fold_map, snapshot) = FoldMap::new(snapshot);        let (tab_map, snapshot) = TabMap::new(snapshot, tab_size);        let (wrap_map, snapshot) = WrapMap::new(snapshot, font, font_size, wrap_width, cx);        let block_map = BlockMap::new(snapshot, buffer_header_height, excerpt_header_height);         cx.observe(&wrap_map, |_, _, cx| cx.notify()).detach();         DisplayMap {            buffer,            buffer_subscription,            fold_map,            inlay_map,            tab_map,            wrap_map,            block_map,            crease_map,            fold_placeholder,            diagnostics_max_severity,            text_highlights: Default::default(),            inlay_highlights: Default::default(),            clip_at_line_ends: false,            masked: false,        }    }     #[instrument(skip_all)]    pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {        let tab_size = Self::tab_size(&self.buffer, cx);         let buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let (inlay_snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);        let (fold_snapshot, edits) = self.fold_map.read(inlay_snapshot, edits);        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot, edits, tab_size);        let (wrap_snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(tab_snapshot, edits, cx));        let block_snapshot = self.block_map.read(wrap_snapshot, edits).snapshot;         DisplaySnapshot {            block_snapshot,            diagnostics_max_severity: self.diagnostics_max_severity,            crease_snapshot: self.crease_map.snapshot(),            text_highlights: self.text_highlights.clone(),            inlay_highlights: self.inlay_highlights.clone(),            clip_at_line_ends: self.clip_at_line_ends,            masked: self.masked,            fold_placeholder: self.fold_placeholder.clone(),        }    }     #[instrument(skip_all)]    pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {        self.fold(            other                .folds_in_range(MultiBufferOffset(0)..other.buffer_snapshot().len())                .map(|fold| {                    Crease::simple(                        fold.range.to_offset(other.buffer_snapshot()),                        fold.placeholder.clone(),                    )                })                .collect(),            cx,        );    }     /// Creates folds for the given creases.    #[instrument(skip_all)]    pub fn fold<T: Clone + ToOffset>(&mut self, creases: Vec<Crease<T>>, cx: &mut Context<Self>) {        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot.clone(), edits);        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        self.block_map.read(snapshot, edits);         let inline = creases.iter().filter_map(|crease| {            if let Crease::Inline {                range, placeholder, ..            } = crease            {                Some((range.clone(), placeholder.clone()))            } else {                None            }        });        let (snapshot, edits) = fold_map.fold(inline);         let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        let blocks = creases.into_iter().filter_map(|crease| {            if let Crease::Block {                range,                block_height,                render_block,                block_style,                block_priority,                ..            } = crease            {                Some((                    range,                    render_block,                    block_height,                    block_style,                    block_priority,                ))            } else {                None            }        });        block_map.insert(            blocks                .into_iter()                .map(|(range, render, height, style, priority)| {                    let start = buffer_snapshot.anchor_before(range.start);                    let end = buffer_snapshot.anchor_after(range.end);                    BlockProperties {                        placement: BlockPlacement::Replace(start..=end),                        render,                        height: Some(height),                        style,                        priority,                    }                }),        );    }     /// Removes any folds with the given ranges.    #[instrument(skip_all)]    pub fn remove_folds_with_type<T: ToOffset>(        &mut self,        ranges: impl IntoIterator<Item = Range<T>>,        type_id: TypeId,        cx: &mut Context<Self>,    ) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        self.block_map.read(snapshot, edits);        let (snapshot, edits) = fold_map.remove_folds(ranges, type_id);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        self.block_map.write(snapshot, edits);    }     /// Removes any folds whose ranges intersect any of the given ranges.    #[instrument(skip_all)]    pub fn unfold_intersecting<T: ToOffset>(        &mut self,        ranges: impl IntoIterator<Item = Range<T>>,        inclusive: bool,        cx: &mut Context<Self>,    ) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let offset_ranges = ranges            .into_iter()            .map(|range| range.start.to_offset(&snapshot)..range.end.to_offset(&snapshot))            .collect::<Vec<_>>();        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (mut fold_map, snapshot, edits) = self.fold_map.write(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        self.block_map.read(snapshot, edits);         let (snapshot, edits) =            fold_map.unfold_intersecting(offset_ranges.iter().cloned(), inclusive);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        block_map.remove_intersecting_replace_blocks(offset_ranges, inclusive);    }     #[instrument(skip_all)]    pub fn disable_header_for_buffer(&mut self, buffer_id: BufferId, cx: &mut Context<Self>) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        block_map.disable_header_for_buffer(buffer_id)    }     #[instrument(skip_all)]    pub fn fold_buffers(        &mut self,        buffer_ids: impl IntoIterator<Item = language::BufferId>,        cx: &mut Context<Self>,    ) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        block_map.fold_buffers(buffer_ids, self.buffer.read(cx), cx)    }     #[instrument(skip_all)]    pub fn unfold_buffers(        &mut self,        buffer_ids: impl IntoIterator<Item = language::BufferId>,        cx: &mut Context<Self>,    ) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        block_map.unfold_buffers(buffer_ids, self.buffer.read(cx), cx)    }     #[instrument(skip_all)]    pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {        self.block_map.folded_buffers.contains(&buffer_id)    }     #[instrument(skip_all)]    pub(crate) fn folded_buffers(&self) -> &HashSet<BufferId> {        &self.block_map.folded_buffers    }     #[instrument(skip_all)]    pub fn insert_creases(        &mut self,        creases: impl IntoIterator<Item = Crease<Anchor>>,        cx: &mut Context<Self>,    ) -> Vec<CreaseId> {        let snapshot = self.buffer.read(cx).snapshot(cx);        self.crease_map.insert(creases, &snapshot)    }     #[instrument(skip_all)]    pub fn remove_creases(        &mut self,        crease_ids: impl IntoIterator<Item = CreaseId>,        cx: &mut Context<Self>,    ) -> Vec<(CreaseId, Range<Anchor>)> {        let snapshot = self.buffer.read(cx).snapshot(cx);        self.crease_map.remove(crease_ids, &snapshot)    }     #[instrument(skip_all)]    pub fn insert_blocks(        &mut self,        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,        cx: &mut Context<Self>,    ) -> Vec<CustomBlockId> {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);        let (snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(snapshot, edits, cx));        let mut block_map = self.block_map.write(snapshot, edits);        block_map.insert(blocks)    }     #[instrument(skip_all)]    pub fn resize_blocks(&mut self, heights: HashMap<CustomBlockId, u32>, cx: &mut Context<Self>) {        let snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let tab_size = Self::tab_size(&self.buffer, cx);        let (snapshot, edits) = self.inlay_map.sync(snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let (snapshot, edits) = self.tab_map.sync(snapshot, edits, tab_size);

Lines 1–500 of 3148.