on master help sign in
theme: Enable style modifiers in theme.toml, add Ingrid's theme (#113)
54f3548Ingrid
Cargo.lock+1
@@ -355,6 +355,7 @@ dependencies = [
355 "helix-core",
355 "helix-core",
356 "helix-lsp",
356 "helix-lsp",
357 "helix-tui",
357 "helix-tui",
358+ "log",
358 "once_cell",
359 "once_cell",
359 "serde",
360 "serde",
360 "slotmap",
361 "slotmap",
book/src/configuration.md+86
@@ -1 +1,87 @@
1# Configuration
1# 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"
28
28
29serde = { version = "1.0", features = ["derive"] }
29serde = { version = "1.0", features = ["derive"] }
30toml = "0.5"
30toml = "0.5"
31+log = "~0.4"
helix-view/src/theme.rs+71−1
@@ -1,10 +1,11 @@
1use std::collections::HashMap;
1use std::collections::HashMap;
2
2
3+use log::warn;
3use serde::{Deserialize, Deserializer};
4use serde::{Deserialize, Deserializer};
4use toml::Value;
5use toml::Value;
5
6
6#[cfg(feature = "term")]
7#[cfg(feature = "term")]
7−pub use tui::style::{Color, Style};
8+pub use tui::style::{Color, Modifier, Style};
8
9
9// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
10// #[derive(Clone, Copy, PartialEq, Eq, Default, Hash)]
10// pub struct Color {
11// pub struct Color {
@@ -115,6 +116,7 @@ impl<'de> Deserialize<'de> for Theme {
115}
116}
116
117
117fn parse_style(style: &mut Style, value: Value) {
118fn parse_style(style: &mut Style, value: Value) {
119+ //TODO: alert user of parsing failures
118 if let Value::Table(entries) = value {
120 if let Value::Table(entries) = value {
119 for (name, value) in entries {
121 for (name, value) in entries {
120 match name.as_str() {
122 match name.as_str() {
@@ -128,6 +130,13 @@ fn parse_style(style: &mut Style, value: Value) {
128 *style = style.bg(color);
130 *style = style.bg(color);
129 }
131 }
130 }
132 }
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+ }
131 _ => (),
140 _ => (),
132 }
141 }
133 }
142 }
@@ -157,9 +166,34 @@ fn parse_color(value: Value) -> Option<Color> {
157 if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
166 if let Some((red, green, blue)) = hex_string_to_rgb(&s) {
158 Some(Color::Rgb(red, green, blue))
167 Some(Color::Rgb(red, green, blue))
159 } else {
168 } else {
169+ warn!("malformed hexcode in theme: {}", s);
160 None
170 None
161 }
171 }
162 } else {
172 } 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);
163 None
197 None
164 }
198 }
165}
199}
@@ -177,3 +211,39 @@ impl Theme {
177 &self.scopes
211 &self.scopes
178 }
212 }
179}
213}
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+}