2857 lines · 102.6 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.//!//! [Editor]: crate::Editor//! [EditorElement]: crate::element::EditorElement mod block_map;mod crease_map;mod custom_highlights;mod fold_map;mod inlay_map;pub(crate) mod invisibles;mod tab_map;mod wrap_map; use crate::{    EditorStyle, InlayId, RowExt, hover_links::InlayHighlight, movement::TextLayoutDetails,};pub use block_map::{    Block, BlockChunks as DisplayChunks, BlockContext, BlockId, BlockMap, BlockPlacement,    BlockPoint, BlockProperties, BlockRows, BlockStyle, CustomBlockId, RenderBlock,    StickyHeaderExcerpt,};use block_map::{BlockRow, BlockSnapshot};use collections::{HashMap, HashSet};pub use crease_map::*;pub use fold_map::{ChunkRenderer, ChunkRendererContext, Fold, FoldId, FoldPlaceholder, FoldPoint};use fold_map::{FoldMap, FoldSnapshot};use gpui::{App, Context, Entity, Font, HighlightStyle, LineLayout, Pixels, UnderlineStyle};pub use inlay_map::Inlay;use inlay_map::{InlayMap, InlaySnapshot};pub use inlay_map::{InlayOffset, InlayPoint};pub use invisibles::{is_invisible, replacement};use language::{    OffsetUtf16, Point, Subscription as BufferSubscription, language_settings::language_settings,};use lsp::DiagnosticSeverity;use multi_buffer::{    Anchor, AnchorRangeExt, ExcerptId, MultiBuffer, MultiBufferPoint, MultiBufferRow,    MultiBufferSnapshot, RowInfo, ToOffset, ToPoint,};use serde::Deserialize;use std::{    any::TypeId,    borrow::Cow,    fmt::Debug,    iter,    num::NonZeroU32,    ops::{Add, Range, Sub},    sync::Arc,};use sum_tree::{Bias, TreeMap};use tab_map::{TabMap, TabSnapshot};use text::{BufferId, LineIndent};use ui::{SharedString, px};use unicode_segmentation::UnicodeSegmentation;use wrap_map::{WrapMap, WrapSnapshot}; #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub enum FoldStatus {    Folded,    Foldable,} pub trait ToDisplayPoint {    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;} type TextHighlights = TreeMap<TypeId, 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,    /// 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,} 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,        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,            text_highlights: Default::default(),            inlay_highlights: Default::default(),            clip_at_line_ends: false,            masked: false,        }    }     pub fn snapshot(&mut self, cx: &mut Context<Self>) -> DisplaySnapshot {        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.clone(), edits);        let tab_size = Self::tab_size(&self.buffer, cx);        let (tab_snapshot, edits) = self.tab_map.sync(fold_snapshot.clone(), edits, tab_size);        let (wrap_snapshot, edits) = self            .wrap_map            .update(cx, |map, cx| map.sync(tab_snapshot.clone(), edits, cx));        let block_snapshot = self.block_map.read(wrap_snapshot.clone(), edits).snapshot;         DisplaySnapshot {            buffer_snapshot: self.buffer.read(cx).snapshot(cx),            fold_snapshot,            inlay_snapshot,            tab_snapshot,            wrap_snapshot,            block_snapshot,            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(),        }    }     pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut Context<Self>) {        self.fold(            other                .folds_in_range(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.    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.    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.    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);    }     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)    }     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)    }     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)    }     pub(crate) fn is_buffer_folded(&self, buffer_id: language::BufferId) -> bool {        self.block_map.folded_buffers.contains(&buffer_id)    }     pub(crate) fn folded_buffers(&self) -> &HashSet<BufferId> {        &self.block_map.folded_buffers    }     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)    }     pub fn remove_creases(        &mut self,        crease_ids: impl IntoIterator<Item = CreaseId>,        cx: &mut Context<Self>,    ) {        let snapshot = self.buffer.read(cx).snapshot(cx);        self.crease_map.remove(crease_ids, &snapshot)    }     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)    }     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);        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.resize(heights);    }     pub fn replace_blocks(&mut self, renderers: HashMap<CustomBlockId, RenderBlock>) {        self.block_map.replace_blocks(renderers);    }     pub fn remove_blocks(&mut self, ids: HashSet<CustomBlockId>, 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.remove(ids);    }     pub fn row_for_block(        &mut self,        block_id: CustomBlockId,        cx: &mut Context<Self>,    ) -> Option<DisplayRow> {        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 block_map = self.block_map.read(snapshot, edits);        let block_row = block_map.row_for_block(block_id)?;        Some(DisplayRow(block_row.0))    }     pub fn highlight_text(        &mut self,        type_id: TypeId,        ranges: Vec<Range<Anchor>>,        style: HighlightStyle,    ) {        self.text_highlights            .insert(type_id, Arc::new((style, ranges)));    }     pub(crate) fn highlight_inlays(        &mut self,        type_id: TypeId,        highlights: Vec<InlayHighlight>,        style: HighlightStyle,    ) {        for highlight in highlights {            let update = self.inlay_highlights.update(&type_id, |highlights| {                highlights.insert(highlight.inlay, (style, highlight.clone()))            });            if update.is_none() {                self.inlay_highlights.insert(                    type_id,                    TreeMap::from_ordered_entries([(highlight.inlay, (style, highlight))]),                );            }        }    }     pub fn text_highlights(&self, type_id: TypeId) -> Option<(HighlightStyle, &[Range<Anchor>])> {        let highlights = self.text_highlights.get(&type_id)?;        Some((highlights.0, &highlights.1))    }

Lines 1–500 of 2857.