use std::sync::Arc; use gpui::{HighlightStyle, Hsla}; #[derive(Debug, PartialEq, Eq, Clone, Default)]pub struct SyntaxTheme { pub highlights: Vec<(String, HighlightStyle)>,} impl SyntaxTheme { #[cfg(any(test, feature = "test-support"))] pub fn new_test(colors: impl IntoIterator<Item = (&'static str, Hsla)>) -> Self { Self::new_test_styles(colors.into_iter().map(|(key, color)| { ( key, HighlightStyle { color: Some(color), ..Default::default() }, ) })) } #[cfg(any(test, feature = "test-support"))] pub fn new_test_styles( colors: impl IntoIterator<Item = (&'static str, HighlightStyle)>, ) -> Self { Self { highlights: colors .into_iter() .map(|(key, style)| (key.to_owned(), style)) .collect(), } } pub fn get(&self, name: &str) -> HighlightStyle { self.highlights .iter() .find_map(|entry| if entry.0 == name { Some(entry.1) } else { None }) .unwrap_or_default() } pub fn color(&self, name: &str) -> Hsla { self.get(name).color.unwrap_or_default() } pub fn highlight_id(&self, name: &str) -> Option<u32> { let ix = self.highlights.iter().position(|entry| entry.0 == name)?; Some(ix as u32) } /// Returns a new [`Arc<SyntaxTheme>`] with the given syntax styles merged in. pub fn merge(base: Arc<Self>, user_syntax_styles: Vec<(String, HighlightStyle)>) -> Arc<Self> { if user_syntax_styles.is_empty() { return base; } let mut merged_highlights = base.highlights.clone(); for (name, highlight) in user_syntax_styles { if let Some((_, existing_highlight)) = merged_highlights .iter_mut() .find(|(existing_name, _)| existing_name == &name) { existing_highlight.color = highlight.color.or(existing_highlight.color); existing_highlight.font_weight = highlight.font_weight.or(existing_highlight.font_weight); existing_highlight.font_style = highlight.font_style.or(existing_highlight.font_style); existing_highlight.background_color = highlight .background_color .or(existing_highlight.background_color); existing_highlight.underline = highlight.underline.or(existing_highlight.underline); existing_highlight.strikethrough = highlight.strikethrough.or(existing_highlight.strikethrough); existing_highlight.fade_out = highlight.fade_out.or(existing_highlight.fade_out); } else { merged_highlights.push((name, highlight)); } } Arc::new(Self { highlights: merged_highlights, }) }} #[cfg(test)]mod tests { use gpui::FontStyle; use super::*; #[test] fn test_syntax_theme_merge() { // Merging into an empty `SyntaxTheme` keeps all the user-defined styles. let syntax_theme = SyntaxTheme::merge( Arc::new(SyntaxTheme::new_test([])), vec![ ( "foo".to_string(), HighlightStyle { color: Some(gpui::red()), ..Default::default() }, ), ( "foo.bar".to_string(), HighlightStyle { color: Some(gpui::green()), ..Default::default() }, ), ], ); assert_eq!( syntax_theme, Arc::new(SyntaxTheme::new_test([ ("foo", gpui::red()), ("foo.bar", gpui::green()) ])) ); // Merging empty user-defined styles keeps all the base styles. let syntax_theme = SyntaxTheme::merge( Arc::new(SyntaxTheme::new_test([ ("foo", gpui::blue()), ("foo.bar", gpui::red()), ])), Vec::new(), ); assert_eq!( syntax_theme, Arc::new(SyntaxTheme::new_test([ ("foo", gpui::blue()), ("foo.bar", gpui::red()) ])) ); let syntax_theme = SyntaxTheme::merge( Arc::new(SyntaxTheme::new_test([ ("foo", gpui::red()), ("foo.bar", gpui::green()), ])), vec![( "foo.bar".to_string(), HighlightStyle { color: Some(gpui::yellow()), ..Default::default() }, )], ); assert_eq!( syntax_theme, Arc::new(SyntaxTheme::new_test([ ("foo", gpui::red()), ("foo.bar", gpui::yellow()) ])) ); let syntax_theme = SyntaxTheme::merge( Arc::new(SyntaxTheme::new_test([ ("foo", gpui::red()), ("foo.bar", gpui::green()), ])), vec![( "foo.bar".to_string(), HighlightStyle { font_style: Some(FontStyle::Italic), ..Default::default() }, )], ); assert_eq!( syntax_theme, Arc::new(SyntaxTheme::new_test_styles([ ( "foo", HighlightStyle { color: Some(gpui::red()), ..Default::default() } ), ( "foo.bar", HighlightStyle { color: Some(gpui::green()), font_style: Some(FontStyle::Italic), ..Default::default() } ) ])) ); }}
use std::sync::Arc;
use gpui::{HighlightStyle, Hsla};
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct SyntaxTheme {
pub highlights: Vec<(String, HighlightStyle)>,
}
impl SyntaxTheme {
#[cfg(any(test, feature = "test-support"))]
pub fn new_test(colors: impl IntoIterator<Item = (&'static str, Hsla)>) -> Self {
Self::new_test_styles(colors.into_iter().map(|(key, color)| {
(
key,
HighlightStyle {
color: Some(color),
..Default::default()
},
)
}))
}
#[cfg(any(test, feature = "test-support"))]
pub fn new_test_styles(
colors: impl IntoIterator<Item = (&'static str, HighlightStyle)>,
) -> Self {
Self {
highlights: colors
.into_iter()
.map(|(key, style)| (key.to_owned(), style))
.collect(),
}
}
pub fn get(&self, name: &str) -> HighlightStyle {
self.highlights
.iter()
.find_map(|entry| if entry.0 == name { Some(entry.1) } else { None })
.unwrap_or_default()
}
pub fn color(&self, name: &str) -> Hsla {
self.get(name).color.unwrap_or_default()
}
pub fn highlight_id(&self, name: &str) -> Option<u32> {
let ix = self.highlights.iter().position(|entry| entry.0 == name)?;
Some(ix as u32)
}
/// Returns a new [`Arc<SyntaxTheme>`] with the given syntax styles merged in.
pub fn merge(base: Arc<Self>, user_syntax_styles: Vec<(String, HighlightStyle)>) -> Arc<Self> {
if user_syntax_styles.is_empty() {
return base;
}
let mut merged_highlights = base.highlights.clone();
for (name, highlight) in user_syntax_styles {
if let Some((_, existing_highlight)) = merged_highlights
.iter_mut()
.find(|(existing_name, _)| existing_name == &name)
{
existing_highlight.color = highlight.color.or(existing_highlight.color);
existing_highlight.font_weight =
highlight.font_weight.or(existing_highlight.font_weight);
existing_highlight.font_style =
highlight.font_style.or(existing_highlight.font_style);
existing_highlight.background_color = highlight
.background_color
.or(existing_highlight.background_color);
existing_highlight.underline = highlight.underline.or(existing_highlight.underline);
existing_highlight.strikethrough =
highlight.strikethrough.or(existing_highlight.strikethrough);
existing_highlight.fade_out = highlight.fade_out.or(existing_highlight.fade_out);
} else {
merged_highlights.push((name, highlight));
}
}
Arc::new(Self {
highlights: merged_highlights,
})
}
}
#[cfg(test)]
mod tests {
use gpui::FontStyle;
use super::*;
#[test]
fn test_syntax_theme_merge() {
// Merging into an empty `SyntaxTheme` keeps all the user-defined styles.
let syntax_theme = SyntaxTheme::merge(
Arc::new(SyntaxTheme::new_test([])),
vec![
(
"foo".to_string(),
HighlightStyle {
color: Some(gpui::red()),
..Default::default()
},
),
(
"foo.bar".to_string(),
HighlightStyle {
color: Some(gpui::green()),
..Default::default()
},
),
],
);
assert_eq!(
syntax_theme,
Arc::new(SyntaxTheme::new_test([
("foo", gpui::red()),
("foo.bar", gpui::green())
]))
);
// Merging empty user-defined styles keeps all the base styles.
let syntax_theme = SyntaxTheme::merge(
Arc::new(SyntaxTheme::new_test([
("foo", gpui::blue()),
("foo.bar", gpui::red()),
])),
Vec::new(),
);
assert_eq!(
syntax_theme,
Arc::new(SyntaxTheme::new_test([
("foo", gpui::blue()),
("foo.bar", gpui::red())
]))
);
let syntax_theme = SyntaxTheme::merge(
Arc::new(SyntaxTheme::new_test([
("foo", gpui::red()),
("foo.bar", gpui::green()),
])),
vec![(
"foo.bar".to_string(),
HighlightStyle {
color: Some(gpui::yellow()),
..Default::default()
},
)],
);
assert_eq!(
syntax_theme,
Arc::new(SyntaxTheme::new_test([
("foo", gpui::red()),
("foo.bar", gpui::yellow())
]))
);
let syntax_theme = SyntaxTheme::merge(
Arc::new(SyntaxTheme::new_test([
("foo", gpui::red()),
("foo.bar", gpui::green()),
])),
vec![(
"foo.bar".to_string(),
HighlightStyle {
font_style: Some(FontStyle::Italic),
..Default::default()
},
)],
);
assert_eq!(
syntax_theme,
Arc::new(SyntaxTheme::new_test_styles([
(
"foo",
HighlightStyle {
color: Some(gpui::red()),
..Default::default()
}
),
(
"foo.bar",
HighlightStyle {
color: Some(gpui::green()),
font_style: Some(FontStyle::Italic),
..Default::default()
}
)
]))
);
}
}