From a5c1dd0da525685d858eeef76a04b69008f05e33 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 14 Dec 2021 13:34:39 -0600 Subject: [PATCH] allow fg, bg, attributes to be set for all colors in color_config (#489) * allow fg, bg, attributes to be set for all colors in color_config * no need for comma between each key value --- crates/nu-command/src/viewers/color_config.rs | 118 +++++++++++++++--- crates/nu-protocol/src/config.rs | 36 +++++- 2 files changed, 136 insertions(+), 18 deletions(-) diff --git a/crates/nu-command/src/viewers/color_config.rs b/crates/nu-command/src/viewers/color_config.rs index 62e93c855c..657cd09beb 100644 --- a/crates/nu-command/src/viewers/color_config.rs +++ b/crates/nu-command/src/viewers/color_config.rs @@ -1,6 +1,7 @@ use nu_ansi_term::{Color, Style}; use nu_protocol::Config; use nu_table::{Alignment, TextStyle}; +use serde::Deserialize; use std::collections::HashMap; //TODO: should this be implemented again? @@ -14,6 +15,105 @@ use std::collections::HashMap; // } // } +#[derive(Deserialize, PartialEq, Debug)] +struct NuStyle { + fg: Option, + bg: Option, + attr: Option, +} + +fn parse_nustyle(nu_style: NuStyle) -> Style { + // get the nu_ansi_term::Color foreground color + let fg_color = match nu_style.fg { + Some(fg) => color_from_hex(&fg).expect("error with foreground color"), + _ => None, + }; + // get the nu_ansi_term::Color background color + let bg_color = match nu_style.bg { + Some(bg) => color_from_hex(&bg).expect("error with background color"), + _ => None, + }; + // get the attributes + let color_attr = match nu_style.attr { + Some(attr) => attr, + _ => "".to_string(), + }; + + // setup the attributes available in nu_ansi_term::Style + let mut bold = false; + let mut dimmed = false; + let mut italic = false; + let mut underline = false; + let mut blink = false; + let mut reverse = false; + let mut hidden = false; + let mut strikethrough = false; + + // since we can combine styles like bold-italic, iterate through the chars + // and set the bools for later use in the nu_ansi_term::Style application + for ch in color_attr.to_lowercase().chars() { + match ch { + 'l' => blink = true, + 'b' => bold = true, + 'd' => dimmed = true, + 'h' => hidden = true, + 'i' => italic = true, + 'r' => reverse = true, + 's' => strikethrough = true, + 'u' => underline = true, + 'n' => (), + _ => (), + } + } + + // here's where we build the nu_ansi_term::Style + Style { + foreground: fg_color, + background: bg_color, + is_blink: blink, + is_bold: bold, + is_dimmed: dimmed, + is_hidden: hidden, + is_italic: italic, + is_reverse: reverse, + is_strikethrough: strikethrough, + is_underline: underline, + } +} + +fn color_string_to_nustyle(color_string: String) -> Style { + // eprintln!("color_string: {}", &color_string); + if color_string.chars().count() < 1 { + Style::default() + } else { + let nu_style = match nu_json::from_str::(&color_string) { + Ok(s) => s, + Err(_) => NuStyle { + fg: None, + bg: None, + attr: None, + }, + }; + + parse_nustyle(nu_style) + } +} + +fn color_from_hex(hex_color: &str) -> std::result::Result, std::num::ParseIntError> { + // right now we only allow hex colors with hashtag and 6 characters + let trimmed = hex_color.trim_matches('#'); + if trimmed.len() != 6 { + Ok(None) + } else { + // make a nu_ansi_term::Color::Rgb color by converting hex to decimal + Ok(Some(Color::Rgb( + u8::from_str_radix(&trimmed[..2], 16)?, + u8::from_str_radix(&trimmed[2..4], 16)?, + u8::from_str_radix(&trimmed[4..6], 16)?, + ))) + } +} + pub fn lookup_ansi_color_style(s: String) -> Style { if s.starts_with('#') { match color_from_hex(&s) { @@ -23,6 +123,8 @@ pub fn lookup_ansi_color_style(s: String) -> Style { }, Err(_) => Style::default(), } + } else if s.starts_with('{') { + color_string_to_nustyle(s) } else { match s.as_str() { "g" | "green" => Color::Green.normal(), @@ -94,21 +196,6 @@ pub fn lookup_ansi_color_style(s: String) -> Style { } } -fn color_from_hex(hex_color: &str) -> std::result::Result, std::num::ParseIntError> { - // right now we only allow hex colors with hashtag and 6 characters - let trimmed = hex_color.trim_matches('#'); - if trimmed.len() != 6 { - Ok(None) - } else { - // make a nu_ansi_term::Color::Rgb color by converting hex to decimal - Ok(Some(Color::Rgb( - u8::from_str_radix(&trimmed[..2], 16)?, - u8::from_str_radix(&trimmed[2..4], 16)?, - u8::from_str_radix(&trimmed[4..6], 16)?, - ))) - } -} - // TODO: i'm not sure how this ever worked but leaving it in case it's used elsewhere but not implemented yet // pub fn string_to_lookup_value(str_prim: &str) -> String { // match str_prim { @@ -136,6 +223,7 @@ fn color_from_hex(hex_color: &str) -> std::result::Result, std::nu // } fn update_hashmap(key: &str, val: &str, hm: &mut HashMap) { + // eprintln!("key: {}, val: {}", &key, &val); let color = lookup_ansi_color_style(val.to_string()); if let Some(v) = hm.get_mut(key) { *v = color; diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 9221b21a37..0b1df20dcd 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -65,10 +65,40 @@ impl Value { config.use_ls_colors = value.as_bool()?; } "color_config" => { - let (cols, vals) = value.as_record()?; + let (cols, inner_vals) = value.as_record()?; let mut hm = HashMap::new(); - for (k, v) in cols.iter().zip(vals) { - hm.insert(k.to_string(), v.as_string()?); + for (k, v) in cols.iter().zip(inner_vals) { + match &v { + Value::Record { + cols: inner_cols, + vals: inner_vals, + span: _, + } => { + // make a string from our config.color_config section that + // looks like this: { fg: "#rrggbb" bg: "#rrggbb" attr: "abc", } + // the real key here was to have quotes around the values but not + // require them around the keys. + + // maybe there's a better way to generate this but i'm not sure + // what it is. + let key = k.to_string(); + let mut val: String = inner_cols + .iter() + .zip(inner_vals) + .map(|(x, y)| { + let clony = y.clone(); + format!("{}: \"{}\" ", x, clony.into_string(", ", &config)) + }) + .collect(); + // now insert the braces at the front and the back to fake the json string + val.insert(0, '{'); + val.push('}'); + hm.insert(key, val); + } + _ => { + hm.insert(k.to_string(), v.as_string()?); + } + } } config.color_config = hm; }