Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'helix-view/src/theme.rs')
-rw-r--r--helix-view/src/theme.rs116
1 files changed, 67 insertions, 49 deletions
diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs
index 4acc5664..9dc32644 100644
--- a/helix-view/src/theme.rs
+++ b/helix-view/src/theme.rs
@@ -53,20 +53,34 @@ impl Loader {
/// Loads a theme searching directories in priority order.
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" {
- return Ok(self.default());
+ return Ok((self.default(), Vec::new()));
}
if name == "base16_default" {
- return Ok(self.base16_default());
+ return Ok((self.base16_default(), Vec::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(),
..theme
- })
+ };
+ Ok((theme, warnings))
}
/// 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 parent_theme_name = parent_theme_name.as_str().ok_or_else(|| {
- anyhow!(
- "Theme: expected 'inherits' to be a string: {}",
- parent_theme_name
- )
+ anyhow!("Expected 'inherits' to be a string: {}", parent_theme_name)
})?;
let parent_theme_toml = match parent_theme_name {
@@ -181,9 +192,9 @@ impl Loader {
})
.ok_or_else(|| {
if cycle_found {
- anyhow!("Theme: cycle found in inheriting: {}", name)
+ anyhow!("Cycle found in inheriting: {}", name)
} else {
- anyhow!("Theme: file not found for: {}", name)
+ anyhow!("File not found for: {}", name)
}
})
}
@@ -220,19 +231,11 @@ pub struct Theme {
impl From<Value> for Theme {
fn from(value: Value) -> Self {
- if let Value::Table(table) = value {
- let (styles, scopes, highlights) = build_theme_values(table);
-
- Self {
- styles,
- scopes,
- highlights,
- ..Default::default()
- }
- } else {
- warn!("Expected theme TOML value to be a table, found {:?}", value);
- Default::default()
+ let (theme, warnings) = Theme::from_toml(value);
+ for warning in warnings {
+ warn!("{}", warning);
}
+ theme
}
}
@@ -242,31 +245,29 @@ impl<'de> Deserialize<'de> for Theme {
D: Deserializer<'de>,
{
let values = Map::<String, Value>::deserialize(deserializer)?;
-
- let (styles, scopes, highlights) = build_theme_values(values);
-
- Ok(Self {
- styles,
- scopes,
- highlights,
- ..Default::default()
- })
+ let (theme, warnings) = Theme::from_keys(values);
+ for warning in warnings {
+ warn!("{}", warning);
+ }
+ Ok(theme)
}
}
fn build_theme_values(
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 scopes = Vec::new();
let mut highlights = Vec::new();
+ let mut warnings = Vec::new();
+
// TODO: alert user of parsing failures in editor
let palette = values
.remove("palette")
.map(|value| {
ThemePalette::try_from(value).unwrap_or_else(|err| {
- warn!("{}", err);
+ warnings.push(err);
ThemePalette::default()
})
})
@@ -279,7 +280,7 @@ fn build_theme_values(
for (name, style_value) in values {
let mut style = Style::default();
if let Err(err) = palette.parse_style(&mut style, style_value) {
- warn!("{}", err);
+ warnings.push(err);
}
// these are used both as UI and as highlights
@@ -288,7 +289,7 @@ fn build_theme_values(
highlights.push(style);
}
- (styles, scopes, highlights)
+ (styles, scopes, highlights, warnings)
}
impl Theme {
@@ -354,6 +355,27 @@ impl Theme {
.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 {
@@ -408,7 +430,7 @@ impl ThemePalette {
if let Ok(index) = s.parse::<u8>() {
return Ok(Color::Indexed(index));
}
- Err(format!("Theme: malformed ANSI: {}", s))
+ Err(format!("Malformed ANSI: {}", s))
}
fn hex_string_to_rgb(s: &str) -> Result<Color, String> {
@@ -422,13 +444,13 @@ impl ThemePalette {
}
}
- Err(format!("Theme: malformed hexcode: {}", s))
+ Err(format!("Malformed hexcode: {}", s))
}
fn parse_value_as_str(value: &Value) -> Result<&str, String> {
value
.as_str()
- .ok_or(format!("Theme: unrecognized value: {}", value))
+ .ok_or(format!("Unrecognized value: {}", value))
}
pub fn parse_color(&self, value: Value) -> Result<Color, String> {
@@ -445,14 +467,14 @@ impl ThemePalette {
value
.as_str()
.and_then(|s| s.parse().ok())
- .ok_or(format!("Theme: invalid modifier: {}", value))
+ .ok_or(format!("Invalid modifier: {}", value))
}
pub fn parse_underline_style(value: &Value) -> Result<UnderlineStyle, String> {
value
.as_str()
.and_then(|s| s.parse().ok())
- .ok_or(format!("Theme: invalid underline style: {}", value))
+ .ok_or(format!("Invalid underline style: {}", value))
}
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)?),
"bg" => *style = style.bg(self.parse_color(value)?),
"underline" => {
- let table = value
- .as_table_mut()
- .ok_or("Theme: underline must be table")?;
+ let table = value.as_table_mut().ok_or("Underline must be table")?;
if let Some(value) = table.remove("color") {
*style = style.underline_color(self.parse_color(value)?);
}
@@ -473,13 +493,11 @@ impl ThemePalette {
}
if let Some(attr) = table.keys().next() {
- return Err(format!("Theme: invalid underline attribute: {attr}"));
+ return Err(format!("Invalid underline attribute: {attr}"));
}
}
"modifiers" => {
- let modifiers = value
- .as_array()
- .ok_or("Theme: modifiers should be an array")?;
+ let modifiers = value.as_array().ok_or("Modifiers should be an array")?;
for modifier in modifiers {
if modifier
@@ -492,7 +510,7 @@ impl ThemePalette {
}
}
}
- _ => return Err(format!("Theme: invalid style attribute: {}", name)),
+ _ => return Err(format!("Invalid style attribute: {}", name)),
}
}
} else {