theme: Enable style modifiers in theme.toml, add Ingrid's theme (#113)
* theme: Enable style modifiers in theme.toml * docs: theme documentation * fixup: parse modifiers with filter_map * theme: tests for parse_style * theme: Log invalid cases in theme.toml parse * docs: theme documentation fixup * docs: Blaz's theming comments * docs: Theme doc fixes from pickfire Co-authored-by: Ivan Tham <pickfire@riseup.net> * theme: More context in logs, TODO for alerting users * contrib: Ingrid's theme * docs: Theme subsection fixes Co-authored-by: Ivan Tham <pickfire@riseup.net>
This commit is contained in:
parent
3280510d5b
commit
54f3548d54
6 changed files with 214 additions and 1 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -355,6 +355,7 @@ dependencies = [
|
|||
"helix-core",
|
||||
"helix-lsp",
|
||||
"helix-tui",
|
||||
"log",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"slotmap",
|
||||
|
|
|
@ -1 +1,87 @@
|
|||
# Configuration
|
||||
|
||||
## Theme
|
||||
|
||||
Use a custom theme by placing a theme.toml in your config directory (i.e ~/.config/helix/theme.toml). The default theme.toml can be found [here](https://github.com/helix-editor/helix/blob/master/theme.toml), and user submitted themes [here](https://github.com/helix-editor/helix/blob/master/contrib/themes).
|
||||
|
||||
Styles in theme.toml are specified of in the form:
|
||||
|
||||
```toml
|
||||
key = { fg = "#ffffff", bg = "#000000", modifiers = ["bold", "italic"] }
|
||||
```
|
||||
|
||||
where `name` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, and `modifiers` is a list of style modifiers. `bg` and `modifiers` can be omitted to defer to the defaults.
|
||||
|
||||
To specify only the foreground color:
|
||||
|
||||
```toml
|
||||
key = "#ffffff"
|
||||
```
|
||||
|
||||
if the key contains a dot `'.'`, it must be quoted to prevent it being parsed as a [dotted key](https://toml.io/en/v1.0.0#keys).
|
||||
|
||||
```toml
|
||||
"key.key" = "#ffffff"
|
||||
```
|
||||
|
||||
Possible modifiers:
|
||||
|
||||
| modifier |
|
||||
| --- |
|
||||
| bold |
|
||||
| dim |
|
||||
| italic |
|
||||
| underlined |
|
||||
| slow\_blink |
|
||||
| rapid\_blink |
|
||||
| reversed |
|
||||
| hidden |
|
||||
| crossed\_out |
|
||||
|
||||
Possible keys:
|
||||
|
||||
| key | notes |
|
||||
| --- | --- |
|
||||
| attribute | |
|
||||
| keyword | |
|
||||
| keyword.directive | preprocessor directives (\#if in C) |
|
||||
| namespace | |
|
||||
| punctuation | |
|
||||
| punctuation.delimiter | |
|
||||
| operator | |
|
||||
| special | |
|
||||
| property | |
|
||||
| variable | |
|
||||
| variable.parameter | |
|
||||
| type | |
|
||||
| type.builtin | |
|
||||
| constructor | |
|
||||
| function | |
|
||||
| function.macro | |
|
||||
| function.builtin | |
|
||||
| comment | |
|
||||
| variable.builtin | |
|
||||
| constant | |
|
||||
| constant.builtin | |
|
||||
| string | |
|
||||
| number | |
|
||||
| escape | escaped characters |
|
||||
| label | used for lifetimes |
|
||||
| module | |
|
||||
| ui.background | |
|
||||
| ui.linenr | |
|
||||
| ui.statusline | |
|
||||
| ui.popup | |
|
||||
| ui.window | |
|
||||
| ui.help | |
|
||||
| ui.text | |
|
||||
| ui.text.focus | |
|
||||
| ui.menu.selected | |
|
||||
| warning | LSP warning |
|
||||
| error | LSP error |
|
||||
| info | LSP info |
|
||||
| hint | LSP hint |
|
||||
|
||||
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.
|
||||
|
|
9
contrib/themes/README.md
Normal file
9
contrib/themes/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# User submitted themes
|
||||
|
||||
If you submit a theme, please include a comment at the top with your name and email address:
|
||||
|
||||
```toml
|
||||
# Author : Name <email@my.domain>
|
||||
```
|
||||
|
||||
We have a preview page for themes on our [wiki](https://github.com/helix-editor/helix/wiki/Themes)!
|
46
contrib/themes/ingrid.toml
Normal file
46
contrib/themes/ingrid.toml
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Author : Ingrid Rebecca Abraham <git@ingrids.email>
|
||||
|
||||
"attribute" = "#839A53"
|
||||
"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"keyword.directive" = "#6F873E"
|
||||
"namespace" = "#839A53"
|
||||
"punctuation" = "#C97270"
|
||||
"punctuation.delimiter" = "#C97270"
|
||||
"operator" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"special" = "#D68482"
|
||||
"property" = "#89BEB7"
|
||||
"variable" = "#A6B6CE"
|
||||
"variable.parameter" = "#89BEB7"
|
||||
"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
|
||||
"type.builtin" = "#839A53"
|
||||
"constructor" = { fg = "#839A53", modifiers = ["bold"] }
|
||||
"function" = { fg = "#89BEB7", modifiers = ["bold"] }
|
||||
"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
|
||||
"function.builtin" = "#89BEB7"
|
||||
"comment" = "#A6B6CE"
|
||||
"variable.builtin" = "#D4A520"
|
||||
"constant" = "#D4A520"
|
||||
"constant.builtin" = "#D4A520"
|
||||
"string" = "#D74E50"
|
||||
"number" = "#D74E50"
|
||||
"escape" = { fg = "#D74E50", modifiers = ["bold"] }
|
||||
"label" = "#D68482"
|
||||
|
||||
"module" = "#839A53"
|
||||
|
||||
"ui.background" = { bg = "#FFFCFD" }
|
||||
"ui.linenr" = { fg = "#bbbbbb" }
|
||||
"ui.statusline" = { bg = "#F3EAE9" }
|
||||
"ui.popup" = { bg = "#F3EAE9" }
|
||||
"ui.window" = { bg = "#D8B8B3" }
|
||||
"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
|
||||
|
||||
"ui.text" = { fg = "#7B91B3" }
|
||||
"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
|
||||
|
||||
"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
|
||||
|
||||
"warning" = "#D4A520"
|
||||
"error" = "#D74E50"
|
||||
"info" = "#839A53"
|
||||
"hint" = "#A6B6CE"
|
|
@ -28,3 +28,4 @@ slotmap = "1"
|
|||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
log = "~0.4"
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use log::warn;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use toml::Value;
|
||||
|
||||
#[cfg(feature = "term")]
|
||||
pub use tui::style::{Color, Style};
|
||||
pub use tui::style::{Color, Modifier, Style};
|
||||
|
||||
// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
|
||||
// pub struct Color {
|
||||
|
@ -115,6 +116,7 @@ impl<'de> Deserialize<'de> for Theme {
|
|||
}
|
||||
|
||||
fn parse_style(style: &mut Style, value: Value) {
|
||||
//TODO: alert user of parsing failures
|
||||
if let Value::Table(entries) = value {
|
||||
for (name, value) in entries {
|
||||
match name.as_str() {
|
||||
|
@ -128,6 +130,13 @@ fn parse_style(style: &mut Style, value: Value) {
|
|||
*style = style.bg(color);
|
||||
}
|
||||
}
|
||||
"modifiers" => {
|
||||
if let Value::Array(arr) = value {
|
||||
for modifier in arr.iter().filter_map(parse_modifier) {
|
||||
*style = style.add_modifier(modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -157,9 +166,34 @@ fn parse_color(value: Value) -> Option<Color> {
|
|||
if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
|
||||
Some(Color::Rgb(red, green, blue))
|
||||
} else {
|
||||
warn!("malformed hexcode in theme: {}", s);
|
||||
None
|
||||
}
|
||||
} else {
|
||||
warn!("unrecognized value in theme: {}", value);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_modifier(value: &Value) -> Option<Modifier> {
|
||||
if let Value::String(s) = value {
|
||||
match s.as_str() {
|
||||
"bold" => Some(Modifier::BOLD),
|
||||
"dim" => Some(Modifier::DIM),
|
||||
"italic" => Some(Modifier::ITALIC),
|
||||
"underlined" => Some(Modifier::UNDERLINED),
|
||||
"slow_blink" => Some(Modifier::SLOW_BLINK),
|
||||
"rapid_blink" => Some(Modifier::RAPID_BLINK),
|
||||
"reversed" => Some(Modifier::REVERSED),
|
||||
"hidden" => Some(Modifier::HIDDEN),
|
||||
"crossed_out" => Some(Modifier::CROSSED_OUT),
|
||||
_ => {
|
||||
warn!("unrecognized modifier in theme: {}", s);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
warn!("unrecognized modifier in theme: {}", value);
|
||||
None
|
||||
}
|
||||
}
|
||||
|
@ -177,3 +211,39 @@ impl Theme {
|
|||
&self.scopes
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_string() {
|
||||
let fg = Value::String("#ffffff".to_string());
|
||||
|
||||
let mut style = Style::default();
|
||||
parse_style(&mut style, fg);
|
||||
|
||||
assert_eq!(style, Style::default().fg(Color::Rgb(255, 255, 255)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_style_table() {
|
||||
let table = toml::toml! {
|
||||
"keyword" = {
|
||||
fg = "#ffffff",
|
||||
bg = "#000000",
|
||||
modifiers = ["bold"],
|
||||
}
|
||||
};
|
||||
|
||||
let mut style = Style::default();
|
||||
if let Value::Table(entries) = table {
|
||||
for (_name, value) in entries {
|
||||
parse_style(&mut style, value);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
style,
|
||||
Style::default()
|
||||
.fg(Color::Rgb(255, 255, 255))
|
||||
.bg(Color::Rgb(0, 0, 0))
|
||||
.add_modifier(Modifier::BOLD)
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue