Start on text highlight support

This commit is contained in:
Nathan Sobo 2022-03-09 14:53:31 -07:00
parent 3dc100adfb
commit ac1eb19f83
15 changed files with 198 additions and 91 deletions

View file

@ -7,10 +7,13 @@ use crate::{Anchor, MultiBuffer, MultiBufferSnapshot, ToOffset, ToPoint};
use block_map::{BlockMap, BlockPoint};
use collections::{HashMap, HashSet};
use fold_map::{FoldMap, ToFoldPoint as _};
use gpui::{fonts::FontId, Entity, ModelContext, ModelHandle};
use gpui::{
fonts::{FontId, HighlightStyle},
Entity, ModelContext, ModelHandle,
};
use language::{Point, Subscription as BufferSubscription};
use std::ops::Range;
use sum_tree::Bias;
use std::{any::TypeId, ops::Range, sync::Arc};
use sum_tree::{Bias, TreeMap};
use tab_map::TabMap;
use wrap_map::WrapMap;
@ -23,6 +26,8 @@ pub trait ToDisplayPoint {
fn to_display_point(&self, map: &DisplaySnapshot) -> DisplayPoint;
}
type TextHighlights = TreeMap<Option<TypeId>, Arc<(HighlightStyle, Vec<Range<Anchor>>)>>;
pub struct DisplayMap {
buffer: ModelHandle<MultiBuffer>,
buffer_subscription: BufferSubscription,
@ -30,6 +35,7 @@ pub struct DisplayMap {
tab_map: TabMap,
wrap_map: ModelHandle<WrapMap>,
block_map: BlockMap,
text_highlights: TextHighlights,
}
impl Entity for DisplayMap {
@ -60,6 +66,7 @@ impl DisplayMap {
tab_map,
wrap_map,
block_map,
text_highlights: Default::default(),
}
}
@ -79,6 +86,7 @@ impl DisplayMap {
tabs_snapshot,
wraps_snapshot,
blocks_snapshot,
text_highlights: self.text_highlights.clone(),
}
}
@ -156,6 +164,20 @@ impl DisplayMap {
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 fn clear_text_highlights(&mut self, type_id: TypeId) {
self.text_highlights.remove(&Some(type_id));
}
pub fn set_font(&self, font_id: FontId, font_size: f32, cx: &mut ModelContext<Self>) {
self.wrap_map
.update(cx, |map, cx| map.set_font(font_id, font_size, cx));
@ -178,6 +200,7 @@ pub struct DisplaySnapshot {
tabs_snapshot: tab_map::TabSnapshot,
wraps_snapshot: wrap_map::WrapSnapshot,
blocks_snapshot: block_map::BlockSnapshot,
text_highlights: TextHighlights,
}
impl DisplaySnapshot {
@ -1146,7 +1169,7 @@ mod tests {
let mut chunks: Vec<(String, Option<Color>)> = Vec::new();
for chunk in snapshot.chunks(rows, true) {
let color = chunk
.highlight_id
.syntax_highlight_id
.and_then(|id| id.style(theme).map(|s| s.color));
if let Some((last_chunk, last_color)) = chunks.last_mut() {
if color == *last_color {

View file

@ -807,7 +807,8 @@ impl<'a> Iterator for BlockChunks<'a> {
return Some(Chunk {
text: unsafe { std::str::from_utf8_unchecked(&NEWLINES[..line_count as usize]) },
highlight_id: None,
syntax_highlight_id: None,
highlight_style: None,
diagnostic: None,
});
}

View file

@ -984,7 +984,8 @@ impl<'a> Iterator for FoldChunks<'a> {
self.output_offset += output_text.len();
return Some(Chunk {
text: output_text,
highlight_id: None,
syntax_highlight_id: None,
highlight_style: None,
diagnostic: None,
});
}

View file

@ -440,7 +440,7 @@ pub struct Editor {
vertical_scroll_margin: f32,
placeholder_text: Option<Arc<str>>,
highlighted_rows: Option<Range<u32>>,
highlighted_ranges: BTreeMap<TypeId, (Color, Vec<Range<Anchor>>)>,
background_highlights: BTreeMap<TypeId, (Color, Vec<Range<Anchor>>)>,
nav_history: Option<ItemNavHistory>,
context_menu: Option<ContextMenu>,
completion_tasks: Vec<(CompletionId, Task<Option<()>>)>,
@ -920,7 +920,7 @@ impl Editor {
vertical_scroll_margin: 3.0,
placeholder_text: None,
highlighted_rows: None,
highlighted_ranges: Default::default(),
background_highlights: Default::default(),
nav_history: None,
context_menu: None,
completion_tasks: Default::default(),
@ -2350,7 +2350,7 @@ impl Editor {
if let Some(editor) = editor.act_as::<Self>(cx) {
editor.update(cx, |editor, cx| {
let color = editor.style(cx).highlighted_line_background;
editor.highlight_ranges::<Self>(ranges_to_highlight, color, cx);
editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
});
}
});
@ -2444,12 +2444,12 @@ impl Editor {
}
}
this.highlight_ranges::<DocumentHighlightRead>(
this.highlight_background::<DocumentHighlightRead>(
read_ranges,
read_background,
cx,
);
this.highlight_ranges::<DocumentHighlightWrite>(
this.highlight_background::<DocumentHighlightWrite>(
write_ranges,
write_background,
cx,
@ -4333,7 +4333,7 @@ impl Editor {
if let Some(editor) = editor.act_as::<Self>(cx) {
editor.update(cx, |editor, cx| {
let color = editor.style(cx).highlighted_line_background;
editor.highlight_ranges::<Self>(ranges_to_highlight, color, cx);
editor.highlight_background::<Self>(ranges_to_highlight, color, cx);
});
}
});
@ -4398,14 +4398,14 @@ impl Editor {
None,
cx,
);
editor.highlight_ranges::<Rename>(
editor.highlight_background::<Rename>(
vec![Anchor::min()..Anchor::max()],
style.diff_background_inserted,
cx,
);
editor
});
this.highlight_ranges::<Rename>(
this.highlight_background::<Rename>(
vec![range.clone()],
style.diff_background_deleted,
cx,
@ -4500,7 +4500,7 @@ impl Editor {
fn take_rename(&mut self, cx: &mut ViewContext<Self>) -> Option<RenameState> {
let rename = self.pending_rename.take()?;
self.remove_blocks([rename.block_id].into_iter().collect(), cx);
self.clear_highlighted_ranges::<Rename>(cx);
self.clear_background_highlights::<Rename>(cx);
let editor = rename.editor.read(cx);
let snapshot = self.buffer.read(cx).snapshot(cx);
@ -4545,7 +4545,7 @@ impl Editor {
}
let rename = self.pending_rename.take().unwrap();
self.remove_blocks([rename.block_id].into_iter().collect(), cx);
self.clear_highlighted_ranges::<Rename>(cx);
self.clear_background_highlights::<Rename>(cx);
}
}
@ -5265,7 +5265,7 @@ impl Editor {
.update(cx, |map, cx| map.set_wrap_width(width, cx))
}
pub fn set_highlighted_rows(&mut self, rows: Option<Range<u32>>) {
pub fn highlight_rows(&mut self, rows: Option<Range<u32>>) {
self.highlighted_rows = rows;
}
@ -5273,27 +5273,27 @@ impl Editor {
self.highlighted_rows.clone()
}
pub fn highlight_ranges<T: 'static>(
pub fn highlight_background<T: 'static>(
&mut self,
ranges: Vec<Range<Anchor>>,
color: Color,
cx: &mut ViewContext<Self>,
) {
self.highlighted_ranges
self.background_highlights
.insert(TypeId::of::<T>(), (color, ranges));
cx.notify();
}
pub fn clear_highlighted_ranges<T: 'static>(
pub fn clear_background_highlights<T: 'static>(
&mut self,
cx: &mut ViewContext<Self>,
) -> Option<(Color, Vec<Range<Anchor>>)> {
cx.notify();
self.highlighted_ranges.remove(&TypeId::of::<T>())
self.background_highlights.remove(&TypeId::of::<T>())
}
#[cfg(feature = "test-support")]
pub fn all_highlighted_ranges(
pub fn all_background_highlights(
&mut self,
cx: &mut ViewContext<Self>,
) -> Vec<(Range<DisplayPoint>, Color)> {
@ -5301,23 +5301,23 @@ impl Editor {
let buffer = &snapshot.buffer_snapshot;
let start = buffer.anchor_before(0);
let end = buffer.anchor_after(buffer.len());
self.highlighted_ranges_in_range(start..end, &snapshot)
self.background_highlights_in_range(start..end, &snapshot)
}
pub fn highlighted_ranges_for_type<T: 'static>(&self) -> Option<(Color, &[Range<Anchor>])> {
self.highlighted_ranges
pub fn background_highlights_for_type<T: 'static>(&self) -> Option<(Color, &[Range<Anchor>])> {
self.background_highlights
.get(&TypeId::of::<T>())
.map(|(color, ranges)| (*color, ranges.as_slice()))
}
pub fn highlighted_ranges_in_range(
pub fn background_highlights_in_range(
&self,
search_range: Range<Anchor>,
display_snapshot: &DisplaySnapshot,
) -> Vec<(Range<DisplayPoint>, Color)> {
let mut results = Vec::new();
let buffer = &display_snapshot.buffer_snapshot;
for (color, ranges) in self.highlighted_ranges.values() {
for (color, ranges) in self.background_highlights.values() {
let start_ix = match ranges.binary_search_by(|probe| {
let cmp = probe.end.cmp(&search_range.start, &buffer).unwrap();
if cmp.is_gt() {
@ -5346,6 +5346,24 @@ impl Editor {
results
}
pub fn highlight_text<T: 'static>(
&mut self,
ranges: Vec<Range<Anchor>>,
style: HighlightStyle,
cx: &mut ViewContext<Self>,
) {
self.display_map.update(cx, |map, _| {
map.highlight_text(TypeId::of::<T>(), ranges, style)
});
cx.notify();
}
pub fn clear_text_highlights<T: 'static>(&mut self, cx: &mut ViewContext<Self>) {
self.display_map
.update(cx, |map, _| map.clear_text_highlights(TypeId::of::<T>()));
cx.notify();
}
fn next_blink_epoch(&mut self) -> usize {
self.blink_epoch += 1;
self.blink_epoch
@ -8868,7 +8886,7 @@ mod tests {
buffer.anchor_after(range.start)..buffer.anchor_after(range.end)
};
editor.highlight_ranges::<Type1>(
editor.highlight_background::<Type1>(
vec![
anchor_range(Point::new(2, 1)..Point::new(2, 3)),
anchor_range(Point::new(4, 2)..Point::new(4, 4)),
@ -8878,7 +8896,7 @@ mod tests {
Color::red(),
cx,
);
editor.highlight_ranges::<Type2>(
editor.highlight_background::<Type2>(
vec![
anchor_range(Point::new(3, 2)..Point::new(3, 5)),
anchor_range(Point::new(5, 3)..Point::new(5, 6)),
@ -8890,7 +8908,7 @@ mod tests {
);
let snapshot = editor.snapshot(cx);
let mut highlighted_ranges = editor.highlighted_ranges_in_range(
let mut highlighted_ranges = editor.background_highlights_in_range(
anchor_range(Point::new(3, 4)..Point::new(7, 4)),
&snapshot,
);
@ -8919,7 +8937,7 @@ mod tests {
]
);
assert_eq!(
editor.highlighted_ranges_in_range(
editor.background_highlights_in_range(
anchor_range(Point::new(5, 6)..Point::new(6, 4)),
&snapshot,
),

View file

@ -606,30 +606,33 @@ impl EditorElement {
} else {
let style = &self.style;
let chunks = snapshot.chunks(rows.clone(), true).map(|chunk| {
let highlight_style = chunk
.highlight_id
.and_then(|highlight_id| highlight_id.style(&style.syntax));
let highlight = if let Some(severity) = chunk.diagnostic {
let mut highlight_style = HighlightStyle {
color: style.text.color,
font_properties: style.text.font_properties,
..Default::default()
};
if let Some(syntax_highlight_style) = chunk
.syntax_highlight_id
.and_then(|id| id.style(&style.syntax))
{
highlight_style.highlight(syntax_highlight_style);
}
if let Some(style) = chunk.highlight_style {
highlight_style.highlight(style);
}
if let Some(severity) = chunk.diagnostic {
let diagnostic_style = super::diagnostic_style(severity, true, style);
let underline = Some(Underline {
highlight_style.underline = Some(Underline {
color: diagnostic_style.message.text.color,
thickness: 1.0.into(),
squiggly: true,
});
if let Some(mut highlight) = highlight_style {
highlight.underline = underline;
Some(highlight)
} else {
Some(HighlightStyle {
underline,
color: style.text.color,
font_properties: style.text.font_properties,
})
}
} else {
highlight_style
};
(chunk.text, highlight)
}
(chunk.text, highlight_style)
});
layout_highlighted_chunks(
chunks,
@ -852,7 +855,7 @@ impl Element for EditorElement {
let display_map = view.display_map.update(cx, |map, cx| map.snapshot(cx));
highlighted_rows = view.highlighted_rows();
highlighted_ranges = view.highlighted_ranges_in_range(
highlighted_ranges = view.background_highlights_in_range(
start_anchor.clone()..end_anchor.clone(),
&display_map,
);