Merge branch 'colored-indent-guides'

pull/6/head
trivernis 2 years ago
commit d18a064fad
Signed by: Trivernis
GPG Key ID: DFFFCC2C7A02DB45

@ -232,10 +232,11 @@ Sets explorer side width and style.
Options for rendering vertical indent guides. Options for rendering vertical indent guides.
| Key | Description | Default | | Key | Description | Default |
| --- | --- | --- | | --- | --- | --- |
| `render` | Whether to render indent guides. | `false` | | `render` | Whether to render indent guides. | `false` |
| `character` | Literal character to use for rendering the indent guide | `│` | | `character` | Literal character to use for rendering the indent guide | `│` |
| `rainbow` | Whether or not the indent guides shall have changing colors. | `false` |
Example: Example:
@ -243,6 +244,7 @@ Example:
[editor.indent-guides] [editor.indent-guides]
render = true render = true
character = "╎" character = "╎"
rainbow = true
``` ```
### `[editor.explorer]` Section ### `[editor.explorer]` Section

@ -89,6 +89,17 @@ Less common modifiers might not be supported by your terminal emulator.
| `hidden` | | `hidden` |
| `crossed_out` | | `crossed_out` |
### Rainbow
The `rainbow` key is used for rainbow highlight for matching brackets.
The key is a list of styles.
```toml
rainbow = ["#ff0000", "#ffa500", "#fff000", { fg = "#00ff00", modifiers = ["bold"] }]
```
Colors from the palette and modifiers may be used.
### Scopes ### Scopes
The following is a list of scopes available to use for styling. The following is a list of scopes available to use for styling.

@ -443,11 +443,18 @@ impl EditorView {
// extra loops if the code is deeply nested. // extra loops if the code is deeply nested.
for i in starting_indent..(indent_level / tab_width as u16) { for i in starting_indent..(indent_level / tab_width as u16) {
let style = if config.indent_guides.rainbow {
let color_index = i as usize % theme.rainbow_length();
indent_guide_style.patch(theme.get(&format!("rainbow.{}", color_index)))
} else {
indent_guide_style
};
surface.set_string( surface.set_string(
viewport.x + (i * tab_width as u16) - offset.col as u16, viewport.x + (i * tab_width as u16) - offset.col as u16,
viewport.y + line, viewport.y + line,
&indent_guide_char, &indent_guide_char,
indent_guide_style, style,
); );
} }
}; };

@ -595,6 +595,7 @@ impl Default for WhitespaceCharacters {
pub struct IndentGuidesConfig { pub struct IndentGuidesConfig {
pub render: bool, pub render: bool,
pub character: char, pub character: char,
pub rainbow: bool,
} }
impl Default for IndentGuidesConfig { impl Default for IndentGuidesConfig {
@ -602,6 +603,7 @@ impl Default for IndentGuidesConfig {
Self { Self {
render: true, render: true,
character: '', character: '',
rainbow: false,
} }
} }
} }

@ -103,6 +103,7 @@ pub struct Theme {
// tree-sitter highlight styles are stored in a Vec to optimize lookups // tree-sitter highlight styles are stored in a Vec to optimize lookups
scopes: Vec<String>, scopes: Vec<String>,
highlights: Vec<Style>, highlights: Vec<Style>,
rainbow_length: usize,
} }
impl<'de> Deserialize<'de> for Theme { impl<'de> Deserialize<'de> for Theme {
@ -113,6 +114,7 @@ impl<'de> Deserialize<'de> for Theme {
let mut styles = HashMap::new(); let mut styles = HashMap::new();
let mut scopes = Vec::new(); let mut scopes = Vec::new();
let mut highlights = Vec::new(); let mut highlights = Vec::new();
let mut rainbow_length = 0;
if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) { if let Ok(mut colors) = HashMap::<String, Value>::deserialize(deserializer) {
// TODO: alert user of parsing failures in editor // TODO: alert user of parsing failures in editor
@ -130,6 +132,26 @@ impl<'de> Deserialize<'de> for Theme {
scopes.reserve(colors.len()); scopes.reserve(colors.len());
highlights.reserve(colors.len()); highlights.reserve(colors.len());
for (i, style) in colors
.remove("rainbow")
.and_then(|value| match palette.parse_style_array(value) {
Ok(styles) => Some(styles),
Err(err) => {
warn!("{}", err);
None
}
})
.unwrap_or_else(Self::default_rainbow)
.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 colors { for (name, style_value) in colors {
let mut style = Style::default(); let mut style = Style::default();
if let Err(err) = palette.parse_style(&mut style, style_value) { if let Err(err) = palette.parse_style(&mut style, style_value) {
@ -147,6 +169,7 @@ impl<'de> Deserialize<'de> for Theme {
scopes, scopes,
styles, styles,
highlights, highlights,
rainbow_length,
}) })
} }
} }
@ -185,6 +208,21 @@ impl Theme {
.all(|color| !matches!(color, Some(Color::Rgb(..)))) .all(|color| !matches!(color, Some(Color::Rgb(..))))
}) })
} }
pub fn rainbow_length(&self) -> usize {
self.rainbow_length
}
pub 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),
]
}
} }
struct ThemePalette { struct ThemePalette {
@ -286,6 +324,24 @@ impl ThemePalette {
} }
Ok(()) Ok(())
} }
/// Parses a TOML array into a [`Vec`] of [`Style`]. If the value cannot be
/// parsed as an array or if any style in the array cannot be parsed then an
/// error is returned.
pub 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!("Theme: 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 { impl TryFrom<Value> for ThemePalette {
@ -362,4 +418,51 @@ mod tests {
.add_modifier(Modifier::BOLD) .add_modifier(Modifier::BOLD)
); );
} }
#[test]
fn test_parse_valid_style_array() {
let theme = toml::toml! {
rainbow = ["#ff0000", "#ffa500", "#fff000", { fg = "#00ff00", modifiers = ["bold"] }]
};
let palette = ThemePalette::default();
let rainbow = theme.as_table().unwrap().get("rainbow").unwrap();
let parse_result = palette.parse_style_array(rainbow.clone());
assert_eq!(
Ok(vec![
Style::default().fg(Color::Rgb(255, 0, 0)),
Style::default().fg(Color::Rgb(255, 165, 0)),
Style::default().fg(Color::Rgb(255, 240, 0)),
Style::default()
.fg(Color::Rgb(0, 255, 0))
.add_modifier(Modifier::BOLD),
]),
parse_result
)
}
#[test]
fn test_parse_invalid_style_array() {
let palette = ThemePalette::default();
let theme = toml::toml! { invalid_hex_code = ["#f00"] };
let invalid_hex_code = theme.as_table().unwrap().get("invalid_hex_code").unwrap();
let parse_result = palette.parse_style_array(invalid_hex_code.clone());
assert_eq!(
Err("Theme: malformed hexcode: #f00".to_string()),
parse_result
);
let theme = toml::toml! { not_an_array = { red = "#ff0000" } };
let not_an_array = theme.as_table().unwrap().get("not_an_array").unwrap();
let parse_result = palette.parse_style_array(not_an_array.clone());
assert_eq!(
Err("Theme: could not parse value as an array: 'red = \"#ff0000\"\n'".to_string()),
parse_result
)
}
} }

@ -848,7 +848,7 @@ indent = { tab-width = 4, unit = " " }
[[grammar]] [[grammar]]
name = "wgsl" name = "wgsl"
source = { git = "https://github.com/szebniok/tree-sitter-wgsl", rev = "f00ff52251edbd58f4d39c9c3204383253032c11" } source = { git = "https://github.com/szebniok/tree-sitter-wgsl", rev = "272e89ef2aeac74178edb9db4a83c1ffef80a463" }
[[language]] [[language]]
name = "llvm" name = "llvm"

@ -1,23 +1,41 @@
(const_literal) @constant.numeric (int_literal) @constant.numeric.integer
(float_literal) @constant.numeric.float
(bool_literal) @constant.builtin.boolean
(type_declaration) @type (global_constant_declaration) @variable
(global_variable_declaration) @variable
(compound_statement) @variable
(const_expression) @function
(variable_identifier_declaration
(identifier) @variable
(type_declaration) @type)
(function_declaration (function_declaration
(identifier) @function) (identifier) @function
(function_return_type_declaration
(type_declaration) @type))
(parameter
(variable_identifier_declaration
(identifier) @variable.parameter
(type_declaration) @type))
(struct_declaration (struct_declaration
(identifier) @type) (identifier) @type)
(struct_declaration
(struct_member
(variable_identifier_declaration
(identifier) @variable.other.member
(type_declaration) @type)))
(type_constructor_or_function_call_expression (type_constructor_or_function_call_expression
(type_declaration) @function) (type_declaration) @function)
(parameter
(variable_identifier_declaration (identifier) @variable.parameter))
[ [
"struct" "struct"
"bitcast" "bitcast"
; "block"
"discard" "discard"
"enable" "enable"
"fallthrough" "fallthrough"
@ -26,36 +44,28 @@
"private" "private"
"read" "read"
"read_write" "read_write"
"return"
"storage" "storage"
"type" "type"
"uniform" "uniform"
"var" "var"
"workgroup" "workgroup"
"write" "write"
"override"
(texel_format) (texel_format)
] @keyword ; TODO reserved keywords ] @keyword
[ "fn" @keyword.function
(true)
(false)
] @constant.builtin.boolean
[ "," "." ":" ";" ] @punctuation.delimiter "return" @keyword.control.return
;; brackets ["," "." ":" ";"] @punctuation.delimiter
[
"(" ["(" ")" "[" "]" "{" "}"] @punctuation.bracket
")"
"["
"]"
"{"
"}"
] @punctuation.bracket
[ [
"loop" "loop"
"for" "for"
"while"
"break" "break"
"continue" "continue"
"continuing" "continuing"
@ -64,7 +74,6 @@
[ [
"if" "if"
"else" "else"
"elseif"
"switch" "switch"
"case" "case"
"default" "default"
@ -92,10 +101,13 @@
"*" "*"
"~" "~"
"^" "^"
"@"
"++"
"--"
] @operator ] @operator
(attribute (attribute
(identifier) @variable.other.member) (identifier) @attribute)
(comment) @comment (comment) @comment

Loading…
Cancel
Save