@ -53,20 +53,34 @@ impl Loader {
/// Loads a theme searching directories in priority order.
/// Loads a theme searching directories in priority order.
pub fn load ( & self , name : & str ) -> Result < Theme > {
pub fn load ( & self , name : & str ) -> Result < Theme > {
let ( theme , warnings ) = self . load_with_warnings ( name ) ? ;
for warning in warnings {
warn ! ( "Theme '{}': {}" , name , warning ) ;
}
Ok ( theme )
}
/// Loads a theme searching directories in priority order, returning any warnings
pub fn load_with_warnings ( & self , name : & str ) -> Result < ( Theme , Vec < String > ) > {
if name = = "default" {
if name = = "default" {
return Ok ( self . default ( ) ) ;
return Ok ( ( self . default ( ) , Vec ::new ( ) ) ) ;
}
}
if name = = "base16_default" {
if name = = "base16_default" {
return Ok ( self . base16_default ( ) ) ;
return Ok ( ( self . base16_default ( ) , Vec ::new ( ) ) ) ;
}
}
let mut visited_paths = HashSet ::new ( ) ;
let mut visited_paths = HashSet ::new ( ) ;
let theme = self . load_theme ( name , & mut visited_paths ) . map ( Theme ::from ) ? ;
let ( theme , warnings ) = self
. load_theme ( name , & mut visited_paths )
. map ( Theme ::from_toml ) ? ;
Ok ( Theme {
let theme = Theme {
name : name . into ( ) ,
name : name . into ( ) ,
.. theme
.. theme
} )
} ;
Ok ( ( theme , warnings ) )
}
}
/// Recursively load a theme, merging with any inherited parent themes.
/// Recursively load a theme, merging with any inherited parent themes.
@ -87,10 +101,7 @@ impl Loader {
let theme_toml = if let Some ( parent_theme_name ) = inherits {
let theme_toml = if let Some ( parent_theme_name ) = inherits {
let parent_theme_name = parent_theme_name . as_str ( ) . ok_or_else ( | | {
let parent_theme_name = parent_theme_name . as_str ( ) . ok_or_else ( | | {
anyhow ! (
anyhow ! ( "Expected 'inherits' to be a string: {}" , parent_theme_name )
"Theme: expected 'inherits' to be a string: {}" ,
parent_theme_name
)
} ) ? ;
} ) ? ;
let parent_theme_toml = match parent_theme_name {
let parent_theme_toml = match parent_theme_name {
@ -181,9 +192,9 @@ impl Loader {
} )
} )
. ok_or_else ( | | {
. ok_or_else ( | | {
if cycle_found {
if cycle_found {
anyhow ! ( " Theme: c ycle found in inheriting: {}", name )
anyhow ! ( " C ycle found in inheriting: {}", name )
} else {
} else {
anyhow ! ( " Theme: f ile not found for: {}", name )
anyhow ! ( " F ile not found for: {}", name )
}
}
} )
} )
}
}
@ -220,19 +231,11 @@ pub struct Theme {
impl From < Value > for Theme {
impl From < Value > for Theme {
fn from ( value : Value ) -> Self {
fn from ( value : Value ) -> Self {
if let Value ::Table ( table ) = value {
let ( theme , warnings ) = Theme ::from_toml ( value ) ;
let ( styles , scopes , highlights ) = build_theme_values ( table ) ;
for warning in warnings {
warn ! ( "{}" , warning ) ;
Self {
styles ,
scopes ,
highlights ,
.. Default ::default ( )
}
} else {
warn ! ( "Expected theme TOML value to be a table, found {:?}" , value ) ;
Default ::default ( )
}
}
theme
}
}
}
}
@ -242,31 +245,29 @@ impl<'de> Deserialize<'de> for Theme {
D : Deserializer < ' de > ,
D : Deserializer < ' de > ,
{
{
let values = Map ::< String , Value > ::deserialize ( deserializer ) ? ;
let values = Map ::< String , Value > ::deserialize ( deserializer ) ? ;
let ( theme , warnings ) = Theme ::from_keys ( values ) ;
let ( styles , scopes , highlights ) = build_theme_values ( values ) ;
for warning in warnings {
warn ! ( "{}" , warning ) ;
Ok ( Self {
}
styles ,
Ok ( theme )
scopes ,
highlights ,
.. Default ::default ( )
} )
}
}
}
}
fn build_theme_values (
fn build_theme_values (
mut values : Map < String , Value > ,
mut values : Map < String , Value > ,
) -> ( HashMap < String , Style > , Vec < String > , Vec < Style > ) {
) -> ( HashMap < String , Style > , Vec < String > , Vec < Style > , Vec < String > ) {
let mut styles = HashMap ::new ( ) ;
let mut styles = HashMap ::new ( ) ;
let mut scopes = Vec ::new ( ) ;
let mut scopes = Vec ::new ( ) ;
let mut highlights = Vec ::new ( ) ;
let mut highlights = Vec ::new ( ) ;
let mut warnings = Vec ::new ( ) ;
// TODO: alert user of parsing failures in editor
// TODO: alert user of parsing failures in editor
let palette = values
let palette = values
. remove ( "palette" )
. remove ( "palette" )
. map ( | value | {
. map ( | value | {
ThemePalette ::try_from ( value ) . unwrap_or_else ( | err | {
ThemePalette ::try_from ( value ) . unwrap_or_else ( | err | {
warn ! ( "{}" , err ) ;
warn ings. push ( err ) ;
ThemePalette ::default ( )
ThemePalette ::default ( )
} )
} )
} )
} )
@ -279,7 +280,7 @@ fn build_theme_values(
for ( name , style_value ) in values {
for ( name , style_value ) in values {
let mut style = Style ::default ( ) ;
let mut style = Style ::default ( ) ;
if let Err ( err ) = palette . parse_style ( & mut style , style_value ) {
if let Err ( err ) = palette . parse_style ( & mut style , style_value ) {
warn ! ( "{}" , err ) ;
warn ings. push ( err ) ;
}
}
// these are used both as UI and as highlights
// these are used both as UI and as highlights
@ -288,7 +289,7 @@ fn build_theme_values(
highlights . push ( style ) ;
highlights . push ( style ) ;
}
}
( styles , scopes , highlights )
( styles , scopes , highlights , warnings )
}
}
impl Theme {
impl Theme {
@ -354,6 +355,27 @@ impl Theme {
. all ( | color | ! matches! ( color , Some ( Color ::Rgb ( .. ) ) ) )
. all ( | color | ! matches! ( color , Some ( Color ::Rgb ( .. ) ) ) )
} )
} )
}
}
fn from_toml ( value : Value ) -> ( Self , Vec < String > ) {
if let Value ::Table ( table ) = value {
Theme ::from_keys ( table )
} else {
warn ! ( "Expected theme TOML value to be a table, found {:?}" , value ) ;
Default ::default ( )
}
}
fn from_keys ( toml_keys : Map < String , Value > ) -> ( Self , Vec < String > ) {
let ( styles , scopes , highlights , load_errors ) = build_theme_values ( toml_keys ) ;
let theme = Self {
styles ,
scopes ,
highlights ,
.. Default ::default ( )
} ;
( theme , load_errors )
}
}
}
struct ThemePalette {
struct ThemePalette {
@ -408,7 +430,7 @@ impl ThemePalette {
if let Ok ( index ) = s . parse ::< u8 > ( ) {
if let Ok ( index ) = s . parse ::< u8 > ( ) {
return Ok ( Color ::Indexed ( index ) ) ;
return Ok ( Color ::Indexed ( index ) ) ;
}
}
Err ( format! ( " Theme: m alformed ANSI: {}", s ) )
Err ( format! ( " M alformed ANSI: {}", s ) )
}
}
fn hex_string_to_rgb ( s : & str ) -> Result < Color , String > {
fn hex_string_to_rgb ( s : & str ) -> Result < Color , String > {
@ -422,13 +444,13 @@ impl ThemePalette {
}
}
}
}
Err ( format! ( " Theme: m alformed hexcode: {}", s ) )
Err ( format! ( " M alformed hexcode: {}", s ) )
}
}
fn parse_value_as_str ( value : & Value ) -> Result < & str , String > {
fn parse_value_as_str ( value : & Value ) -> Result < & str , String > {
value
value
. as_str ( )
. as_str ( )
. ok_or ( format! ( " Theme: u nrecognized value: {}", value ) )
. ok_or ( format! ( " U nrecognized value: {}", value ) )
}
}
pub fn parse_color ( & self , value : Value ) -> Result < Color , String > {
pub fn parse_color ( & self , value : Value ) -> Result < Color , String > {
@ -445,14 +467,14 @@ impl ThemePalette {
value
value
. as_str ( )
. as_str ( )
. and_then ( | s | s . parse ( ) . ok ( ) )
. and_then ( | s | s . parse ( ) . ok ( ) )
. ok_or ( format! ( " Theme: i nvalid modifier: {}", value ) )
. ok_or ( format! ( " I nvalid modifier: {}", value ) )
}
}
pub fn parse_underline_style ( value : & Value ) -> Result < UnderlineStyle , String > {
pub fn parse_underline_style ( value : & Value ) -> Result < UnderlineStyle , String > {
value
value
. as_str ( )
. as_str ( )
. and_then ( | s | s . parse ( ) . ok ( ) )
. and_then ( | s | s . parse ( ) . ok ( ) )
. ok_or ( format! ( " Theme: i nvalid underline style: {}", value ) )
. ok_or ( format! ( " I nvalid underline style: {}", value ) )
}
}
pub fn parse_style ( & self , style : & mut Style , value : Value ) -> Result < ( ) , String > {
pub fn parse_style ( & self , style : & mut Style , value : Value ) -> Result < ( ) , String > {
@ -462,9 +484,7 @@ impl ThemePalette {
"fg" = > * style = style . fg ( self . parse_color ( value ) ? ) ,
"fg" = > * style = style . fg ( self . parse_color ( value ) ? ) ,
"bg" = > * style = style . bg ( self . parse_color ( value ) ? ) ,
"bg" = > * style = style . bg ( self . parse_color ( value ) ? ) ,
"underline" = > {
"underline" = > {
let table = value
let table = value . as_table_mut ( ) . ok_or ( "Underline must be table" ) ? ;
. as_table_mut ( )
. ok_or ( "Theme: underline must be table" ) ? ;
if let Some ( value ) = table . remove ( "color" ) {
if let Some ( value ) = table . remove ( "color" ) {
* style = style . underline_color ( self . parse_color ( value ) ? ) ;
* style = style . underline_color ( self . parse_color ( value ) ? ) ;
}
}
@ -473,13 +493,11 @@ impl ThemePalette {
}
}
if let Some ( attr ) = table . keys ( ) . next ( ) {
if let Some ( attr ) = table . keys ( ) . next ( ) {
return Err ( format! ( " Theme: i nvalid underline attribute: {attr}") ) ;
return Err ( format! ( " I nvalid underline attribute: {attr}") ) ;
}
}
}
}
"modifiers" = > {
"modifiers" = > {
let modifiers = value
let modifiers = value . as_array ( ) . ok_or ( "Modifiers should be an array" ) ? ;
. as_array ( )
. ok_or ( "Theme: modifiers should be an array" ) ? ;
for modifier in modifiers {
for modifier in modifiers {
if modifier
if modifier
@ -492,7 +510,7 @@ impl ThemePalette {
}
}
}
}
}
}
_ = > return Err ( format! ( " Theme: i nvalid style attribute: {}", name ) ) ,
_ = > return Err ( format! ( " I nvalid style attribute: {}", name ) ) ,
}
}
}
}
} else {
} else {