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.rs | 209 |
1 files changed, 48 insertions, 161 deletions
diff --git a/helix-view/src/theme.rs b/helix-view/src/theme.rs index 173a40f3..af8f03bc 100644 --- a/helix-view/src/theme.rs +++ b/helix-view/src/theme.rs @@ -35,75 +35,6 @@ pub static BASE16_DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| Theme { ..Theme::from(BASE16_DEFAULT_THEME_DATA.clone()) }); -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub enum Mode { - Dark, - Light, -} - -#[cfg(feature = "term")] -impl From<termina::escape::csi::ThemeMode> for Mode { - fn from(mode: termina::escape::csi::ThemeMode) -> Self { - match mode { - termina::escape::csi::ThemeMode::Dark => Self::Dark, - termina::escape::csi::ThemeMode::Light => Self::Light, - } - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Config { - light: String, - dark: String, - /// A theme to choose when the terminal did not declare either light or dark mode. - /// When not specified the dark theme is preferred. - fallback: Option<String>, -} - -impl Config { - pub fn choose(&self, preference: Option<Mode>) -> &str { - match preference { - Some(Mode::Light) => &self.light, - Some(Mode::Dark) => &self.dark, - None => self.fallback.as_ref().unwrap_or(&self.dark), - } - } -} - -impl<'de> Deserialize<'de> for Config { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: serde::Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(untagged, deny_unknown_fields, rename_all = "kebab-case")] - enum InnerConfig { - Constant(String), - Adaptive { - dark: String, - light: String, - fallback: Option<String>, - }, - } - - let inner = InnerConfig::deserialize(deserializer)?; - - let (light, dark, fallback) = match inner { - InnerConfig::Constant(theme) => (theme.clone(), theme.clone(), None), - InnerConfig::Adaptive { - light, - dark, - fallback, - } => (light, dark, fallback), - }; - - Ok(Self { - light, - dark, - fallback, - }) - } -} #[derive(Clone, Debug)] pub struct Loader { /// Theme directories to search from highest to lowest priority @@ -296,7 +227,6 @@ 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 From<Value> for Theme { @@ -323,20 +253,12 @@ impl<'de> Deserialize<'de> for Theme { } } -#[allow(clippy::type_complexity)] fn build_theme_values( mut values: Map<String, Value>, -) -> ( - HashMap<String, Style>, - Vec<String>, - Vec<Style>, - usize, - Vec<String>, -) { +) -> (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 rainbow_length = 0; let mut warnings = Vec::new(); @@ -355,27 +277,6 @@ fn build_theme_values( styles.reserve(values.len()); scopes.reserve(values.len()); highlights.reserve(values.len()); - - for (i, style) in values - .remove("rainbow") - .and_then(|value| match palette.parse_style_array(value) { - Ok(styles) => Some(styles), - Err(err) => { - warnings.push(err); - None - } - }) - .unwrap_or_else(default_rainbow) - .into_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 values { let mut style = Style::default(); if let Err(err) = palette.parse_style(&mut style, style_value) { @@ -388,51 +289,48 @@ fn build_theme_values( highlights.push(style); } - (styles, scopes, highlights, rainbow_length, warnings) + (styles, scopes, highlights, warnings) } -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), - ] -} impl Theme { /// To allow `Highlight` to represent arbitrary RGB colors without turning it into an enum, - /// we interpret the last 256^3 numbers as RGB. - const RGB_START: u32 = (u32::MAX << (8 + 8 + 8)) - 1 - (u32::MAX - Highlight::MAX); + /// we interpret the last 3 bytes of a `Highlight` as RGB colors. + const RGB_START: usize = (usize::MAX << (8 + 8 + 8)) - 1; /// Interpret a Highlight with the RGB foreground - fn decode_rgb_highlight(highlight: Highlight) -> Option<(u8, u8, u8)> { - (highlight.get() > Self::RGB_START).then(|| { - let [b, g, r, ..] = (highlight.get() + 1).to_le_bytes(); + fn decode_rgb_highlight(rgb: usize) -> Option<(u8, u8, u8)> { + (rgb > Self::RGB_START).then(|| { + let [b, g, r, ..] = rgb.to_ne_bytes(); (r, g, b) }) } /// Create a Highlight that represents an RGB color pub fn rgb_highlight(r: u8, g: u8, b: u8) -> Highlight { - // -1 because highlight is "non-max": u32::MAX is reserved for the null pointer - // optimization. - Highlight::new(u32::from_le_bytes([b, g, r, u8::MAX]) - 1) + Highlight(usize::from_ne_bytes([ + b, + g, + r, + u8::MAX, + u8::MAX, + u8::MAX, + u8::MAX, + u8::MAX, + ])) } #[inline] - pub fn highlight(&self, highlight: Highlight) -> Style { - if let Some((red, green, blue)) = Self::decode_rgb_highlight(highlight) { + pub fn highlight(&self, index: usize) -> Style { + if let Some((red, green, blue)) = Self::decode_rgb_highlight(index) { Style::new().fg(Color::Rgb(red, green, blue)) } else { - self.highlights[highlight.idx()] + self.highlights[index] } } #[inline] - pub fn scope(&self, highlight: Highlight) -> &str { - &self.scopes[highlight.idx()] + pub fn scope(&self, index: usize) -> &str { + &self.scopes[index] } pub fn name(&self) -> &str { @@ -463,16 +361,13 @@ impl Theme { &self.scopes } - pub fn find_highlight_exact(&self, scope: &str) -> Option<Highlight> { - self.scopes() - .iter() - .position(|s| s == scope) - .map(|idx| Highlight::new(idx as u32)) + pub fn find_scope_index_exact(&self, scope: &str) -> Option<usize> { + self.scopes().iter().position(|s| s == scope) } - pub fn find_highlight(&self, mut scope: &str) -> Option<Highlight> { + pub fn find_scope_index(&self, mut scope: &str) -> Option<usize> { loop { - if let Some(highlight) = self.find_highlight_exact(scope) { + if let Some(highlight) = self.find_scope_index_exact(scope) { return Some(highlight); } if let Some(new_end) = scope.rfind('.') { @@ -491,10 +386,6 @@ impl Theme { }) } - pub fn rainbow_length(&self) -> usize { - self.rainbow_length - } - fn from_toml(value: Value) -> (Self, Vec<String>) { if let Value::Table(table) = value { Theme::from_keys(table) @@ -505,14 +396,12 @@ impl Theme { } fn from_keys(toml_keys: Map<String, Value>) -> (Self, Vec<String>) { - let (styles, scopes, highlights, rainbow_length, load_errors) = - build_theme_values(toml_keys); + let (styles, scopes, highlights, load_errors) = build_theme_values(toml_keys); let theme = Self { styles, scopes, highlights, - rainbow_length, ..Default::default() }; (theme, load_errors) @@ -656,21 +545,6 @@ impl ThemePalette { } Ok(()) } - - 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!("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 { @@ -752,13 +626,23 @@ mod tests { fn convert_to_and_from() { let (r, g, b) = (0xFF, 0xFE, 0xFA); let highlight = Theme::rgb_highlight(r, g, b); - assert_eq!(Theme::decode_rgb_highlight(highlight), Some((r, g, b))); + assert_eq!(Theme::decode_rgb_highlight(highlight.0), Some((r, g, b))); } /// make sure we can store all the colors at the end + /// ``` + /// FF FF FF FF FF FF FF FF + /// xor + /// FF FF FF FF FF 00 00 00 + /// = + /// 00 00 00 00 00 FF FF FF + /// ``` + /// + /// where the ending `(FF, FF, FF)` represents `(r, g, b)` #[test] fn full_numeric_range() { - assert_eq!(Highlight::MAX - Theme::RGB_START, 256_u32.pow(3)); + assert_eq!(usize::MAX ^ Theme::RGB_START, 256_usize.pow(3)); + assert_eq!(Theme::RGB_START + 256_usize.pow(3), usize::MAX); } #[test] @@ -766,27 +650,30 @@ mod tests { // color in the middle let (r, g, b) = (0x14, 0xAA, 0xF7); assert_eq!( - Theme::default().highlight(Theme::rgb_highlight(r, g, b)), + Theme::default().highlight(Theme::rgb_highlight(r, g, b).0), Style::new().fg(Color::Rgb(r, g, b)) ); // pure black let (r, g, b) = (0x00, 0x00, 0x00); assert_eq!( - Theme::default().highlight(Theme::rgb_highlight(r, g, b)), + Theme::default().highlight(Theme::rgb_highlight(r, g, b).0), Style::new().fg(Color::Rgb(r, g, b)) ); // pure white let (r, g, b) = (0xff, 0xff, 0xff); assert_eq!( - Theme::default().highlight(Theme::rgb_highlight(r, g, b)), + Theme::default().highlight(Theme::rgb_highlight(r, g, b).0), Style::new().fg(Color::Rgb(r, g, b)) ); } #[test] - #[should_panic(expected = "index out of bounds: the len is 0 but the index is 4278190078")] + #[should_panic( + expected = "index out of bounds: the len is 0 but the index is 18446744073692774399" + )] fn out_of_bounds() { - let highlight = Highlight::new(Theme::rgb_highlight(0, 0, 0).get() - 1); - Theme::default().highlight(highlight); + let (r, g, b) = (0x00, 0x00, 0x00); + + Theme::default().highlight(Theme::rgb_highlight(r, g, b).0 - 1); } } |