|
|
|
@ -103,6 +103,7 @@ pub struct Theme {
|
|
|
|
|
// tree-sitter highlight styles are stored in a Vec to optimize lookups
|
|
|
|
|
scopes: Vec<String>,
|
|
|
|
|
highlights: Vec<Style>,
|
|
|
|
|
rainbow_length: usize,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for Theme {
|
|
|
|
@ -113,6 +114,7 @@ impl<'de> Deserialize<'de> for Theme {
|
|
|
|
|
let mut styles = HashMap::new();
|
|
|
|
|
let mut scopes = Vec::new();
|
|
|
|
|
let mut highlights = Vec::new();
|
|
|
|
|
let mut rainbow_length = 0;
|
|
|
|
|
|
|
|
|
|
if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
|
|
|
|
|
// TODO: alert user of parsing failures in editor
|
|
|
|
@ -130,6 +132,26 @@ impl<'de> Deserialize<'de> for Theme {
|
|
|
|
|
scopes.reserve(colors.len());
|
|
|
|
|
highlights.reserve(colors.len());
|
|
|
|
|
|
|
|
|
|
for (i, style) in colors
|
|
|
|
|
.remove("rainbow")
|
|
|
|
|
.and_then(|value| match palette.parse_style_array(value) {
|
|
|
|
|
Ok(styles) => Some(styles),
|
|
|
|
|
Err(err) => {
|
|
|
|
|
warn!("{}", err);
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or_else(Self::default_rainbow)
|
|
|
|
|
.iter()
|
|
|
|
|
.enumerate()
|
|
|
|
|
{
|
|
|
|
|
let name = format!("rainbow.{}", i);
|
|
|
|
|
styles.insert(name.clone(), *style);
|
|
|
|
|
scopes.push(name);
|
|
|
|
|
highlights.push(*style);
|
|
|
|
|
rainbow_length += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (name, style_value) in colors {
|
|
|
|
|
let mut style = Style::default();
|
|
|
|
|
if let Err(err) = palette.parse_style(&mut style, style_value) {
|
|
|
|
@ -147,6 +169,7 @@ impl<'de> Deserialize<'de> for Theme {
|
|
|
|
|
scopes,
|
|
|
|
|
styles,
|
|
|
|
|
highlights,
|
|
|
|
|
rainbow_length,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -185,6 +208,21 @@ impl Theme {
|
|
|
|
|
.all(|color| !matches!(color, Some(Color::Rgb(..))))
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn rainbow_length(&self) -> usize {
|
|
|
|
|
self.rainbow_length
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn default_rainbow() -> Vec<Style> {
|
|
|
|
|
vec![
|
|
|
|
|
Style::default().fg(Color::Red),
|
|
|
|
|
Style::default().fg(Color::Yellow),
|
|
|
|
|
Style::default().fg(Color::Green),
|
|
|
|
|
Style::default().fg(Color::Blue),
|
|
|
|
|
Style::default().fg(Color::Cyan),
|
|
|
|
|
Style::default().fg(Color::Magenta),
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct ThemePalette {
|
|
|
|
@ -286,6 +324,24 @@ impl ThemePalette {
|
|
|
|
|
}
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Parses a TOML array into a [`Vec`] of [`Style`]. If the value cannot be
|
|
|
|
|
/// parsed as an array or if any style in the array cannot be parsed then an
|
|
|
|
|
/// error is returned.
|
|
|
|
|
pub fn parse_style_array(&self, value: Value) -> Result<Vec<Style>, String> {
|
|
|
|
|
let mut styles = Vec::new();
|
|
|
|
|
|
|
|
|
|
for v in value
|
|
|
|
|
.as_array()
|
|
|
|
|
.ok_or_else(|| format!("Theme: could not parse value as an array: '{}'", value))?
|
|
|
|
|
{
|
|
|
|
|
let mut style = Style::default();
|
|
|
|
|
self.parse_style(&mut style, v.clone())?;
|
|
|
|
|
styles.push(style);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(styles)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<Value> for ThemePalette {
|
|
|
|
@ -362,4 +418,51 @@ mod tests {
|
|
|
|
|
.add_modifier(Modifier::BOLD)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_valid_style_array() {
|
|
|
|
|
let theme = toml::toml! {
|
|
|
|
|
rainbow = ["#ff0000", "#ffa500", "#fff000", { fg = "#00ff00", modifiers = ["bold"] }]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let palette = ThemePalette::default();
|
|
|
|
|
|
|
|
|
|
let rainbow = theme.as_table().unwrap().get("rainbow").unwrap();
|
|
|
|
|
let parse_result = palette.parse_style_array(rainbow.clone());
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Ok(vec![
|
|
|
|
|
Style::default().fg(Color::Rgb(255, 0, 0)),
|
|
|
|
|
Style::default().fg(Color::Rgb(255, 165, 0)),
|
|
|
|
|
Style::default().fg(Color::Rgb(255, 240, 0)),
|
|
|
|
|
Style::default()
|
|
|
|
|
.fg(Color::Rgb(0, 255, 0))
|
|
|
|
|
.add_modifier(Modifier::BOLD),
|
|
|
|
|
]),
|
|
|
|
|
parse_result
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_parse_invalid_style_array() {
|
|
|
|
|
let palette = ThemePalette::default();
|
|
|
|
|
|
|
|
|
|
let theme = toml::toml! { invalid_hex_code = ["#f00"] };
|
|
|
|
|
let invalid_hex_code = theme.as_table().unwrap().get("invalid_hex_code").unwrap();
|
|
|
|
|
let parse_result = palette.parse_style_array(invalid_hex_code.clone());
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Err("Theme: malformed hexcode: #f00".to_string()),
|
|
|
|
|
parse_result
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let theme = toml::toml! { not_an_array = { red = "#ff0000" } };
|
|
|
|
|
let not_an_array = theme.as_table().unwrap().get("not_an_array").unwrap();
|
|
|
|
|
let parse_result = palette.parse_style_array(not_an_array.clone());
|
|
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
Err("Theme: could not parse value as an array: 'red = \"#ff0000\"\n'".to_string()),
|
|
|
|
|
parse_result
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|