1887 lines · 68.9 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 fold_map;mod inlay_map;mod tab_map;mod wrap_map; use crate::{hover_links::InlayHighlight, movement::TextLayoutDetails, InlayId};use crate::{EditorStyle, RowExt};pub use block_map::{BlockMap, BlockPoint};use collections::{HashMap, HashSet};use fold_map::FoldMap;use gpui::{Font, HighlightStyle, Hsla, LineLayout, Model, ModelContext, Pixels, UnderlineStyle};use inlay_map::InlayMap;use language::{    language_settings::language_settings, OffsetUtf16, Point, Subscription as BufferSubscription,};use lsp::DiagnosticSeverity;use multi_buffer::{    Anchor, AnchorRangeExt, MultiBuffer, MultiBufferPoint, MultiBufferRow, MultiBufferSnapshot,    ToOffset, ToPoint,};use serde::Deserialize;use std::{any::TypeId, borrow::Cow, fmt::Debug, num::NonZeroU32, ops::Range, sync::Arc};use sum_tree::{Bias, TreeMap};use tab_map::TabMap; use wrap_map::WrapMap; pub use block_map::{    BlockBufferRows, BlockChunks as DisplayChunks, BlockContext, BlockDisposition, BlockId,    BlockProperties, BlockStyle, RenderBlock, TransformBlock,}; use self::block_map::BlockRow;pub use self::fold_map::{Fold, FoldId, FoldPoint};pub use self::inlay_map::{InlayOffset, InlayPoint};pub(crate) use inlay_map::Inlay; #[derive(Copy, Clone, Debug, PartialEq, Eq)]pub enum FoldStatus {    Folded,    Foldable,} const UNNECESSARY_CODE_FADE: f32 = 0.3; pub trait ToDisplayPoint {    fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;} type TextHighlights = TreeMap<Option<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: Model<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: Model<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,    pub clip_at_line_ends: bool,} impl DisplayMap {    pub fn new(        buffer: Model<MultiBuffer>,        font: Font,        font_size: Pixels,        wrap_width: Option<Pixels>,        buffer_header_height: u8,        excerpt_header_height: u8,        cx: &mut ModelContext<Self>,    ) -> Self {        let buffer_subscription = buffer.update(cx, |buffer, _| buffer.subscribe());         let tab_size = Self::tab_size(&buffer, cx);        let (inlay_map, snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));        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,            text_highlights: Default::default(),            inlay_highlights: Default::default(),            clip_at_line_ends: false,        }    }     pub fn snapshot(&mut self, cx: &mut ModelContext<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);         DisplaySnapshot {            buffer_snapshot: self.buffer.read(cx).snapshot(cx),            fold_snapshot,            inlay_snapshot,            tab_snapshot,            wrap_snapshot,            block_snapshot,            text_highlights: self.text_highlights.clone(),            inlay_highlights: self.inlay_highlights.clone(),            clip_at_line_ends: self.clip_at_line_ends,        }    }     pub fn set_state(&mut self, other: &DisplaySnapshot, cx: &mut ModelContext<Self>) {        self.fold(            other                .folds_in_range(0..other.buffer_snapshot.len())                .map(|fold| fold.range.to_offset(&other.buffer_snapshot)),            cx,        );    }     pub fn fold<T: ToOffset>(        &mut self,        ranges: impl IntoIterator<Item = Range<T>>,        cx: &mut ModelContext<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.fold(ranges);        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);    }     pub fn unfold<T: ToOffset>(        &mut self,        ranges: impl IntoIterator<Item = Range<T>>,        inclusive: bool,        cx: &mut ModelContext<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.unfold(ranges, 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));        self.block_map.read(snapshot, edits);    }     pub fn insert_blocks(        &mut self,        blocks: impl IntoIterator<Item = BlockProperties<Anchor>>,        cx: &mut ModelContext<Self>,    ) -> Vec<BlockId> {        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 replace_blocks(&mut self, styles: HashMap<BlockId, RenderBlock>) {        self.block_map.replace(styles);    }     pub fn remove_blocks(&mut self, ids: HashSet<BlockId>, cx: &mut ModelContext<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 highlight_text(        &mut self,        type_id: TypeId,        ranges: Vec<Range<Anchor>>,        style: HighlightStyle,    ) {        self.text_highlights            .insert(Some(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(&Some(type_id))?;        Some((highlights.0, &highlights.1))    }    pub fn clear_highlights(&mut self, type_id: TypeId) -> bool {        let mut cleared = self.text_highlights.remove(&Some(type_id)).is_some();        cleared |= self.inlay_highlights.remove(&type_id).is_some();        cleared    }     pub fn set_font(&self, font: Font, font_size: Pixels, cx: &mut ModelContext<Self>) -> bool {        self.wrap_map            .update(cx, |map, cx| map.set_font_with_size(font, font_size, cx))    }     pub fn set_fold_ellipses_color(&mut self, color: Hsla) -> bool {        self.fold_map.set_ellipses_color(color)    }     pub fn set_wrap_width(&self, width: Option<Pixels>, cx: &mut ModelContext<Self>) -> bool {        self.wrap_map            .update(cx, |map, cx| map.set_wrap_width(width, cx))    }     pub(crate) fn current_inlays(&self) -> impl Iterator<Item = &Inlay> {        self.inlay_map.current_inlays()    }     pub(crate) fn splice_inlays(        &mut self,        to_remove: Vec<InlayId>,        to_insert: Vec<Inlay>,        cx: &mut ModelContext<Self>,    ) {        if to_remove.is_empty() && to_insert.is_empty() {            return;        }        let buffer_snapshot = self.buffer.read(cx).snapshot(cx);        let edits = self.buffer_subscription.consume().into_inner();        let (snapshot, edits) = self.inlay_map.sync(buffer_snapshot, edits);        let (snapshot, edits) = self.fold_map.read(snapshot, edits);        let tab_size = Self::tab_size(&self.buffer, cx);        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) = self.inlay_map.splice(to_remove, to_insert);        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));        self.block_map.read(snapshot, edits);    }     fn tab_size(buffer: &Model<MultiBuffer>, cx: &mut ModelContext<Self>) -> NonZeroU32 {        let language = buffer            .read(cx)            .as_singleton()            .and_then(|buffer| buffer.read(cx).language());        language_settings(language, None, cx).tab_size    }     #[cfg(test)]    pub fn is_rewrapping(&self, cx: &gpui::AppContext) -> bool {        self.wrap_map.read(cx).is_rewrapping()    }} #[derive(Debug, Default)]pub(crate) struct Highlights<'a> {    pub text_highlights: Option<&'a TextHighlights>,    pub inlay_highlights: Option<&'a InlayHighlights>,    pub styles: HighlightStyles,} #[derive(Default, Debug, Clone, Copy)]pub struct HighlightStyles {    pub inlay_hint: Option<HighlightStyle>,    pub suggestion: Option<HighlightStyle>,} pub struct HighlightedChunk<'a> {    pub chunk: &'a str,    pub style: Option<HighlightStyle>,    pub is_tab: bool,} #[derive(Clone)]pub struct DisplaySnapshot {    pub buffer_snapshot: MultiBufferSnapshot,    pub fold_snapshot: fold_map::FoldSnapshot,    inlay_snapshot: inlay_map::InlaySnapshot,    tab_snapshot: tab_map::TabSnapshot,    wrap_snapshot: wrap_map::WrapSnapshot,    block_snapshot: block_map::BlockSnapshot,    text_highlights: TextHighlights,    inlay_highlights: InlayHighlights,    clip_at_line_ends: bool,} impl DisplaySnapshot {    #[cfg(test)]    pub fn fold_count(&self) -> usize {        self.fold_snapshot.fold_count()    }     pub fn is_empty(&self) -> bool {        self.buffer_snapshot.len() == 0    }     pub fn buffer_rows(        &self,        start_row: DisplayRow,    ) -> impl Iterator<Item = Option<MultiBufferRow>> + '_ {        self.block_snapshot            .buffer_rows(BlockRow(start_row.0))            .map(|row| row.map(|row| MultiBufferRow(row.0)))    }     pub fn max_buffer_row(&self) -> MultiBufferRow {        self.buffer_snapshot.max_buffer_row()    }     pub fn prev_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {        loop {            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Left);            fold_point.0.column = 0;            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);            point = self.inlay_snapshot.to_buffer_point(inlay_point);             let mut display_point = self.point_to_display_point(point, Bias::Left);            *display_point.column_mut() = 0;            let next_point = self.display_point_to_point(display_point, Bias::Left);            if next_point == point {                return (point, display_point);            }            point = next_point;        }    }     pub fn next_line_boundary(&self, mut point: MultiBufferPoint) -> (Point, DisplayPoint) {        loop {            let mut inlay_point = self.inlay_snapshot.to_inlay_point(point);            let mut fold_point = self.fold_snapshot.to_fold_point(inlay_point, Bias::Right);            fold_point.0.column = self.fold_snapshot.line_len(fold_point.row());            inlay_point = fold_point.to_inlay_point(&self.fold_snapshot);            point = self.inlay_snapshot.to_buffer_point(inlay_point);             let mut display_point = self.point_to_display_point(point, Bias::Right);            *display_point.column_mut() = self.line_len(display_point.row());            let next_point = self.display_point_to_point(display_point, Bias::Right);            if next_point == point {                return (point, display_point);            }            point = next_point;        }    }     // used by line_mode selections and tries to match vim behaviour    pub fn expand_to_line(&self, range: Range<Point>) -> Range<Point> {        let new_start = if range.start.row == 0 {            MultiBufferPoint::new(0, 0)        } else if range.start.row == self.max_buffer_row().0            || (range.end.column > 0 && range.end.row == self.max_buffer_row().0)        {            MultiBufferPoint::new(                range.start.row - 1,                self.buffer_snapshot                    .line_len(MultiBufferRow(range.start.row - 1)),            )        } else {            self.prev_line_boundary(range.start).0        };         let new_end = if range.end.column == 0 {            range.end        } else if range.end.row < self.max_buffer_row().0 {            self.buffer_snapshot                .clip_point(MultiBufferPoint::new(range.end.row + 1, 0), Bias::Left)        } else {            self.buffer_snapshot.max_point()        };         new_start..new_end    }     fn point_to_display_point(&self, point: MultiBufferPoint, bias: Bias) -> DisplayPoint {        let inlay_point = self.inlay_snapshot.to_inlay_point(point);        let fold_point = self.fold_snapshot.to_fold_point(inlay_point, bias);        let tab_point = self.tab_snapshot.to_tab_point(fold_point);        let wrap_point = self.wrap_snapshot.tab_point_to_wrap_point(tab_point);        let block_point = self.block_snapshot.to_block_point(wrap_point);        DisplayPoint(block_point)    }     fn display_point_to_point(&self, point: DisplayPoint, bias: Bias) -> Point {        self.inlay_snapshot            .to_buffer_point(self.display_point_to_inlay_point(point, bias))    }     pub fn display_point_to_inlay_offset(&self, point: DisplayPoint, bias: Bias) -> InlayOffset {        self.inlay_snapshot            .to_offset(self.display_point_to_inlay_point(point, bias))    }     pub fn anchor_to_inlay_offset(&self, anchor: Anchor) -> InlayOffset {        self.inlay_snapshot            .to_inlay_offset(anchor.to_offset(&self.buffer_snapshot))    }     pub fn display_point_to_anchor(&self, point: DisplayPoint, bias: Bias) -> Anchor {        self.buffer_snapshot            .anchor_at(point.to_offset(&self, bias), bias)    }     fn display_point_to_inlay_point(&self, point: DisplayPoint, bias: Bias) -> InlayPoint {        let block_point = point.0;        let wrap_point = self.block_snapshot.to_wrap_point(block_point);        let tab_point = self.wrap_snapshot.to_tab_point(wrap_point);        let fold_point = self.tab_snapshot.to_fold_point(tab_point, bias).0;

Lines 1–500 of 1887.