mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 17:44:30 +00:00
Start on text highlight support
This commit is contained in:
parent
3dc100adfb
commit
ac1eb19f83
15 changed files with 198 additions and 91 deletions
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -126,7 +126,7 @@ impl GoToLine {
|
|||
let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
|
||||
let display_point = point.to_display_point(&snapshot);
|
||||
let row = display_point.row();
|
||||
active_editor.set_highlighted_rows(Some(row..row + 1));
|
||||
active_editor.highlight_rows(Some(row..row + 1));
|
||||
active_editor.request_autoscroll(Autoscroll::Center, cx);
|
||||
});
|
||||
cx.notify();
|
||||
|
@ -143,7 +143,7 @@ impl Entity for GoToLine {
|
|||
fn release(&mut self, cx: &mut MutableAppContext) {
|
||||
let scroll_position = self.prev_scroll_position.take();
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.set_highlighted_rows(None);
|
||||
editor.highlight_rows(None);
|
||||
if let Some(scroll_position) = scroll_position {
|
||||
editor.set_scroll_position(scroll_position, cx);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
|||
};
|
||||
|
||||
use crate::json::ToJson;
|
||||
use pathfinder_color::ColorU;
|
||||
use pathfinder_color::{ColorF, ColorU};
|
||||
use serde::{
|
||||
de::{self, Unexpected},
|
||||
Deserialize, Deserializer,
|
||||
|
@ -48,6 +48,29 @@ impl Color {
|
|||
pub fn from_u32(rgba: u32) -> Self {
|
||||
Self(ColorU::from_u32(rgba))
|
||||
}
|
||||
|
||||
pub fn blend(source: Color, dest: Color) -> Color {
|
||||
if dest.a == 255 {
|
||||
return dest;
|
||||
}
|
||||
|
||||
let source = source.0.to_f32();
|
||||
let dest = dest.0.to_f32();
|
||||
|
||||
let a = source.a() + (dest.a() * (1. - source.a()));
|
||||
let r = ((source.r() * source.a()) + (dest.r() * dest.a() * (1. - source.a()))) / a;
|
||||
let g = ((source.g() * source.a()) + (dest.g() * dest.a() * (1. - source.a()))) / a;
|
||||
let b = ((source.b() * source.a()) + (dest.b() * dest.a() * (1. - source.a()))) / a;
|
||||
|
||||
Self(ColorF::new(r, g, b, a).to_u8())
|
||||
}
|
||||
|
||||
pub fn fade_out(&mut self, factor: f32) {
|
||||
let source_alpha = 1. - factor.clamp(0., 1.);
|
||||
let dest_alpha = self.0.a as f32 / 255.;
|
||||
let dest_alpha = source_alpha + (dest_alpha * (1. - source_alpha));
|
||||
self.0.a = (dest_alpha * (1. / 255.)) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Color {
|
||||
|
|
|
@ -67,17 +67,20 @@ impl Element for Text {
|
|||
let mut highlight_ranges = self.highlights.iter().peekable();
|
||||
let chunks = std::iter::from_fn(|| {
|
||||
let result;
|
||||
if let Some((range, highlight)) = highlight_ranges.peek() {
|
||||
if let Some((range, highlight_style)) = highlight_ranges.peek() {
|
||||
if offset < range.start {
|
||||
result = Some((&self.text[offset..range.start], None));
|
||||
result = Some((
|
||||
&self.text[offset..range.start],
|
||||
HighlightStyle::from(&self.style),
|
||||
));
|
||||
offset = range.start;
|
||||
} else {
|
||||
result = Some((&self.text[range.clone()], Some(*highlight)));
|
||||
result = Some((&self.text[range.clone()], *highlight_style));
|
||||
highlight_ranges.next();
|
||||
offset = range.end;
|
||||
}
|
||||
} else if offset < self.text.len() {
|
||||
result = Some((&self.text[offset..], None));
|
||||
result = Some((&self.text[offset..], HighlightStyle::from(&self.style)));
|
||||
offset = self.text.len();
|
||||
} else {
|
||||
result = None;
|
||||
|
@ -197,24 +200,24 @@ impl Element for Text {
|
|||
|
||||
/// Perform text layout on a series of highlighted chunks of text.
|
||||
pub fn layout_highlighted_chunks<'a>(
|
||||
chunks: impl Iterator<Item = (&'a str, Option<HighlightStyle>)>,
|
||||
style: &'a TextStyle,
|
||||
chunks: impl Iterator<Item = (&'a str, HighlightStyle)>,
|
||||
text_style: &'a TextStyle,
|
||||
text_layout_cache: &'a TextLayoutCache,
|
||||
font_cache: &'a Arc<FontCache>,
|
||||
max_line_len: usize,
|
||||
max_line_count: usize,
|
||||
) -> Vec<Line> {
|
||||
let mut layouts = Vec::with_capacity(max_line_count);
|
||||
let mut prev_font_properties = style.font_properties.clone();
|
||||
let mut prev_font_id = style.font_id;
|
||||
let mut prev_font_properties = text_style.font_properties.clone();
|
||||
let mut prev_font_id = text_style.font_id;
|
||||
let mut line = String::new();
|
||||
let mut styles = Vec::new();
|
||||
let mut row = 0;
|
||||
let mut line_exceeded_max_len = false;
|
||||
for (chunk, highlight_style) in chunks.chain([("\n", None)]) {
|
||||
for (chunk, highlight_style) in chunks.chain([("\n", Default::default())]) {
|
||||
for (ix, mut line_chunk) in chunk.split('\n').enumerate() {
|
||||
if ix > 0 {
|
||||
layouts.push(text_layout_cache.layout_str(&line, style.font_size, &styles));
|
||||
layouts.push(text_layout_cache.layout_str(&line, text_style.font_size, &styles));
|
||||
line.clear();
|
||||
styles.clear();
|
||||
row += 1;
|
||||
|
@ -225,15 +228,13 @@ pub fn layout_highlighted_chunks<'a>(
|
|||
}
|
||||
|
||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||
let highlight_style = highlight_style.unwrap_or(style.clone().into());
|
||||
|
||||
// Avoid a lookup if the font properties match the previous ones.
|
||||
let font_id = if highlight_style.font_properties == prev_font_properties {
|
||||
prev_font_id
|
||||
} else {
|
||||
font_cache
|
||||
.select_font(style.font_family_id, &highlight_style.font_properties)
|
||||
.unwrap_or(style.font_id)
|
||||
.select_font(text_style.font_family_id, &highlight_style.font_properties)
|
||||
.unwrap_or(text_style.font_id)
|
||||
};
|
||||
|
||||
if line.len() + line_chunk.len() > max_line_len {
|
||||
|
|
|
@ -31,13 +31,16 @@ pub struct TextStyle {
|
|||
pub underline: Option<Underline>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct HighlightStyle {
|
||||
pub color: Color,
|
||||
pub font_properties: Properties,
|
||||
pub underline: Option<Underline>,
|
||||
pub fade_out: Option<f32>,
|
||||
}
|
||||
|
||||
impl Eq for HighlightStyle {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct Underline {
|
||||
pub color: Color,
|
||||
|
@ -83,6 +86,8 @@ struct HighlightStyleJson {
|
|||
italic: bool,
|
||||
#[serde(default)]
|
||||
underline: UnderlineStyleJson,
|
||||
#[serde(default)]
|
||||
fade_out: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -131,7 +136,10 @@ impl TextStyle {
|
|||
if self.font_properties != style.font_properties {
|
||||
self.font_id = font_cache.select_font(self.font_family_id, &style.font_properties)?;
|
||||
}
|
||||
self.color = style.color;
|
||||
self.color = Color::blend(self.color, style.color);
|
||||
if let Some(factor) = style.fade_out {
|
||||
self.color.fade_out(factor);
|
||||
}
|
||||
self.underline = style.underline;
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -199,10 +207,17 @@ impl TextStyle {
|
|||
|
||||
impl From<TextStyle> for HighlightStyle {
|
||||
fn from(other: TextStyle) -> Self {
|
||||
Self::from(&other)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TextStyle> for HighlightStyle {
|
||||
fn from(other: &TextStyle) -> Self {
|
||||
Self {
|
||||
color: other.color,
|
||||
font_properties: other.font_properties,
|
||||
underline: other.underline,
|
||||
fade_out: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -246,6 +261,18 @@ impl HighlightStyle {
|
|||
color: json.color,
|
||||
font_properties,
|
||||
underline: underline_from_json(json.underline, json.color),
|
||||
fade_out: json.fade_out,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn highlight(&mut self, other: HighlightStyle) {
|
||||
self.color = Color::blend(other.color, self.color);
|
||||
if let Some(factor) = other.fade_out {
|
||||
self.color.fade_out(factor);
|
||||
}
|
||||
self.font_properties = other.font_properties;
|
||||
if other.underline.is_some() {
|
||||
self.underline = other.underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,6 +283,7 @@ impl From<Color> for HighlightStyle {
|
|||
color,
|
||||
font_properties: Default::default(),
|
||||
underline: None,
|
||||
fade_out: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -295,6 +323,7 @@ impl<'de> Deserialize<'de> for HighlightStyle {
|
|||
color: serde_json::from_value(json).map_err(de::Error::custom)?,
|
||||
font_properties: Properties::new(),
|
||||
underline: None,
|
||||
fade_out: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::{
|
|||
use anyhow::{anyhow, Result};
|
||||
use clock::ReplicaId;
|
||||
use futures::FutureExt as _;
|
||||
use gpui::{AppContext, Entity, ModelContext, MutableAppContext, Task};
|
||||
use gpui::{fonts::HighlightStyle, AppContext, Entity, ModelContext, MutableAppContext, Task};
|
||||
use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use similar::{ChangeTag, TextDiff};
|
||||
|
@ -248,7 +248,8 @@ pub struct BufferChunks<'a> {
|
|||
#[derive(Clone, Copy, Debug, Default)]
|
||||
pub struct Chunk<'a> {
|
||||
pub text: &'a str,
|
||||
pub highlight_id: Option<HighlightId>,
|
||||
pub syntax_highlight_id: Option<HighlightId>,
|
||||
pub highlight_style: Option<HighlightStyle>,
|
||||
pub diagnostic: Option<DiagnosticSeverity>,
|
||||
}
|
||||
|
||||
|
@ -1716,7 +1717,7 @@ impl BufferSnapshot {
|
|||
offset += chunk.text.len();
|
||||
}
|
||||
let style = chunk
|
||||
.highlight_id
|
||||
.syntax_highlight_id
|
||||
.zip(theme)
|
||||
.and_then(|(highlight, theme)| highlight.style(theme));
|
||||
if let Some(style) = style {
|
||||
|
@ -2086,7 +2087,8 @@ impl<'a> Iterator for BufferChunks<'a> {
|
|||
|
||||
Some(Chunk {
|
||||
text: slice,
|
||||
highlight_id,
|
||||
syntax_highlight_id: highlight_id,
|
||||
highlight_style: None,
|
||||
diagnostic: self.current_diagnostic_severity(),
|
||||
})
|
||||
} else {
|
||||
|
|
|
@ -8,7 +8,7 @@ pub struct HighlightMap(Arc<[HighlightId]>);
|
|||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct HighlightId(pub u32);
|
||||
|
||||
const DEFAULT_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
|
||||
const DEFAULT_SYNTAX_HIGHLIGHT_ID: HighlightId = HighlightId(u32::MAX);
|
||||
|
||||
impl HighlightMap {
|
||||
pub fn new(capture_names: &[String], theme: &SyntaxTheme) -> Self {
|
||||
|
@ -36,7 +36,7 @@ impl HighlightMap {
|
|||
Some((i, len))
|
||||
})
|
||||
.max_by_key(|(_, len)| *len)
|
||||
.map_or(DEFAULT_HIGHLIGHT_ID, |(i, _)| HighlightId(i as u32))
|
||||
.map_or(DEFAULT_SYNTAX_HIGHLIGHT_ID, |(i, _)| HighlightId(i as u32))
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
|
@ -46,7 +46,7 @@ impl HighlightMap {
|
|||
self.0
|
||||
.get(capture_id as usize)
|
||||
.copied()
|
||||
.unwrap_or(DEFAULT_HIGHLIGHT_ID)
|
||||
.unwrap_or(DEFAULT_SYNTAX_HIGHLIGHT_ID)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ impl Default for HighlightMap {
|
|||
|
||||
impl Default for HighlightId {
|
||||
fn default() -> Self {
|
||||
DEFAULT_HIGHLIGHT_ID
|
||||
DEFAULT_SYNTAX_HIGHLIGHT_ID
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -515,7 +515,7 @@ impl Language {
|
|||
for chunk in BufferChunks::new(text, range, Some(&tree), self.grammar.as_ref(), vec![])
|
||||
{
|
||||
let end_offset = offset + chunk.text.len();
|
||||
if let Some(highlight_id) = chunk.highlight_id {
|
||||
if let Some(highlight_id) = chunk.syntax_highlight_id {
|
||||
result.push((offset..end_offset, highlight_id));
|
||||
}
|
||||
offset = end_offset;
|
||||
|
|
|
@ -186,7 +186,7 @@ impl OutlineView {
|
|||
let end = outline_item.range.end.to_point(&buffer_snapshot);
|
||||
let display_rows = start.to_display_point(&snapshot).row()
|
||||
..end.to_display_point(&snapshot).row() + 1;
|
||||
active_editor.set_highlighted_rows(Some(display_rows));
|
||||
active_editor.highlight_rows(Some(display_rows));
|
||||
active_editor.request_autoscroll(Autoscroll::Center, cx);
|
||||
});
|
||||
}
|
||||
|
@ -207,7 +207,7 @@ impl OutlineView {
|
|||
|
||||
fn restore_active_editor(&mut self, cx: &mut MutableAppContext) {
|
||||
self.active_editor.update(cx, |editor, cx| {
|
||||
editor.set_highlighted_rows(None);
|
||||
editor.highlight_rows(None);
|
||||
if let Some(scroll_position) = self.prev_scroll_position {
|
||||
editor.set_scroll_position(scroll_position, cx);
|
||||
}
|
||||
|
|
|
@ -151,7 +151,9 @@ impl Toolbar for SearchBar {
|
|||
self.dismissed = true;
|
||||
for (editor, _) in &self.editors_with_matches {
|
||||
if let Some(editor) = editor.upgrade(cx) {
|
||||
editor.update(cx, |editor, cx| editor.clear_highlighted_ranges::<Self>(cx));
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.clear_background_highlights::<Self>(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -397,7 +399,9 @@ impl SearchBar {
|
|||
if Some(&editor) == self.active_editor.as_ref() {
|
||||
active_editor_matches = Some((editor.downgrade(), ranges));
|
||||
} else {
|
||||
editor.update(cx, |editor, cx| editor.clear_highlighted_ranges::<Self>(cx));
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.clear_background_highlights::<Self>(cx)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -410,7 +414,9 @@ impl SearchBar {
|
|||
if let Some(editor) = self.active_editor.as_ref() {
|
||||
if query.is_empty() {
|
||||
self.active_match_index.take();
|
||||
editor.update(cx, |editor, cx| editor.clear_highlighted_ranges::<Self>(cx));
|
||||
editor.update(cx, |editor, cx| {
|
||||
editor.clear_background_highlights::<Self>(cx)
|
||||
});
|
||||
} else {
|
||||
let buffer = editor.read(cx).buffer().read(cx).snapshot(cx);
|
||||
let query = if self.regex {
|
||||
|
@ -480,7 +486,7 @@ impl SearchBar {
|
|||
}
|
||||
}
|
||||
|
||||
editor.highlight_ranges::<Self>(
|
||||
editor.highlight_background::<Self>(
|
||||
ranges,
|
||||
theme.match_background,
|
||||
cx,
|
||||
|
@ -557,7 +563,7 @@ mod tests {
|
|||
editor.next_notification(&cx).await;
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.all_highlighted_ranges(cx),
|
||||
editor.all_background_highlights(cx),
|
||||
&[
|
||||
(
|
||||
DisplayPoint::new(2, 17)..DisplayPoint::new(2, 19),
|
||||
|
@ -578,7 +584,7 @@ mod tests {
|
|||
editor.next_notification(&cx).await;
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.all_highlighted_ranges(cx),
|
||||
editor.all_background_highlights(cx),
|
||||
&[(
|
||||
DisplayPoint::new(2, 43)..DisplayPoint::new(2, 45),
|
||||
Color::red(),
|
||||
|
@ -594,7 +600,7 @@ mod tests {
|
|||
editor.next_notification(&cx).await;
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.all_highlighted_ranges(cx),
|
||||
editor.all_background_highlights(cx),
|
||||
&[
|
||||
(
|
||||
DisplayPoint::new(0, 24)..DisplayPoint::new(0, 26),
|
||||
|
@ -635,7 +641,7 @@ mod tests {
|
|||
editor.next_notification(&cx).await;
|
||||
editor.update(cx, |editor, cx| {
|
||||
assert_eq!(
|
||||
editor.all_highlighted_ranges(cx),
|
||||
editor.all_background_highlights(cx),
|
||||
&[
|
||||
(
|
||||
DisplayPoint::new(0, 41)..DisplayPoint::new(0, 43),
|
||||
|
|
|
@ -565,7 +565,7 @@ impl ProjectSearchView {
|
|||
if reset_selections {
|
||||
editor.select_ranges(match_ranges.first().cloned(), Some(Autoscroll::Fit), cx);
|
||||
}
|
||||
editor.highlight_ranges::<Self>(match_ranges, theme.match_background, cx);
|
||||
editor.highlight_background::<Self>(match_ranges, theme.match_background, cx);
|
||||
});
|
||||
if self.query_editor.is_focused(cx) {
|
||||
self.focus_results_editor(cx);
|
||||
|
@ -764,7 +764,7 @@ mod tests {
|
|||
assert_eq!(
|
||||
search_view
|
||||
.results_editor
|
||||
.update(cx, |editor, cx| editor.all_highlighted_ranges(cx)),
|
||||
.update(cx, |editor, cx| editor.all_background_highlights(cx)),
|
||||
&[
|
||||
(
|
||||
DisplayPoint::new(2, 32)..DisplayPoint::new(2, 35),
|
||||
|
|
Loading…
Reference in a new issue