diff --git a/crates/editor/src/display_map/fold_map.rs b/crates/editor/src/display_map/fold_map.rs index fe3c0d86ab..7d8eae29ab 100644 --- a/crates/editor/src/display_map/fold_map.rs +++ b/crates/editor/src/display_map/fold_map.rs @@ -3,15 +3,13 @@ use super::{ TextHighlights, }; use crate::{Anchor, AnchorRangeExt, MultiBufferSnapshot, ToOffset}; -use collections::BTreeMap; use gpui::{color::Color, fonts::HighlightStyle}; use language::{Chunk, Edit, Point, TextSummary}; use std::{ any::TypeId, cmp::{self, Ordering}, - iter::{self, Peekable}, + iter, ops::{Add, AddAssign, Range, Sub}, - vec, }; use sum_tree::{Bias, Cursor, FilterCursor, SumTree}; @@ -656,7 +654,6 @@ impl FoldSnapshot { text_highlights: Option<&'a TextHighlights>, inlay_highlights: Option, ) -> FoldChunks<'a> { - let mut highlight_endpoints = Vec::new(); let mut transform_cursor = self.transforms.cursor::<(FoldOffset, InlayOffset)>(); let inlay_end = { @@ -671,92 +668,18 @@ impl FoldSnapshot { transform_cursor.start().1 + InlayOffset(overshoot) }; - if let Some(text_highlights) = text_highlights { - if !text_highlights.is_empty() { - while transform_cursor.start().0 < range.end { - if !transform_cursor.item().unwrap().is_fold() { - let transform_start = self.inlay_snapshot.buffer.anchor_after( - self.inlay_snapshot.to_buffer_offset(cmp::max( - inlay_start, - transform_cursor.start().1, - )), - ); - - let transform_end = { - let overshoot = - InlayOffset(range.end.0 - transform_cursor.start().0 .0); - self.inlay_snapshot.buffer.anchor_before( - self.inlay_snapshot.to_buffer_offset(cmp::min( - transform_cursor.end(&()).1, - transform_cursor.start().1 + overshoot, - )), - ) - }; - - for (tag, highlights) in text_highlights.iter() { - let style = highlights.0; - let ranges = &highlights.1; - - let start_ix = match ranges.binary_search_by(|probe| { - let cmp = - probe.end.cmp(&transform_start, &self.inlay_snapshot.buffer); - if cmp.is_gt() { - Ordering::Greater - } else { - Ordering::Less - } - }) { - Ok(i) | Err(i) => i, - }; - for range in &ranges[start_ix..] { - if range - .start - .cmp(&transform_end, &self.inlay_snapshot.buffer) - .is_ge() - { - break; - } - - highlight_endpoints.push(HighlightEndpoint { - offset: self.inlay_snapshot.to_inlay_offset( - range.start.to_offset(&self.inlay_snapshot.buffer), - ), - is_start: true, - tag: *tag, - style, - }); - highlight_endpoints.push(HighlightEndpoint { - offset: self.inlay_snapshot.to_inlay_offset( - range.end.to_offset(&self.inlay_snapshot.buffer), - ), - is_start: false, - tag: *tag, - style, - }); - } - } - } - - transform_cursor.next(&()); - } - highlight_endpoints.sort(); - transform_cursor.seek(&range.start, Bias::Right, &()); - } - } - FoldChunks { transform_cursor, inlay_chunks: self.inlay_snapshot.chunks( inlay_start..inlay_end, language_aware, + text_highlights, inlay_highlights, ), inlay_chunk: None, inlay_offset: inlay_start, output_offset: range.start.0, max_output_offset: range.end.0, - highlight_endpoints: highlight_endpoints.into_iter().peekable(), - active_highlights: Default::default(), ellipses_color: self.ellipses_color, } } @@ -1034,8 +957,6 @@ pub struct FoldChunks<'a> { inlay_offset: InlayOffset, output_offset: usize, max_output_offset: usize, - highlight_endpoints: Peekable>, - active_highlights: BTreeMap, HighlightStyle>, ellipses_color: Option, } @@ -1073,21 +994,6 @@ impl<'a> Iterator for FoldChunks<'a> { }); } - let mut next_highlight_endpoint = InlayOffset(usize::MAX); - while let Some(endpoint) = self.highlight_endpoints.peek().copied() { - if endpoint.offset <= self.inlay_offset { - if endpoint.is_start { - self.active_highlights.insert(endpoint.tag, endpoint.style); - } else { - self.active_highlights.remove(&endpoint.tag); - } - self.highlight_endpoints.next(); - } else { - next_highlight_endpoint = endpoint.offset; - break; - } - } - // Retrieve a chunk from the current location in the buffer. if self.inlay_chunk.is_none() { let chunk_offset = self.inlay_chunks.offset(); @@ -1098,21 +1004,11 @@ impl<'a> Iterator for FoldChunks<'a> { if let Some((buffer_chunk_start, mut chunk)) = self.inlay_chunk { let buffer_chunk_end = buffer_chunk_start + InlayOffset(chunk.text.len()); let transform_end = self.transform_cursor.end(&()).1; - let chunk_end = buffer_chunk_end - .min(transform_end) - .min(next_highlight_endpoint); + let chunk_end = buffer_chunk_end.min(transform_end); chunk.text = &chunk.text [(self.inlay_offset - buffer_chunk_start).0..(chunk_end - buffer_chunk_start).0]; - if !self.active_highlights.is_empty() { - let mut highlight_style = HighlightStyle::default(); - for active_highlight in self.active_highlights.values() { - highlight_style.highlight(*active_highlight); - } - chunk.highlight_style = Some(highlight_style); - } - if chunk_end == transform_end { self.transform_cursor.next(&()); } else if chunk_end == buffer_chunk_end { diff --git a/crates/editor/src/display_map/inlay_map.rs b/crates/editor/src/display_map/inlay_map.rs index effd0576ef..7806c75f17 100644 --- a/crates/editor/src/display_map/inlay_map.rs +++ b/crates/editor/src/display_map/inlay_map.rs @@ -2,16 +2,21 @@ use crate::{ multi_buffer::{MultiBufferChunks, MultiBufferRows}, Anchor, InlayId, MultiBufferSnapshot, ToOffset, }; -use collections::{BTreeSet, HashMap}; +use collections::{BTreeMap, BTreeSet, HashMap}; use gpui::fonts::HighlightStyle; use language::{Chunk, Edit, Point, Rope, TextSummary}; use std::{ + any::TypeId, cmp, + iter::Peekable, ops::{Add, AddAssign, Range, Sub, SubAssign}, + vec, }; use sum_tree::{Bias, Cursor, SumTree}; use text::Patch; +use super::TextHighlights; + pub struct InlayMap { snapshot: InlaySnapshot, inlays_by_id: HashMap, @@ -160,6 +165,28 @@ pub struct InlayBufferRows<'a> { max_buffer_row: u32, } +#[derive(Copy, Clone, Eq, PartialEq)] +struct HighlightEndpoint { + offset: InlayOffset, + is_start: bool, + tag: Option, + style: HighlightStyle, +} + +impl PartialOrd for HighlightEndpoint { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for HighlightEndpoint { + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.offset + .cmp(&other.offset) + .then_with(|| other.is_start.cmp(&self.is_start)) + } +} + pub struct InlayChunks<'a> { transforms: Cursor<'a, Transform, (InlayOffset, usize)>, buffer_chunks: MultiBufferChunks<'a>, @@ -168,6 +195,8 @@ pub struct InlayChunks<'a> { output_offset: InlayOffset, max_output_offset: InlayOffset, highlight_style: Option, + highlight_endpoints: Peekable>, + active_highlights: BTreeMap, HighlightStyle>, snapshot: &'a InlaySnapshot, } @@ -195,6 +224,21 @@ impl<'a> Iterator for InlayChunks<'a> { return None; } + let mut next_highlight_endpoint = InlayOffset(usize::MAX); + while let Some(endpoint) = self.highlight_endpoints.peek().copied() { + if endpoint.offset <= self.output_offset { + if endpoint.is_start { + self.active_highlights.insert(endpoint.tag, endpoint.style); + } else { + self.active_highlights.remove(&endpoint.tag); + } + self.highlight_endpoints.next(); + } else { + next_highlight_endpoint = endpoint.offset; + break; + } + } + let chunk = match self.transforms.item()? { Transform::Isomorphic(_) => { let chunk = self @@ -204,17 +248,28 @@ impl<'a> Iterator for InlayChunks<'a> { *chunk = self.buffer_chunks.next().unwrap(); } - let (prefix, suffix) = chunk.text.split_at(cmp::min( - self.transforms.end(&()).0 .0 - self.output_offset.0, - chunk.text.len(), - )); + let (prefix, suffix) = chunk.text.split_at( + chunk + .text + .len() + .min(self.transforms.end(&()).0 .0 - self.output_offset.0) + .min(next_highlight_endpoint.0 - self.output_offset.0), + ); chunk.text = suffix; self.output_offset.0 += prefix.len(); - Chunk { + let mut prefix = Chunk { text: prefix, ..chunk.clone() + }; + if !self.active_highlights.is_empty() { + let mut highlight_style = HighlightStyle::default(); + for active_highlight in self.active_highlights.values() { + highlight_style.highlight(*active_highlight); + } + prefix.highlight_style = Some(highlight_style); } + prefix } Transform::Inlay(inlay) => { let inlay_chunks = self.inlay_chunks.get_or_insert_with(|| { @@ -871,11 +926,72 @@ impl InlaySnapshot { &'a self, range: Range, language_aware: bool, + text_highlights: Option<&'a TextHighlights>, inlay_highlight_style: Option, ) -> InlayChunks<'a> { let mut cursor = self.transforms.cursor::<(InlayOffset, usize)>(); cursor.seek(&range.start, Bias::Right, &()); + let mut highlight_endpoints = Vec::new(); + if let Some(text_highlights) = text_highlights { + if !text_highlights.is_empty() { + while cursor.start().0 < range.end { + if true { + let transform_start = self.buffer.anchor_after( + self.to_buffer_offset(cmp::max(range.start, cursor.start().0)), + ); + + let transform_end = { + let overshoot = InlayOffset(range.end.0 - cursor.start().0 .0); + self.buffer.anchor_before(self.to_buffer_offset(cmp::min( + cursor.end(&()).0, + cursor.start().0 + overshoot, + ))) + }; + + for (tag, highlights) in text_highlights.iter() { + let style = highlights.0; + let ranges = &highlights.1; + + let start_ix = match ranges.binary_search_by(|probe| { + let cmp = probe.end.cmp(&transform_start, &self.buffer); + if cmp.is_gt() { + cmp::Ordering::Greater + } else { + cmp::Ordering::Less + } + }) { + Ok(i) | Err(i) => i, + }; + for range in &ranges[start_ix..] { + if range.start.cmp(&transform_end, &self.buffer).is_ge() { + break; + } + + highlight_endpoints.push(HighlightEndpoint { + offset: self + .to_inlay_offset(range.start.to_offset(&self.buffer)), + is_start: true, + tag: *tag, + style, + }); + highlight_endpoints.push(HighlightEndpoint { + offset: self.to_inlay_offset(range.end.to_offset(&self.buffer)), + is_start: false, + tag: *tag, + style, + }); + } + } + } + + cursor.next(&()); + } + highlight_endpoints.sort(); + cursor.seek(&range.start, Bias::Right, &()); + } + } + let buffer_range = self.to_buffer_offset(range.start)..self.to_buffer_offset(range.end); let buffer_chunks = self.buffer.chunks(buffer_range, language_aware); @@ -887,13 +1003,15 @@ impl InlaySnapshot { output_offset: range.start, max_output_offset: range.end, highlight_style: inlay_highlight_style, + highlight_endpoints: highlight_endpoints.into_iter().peekable(), + active_highlights: Default::default(), snapshot: self, } } #[cfg(test)] pub fn text(&self) -> String { - self.chunks(Default::default()..self.len(), false, None) + self.chunks(Default::default()..self.len(), false, None, None) .map(|chunk| chunk.text) .collect() } @@ -1371,7 +1489,7 @@ mod tests { start = expected_text.clip_offset(start, Bias::Right); let actual_text = inlay_snapshot - .chunks(InlayOffset(start)..InlayOffset(end), false, None) + .chunks(InlayOffset(start)..InlayOffset(end), false, None, None) .map(|chunk| chunk.text) .collect::(); assert_eq!(