@ -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
)
}
}