mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 02:48:34 +00:00
Make all HighlightStyle
properties optional
Previously, some of those properties such the font weight, style and color would be mandatory: when the theme didn't specify them, Zed would use a default value during deserialization. This meant that those default properties would unconditionally override the base text style, causing a rendering bug when combining syntax highlights with diagnostic styles. This commit fixes that by making `HighlightStyle`s more additive: each property can be set independently and only the properties that theme specifies get overridden in the base text style.
This commit is contained in:
parent
72692f1700
commit
fbf7cdf4f2
10 changed files with 149 additions and 146 deletions
|
@ -1174,7 +1174,7 @@ mod tests {
|
||||||
for chunk in snapshot.chunks(rows, true) {
|
for chunk in snapshot.chunks(rows, true) {
|
||||||
let color = chunk
|
let color = chunk
|
||||||
.syntax_highlight_id
|
.syntax_highlight_id
|
||||||
.and_then(|id| id.style(theme).map(|s| s.color));
|
.and_then(|id| id.style(theme)?.color);
|
||||||
if let Some((last_chunk, last_color)) = chunks.last_mut() {
|
if let Some((last_chunk, last_color)) = chunks.last_mut() {
|
||||||
if color == *last_color {
|
if color == *last_color {
|
||||||
last_chunk.push_str(chunk.text);
|
last_chunk.push_str(chunk.text);
|
||||||
|
|
|
@ -615,11 +615,7 @@ impl CompletionsMenu {
|
||||||
.with_highlights(combine_syntax_and_fuzzy_match_highlights(
|
.with_highlights(combine_syntax_and_fuzzy_match_highlights(
|
||||||
&completion.label.text,
|
&completion.label.text,
|
||||||
style.text.color.into(),
|
style.text.color.into(),
|
||||||
styled_runs_for_code_label(
|
styled_runs_for_code_label(&completion.label, &style.syntax),
|
||||||
&completion.label,
|
|
||||||
style.text.color,
|
|
||||||
&style.syntax,
|
|
||||||
),
|
|
||||||
&mat.positions,
|
&mat.positions,
|
||||||
))
|
))
|
||||||
.contained()
|
.contained()
|
||||||
|
@ -5716,7 +5712,7 @@ fn build_style(
|
||||||
font_id,
|
font_id,
|
||||||
font_size,
|
font_size,
|
||||||
font_properties,
|
font_properties,
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
placeholder_text: None,
|
placeholder_text: None,
|
||||||
theme,
|
theme,
|
||||||
|
@ -5938,7 +5934,7 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||||
|
|
||||||
for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
|
for (range, mut syntax_highlight) in syntax_ranges.chain([(usize::MAX..0, Default::default())])
|
||||||
{
|
{
|
||||||
syntax_highlight.font_properties.weight(Default::default());
|
syntax_highlight.weight = None;
|
||||||
|
|
||||||
// Add highlights for any fuzzy match characters before the next
|
// Add highlights for any fuzzy match characters before the next
|
||||||
// syntax highlight range.
|
// syntax highlight range.
|
||||||
|
@ -5949,7 +5945,7 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||||
match_indices.next();
|
match_indices.next();
|
||||||
let end_index = char_ix_after(match_index, text);
|
let end_index = char_ix_after(match_index, text);
|
||||||
let mut match_style = default_style;
|
let mut match_style = default_style;
|
||||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
match_style.weight = Some(fonts::Weight::BOLD);
|
||||||
result.push((match_index..end_index, match_style));
|
result.push((match_index..end_index, match_style));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5981,7 +5977,7 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut match_style = syntax_highlight;
|
let mut match_style = syntax_highlight;
|
||||||
match_style.font_properties.weight(fonts::Weight::BOLD);
|
match_style.weight = Some(fonts::Weight::BOLD);
|
||||||
result.push((match_index..end_index, match_style));
|
result.push((match_index..end_index, match_style));
|
||||||
offset = end_index;
|
offset = end_index;
|
||||||
}
|
}
|
||||||
|
@ -6000,16 +5996,12 @@ pub fn combine_syntax_and_fuzzy_match_highlights(
|
||||||
|
|
||||||
pub fn styled_runs_for_code_label<'a>(
|
pub fn styled_runs_for_code_label<'a>(
|
||||||
label: &'a CodeLabel,
|
label: &'a CodeLabel,
|
||||||
default_color: Color,
|
|
||||||
syntax_theme: &'a theme::SyntaxTheme,
|
syntax_theme: &'a theme::SyntaxTheme,
|
||||||
) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
|
) -> impl 'a + Iterator<Item = (Range<usize>, HighlightStyle)> {
|
||||||
const MUTED_OPACITY: usize = 165;
|
let fade_out = HighlightStyle {
|
||||||
|
fade_out: Some(0.35),
|
||||||
let mut muted_default_style = HighlightStyle {
|
|
||||||
color: default_color,
|
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
muted_default_style.color.a = ((default_color.a as usize * MUTED_OPACITY) / 255) as u8;
|
|
||||||
|
|
||||||
let mut prev_end = label.filter_range.end;
|
let mut prev_end = label.filter_range.end;
|
||||||
label
|
label
|
||||||
|
@ -6023,12 +6015,12 @@ pub fn styled_runs_for_code_label<'a>(
|
||||||
return Default::default();
|
return Default::default();
|
||||||
};
|
};
|
||||||
let mut muted_style = style.clone();
|
let mut muted_style = style.clone();
|
||||||
muted_style.color.a = ((style.color.a as usize * MUTED_OPACITY) / 255) as u8;
|
muted_style.highlight(fade_out);
|
||||||
|
|
||||||
let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
|
let mut runs = SmallVec::<[(Range<usize>, HighlightStyle); 3]>::new();
|
||||||
if range.start >= label.filter_range.end {
|
if range.start >= label.filter_range.end {
|
||||||
if range.start > prev_end {
|
if range.start > prev_end {
|
||||||
runs.push((prev_end..range.start, muted_default_style));
|
runs.push((prev_end..range.start, fade_out));
|
||||||
}
|
}
|
||||||
runs.push((range.clone(), muted_style));
|
runs.push((range.clone(), muted_style));
|
||||||
} else if range.end <= label.filter_range.end {
|
} else if range.end <= label.filter_range.end {
|
||||||
|
@ -6040,7 +6032,7 @@ pub fn styled_runs_for_code_label<'a>(
|
||||||
prev_end = cmp::max(prev_end, range.end);
|
prev_end = cmp::max(prev_end, range.end);
|
||||||
|
|
||||||
if ix + 1 == label.runs.len() && label.text.len() > prev_end {
|
if ix + 1 == label.runs.len() && label.text.len() > prev_end {
|
||||||
runs.push((prev_end..label.text.len(), muted_default_style));
|
runs.push((prev_end..label.text.len(), fade_out));
|
||||||
}
|
}
|
||||||
|
|
||||||
runs
|
runs
|
||||||
|
@ -8995,20 +8987,19 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_combine_syntax_and_fuzzy_match_highlights() {
|
fn test_combine_syntax_and_fuzzy_match_highlights() {
|
||||||
let string = "abcdefghijklmnop";
|
let string = "abcdefghijklmnop";
|
||||||
let default = HighlightStyle::default();
|
|
||||||
let syntax_ranges = [
|
let syntax_ranges = [
|
||||||
(
|
(
|
||||||
0..3,
|
0..3,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::red(),
|
color: Some(Color::red()),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
4..8,
|
4..8,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::green(),
|
color: Some(Color::green()),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -9016,7 +9007,7 @@ mod tests {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
combine_syntax_and_fuzzy_match_highlights(
|
combine_syntax_and_fuzzy_match_highlights(
|
||||||
&string,
|
&string,
|
||||||
default,
|
Default::default(),
|
||||||
syntax_ranges.into_iter(),
|
syntax_ranges.into_iter(),
|
||||||
&match_indices,
|
&match_indices,
|
||||||
),
|
),
|
||||||
|
@ -9024,38 +9015,38 @@ mod tests {
|
||||||
(
|
(
|
||||||
0..3,
|
0..3,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::red(),
|
color: Some(Color::red()),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
4..5,
|
4..5,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::green(),
|
color: Some(Color::green()),
|
||||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
weight: Some(fonts::Weight::BOLD),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
5..6,
|
5..6,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::green(),
|
color: Some(Color::green()),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
6..8,
|
6..8,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
color: Color::green(),
|
color: Some(Color::green()),
|
||||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
weight: Some(fonts::Weight::BOLD),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
8..9,
|
8..9,
|
||||||
HighlightStyle {
|
HighlightStyle {
|
||||||
font_properties: *fonts::Properties::default().weight(fonts::Weight::BOLD),
|
weight: Some(fonts::Weight::BOLD),
|
||||||
..default
|
..Default::default()
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -397,7 +397,7 @@ impl EditorElement {
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id,
|
font_id,
|
||||||
color: style.background,
|
color: style.background,
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
))
|
))
|
||||||
|
@ -555,7 +555,7 @@ impl EditorElement {
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id: style.text.font_id,
|
font_id: style.text.font_id,
|
||||||
color: Color::black(),
|
color: Color::black(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
|
@ -596,7 +596,7 @@ impl EditorElement {
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id: style.text.font_id,
|
font_id: style.text.font_id,
|
||||||
color,
|
color,
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
)));
|
)));
|
||||||
|
@ -644,7 +644,7 @@ impl EditorElement {
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id: placeholder_style.font_id,
|
font_id: placeholder_style.font_id,
|
||||||
color: placeholder_style.color,
|
color: placeholder_style.color,
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
|
@ -669,7 +669,7 @@ impl EditorElement {
|
||||||
let diagnostic_style = super::diagnostic_style(severity, true, style);
|
let diagnostic_style = super::diagnostic_style(severity, true, style);
|
||||||
let diagnostic_highlight = HighlightStyle {
|
let diagnostic_highlight = HighlightStyle {
|
||||||
underline: Some(Underline {
|
underline: Some(Underline {
|
||||||
color: diagnostic_style.message.text.color,
|
color: Some(diagnostic_style.message.text.color),
|
||||||
thickness: 1.0.into(),
|
thickness: 1.0.into(),
|
||||||
squiggly: true,
|
squiggly: true,
|
||||||
}),
|
}),
|
||||||
|
@ -1237,7 +1237,7 @@ fn layout_line(
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id: style.text.font_id,
|
font_id: style.text.font_id,
|
||||||
color: Color::black(),
|
color: Color::black(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl gpui::Element for TextElement {
|
||||||
.select_font(family, &Default::default())
|
.select_font(family, &Default::default())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
color: Color::default(),
|
color: Color::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
let bold = RunStyle {
|
let bold = RunStyle {
|
||||||
font_id: cx
|
font_id: cx
|
||||||
|
@ -76,7 +76,7 @@ impl gpui::Element for TextElement {
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
color: Color::default(),
|
color: Color::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = "Hello world!";
|
let text = "Hello world!";
|
||||||
|
|
|
@ -214,7 +214,7 @@ mod tests {
|
||||||
"Menlo",
|
"Menlo",
|
||||||
12.,
|
12.,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
None,
|
Default::default(),
|
||||||
Color::black(),
|
Color::black(),
|
||||||
cx.font_cache(),
|
cx.font_cache(),
|
||||||
)
|
)
|
||||||
|
@ -223,7 +223,7 @@ mod tests {
|
||||||
"Menlo",
|
"Menlo",
|
||||||
12.,
|
12.,
|
||||||
*FontProperties::new().weight(Weight::BOLD),
|
*FontProperties::new().weight(Weight::BOLD),
|
||||||
None,
|
Default::default(),
|
||||||
Color::new(255, 0, 0, 255),
|
Color::new(255, 0, 0, 255),
|
||||||
cx.font_cache(),
|
cx.font_cache(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{borrow::Cow, ops::Range, sync::Arc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Color,
|
color::Color,
|
||||||
|
@ -205,8 +205,6 @@ pub fn layout_highlighted_chunks<'a>(
|
||||||
max_line_count: usize,
|
max_line_count: usize,
|
||||||
) -> Vec<Line> {
|
) -> Vec<Line> {
|
||||||
let mut layouts = Vec::with_capacity(max_line_count);
|
let mut layouts = Vec::with_capacity(max_line_count);
|
||||||
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 line = String::new();
|
||||||
let mut styles = Vec::new();
|
let mut styles = Vec::new();
|
||||||
let mut row = 0;
|
let mut row = 0;
|
||||||
|
@ -225,30 +223,14 @@ pub fn layout_highlighted_chunks<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
if !line_chunk.is_empty() && !line_exceeded_max_len {
|
||||||
let font_properties;
|
let text_style = if let Some(style) = highlight_style {
|
||||||
let mut color;
|
text_style
|
||||||
let underline;
|
.clone()
|
||||||
|
.highlight(style, font_cache)
|
||||||
if let Some(highlight_style) = highlight_style {
|
.map(Cow::Owned)
|
||||||
font_properties = highlight_style.font_properties;
|
.unwrap_or_else(|_| Cow::Borrowed(text_style))
|
||||||
color = Color::blend(highlight_style.color, text_style.color);
|
|
||||||
if let Some(fade) = highlight_style.fade_out {
|
|
||||||
color.fade_out(fade);
|
|
||||||
}
|
|
||||||
underline = highlight_style.underline;
|
|
||||||
} else {
|
} else {
|
||||||
font_properties = text_style.font_properties;
|
Cow::Borrowed(text_style)
|
||||||
color = text_style.color;
|
|
||||||
underline = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid a lookup if the font properties match the previous ones.
|
|
||||||
let font_id = if font_properties == prev_font_properties {
|
|
||||||
prev_font_id
|
|
||||||
} else {
|
|
||||||
font_cache
|
|
||||||
.select_font(text_style.font_family_id, &font_properties)
|
|
||||||
.unwrap_or(text_style.font_id)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if line.len() + line_chunk.len() > max_line_len {
|
if line.len() + line_chunk.len() > max_line_len {
|
||||||
|
@ -264,13 +246,11 @@ pub fn layout_highlighted_chunks<'a>(
|
||||||
styles.push((
|
styles.push((
|
||||||
line_chunk.len(),
|
line_chunk.len(),
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id,
|
font_id: text_style.font_id,
|
||||||
color,
|
color: text_style.color,
|
||||||
underline,
|
underline: text_style.underline,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
prev_font_id = font_id;
|
|
||||||
prev_font_properties = font_properties;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,14 @@ pub struct TextStyle {
|
||||||
pub font_id: FontId,
|
pub font_id: FontId,
|
||||||
pub font_size: f32,
|
pub font_size: f32,
|
||||||
pub font_properties: Properties,
|
pub font_properties: Properties,
|
||||||
pub underline: Option<Underline>,
|
pub underline: Underline,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
pub struct HighlightStyle {
|
pub struct HighlightStyle {
|
||||||
pub color: Color,
|
pub color: Option<Color>,
|
||||||
pub font_properties: Properties,
|
pub weight: Option<Weight>,
|
||||||
|
pub italic: Option<bool>,
|
||||||
pub underline: Option<Underline>,
|
pub underline: Option<Underline>,
|
||||||
pub fade_out: Option<f32>,
|
pub fade_out: Option<f32>,
|
||||||
}
|
}
|
||||||
|
@ -43,7 +44,7 @@ impl Eq for HighlightStyle {}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct Underline {
|
pub struct Underline {
|
||||||
pub color: Color,
|
pub color: Option<Color>,
|
||||||
pub thickness: OrderedFloat<f32>,
|
pub thickness: OrderedFloat<f32>,
|
||||||
pub squiggly: bool,
|
pub squiggly: bool,
|
||||||
}
|
}
|
||||||
|
@ -80,13 +81,10 @@ struct TextStyleJson {
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct HighlightStyleJson {
|
struct HighlightStyleJson {
|
||||||
color: Color,
|
color: Option<Color>,
|
||||||
weight: Option<WeightJson>,
|
weight: Option<WeightJson>,
|
||||||
#[serde(default)]
|
italic: Option<bool>,
|
||||||
italic: bool,
|
underline: Option<UnderlineStyleJson>,
|
||||||
#[serde(default)]
|
|
||||||
underline: UnderlineStyleJson,
|
|
||||||
#[serde(default)]
|
|
||||||
fade_out: Option<f32>,
|
fade_out: Option<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +107,7 @@ impl TextStyle {
|
||||||
font_family_name: impl Into<Arc<str>>,
|
font_family_name: impl Into<Arc<str>>,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
font_properties: Properties,
|
font_properties: Properties,
|
||||||
underline: Option<Underline>,
|
underline: Underline,
|
||||||
color: Color,
|
color: Color,
|
||||||
font_cache: &FontCache,
|
font_cache: &FontCache,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
|
@ -133,14 +131,31 @@ impl TextStyle {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlight(mut self, style: HighlightStyle, font_cache: &FontCache) -> Result<Self> {
|
pub fn highlight(mut self, style: HighlightStyle, font_cache: &FontCache) -> Result<Self> {
|
||||||
if self.font_properties != style.font_properties {
|
let mut font_properties = self.font_properties;
|
||||||
self.font_id = font_cache.select_font(self.font_family_id, &style.font_properties)?;
|
if let Some(weight) = style.weight {
|
||||||
|
font_properties.weight(weight);
|
||||||
|
}
|
||||||
|
if let Some(italic) = style.italic {
|
||||||
|
if italic {
|
||||||
|
font_properties.style(Style::Italic);
|
||||||
|
} else {
|
||||||
|
font_properties.style(Style::Normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.font_properties != font_properties {
|
||||||
|
self.font_id = font_cache.select_font(self.font_family_id, &font_properties)?;
|
||||||
|
}
|
||||||
|
if let Some(color) = style.color {
|
||||||
|
self.color = Color::blend(color, self.color);
|
||||||
}
|
}
|
||||||
self.color = Color::blend(style.color, self.color);
|
|
||||||
if let Some(factor) = style.fade_out {
|
if let Some(factor) = style.fade_out {
|
||||||
self.color.fade_out(factor);
|
self.color.fade_out(factor);
|
||||||
}
|
}
|
||||||
self.underline = style.underline;
|
if let Some(underline) = style.underline {
|
||||||
|
self.underline = underline;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -160,7 +175,7 @@ impl TextStyle {
|
||||||
json.family,
|
json.family,
|
||||||
json.size,
|
json.size,
|
||||||
font_properties,
|
font_properties,
|
||||||
underline_from_json(json.underline, json.color),
|
underline_from_json(json.underline),
|
||||||
json.color,
|
json.color,
|
||||||
font_cache,
|
font_cache,
|
||||||
)
|
)
|
||||||
|
@ -214,9 +229,10 @@ impl From<TextStyle> for HighlightStyle {
|
||||||
impl From<&TextStyle> for HighlightStyle {
|
impl From<&TextStyle> for HighlightStyle {
|
||||||
fn from(other: &TextStyle) -> Self {
|
fn from(other: &TextStyle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
color: other.color,
|
color: Some(other.color),
|
||||||
font_properties: other.font_properties,
|
weight: Some(other.font_properties.weight),
|
||||||
underline: other.underline,
|
italic: Some(other.font_properties.style == Style::Italic),
|
||||||
|
underline: Some(other.underline),
|
||||||
fade_out: None,
|
fade_out: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,17 +272,38 @@ impl Default for TextStyle {
|
||||||
|
|
||||||
impl HighlightStyle {
|
impl HighlightStyle {
|
||||||
fn from_json(json: HighlightStyleJson) -> Self {
|
fn from_json(json: HighlightStyleJson) -> Self {
|
||||||
let font_properties = properties_from_json(json.weight, json.italic);
|
|
||||||
Self {
|
Self {
|
||||||
color: json.color,
|
color: json.color,
|
||||||
font_properties,
|
weight: json.weight.map(weight_from_json),
|
||||||
underline: underline_from_json(json.underline, json.color),
|
italic: json.italic,
|
||||||
|
underline: json.underline.map(underline_from_json),
|
||||||
fade_out: json.fade_out,
|
fade_out: json.fade_out,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn highlight(&mut self, other: HighlightStyle) {
|
pub fn highlight(&mut self, other: HighlightStyle) {
|
||||||
self.color = Color::blend(other.color, self.color);
|
match (self.color, other.color) {
|
||||||
|
(Some(self_color), Some(other_color)) => {
|
||||||
|
self.color = Some(Color::blend(other_color, self_color));
|
||||||
|
}
|
||||||
|
(None, Some(other_color)) => {
|
||||||
|
self.color = Some(other_color);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.weight.is_some() {
|
||||||
|
self.weight = other.weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.italic.is_some() {
|
||||||
|
self.italic = other.italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if other.underline.is_some() {
|
||||||
|
self.underline = other.underline;
|
||||||
|
}
|
||||||
|
|
||||||
match (other.fade_out, self.fade_out) {
|
match (other.fade_out, self.fade_out) {
|
||||||
(Some(source_fade), None) => self.fade_out = Some(source_fade),
|
(Some(source_fade), None) => self.fade_out = Some(source_fade),
|
||||||
(Some(source_fade), Some(dest_fade)) => {
|
(Some(source_fade), Some(dest_fade)) => {
|
||||||
|
@ -278,20 +315,14 @@ impl HighlightStyle {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
self.font_properties = other.font_properties;
|
|
||||||
if other.underline.is_some() {
|
|
||||||
self.underline = other.underline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for HighlightStyle {
|
impl From<Color> for HighlightStyle {
|
||||||
fn from(color: Color) -> Self {
|
fn from(color: Color) -> Self {
|
||||||
Self {
|
Self {
|
||||||
color,
|
color: Some(color),
|
||||||
font_properties: Default::default(),
|
..Default::default()
|
||||||
underline: None,
|
|
||||||
fade_out: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -329,36 +360,40 @@ impl<'de> Deserialize<'de> for HighlightStyle {
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
color: serde_json::from_value(json).map_err(de::Error::custom)?,
|
color: serde_json::from_value(json).map_err(de::Error::custom)?,
|
||||||
font_properties: Properties::new(),
|
..Default::default()
|
||||||
underline: None,
|
|
||||||
fade_out: None,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn underline_from_json(json: UnderlineStyleJson, text_color: Color) -> Option<Underline> {
|
fn underline_from_json(json: UnderlineStyleJson) -> Underline {
|
||||||
match json {
|
match json {
|
||||||
UnderlineStyleJson::Underlined(false) => None,
|
UnderlineStyleJson::Underlined(false) => Underline::default(),
|
||||||
UnderlineStyleJson::Underlined(true) => Some(Underline {
|
UnderlineStyleJson::Underlined(true) => Underline {
|
||||||
color: text_color,
|
color: None,
|
||||||
thickness: 1.0.into(),
|
thickness: 1.0.into(),
|
||||||
squiggly: false,
|
squiggly: false,
|
||||||
}),
|
},
|
||||||
UnderlineStyleJson::UnderlinedWithProperties {
|
UnderlineStyleJson::UnderlinedWithProperties {
|
||||||
color,
|
color,
|
||||||
thickness,
|
thickness,
|
||||||
squiggly,
|
squiggly,
|
||||||
} => Some(Underline {
|
} => Underline {
|
||||||
color: color.unwrap_or(text_color),
|
color,
|
||||||
thickness: thickness.unwrap_or(1.).into(),
|
thickness: thickness.unwrap_or(1.).into(),
|
||||||
squiggly,
|
squiggly,
|
||||||
}),
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn properties_from_json(weight: Option<WeightJson>, italic: bool) -> Properties {
|
fn properties_from_json(weight: Option<WeightJson>, italic: bool) -> Properties {
|
||||||
let weight = match weight.unwrap_or(WeightJson::normal) {
|
let weight = weight.map(weight_from_json).unwrap_or_default();
|
||||||
|
let style = if italic { Style::Italic } else { Style::Normal };
|
||||||
|
*Properties::new().weight(weight).style(style)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn weight_from_json(weight: WeightJson) -> Weight {
|
||||||
|
match weight {
|
||||||
WeightJson::thin => Weight::THIN,
|
WeightJson::thin => Weight::THIN,
|
||||||
WeightJson::extra_light => Weight::EXTRA_LIGHT,
|
WeightJson::extra_light => Weight::EXTRA_LIGHT,
|
||||||
WeightJson::light => Weight::LIGHT,
|
WeightJson::light => Weight::LIGHT,
|
||||||
|
@ -368,9 +403,7 @@ fn properties_from_json(weight: Option<WeightJson>, italic: bool) -> Properties
|
||||||
WeightJson::bold => Weight::BOLD,
|
WeightJson::bold => Weight::BOLD,
|
||||||
WeightJson::extra_bold => Weight::EXTRA_BOLD,
|
WeightJson::extra_bold => Weight::EXTRA_BOLD,
|
||||||
WeightJson::black => Weight::BLACK,
|
WeightJson::black => Weight::BLACK,
|
||||||
};
|
}
|
||||||
let style = if italic { Style::Italic } else { Style::Normal };
|
|
||||||
*Properties::new().weight(weight).style(style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToJson for Properties {
|
impl ToJson for Properties {
|
||||||
|
|
|
@ -425,21 +425,21 @@ mod tests {
|
||||||
let menlo_regular = RunStyle {
|
let menlo_regular = RunStyle {
|
||||||
font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
|
font_id: fonts.select_font(&menlo, &Properties::new()).unwrap(),
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
let menlo_italic = RunStyle {
|
let menlo_italic = RunStyle {
|
||||||
font_id: fonts
|
font_id: fonts
|
||||||
.select_font(&menlo, &Properties::new().style(Style::Italic))
|
.select_font(&menlo, &Properties::new().style(Style::Italic))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
let menlo_bold = RunStyle {
|
let menlo_bold = RunStyle {
|
||||||
font_id: fonts
|
font_id: fonts
|
||||||
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
|
.select_font(&menlo, &Properties::new().weight(Weight::BOLD))
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
assert_ne!(menlo_regular, menlo_italic);
|
assert_ne!(menlo_regular, menlo_italic);
|
||||||
assert_ne!(menlo_regular, menlo_bold);
|
assert_ne!(menlo_regular, menlo_bold);
|
||||||
|
@ -466,13 +466,13 @@ mod tests {
|
||||||
let zapfino_regular = RunStyle {
|
let zapfino_regular = RunStyle {
|
||||||
font_id: fonts.select_font(&zapfino, &Properties::new())?,
|
font_id: fonts.select_font(&zapfino, &Properties::new())?,
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
let menlo = fonts.load_family("Menlo")?;
|
let menlo = fonts.load_family("Menlo")?;
|
||||||
let menlo_regular = RunStyle {
|
let menlo_regular = RunStyle {
|
||||||
font_id: fonts.select_font(&menlo, &Properties::new())?,
|
font_id: fonts.select_font(&menlo, &Properties::new())?,
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
|
let text = "This is, m𐍈re 𐍈r less, Zapfino!𐍈";
|
||||||
|
@ -551,7 +551,7 @@ mod tests {
|
||||||
let style = RunStyle {
|
let style = RunStyle {
|
||||||
font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
|
font_id: fonts.select_font(&font_ids, &Default::default()).unwrap(),
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let line = "\u{feff}";
|
let line = "\u{feff}";
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub struct TextLayoutCache {
|
||||||
pub struct RunStyle {
|
pub struct RunStyle {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
pub font_id: FontId,
|
pub font_id: FontId,
|
||||||
pub underline: Option<Underline>,
|
pub underline: Underline,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TextLayoutCache {
|
impl TextLayoutCache {
|
||||||
|
@ -167,7 +167,7 @@ impl<'a> Hash for CacheKeyRef<'a> {
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Line {
|
pub struct Line {
|
||||||
layout: Arc<LineLayout>,
|
layout: Arc<LineLayout>,
|
||||||
style_runs: SmallVec<[(u32, Color, Option<Underline>); 32]>,
|
style_runs: SmallVec<[(u32, Color, Underline); 32]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
|
@ -283,17 +283,21 @@ impl Line {
|
||||||
if glyph.index >= run_end {
|
if glyph.index >= run_end {
|
||||||
if let Some((run_len, run_color, run_underline)) = style_runs.next() {
|
if let Some((run_len, run_color, run_underline)) = style_runs.next() {
|
||||||
if let Some((_, underline_style)) = underline {
|
if let Some((_, underline_style)) = underline {
|
||||||
if *run_underline != Some(underline_style) {
|
if *run_underline != underline_style {
|
||||||
finished_underline = underline.take();
|
finished_underline = underline.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(run_underline) = run_underline {
|
if run_underline.thickness.into_inner() > 0. {
|
||||||
underline.get_or_insert((
|
underline.get_or_insert((
|
||||||
vec2f(
|
vec2f(
|
||||||
glyph_origin.x(),
|
glyph_origin.x(),
|
||||||
origin.y() + baseline_offset.y() + 0.618 * self.layout.descent,
|
origin.y() + baseline_offset.y() + 0.618 * self.layout.descent,
|
||||||
),
|
),
|
||||||
*run_underline,
|
Underline {
|
||||||
|
color: Some(run_underline.color.unwrap_or(*run_color)),
|
||||||
|
thickness: run_underline.thickness.into(),
|
||||||
|
squiggly: run_underline.squiggly,
|
||||||
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +305,6 @@ impl Line {
|
||||||
color = *run_color;
|
color = *run_color;
|
||||||
} else {
|
} else {
|
||||||
run_end = self.layout.len;
|
run_end = self.layout.len;
|
||||||
color = Color::black();
|
|
||||||
finished_underline = underline.take();
|
finished_underline = underline.take();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -315,7 +318,7 @@ impl Line {
|
||||||
origin: underline_origin,
|
origin: underline_origin,
|
||||||
width: glyph_origin.x() - underline_origin.x(),
|
width: glyph_origin.x() - underline_origin.x(),
|
||||||
thickness: underline_style.thickness.into(),
|
thickness: underline_style.thickness.into(),
|
||||||
color: underline_style.color,
|
color: underline_style.color.unwrap(),
|
||||||
squiggly: underline_style.squiggly,
|
squiggly: underline_style.squiggly,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -335,7 +338,7 @@ impl Line {
|
||||||
cx.scene.push_underline(scene::Underline {
|
cx.scene.push_underline(scene::Underline {
|
||||||
origin: underline_start,
|
origin: underline_start,
|
||||||
width: line_end_x - underline_start.x(),
|
width: line_end_x - underline_start.x(),
|
||||||
color: underline_style.color,
|
color: underline_style.color.unwrap(),
|
||||||
thickness: underline_style.thickness.into(),
|
thickness: underline_style.thickness.into(),
|
||||||
squiggly: underline_style.squiggly,
|
squiggly: underline_style.squiggly,
|
||||||
});
|
});
|
||||||
|
@ -610,7 +613,7 @@ impl LineWrapper {
|
||||||
RunStyle {
|
RunStyle {
|
||||||
font_id: self.font_id,
|
font_id: self.font_id,
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
},
|
},
|
||||||
)],
|
)],
|
||||||
)
|
)
|
||||||
|
@ -694,7 +697,7 @@ mod tests {
|
||||||
let normal = RunStyle {
|
let normal = RunStyle {
|
||||||
font_id,
|
font_id,
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
let bold = RunStyle {
|
let bold = RunStyle {
|
||||||
font_id: font_cache
|
font_id: font_cache
|
||||||
|
@ -707,7 +710,7 @@ mod tests {
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
color: Default::default(),
|
color: Default::default(),
|
||||||
underline: None,
|
underline: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let text = "aa bbb cccc ddddd eeee";
|
let text = "aa bbb cccc ddddd eeee";
|
||||||
|
|
|
@ -284,11 +284,7 @@ impl ProjectSymbolsView {
|
||||||
&settings.theme.selector.item
|
&settings.theme.selector.item
|
||||||
};
|
};
|
||||||
let symbol = &self.symbols[string_match.candidate_id];
|
let symbol = &self.symbols[string_match.candidate_id];
|
||||||
let syntax_runs = styled_runs_for_code_label(
|
let syntax_runs = styled_runs_for_code_label(&symbol.label, &settings.theme.editor.syntax);
|
||||||
&symbol.label,
|
|
||||||
style.label.text.color,
|
|
||||||
&settings.theme.editor.syntax,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut path = symbol.path.to_string_lossy();
|
let mut path = symbol.path.to_string_lossy();
|
||||||
if show_worktree_root_name {
|
if show_worktree_root_name {
|
||||||
|
|
Loading…
Reference in a new issue