Color palettes (#393)
* Enable using color palettes in theme files. * Add an example theme defined using a gruvbox color palette. * Fix clippy error. * Small style improvement. * Add documentation for the features to themes.md. * Update runtime/themes/gruvbox.toml Fix the value of purple0. Co-authored-by: DrZingo <DrZingo@users.noreply.github.com> Co-authored-by: DrZingo <DrZingo@users.noreply.github.com>
This commit is contained in:
parent
2a92dd8d4d
commit
79f096963c
3 changed files with 143 additions and 10 deletions
|
@ -99,3 +99,21 @@ Possible keys:
|
|||
These keys match [tree-sitter scopes](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#theme). We half-follow the common scopes from [macromates language grammars](https://macromates.com/manual/en/language_grammars) with some differences.
|
||||
|
||||
For a given highlight produced, styling will be determined based on the longest matching theme key. So it's enough to provide function to highlight `function.macro` and `function.builtin` as well, but you can use more specific scopes to highlight specific cases differently.
|
||||
|
||||
## Color palettes
|
||||
|
||||
You can define a palette of named colors, and refer to them from the
|
||||
configuration values in your theme. To do this, add a table called
|
||||
`palette` to your theme file:
|
||||
|
||||
```toml
|
||||
ui.background = "white"
|
||||
ui.text = "black"
|
||||
|
||||
[palette]
|
||||
white = "#ffffff"
|
||||
black = "#000000"
|
||||
```
|
||||
|
||||
Remember that the `[palette]` table includes all keys after its header,
|
||||
so you should define the palette after normal theme options.
|
||||
|
|
|
@ -102,12 +102,13 @@ impl<'de> Deserialize<'de> for Theme {
|
|||
{
|
||||
let mut styles = HashMap::new();
|
||||
|
||||
if let Ok(colors) = HashMap::<String, Value>::deserialize(deserializer) {
|
||||
if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
|
||||
let palette = parse_palette(colors.remove("palette"));
|
||||
// scopes.reserve(colors.len());
|
||||
styles.reserve(colors.len());
|
||||
for (name, style_value) in colors {
|
||||
let mut style = Style::default();
|
||||
parse_style(&mut style, style_value);
|
||||
parse_style(&mut style, style_value, &palette);
|
||||
// scopes.push(name);
|
||||
styles.insert(name, style);
|
||||
}
|
||||
|
@ -118,18 +119,31 @@ impl<'de> Deserialize<'de> for Theme {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_style(style: &mut Style, value: Value) {
|
||||
fn parse_palette(value: Option<Value>) -> HashMap<String, Color> {
|
||||
match value {
|
||||
Some(Value::Table(entries)) => entries,
|
||||
_ => return HashMap::default(),
|
||||
}
|
||||
.into_iter()
|
||||
.filter_map(|(name, value)| {
|
||||
let color = parse_color(value, &HashMap::default())?;
|
||||
Some((name, color))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_style(style: &mut Style, value: Value, palette: &HashMap<String, Color>) {
|
||||
//TODO: alert user of parsing failures
|
||||
if let Value::Table(entries) = value {
|
||||
for (name, value) in entries {
|
||||
match name.as_str() {
|
||||
"fg" => {
|
||||
if let Some(color) = parse_color(value) {
|
||||
if let Some(color) = parse_color(value, palette) {
|
||||
*style = style.fg(color);
|
||||
}
|
||||
}
|
||||
"bg" => {
|
||||
if let Some(color) = parse_color(value) {
|
||||
if let Some(color) = parse_color(value, palette) {
|
||||
*style = style.bg(color);
|
||||
}
|
||||
}
|
||||
|
@ -143,7 +157,7 @@ fn parse_style(style: &mut Style, value: Value) {
|
|||
_ => (),
|
||||
}
|
||||
}
|
||||
} else if let Some(color) = parse_color(value) {
|
||||
} else if let Some(color) = parse_color(value, palette) {
|
||||
*style = style.fg(color);
|
||||
}
|
||||
}
|
||||
|
@ -164,9 +178,11 @@ fn hex_string_to_rgb(s: &str) -> Option<(u8, u8, u8)> {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_color(value: Value) -> Option<Color> {
|
||||
fn parse_color(value: Value, palette: &HashMap<String, Color>) -> Option<Color> {
|
||||
if let Value::String(s) = value {
|
||||
if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
|
||||
if let Some(color) = palette.get(&s) {
|
||||
Some(*color)
|
||||
} else if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
|
||||
Some(Color::Rgb(red, green, blue))
|
||||
} else {
|
||||
warn!("malformed hexcode in theme: {}", s);
|
||||
|
@ -226,7 +242,23 @@ fn test_parse_style_string() {
|
|||
let fg = Value::String("#ffffff".to_string());
|
||||
|
||||
let mut style = Style::default();
|
||||
parse_style(&mut style, fg);
|
||||
parse_style(&mut style, fg, &HashMap::default());
|
||||
|
||||
assert_eq!(style, Style::default().fg(Color::Rgb(255, 255, 255)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_palette() {
|
||||
let fg = Value::String("my_color".to_string());
|
||||
|
||||
let mut style = Style::default();
|
||||
parse_style(
|
||||
&mut style,
|
||||
fg,
|
||||
&vec![("my_color".to_string(), Color::Rgb(255, 255, 255))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
);
|
||||
|
||||
assert_eq!(style, Style::default().fg(Color::Rgb(255, 255, 255)));
|
||||
}
|
||||
|
@ -244,7 +276,7 @@ fn test_parse_style_table() {
|
|||
let mut style = Style::default();
|
||||
if let Value::Table(entries) = table {
|
||||
for (_name, value) in entries {
|
||||
parse_style(&mut style, value);
|
||||
parse_style(&mut style, value, &HashMap::default());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
83
runtime/themes/gruvbox.toml
Normal file
83
runtime/themes/gruvbox.toml
Normal file
|
@ -0,0 +1,83 @@
|
|||
# Author : Jakub Bartodziej <kubabartodziej@gmail.com>
|
||||
# The theme uses the gruvbox dark palette with standard contrast: github.com/morhetz/gruvbox
|
||||
|
||||
"attribute" = "fg2"
|
||||
"keyword" = { fg = "red1" }
|
||||
"keyword.directive" = "red0"
|
||||
"namespace" = "yellow0"
|
||||
"punctuation" = "fg4"
|
||||
"punctuation.delimiter" = "fg4"
|
||||
"operator" = "orange0"
|
||||
"special" = "purple0"
|
||||
"property" = "fg2"
|
||||
"variable" = "fg2"
|
||||
"variable.builtin" = "fg3"
|
||||
"variable.parameter" = "fg2"
|
||||
"type" = "orange1"
|
||||
"type.builtin" = "orange0"
|
||||
"constructor" = "fg2"
|
||||
"function" = "green0"
|
||||
"function.macro" = "aqua1"
|
||||
"function.builtin" = "yellow1"
|
||||
"comment" = "gray1"
|
||||
"constant" = { fg = "fg2", modifiers = ["bold"] }
|
||||
"constant.builtin" = { fg = "fg1", modifiers = ["bold"] }
|
||||
"string" = "green1"
|
||||
"number" = "purple1"
|
||||
"escape" = { fg = "fg2", modifiers = ["bold"] }
|
||||
"label" = "aqua1"
|
||||
"module" = "yellow1"
|
||||
|
||||
"warning" = "orange0"
|
||||
"error" = "red0"
|
||||
"info" = "purple0"
|
||||
"hint" = "blue0"
|
||||
|
||||
"ui.background" = { bg = "bg0" }
|
||||
"ui.linenr" = { fg = "fg3" }
|
||||
"ui.linenr.selected" = { fg = "fg1", modifiers = ["bold"] }
|
||||
"ui.statusline" = { fg = "fg2", bg = "bg2" }
|
||||
"ui.statusline.inactive" = { fg = "fg3", bg = "bg4" }
|
||||
"ui.popup" = { bg = "bg1" }
|
||||
"ui.window" = { bg = "bg1" }
|
||||
"ui.help" = { bg = "bg1", fg = "fg1" }
|
||||
"ui.text" = { fg = "fg1" }
|
||||
"ui.text.focus" = { fg = "fg1", modifiers = ["bold"] }
|
||||
"ui.selection" = { bg = "bg3" }
|
||||
"ui.cursor.primary" = { bg = "bg3" }
|
||||
"ui.cursor.match" = { bg = "bg4" }
|
||||
"ui.menu" = { fg = "fg1" }
|
||||
"ui.menu.selected" = { fg = "fg3", bg = "bg3" }
|
||||
|
||||
"diagnostic" = { modifiers = ["underlined"] }
|
||||
|
||||
[palette]
|
||||
bg0 = "#282828" # main background
|
||||
bg1 = "#3c3836"
|
||||
bg2 = "#504945"
|
||||
bg3 = "#665c54"
|
||||
bg4 = "#7c6f64"
|
||||
|
||||
fg0 = "#fbf1c7"
|
||||
fg1 = "#ebdbb2" # main foreground
|
||||
fg2 = "#d5c4a1"
|
||||
fg3 = "#bdae93"
|
||||
fg4 = "#a89984" # gray0
|
||||
|
||||
gray0 = "#a89984"
|
||||
gray1 = "#928374"
|
||||
|
||||
red0 = "#cc241d" # neutral
|
||||
red1 = "#fb4934" # bright
|
||||
green0 = "#98971a"
|
||||
green1 = "#b8bb26"
|
||||
yellow0 = "#d79921"
|
||||
yellow1 = "#fabd2f"
|
||||
blue0 = "#458588"
|
||||
blue1 = "#83a598"
|
||||
purple0 = "#b16286"
|
||||
purple1 = "#d3869b"
|
||||
aqua0 = "#689d6a"
|
||||
aqua1 = "#8ec07c"
|
||||
orange0 = "#d65d0e"
|
||||
orange1 = "#fe8019"
|
Loading…
Add table
Reference in a new issue