on master help sign in
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>

commit54f3548d541c3fc6fd793aeacb584cbe0d934b82 authorIngrid <intarga@protonmail.com> dateSun 6 Jun 2021, 14:45 (5 years ago) parent3280510
6 files changed+214−1
Cargo.lock+1 book/src/configuration.md+86 contrib/themes/README.md+9 contrib/themes/ingrid.toml+46 helix-view/Cargo.toml+1 helix-view/src/theme.rs+71−1
Cargo.lock+1
@@ -355,6 +355,7 @@ dependencies = [
355355 "helix-core",
356356 "helix-lsp",
357357 "helix-tui",
358+ "log",
358359 "once_cell",
359360 "serde",
360361 "slotmap",
book/src/configuration.md+86
@@ -1 +1,87 @@
11# Configuration
2+
3+## Theme
4+
5+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).
6+
7+Styles in theme.toml are specified of in the form:
8+
9+```toml
10+key = { fg = "#ffffff", bg = "#000000", modifiers = ["bold", "italic"] }
11+```
12+
13+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.
14+
15+To specify only the foreground color:
16+
17+```toml
18+key = "#ffffff"
19+```
20+
21+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).
22+
23+```toml
24+"key.key" = "#ffffff"
25+```
26+
27+Possible modifiers:
28+
29+| modifier |
30+| --- |
31+| bold |
32+| dim |
33+| italic |
34+| underlined |
35+| slow\_blink |
36+| rapid\_blink |
37+| reversed |
38+| hidden |
39+| crossed\_out |
40+
41+Possible keys:
42+
43+| key | notes |
44+| --- | --- |
45+| attribute | |
46+| keyword | |
47+| keyword.directive | preprocessor directives (\#if in C) |
48+| namespace | |
49+| punctuation | |
50+| punctuation.delimiter | |
51+| operator | |
52+| special | |
53+| property | |
54+| variable | |
55+| variable.parameter | |
56+| type | |
57+| type.builtin | |
58+| constructor | |
59+| function | |
60+| function.macro | |
61+| function.builtin | |
62+| comment | |
63+| variable.builtin | |
64+| constant | |
65+| constant.builtin | |
66+| string | |
67+| number | |
68+| escape | escaped characters |
69+| label | used for lifetimes |
70+| module | |
71+| ui.background | |
72+| ui.linenr | |
73+| ui.statusline | |
74+| ui.popup | |
75+| ui.window | |
76+| ui.help | |
77+| ui.text | |
78+| ui.text.focus | |
79+| ui.menu.selected | |
80+| warning | LSP warning |
81+| error | LSP error |
82+| info | LSP info |
83+| hint | LSP hint |
84+
85+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.
86+
87+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.
contrib/themes/README.mdnew file+9
@@ -0,0 +1,9 @@
1+# User submitted themes
2+
3+If you submit a theme, please include a comment at the top with your name and email address:
4+
5+```toml
6+# Author : Name <email@my.domain>
7+```
8+
9+We have a preview page for themes on our [wiki](https://github.com/helix-editor/helix/wiki/Themes)!
contrib/themes/ingrid.tomlnew file+46
@@ -0,0 +1,46 @@
1+# Author : Ingrid Rebecca Abraham <git@ingrids.email>
2+
3+"attribute" = "#839A53"
4+"keyword" = { fg = "#D74E50", modifiers = ["bold"] }
5+"keyword.directive" = "#6F873E"
6+"namespace" = "#839A53"
7+"punctuation" = "#C97270"
8+"punctuation.delimiter" = "#C97270"
9+"operator" = { fg = "#D74E50", modifiers = ["bold"] }
10+"special" = "#D68482"
11+"property" = "#89BEB7"
12+"variable" = "#A6B6CE"
13+"variable.parameter" = "#89BEB7"
14+"type" = { fg = "#A6B6CE", modifiers = ["bold"] }
15+"type.builtin" = "#839A53"
16+"constructor" = { fg = "#839A53", modifiers = ["bold"] }
17+"function" = { fg = "#89BEB7", modifiers = ["bold"] }
18+"function.macro" = { fg = "#D4A520", modifiers = ["bold"] }
19+"function.builtin" = "#89BEB7"
20+"comment" = "#A6B6CE"
21+"variable.builtin" = "#D4A520"
22+"constant" = "#D4A520"
23+"constant.builtin" = "#D4A520"
24+"string" = "#D74E50"
25+"number" = "#D74E50"
26+"escape" = { fg = "#D74E50", modifiers = ["bold"] }
27+"label" = "#D68482"
28+
29+"module" = "#839A53"
30+
31+"ui.background" = { bg = "#FFFCFD" }
32+"ui.linenr" = { fg = "#bbbbbb" }
33+"ui.statusline" = { bg = "#F3EAE9" }
34+"ui.popup" = { bg = "#F3EAE9" }
35+"ui.window" = { bg = "#D8B8B3" }
36+"ui.help" = { bg = "#D8B8B3", fg = "#250E07" }
37+
38+"ui.text" = { fg = "#7B91B3" }
39+"ui.text.focus" = { fg = "#250E07", modifiers= ["bold"] }
40+
41+"ui.menu.selected" = { fg = "#D74E50", bg = "#F3EAE9" }
42+
43+"warning" = "#D4A520"
44+"error" = "#D74E50"
45+"info" = "#839A53"
46+"hint" = "#A6B6CE"
helix-view/Cargo.toml+1
@@ -28,3 +28,4 @@ slotmap = "1"
2828
2929serde = { version = "1.0", features = ["derive"] }
3030toml = "0.5"
31+log = "~0.4"
helix-view/src/theme.rs+71−1
@@ -1,10 +1,11 @@
11use std::collections::HashMap;
22
3+use log::warn;
34use serde::{Deserialize, Deserializer};
45use toml::Value;
56
67#[cfg(feature = "term")]
7−pub use tui::style::{Color, Style};
8+pub use tui::style::{Color, Modifier, Style};
89
910// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
1011// pub struct Color {
@@ -115,6 +116,7 @@ impl<'de> Deserialize<'de> for Theme {
115116}
116117
117118fn parse_style(style: &mut Style, value: Value) {
119+ //TODO: alert user of parsing failures
118120 if let Value::Table(entries) = value {
119121 for (name, value) in entries {
120122 match name.as_str() {
@@ -128,6 +130,13 @@ fn parse_style(style: &mut Style, value: Value) {
128130 *style = style.bg(color);
129131 }
130132 }
133+ "modifiers" => {
134+ if let Value::Array(arr) = value {
135+ for modifier in arr.iter().filter_map(parse_modifier) {
136+ *style = style.add_modifier(modifier);
137+ }
138+ }
139+ }
131140 _ => (),
132141 }
133142 }
@@ -157,9 +166,34 @@ fn parse_color(value: Value) -> Option<Color> {
157166 if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
158167 Some(Color::Rgb(red, green, blue))
159168 } else {
169+ warn!("malformed hexcode in theme: {}", s);
160170 None
161171 }
162172 } else {
173+ warn!("unrecognized value in theme: {}", value);
174+ None
175+ }
176+}
177+
178+fn parse_modifier(value: &Value) -> Option<Modifier> {
179+ if let Value::String(s) = value {
180+ match s.as_str() {
181+ "bold" => Some(Modifier::BOLD),
182+ "dim" => Some(Modifier::DIM),
183+ "italic" => Some(Modifier::ITALIC),
184+ "underlined" => Some(Modifier::UNDERLINED),
185+ "slow_blink" => Some(Modifier::SLOW_BLINK),
186+ "rapid_blink" => Some(Modifier::RAPID_BLINK),
187+ "reversed" => Some(Modifier::REVERSED),
188+ "hidden" => Some(Modifier::HIDDEN),
189+ "crossed_out" => Some(Modifier::CROSSED_OUT),
190+ _ => {
191+ warn!("unrecognized modifier in theme: {}", s);
192+ None
193+ }
194+ }
195+ } else {
196+ warn!("unrecognized modifier in theme: {}", value);
163197 None
164198 }
165199}
@@ -177,3 +211,39 @@ impl Theme {
177211 &self.scopes
178212 }
179213}
214+
215+#[test]
216+fn test_parse_style_string() {
217+ let fg = Value::String("#ffffff".to_string());
218+
219+ let mut style = Style::default();
220+ parse_style(&mut style, fg);
221+
222+ assert_eq!(style, Style::default().fg(Color::Rgb(255, 255, 255)));
223+}
224+
225+#[test]
226+fn test_parse_style_table() {
227+ let table = toml::toml! {
228+ "keyword" = {
229+ fg = "#ffffff",
230+ bg = "#000000",
231+ modifiers = ["bold"],
232+ }
233+ };
234+
235+ let mut style = Style::default();
236+ if let Value::Table(entries) = table {
237+ for (_name, value) in entries {
238+ parse_style(&mut style, value);
239+ }
240+ }
241+
242+ assert_eq!(
243+ style,
244+ Style::default()
245+ .fg(Color::Rgb(255, 255, 255))
246+ .bg(Color::Rgb(0, 0, 0))
247+ .add_modifier(Modifier::BOLD)
248+ );
249+}