From adbbcafd30e23847af6ac988385d4f4c74807ea6 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Tue, 1 Sep 2020 00:09:55 -0500 Subject: [PATCH] Add minor theme support (#2449) * WIP - compiling but not working * semi-working * making progress * working except for table lines * fmt + clippy * cleaned up some comments * working line colors * fmt, clippy, updated sample config.toml * removed extra comments --- crates/nu-cli/src/commands/ansi.rs | 103 ++--- .../nu-cli/src/commands/autoview/command.rs | 14 +- crates/nu-cli/src/commands/table/command.rs | 62 +-- crates/nu-cli/src/commands/table/options.rs | 114 +++-- crates/nu-data/src/config/table.rs | 143 ------ crates/nu-data/src/primitive.rs | 352 +++++++++++++- crates/nu-data/src/value.rs | 16 +- crates/nu-protocol/src/value/primitive.rs | 6 + crates/nu-table/src/main.rs | 5 +- crates/nu-table/src/table.rs | 431 +++++++++++++++--- crates/nu-table/src/wrap.rs | 3 +- docs/sample_config/config.toml | 39 +- 12 files changed, 904 insertions(+), 384 deletions(-) delete mode 100644 crates/nu-data/src/config/table.rs diff --git a/crates/nu-cli/src/commands/ansi.rs b/crates/nu-cli/src/commands/ansi.rs index 6d105b0225..1f2d78c26c 100644 --- a/crates/nu-cli/src/commands/ansi.rs +++ b/crates/nu-cli/src/commands/ansi.rs @@ -1,5 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::prelude::*; +use ansi_term::Color; use nu_errors::ShellError; use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value}; @@ -76,60 +77,56 @@ impl WholeStreamCommand for Ansi { } } -fn str_to_ansi_color(s: String) -> Option { +pub fn str_to_ansi_color(s: String) -> Option { match s.as_str() { - "g" | "green" => Some(ansi_term::Color::Green.prefix().to_string()), - "gb" | "green_bold" => Some(ansi_term::Color::Green.bold().prefix().to_string()), - "gu" | "green_underline" => Some(ansi_term::Color::Green.underline().prefix().to_string()), - "gi" | "green_italic" => Some(ansi_term::Color::Green.italic().prefix().to_string()), - "gd" | "green_dimmed" => Some(ansi_term::Color::Green.dimmed().prefix().to_string()), - "gr" | "green_reverse" => Some(ansi_term::Color::Green.reverse().prefix().to_string()), - "r" | "red" => Some(ansi_term::Color::Red.prefix().to_string()), - "rb" | "red_bold" => Some(ansi_term::Color::Red.bold().prefix().to_string()), - "ru" | "red_underline" => Some(ansi_term::Color::Red.underline().prefix().to_string()), - "ri" | "red_italic" => Some(ansi_term::Color::Red.italic().prefix().to_string()), - "rd" | "red_dimmed" => Some(ansi_term::Color::Red.dimmed().prefix().to_string()), - "rr" | "red_reverse" => Some(ansi_term::Color::Red.reverse().prefix().to_string()), - "u" | "blue" => Some(ansi_term::Color::Blue.prefix().to_string()), - "ub" | "blue_bold" => Some(ansi_term::Color::Blue.bold().prefix().to_string()), - "uu" | "blue_underline" => Some(ansi_term::Color::Blue.underline().prefix().to_string()), - "ui" | "blue_italic" => Some(ansi_term::Color::Blue.italic().prefix().to_string()), - "ud" | "blue_dimmed" => Some(ansi_term::Color::Blue.dimmed().prefix().to_string()), - "ur" | "blue_reverse" => Some(ansi_term::Color::Blue.reverse().prefix().to_string()), - "b" | "black" => Some(ansi_term::Color::Black.prefix().to_string()), - "bb" | "black_bold" => Some(ansi_term::Color::Black.bold().prefix().to_string()), - "bu" | "black_underline" => Some(ansi_term::Color::Black.underline().prefix().to_string()), - "bi" | "black_italic" => Some(ansi_term::Color::Black.italic().prefix().to_string()), - "bd" | "black_dimmed" => Some(ansi_term::Color::Black.dimmed().prefix().to_string()), - "br" | "black_reverse" => Some(ansi_term::Color::Black.reverse().prefix().to_string()), - "y" | "yellow" => Some(ansi_term::Color::Yellow.prefix().to_string()), - "yb" | "yellow_bold" => Some(ansi_term::Color::Yellow.bold().prefix().to_string()), - "yu" | "yellow_underline" => { - Some(ansi_term::Color::Yellow.underline().prefix().to_string()) - } - "yi" | "yellow_italic" => Some(ansi_term::Color::Yellow.italic().prefix().to_string()), - "yd" | "yellow_dimmed" => Some(ansi_term::Color::Yellow.dimmed().prefix().to_string()), - "yr" | "yellow_reverse" => Some(ansi_term::Color::Yellow.reverse().prefix().to_string()), - "p" | "purple" => Some(ansi_term::Color::Purple.prefix().to_string()), - "pb" | "purple_bold" => Some(ansi_term::Color::Purple.bold().prefix().to_string()), - "pu" | "purple_underline" => { - Some(ansi_term::Color::Purple.underline().prefix().to_string()) - } - "pi" | "purple_italic" => Some(ansi_term::Color::Purple.italic().prefix().to_string()), - "pd" | "purple_dimmed" => Some(ansi_term::Color::Purple.dimmed().prefix().to_string()), - "pr" | "purple_reverse" => Some(ansi_term::Color::Purple.reverse().prefix().to_string()), - "c" | "cyan" => Some(ansi_term::Color::Cyan.prefix().to_string()), - "cb" | "cyan_bold" => Some(ansi_term::Color::Cyan.bold().prefix().to_string()), - "cu" | "cyan_underline" => Some(ansi_term::Color::Cyan.underline().prefix().to_string()), - "ci" | "cyan_italic" => Some(ansi_term::Color::Cyan.italic().prefix().to_string()), - "cd" | "cyan_dimmed" => Some(ansi_term::Color::Cyan.dimmed().prefix().to_string()), - "cr" | "cyan_reverse" => Some(ansi_term::Color::Cyan.reverse().prefix().to_string()), - "w" | "white" => Some(ansi_term::Color::White.prefix().to_string()), - "wb" | "white_bold" => Some(ansi_term::Color::White.bold().prefix().to_string()), - "wu" | "white_underline" => Some(ansi_term::Color::White.underline().prefix().to_string()), - "wi" | "white_italic" => Some(ansi_term::Color::White.italic().prefix().to_string()), - "wd" | "white_dimmed" => Some(ansi_term::Color::White.dimmed().prefix().to_string()), - "wr" | "white_reverse" => Some(ansi_term::Color::White.reverse().prefix().to_string()), + "g" | "green" => Some(Color::Green.prefix().to_string()), + "gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()), + "gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()), + "gi" | "green_italic" => Some(Color::Green.italic().prefix().to_string()), + "gd" | "green_dimmed" => Some(Color::Green.dimmed().prefix().to_string()), + "gr" | "green_reverse" => Some(Color::Green.reverse().prefix().to_string()), + "r" | "red" => Some(Color::Red.prefix().to_string()), + "rb" | "red_bold" => Some(Color::Red.bold().prefix().to_string()), + "ru" | "red_underline" => Some(Color::Red.underline().prefix().to_string()), + "ri" | "red_italic" => Some(Color::Red.italic().prefix().to_string()), + "rd" | "red_dimmed" => Some(Color::Red.dimmed().prefix().to_string()), + "rr" | "red_reverse" => Some(Color::Red.reverse().prefix().to_string()), + "u" | "blue" => Some(Color::Blue.prefix().to_string()), + "ub" | "blue_bold" => Some(Color::Blue.bold().prefix().to_string()), + "uu" | "blue_underline" => Some(Color::Blue.underline().prefix().to_string()), + "ui" | "blue_italic" => Some(Color::Blue.italic().prefix().to_string()), + "ud" | "blue_dimmed" => Some(Color::Blue.dimmed().prefix().to_string()), + "ur" | "blue_reverse" => Some(Color::Blue.reverse().prefix().to_string()), + "b" | "black" => Some(Color::Black.prefix().to_string()), + "bb" | "black_bold" => Some(Color::Black.bold().prefix().to_string()), + "bu" | "black_underline" => Some(Color::Black.underline().prefix().to_string()), + "bi" | "black_italic" => Some(Color::Black.italic().prefix().to_string()), + "bd" | "black_dimmed" => Some(Color::Black.dimmed().prefix().to_string()), + "br" | "black_reverse" => Some(Color::Black.reverse().prefix().to_string()), + "y" | "yellow" => Some(Color::Yellow.prefix().to_string()), + "yb" | "yellow_bold" => Some(Color::Yellow.bold().prefix().to_string()), + "yu" | "yellow_underline" => Some(Color::Yellow.underline().prefix().to_string()), + "yi" | "yellow_italic" => Some(Color::Yellow.italic().prefix().to_string()), + "yd" | "yellow_dimmed" => Some(Color::Yellow.dimmed().prefix().to_string()), + "yr" | "yellow_reverse" => Some(Color::Yellow.reverse().prefix().to_string()), + "p" | "purple" => Some(Color::Purple.prefix().to_string()), + "pb" | "purple_bold" => Some(Color::Purple.bold().prefix().to_string()), + "pu" | "purple_underline" => Some(Color::Purple.underline().prefix().to_string()), + "pi" | "purple_italic" => Some(Color::Purple.italic().prefix().to_string()), + "pd" | "purple_dimmed" => Some(Color::Purple.dimmed().prefix().to_string()), + "pr" | "purple_reverse" => Some(Color::Purple.reverse().prefix().to_string()), + "c" | "cyan" => Some(Color::Cyan.prefix().to_string()), + "cb" | "cyan_bold" => Some(Color::Cyan.bold().prefix().to_string()), + "cu" | "cyan_underline" => Some(Color::Cyan.underline().prefix().to_string()), + "ci" | "cyan_italic" => Some(Color::Cyan.italic().prefix().to_string()), + "cd" | "cyan_dimmed" => Some(Color::Cyan.dimmed().prefix().to_string()), + "cr" | "cyan_reverse" => Some(Color::Cyan.reverse().prefix().to_string()), + "w" | "white" => Some(Color::White.prefix().to_string()), + "wb" | "white_bold" => Some(Color::White.bold().prefix().to_string()), + "wu" | "white_underline" => Some(Color::White.underline().prefix().to_string()), + "wi" | "white_italic" => Some(Color::White.italic().prefix().to_string()), + "wd" | "white_dimmed" => Some(Color::White.dimmed().prefix().to_string()), + "wr" | "white_reverse" => Some(Color::White.reverse().prefix().to_string()), "reset" => Some("\x1b[0m".to_owned()), _ => None, } diff --git a/crates/nu-cli/src/commands/autoview/command.rs b/crates/nu-cli/src/commands/autoview/command.rs index 9ba19caba0..8217e07878 100644 --- a/crates/nu-cli/src/commands/autoview/command.rs +++ b/crates/nu-cli/src/commands/autoview/command.rs @@ -1,10 +1,12 @@ use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration}; use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::prelude::*; +use crate::primitive::get_color_config; use nu_data::value::format_leaf; use nu_errors::ShellError; use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression}; use nu_protocol::{Primitive, Scope, Signature, UntaggedValue, Value}; +use nu_table::TextStyle; use parking_lot::Mutex; use std::sync::atomic::AtomicBool; @@ -92,6 +94,7 @@ pub async fn autoview(context: RunnableContext) -> Result Result Result, ) -> nu_table::Table { - let header_style = TextStyle { - is_bold: configuration.header_bold(), - alignment: configuration.header_alignment(), - color: configuration.header_color(), - }; - + let header_style = configuration.header_style(); let mut headers: Vec = nu_protocol::merge_descriptors(values) .into_iter() - .map(|x| StyledString::new(x, header_style.clone())) + .map(|x| StyledString::new(x, header_style)) .collect(); - let entries = values_to_entries(values, &mut headers, configuration, starting_idx); - + let entries = values_to_entries(values, &mut headers, configuration, starting_idx, &color_hm); nu_table::Table { headers, data: entries, @@ -69,9 +66,9 @@ fn values_to_entries( headers: &mut Vec, configuration: &TableConfiguration, starting_idx: usize, + color_hm: &HashMap, ) -> Vec> { let disable_indexes = configuration.disabled_indexes(); - let mut entries = vec![]; if headers.is_empty() { @@ -89,11 +86,11 @@ fn values_to_entries( .. } => StyledString::new( format_leaf(&UntaggedValue::nothing()).plain_string(100_000), - style_leaf(&UntaggedValue::nothing()), + style_leaf(&UntaggedValue::nothing(), &color_hm), ), _ => StyledString::new( format_leaf(value).plain_string(100_000), - style_leaf(value), + style_leaf(value, &color_hm), ), } } else { @@ -106,12 +103,12 @@ fn values_to_entries( StyledString::new( format_leaf(data.borrow()).plain_string(100_000), - style_leaf(data.borrow()), + style_leaf(data.borrow(), &color_hm), ) } _ => StyledString::new( format_leaf(&UntaggedValue::nothing()).plain_string(100_000), - style_leaf(&UntaggedValue::nothing()), + style_leaf(&UntaggedValue::nothing(), &color_hm), ), } } @@ -119,16 +116,22 @@ fn values_to_entries( .collect(); // Indices are green, bold, right-aligned: + // unless we change them :) if !disable_indexes { row.insert( 0, StyledString::new( (starting_idx + idx).to_string(), - TextStyle { - alignment: Alignment::Right, - color: Some(ansi_term::Color::Green), - is_bold: true, - }, + TextStyle::new().alignment(Alignment::Right).style( + color_hm + .get("index_color") + .unwrap_or( + &ansi_term::Style::default() + .bold() + .fg(ansi_term::Color::Green), + ) + .to_owned(), + ), ), ); } @@ -141,11 +144,10 @@ fn values_to_entries( 0, StyledString::new( "#".to_owned(), - TextStyle { - alignment: Alignment::Center, - color: Some(ansi_term::Color::Green), - is_bold: true, - }, + TextStyle::new() + .alignment(Alignment::Center) + .fg(ansi_term::Color::Green) + .bold(Some(true)), ), ); } @@ -161,8 +163,14 @@ async fn table( let registry = registry.clone(); let mut args = args.evaluate_once(®istry).await?; let mut finished = false; + // Ideally, get_color_config would get all the colors configured in the config.toml + // and create a style based on those settings. However, there are few places where + // this just won't work right now, like header styling, because a style needs to know + // more than just color, it needs fg & bg color, bold, dimmed, italic, underline, + // blink, reverse, hidden, strikethrough and most of those aren't available in the + // config.toml.... yet. + let color_hm = get_color_config(); - // let host = args.host.clone(); let mut start_number = match args.get("start_number") { Some(Value { value: UntaggedValue::Primitive(Primitive::Int(i)), @@ -234,9 +242,9 @@ async fn table( let input: Vec = new_input.into(); if !input.is_empty() { - let t = from_list(&input, &configuration, start_number); + let t = from_list(&input, &configuration, start_number, &color_hm); - draw_table(&t, term_width); + draw_table(&t, term_width, &color_hm); } start_number += input.len(); diff --git a/crates/nu-cli/src/commands/table/options.rs b/crates/nu-cli/src/commands/table/options.rs index 5f69587cf0..cf2bf1bac8 100644 --- a/crates/nu-cli/src/commands/table/options.rs +++ b/crates/nu-cli/src/commands/table/options.rs @@ -1,62 +1,51 @@ pub use nu_data::config::NuConfig; +use nu_data::primitive::lookup_ansi_color_style; +use nu_protocol::{UntaggedValue, Value}; +use nu_source::Tag; +use nu_table::TextStyle; use std::fmt::Debug; pub trait ConfigExtensions: Debug + Send { - fn header_alignment(&self) -> nu_table::Alignment; - fn header_color(&self) -> Option; - fn header_bold(&self) -> bool; fn table_mode(&self) -> nu_table::Theme; fn disabled_indexes(&self) -> bool; - fn text_color(&self) -> Option; - fn line_color(&self) -> Option; + fn header_style(&self) -> TextStyle; } -pub fn header_alignment(config: &NuConfig) -> nu_table::Alignment { +pub fn header_alignment_from_value(align_value: Option<&Value>) -> nu_table::Alignment { + match align_value { + Some(v) => match v + .as_string() + .unwrap_or_else(|_| "none".to_string()) + .as_ref() + { + "l" | "left" => nu_table::Alignment::Left, + "c" | "center" => nu_table::Alignment::Center, + "r" | "right" => nu_table::Alignment::Right, + _ => nu_table::Alignment::Center, + }, + _ => nu_table::Alignment::Center, + } +} + +pub fn get_color_from_key_and_subkey(config: &NuConfig, key: &str, subkey: &str) -> Value { let vars = config.vars.lock(); - let alignment = vars.get("header_align"); - - if alignment.is_none() { - return nu_table::Alignment::Center; + let mut v: Value = + UntaggedValue::Primitive(nu_protocol::Primitive::String("nocolor".to_string())) + .into_value(Tag::unknown()); + if let Some(config_vars) = vars.get(key) { + for (kee, value) in config_vars.row_entries() { + if kee == subkey { + v = value.to_owned(); + } + } } - alignment.map_or(nu_table::Alignment::Left, |a| { - a.as_string().map_or(nu_table::Alignment::Center, |a| { - match a.to_lowercase().as_str() { - "center" | "c" => nu_table::Alignment::Center, - "right" | "r" => nu_table::Alignment::Right, - _ => nu_table::Alignment::Center, - } - }) - }) + v } -pub fn get_color_for_config_key(config: &NuConfig, key: &str) -> Option { - let vars = config.vars.lock(); - - Some(match vars.get(key) { - Some(c) => match c.as_string() { - Ok(color) => match color.to_lowercase().as_str() { - "g" | "green" => ansi_term::Color::Green, - "r" | "red" => ansi_term::Color::Red, - "u" | "blue" => ansi_term::Color::Blue, - "b" | "black" => ansi_term::Color::Black, - "y" | "yellow" => ansi_term::Color::Yellow, - "p" | "purple" => ansi_term::Color::Purple, - "c" | "cyan" => ansi_term::Color::Cyan, - "w" | "white" => ansi_term::Color::White, - _ => ansi_term::Color::Green, - }, - _ => ansi_term::Color::Green, - }, - _ => ansi_term::Color::Green, - }) -} - -pub fn header_bold(config: &NuConfig) -> bool { - let vars = config.vars.lock(); - - vars.get("header_bold") +pub fn header_bold_from_value(bold_value: Option<&Value>) -> bool { + bold_value .map(|x| x.as_bool().unwrap_or(true)) .unwrap_or(true) } @@ -84,24 +73,25 @@ pub fn disabled_indexes(config: &NuConfig) -> bool { } impl ConfigExtensions for NuConfig { - fn header_alignment(&self) -> nu_table::Alignment { - header_alignment(self) - } + fn header_style(&self) -> TextStyle { + // FIXME: I agree, this is the long way around, please suggest and alternative. + let head_color = get_color_from_key_and_subkey(self, "color_config", "header_color"); + let head_color_style = lookup_ansi_color_style( + head_color + .as_string() + .unwrap_or_else(|_| "green".to_string()), + ); + let head_bold = get_color_from_key_and_subkey(self, "color_config", "header_bold"); + let head_bold_bool = header_bold_from_value(Some(&head_bold)); + let head_align = get_color_from_key_and_subkey(self, "color_config", "header_align"); + let head_alignment = header_alignment_from_value(Some(&head_align)); - fn header_color(&self) -> Option { - get_color_for_config_key(self, "header_color") - } - - fn text_color(&self) -> Option { - get_color_for_config_key(self, "text_color") - } - - fn line_color(&self) -> Option { - get_color_for_config_key(self, "line_color") - } - - fn header_bold(&self) -> bool { - header_bold(self) + TextStyle::new() + .alignment(head_alignment) + .bold(Some(head_bold_bool)) + .fg(head_color_style + .foreground + .unwrap_or(ansi_term::Color::Green)) } fn table_mode(&self) -> nu_table::Theme { diff --git a/crates/nu-data/src/config/table.rs b/crates/nu-data/src/config/table.rs deleted file mode 100644 index a118822ad8..0000000000 --- a/crates/nu-data/src/config/table.rs +++ /dev/null @@ -1,143 +0,0 @@ -use crate::config::nuconfig::NuConfig; -use std::fmt::Debug; - -#[derive(PartialEq, Debug)] -pub enum AutoPivotMode { - Auto, - Always, - Never, -} - -pub trait HasTableProperties: Debug + Send { - fn pivot_mode(&self) -> AutoPivotMode; - fn header_alignment(&self) -> nu_table::Alignment; - fn header_color(&self) -> Option; - fn header_bold(&self) -> bool; - fn table_mode(&self) -> nu_table::Theme; - fn disabled_indexes(&self) -> bool; - fn text_color(&self) -> Option; - fn line_color(&self) -> Option; -} - -pub fn pivot_mode(config: &NuConfig) -> AutoPivotMode { - let vars = config.vars.lock(); - - if let Some(mode) = vars.get("pivot_mode") { - let mode = match mode.as_string() { - Ok(m) if m.to_lowercase() == "auto" => AutoPivotMode::Auto, - Ok(m) if m.to_lowercase() == "always" => AutoPivotMode::Always, - Ok(m) if m.to_lowercase() == "never" => AutoPivotMode::Never, - _ => AutoPivotMode::Always, - }; - - return mode; - } - - AutoPivotMode::Always -} - -pub fn header_alignment(config: &NuConfig) -> nu_table::Alignment { - let vars = config.vars.lock(); - - let alignment = vars.get("header_align"); - - if alignment.is_none() { - return nu_table::Alignment::Center; - } - - alignment.map_or(nu_table::Alignment::Left, |a| { - a.as_string().map_or(nu_table::Alignment::Center, |a| { - match a.to_lowercase().as_str() { - "center" | "c" => nu_table::Alignment::Center, - "right" | "r" => nu_table::Alignment::Right, - _ => nu_table::Alignment::Center, - } - }) - }) -} - -pub fn get_color_for_config_key(config: &NuConfig, key: &str) -> Option { - let vars = config.vars.lock(); - - Some(match vars.get(key) { - Some(c) => match c.as_string() { - Ok(color) => match color.to_lowercase().as_str() { - "g" | "green" => ansi_term::Color::Green, - "r" | "red" => ansi_term::Color::Red, - "u" | "blue" => ansi_term::Color::Blue, - "b" | "black" => ansi_term::Color::Black, - "y" | "yellow" => ansi_term::Color::Yellow, - "p" | "purple" => ansi_term::Color::Purple, - "c" | "cyan" => ansi_term::Color::Cyan, - "w" | "white" => ansi_term::Color::White, - _ => ansi_term::Color::Green, - }, - _ => ansi_term::Color::Green, - }, - _ => ansi_term::Color::Green, - }) -} - -pub fn header_bold(config: &NuConfig) -> bool { - let vars = config.vars.lock(); - - vars.get("header_bold") - .map(|x| x.as_bool().unwrap_or(true)) - .unwrap_or(true) -} - -pub fn table_mode(config: &NuConfig) -> nu_table::Theme { - let vars = config.vars.lock(); - - vars.get("table_mode") - .map_or(nu_table::Theme::compact(), |mode| match mode.as_string() { - Ok(m) if m == "basic" => nu_table::Theme::basic(), - Ok(m) if m == "compact" => nu_table::Theme::compact(), - Ok(m) if m == "light" => nu_table::Theme::light(), - Ok(m) if m == "thin" => nu_table::Theme::thin(), - Ok(m) if m == "with_love" => nu_table::Theme::with_love(), - Ok(m) if m == "compact_double" => nu_table::Theme::compact_double(), - _ => nu_table::Theme::compact(), - }) -} - -pub fn disabled_indexes(config: &NuConfig) -> bool { - let vars = config.vars.lock(); - - vars.get("disable_table_indexes") - .map_or(false, |x| x.as_bool().unwrap_or(false)) -} - -impl HasTableProperties for NuConfig { - fn pivot_mode(&self) -> AutoPivotMode { - pivot_mode(self) - } - - fn header_alignment(&self) -> nu_table::Alignment { - header_alignment(self) - } - - fn header_color(&self) -> Option { - get_color_for_config_key(self, "header_color") - } - - fn text_color(&self) -> Option { - get_color_for_config_key(self, "text_color") - } - - fn line_color(&self) -> Option { - get_color_for_config_key(self, "line_color") - } - - fn header_bold(&self) -> bool { - header_bold(self) - } - - fn table_mode(&self) -> nu_table::Theme { - table_mode(self) - } - - fn disabled_indexes(&self) -> bool { - disabled_indexes(self) - } -} diff --git a/crates/nu-data/src/primitive.rs b/crates/nu-data/src/primitive.rs index d9cabc9810..429cbd4335 100644 --- a/crates/nu-data/src/primitive.rs +++ b/crates/nu-data/src/primitive.rs @@ -1,5 +1,8 @@ -use nu_protocol::{hir::Number, Primitive}; -use nu_table::TextStyle; +use ansi_term::{Color, Style}; +use nu_protocol::{hir::Number, Primitive, Value}; +use nu_source::Tag; +use nu_table::{Alignment, TextStyle}; +use std::collections::HashMap; pub fn number(number: impl Into) -> Primitive { let number = number.into(); @@ -10,10 +13,349 @@ pub fn number(number: impl Into) -> Primitive { } } -pub fn style_primitive(primitive: &Primitive) -> TextStyle { +pub fn lookup_ansi_color_style(s: String) -> Style { + match s.as_str() { + "g" | "green" => Color::Green.normal(), + "gb" | "green_bold" => Color::Green.bold(), + "gu" | "green_underline" => Color::Green.underline(), + "gi" | "green_italic" => Color::Green.italic(), + "gd" | "green_dimmed" => Color::Green.dimmed(), + "gr" | "green_reverse" => Color::Green.reverse(), + "r" | "red" => Color::Red.normal(), + "rb" | "red_bold" => Color::Red.bold(), + "ru" | "red_underline" => Color::Red.underline(), + "ri" | "red_italic" => Color::Red.italic(), + "rd" | "red_dimmed" => Color::Red.dimmed(), + "rr" | "red_reverse" => Color::Red.reverse(), + "u" | "blue" => Color::Blue.normal(), + "ub" | "blue_bold" => Color::Blue.bold(), + "uu" | "blue_underline" => Color::Blue.underline(), + "ui" | "blue_italic" => Color::Blue.italic(), + "ud" | "blue_dimmed" => Color::Blue.dimmed(), + "ur" | "blue_reverse" => Color::Blue.reverse(), + "b" | "black" => Color::Black.normal(), + "bb" | "black_bold" => Color::Black.bold(), + "bu" | "black_underline" => Color::Black.underline(), + "bi" | "black_italic" => Color::Black.italic(), + "bd" | "black_dimmed" => Color::Black.dimmed(), + "br" | "black_reverse" => Color::Black.reverse(), + "y" | "yellow" => Color::Yellow.normal(), + "yb" | "yellow_bold" => Color::Yellow.bold(), + "yu" | "yellow_underline" => Color::Yellow.underline(), + "yi" | "yellow_italic" => Color::Yellow.italic(), + "yd" | "yellow_dimmed" => Color::Yellow.dimmed(), + "yr" | "yellow_reverse" => Color::Yellow.reverse(), + "p" | "purple" => Color::Purple.normal(), + "pb" | "purple_bold" => Color::Purple.bold(), + "pu" | "purple_underline" => Color::Purple.underline(), + "pi" | "purple_italic" => Color::Purple.italic(), + "pd" | "purple_dimmed" => Color::Purple.dimmed(), + "pr" | "purple_reverse" => Color::Purple.reverse(), + "c" | "cyan" => Color::Cyan.normal(), + "cb" | "cyan_bold" => Color::Cyan.bold(), + "cu" | "cyan_underline" => Color::Cyan.underline(), + "ci" | "cyan_italic" => Color::Cyan.italic(), + "cd" | "cyan_dimmed" => Color::Cyan.dimmed(), + "cr" | "cyan_reverse" => Color::Cyan.reverse(), + "w" | "white" => Color::White.normal(), + "wb" | "white_bold" => Color::White.bold(), + "wu" | "white_underline" => Color::White.underline(), + "wi" | "white_italic" => Color::White.italic(), + "wd" | "white_dimmed" => Color::White.dimmed(), + "wr" | "white_reverse" => Color::White.reverse(), + _ => Color::White.normal(), + } +} + +pub fn string_to_lookup_value(str_prim: &str) -> String { + match str_prim { + "primitive_int" => "Primitive::Int".to_string(), + "primitive_decimal" => "Primitive::Decimal".to_string(), + "primitive_filesize" => "Primitive::Filesize".to_string(), + "primitive_string" => "Primitive::String".to_string(), + "primitive_line" => "Primitive::Line".to_string(), + "primitive_columnpath" => "Primitive::ColumnPath".to_string(), + "primitive_pattern" => "Primitive::Pattern".to_string(), + "primitive_boolean" => "Primitive::Boolean".to_string(), + "primitive_date" => "Primitive::Date".to_string(), + "primitive_duration" => "Primitive::Duration".to_string(), + "primitive_range" => "Primitive::Range".to_string(), + "primitive_path" => "Primitive::Path".to_string(), + "primitive_binary" => "Primitive::Binary".to_string(), + "separator_color" => "separator_color".to_string(), + "header_align" => "header_align".to_string(), + "header_color" => "header_color".to_string(), + "header_bold" => "header_bold".to_string(), + "header_style" => "header_style".to_string(), + "index_color" => "index_color".to_string(), + _ => "Primitive::Nothing".to_string(), + } +} + +fn update_hashmap(key: &str, val: &Value, hm: &mut HashMap) { + if let Ok(var) = val.as_string() { + let color = lookup_ansi_color_style(var); + let prim = string_to_lookup_value(&key); + if let Some(v) = hm.get_mut(&prim) { + *v = color; + } else { + hm.insert(prim, color); + } + } +} + +pub fn get_color_config() -> HashMap { + // create the hashmap + let mut hm: HashMap = HashMap::new(); + // set some defaults + hm.insert("primitive_int".to_string(), Color::White.normal()); + hm.insert("primitive_decimal".to_string(), Color::White.normal()); + hm.insert("primitive_filesize".to_string(), Color::White.normal()); + hm.insert("primitive_string".to_string(), Color::White.normal()); + hm.insert("primitive_line".to_string(), Color::White.normal()); + hm.insert("primitive_columnpath".to_string(), Color::White.normal()); + hm.insert("primitive_pattern".to_string(), Color::White.normal()); + hm.insert("primitive_boolean".to_string(), Color::White.normal()); + hm.insert("primitive_date".to_string(), Color::White.normal()); + hm.insert("primitive_duration".to_string(), Color::White.normal()); + hm.insert("primitive_range".to_string(), Color::White.normal()); + hm.insert("primitive_path".to_string(), Color::White.normal()); + hm.insert("primitive_binary".to_string(), Color::White.normal()); + hm.insert("separator_color".to_string(), Color::White.normal()); + hm.insert("header_align".to_string(), Color::White.normal()); + hm.insert("header_color".to_string(), Color::White.normal()); + hm.insert("header_bold".to_string(), Color::White.normal()); + hm.insert("header_style".to_string(), Style::default()); + hm.insert("index_color".to_string(), Color::Green.normal()); + + // populate hashmap from config values + if let Ok(config) = crate::config::config(Tag::unknown()) { + if let Some(primitive_color_vars) = config.get("color_config") { + for (key, value) in primitive_color_vars.row_entries() { + match key.as_ref() { + "primitive_int" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_decimal" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_filesize" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_string" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_line" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_columnpath" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_pattern" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_boolean" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_date" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_duration" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_range" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_path" => { + update_hashmap(&key, &value, &mut hm); + } + "primitive_binary" => { + update_hashmap(&key, &value, &mut hm); + } + "separator_color" => { + update_hashmap(&key, &value, &mut hm); + } + "header_align" => { + update_hashmap(&key, &value, &mut hm); + } + "header_color" => { + update_hashmap(&key, &value, &mut hm); + } + "header_bold" => { + update_hashmap(&key, &value, &mut hm); + } + "header_style" => { + update_hashmap(&key, &value, &mut hm); + } + "index_color" => { + update_hashmap(&key, &value, &mut hm); + } + _ => (), + } + } + } + } + + hm +} + +// This function will assign a text style to a primitive, or really any string that's +// in the hashmap. The hashmap actually contains the style to be applied. +pub fn style_primitive(primitive: &str, color_hm: &HashMap) -> TextStyle { match primitive { - Primitive::Int(_) | Primitive::Filesize(_) | Primitive::Decimal(_) => { - TextStyle::basic_right() + "Int" => { + let style = color_hm.get("Primitive::Int"); + match style { + Some(s) => TextStyle::with_style(Alignment::Right, *s), + None => TextStyle::basic_right(), + } + } + "Decimal" => { + let style = color_hm.get("Primitive::Decimal"); + match style { + Some(s) => TextStyle::with_style(Alignment::Right, *s), + None => TextStyle::basic_right(), + } + } + "Filesize" => { + let style = color_hm.get("Primitive::Filesize"); + match style { + Some(s) => TextStyle::with_style(Alignment::Right, *s), + None => TextStyle::basic_right(), + } + } + "String" => { + let style = color_hm.get("Primitive::String"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Line" => { + let style = color_hm.get("Primitive::Line"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "ColumnPath" => { + let style = color_hm.get("Primitive::ColumnPath"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Pattern" => { + let style = color_hm.get("Primitive::Pattern"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Boolean" => { + let style = color_hm.get("Primitive::Boolean"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Date" => { + let style = color_hm.get("Primitive::Date"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Duration" => { + let style = color_hm.get("Primitive::Duration"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Range" => { + let style = color_hm.get("Primitive::Range"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Path" => { + let style = color_hm.get("Primitive::Path"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Binary" => { + let style = color_hm.get("Primitive::Binary"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "BeginningOfStream" => { + let style = color_hm.get("Primitive::BeginningOfStream"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "EndOfStream" => { + let style = color_hm.get("Primitive::EndOfStream"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "Nothing" => { + let style = color_hm.get("Primitive::Nothing"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "separator_color" => { + let style = color_hm.get("separator"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "header_align" => { + let style = color_hm.get("header_align"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "header_color" => { + let style = color_hm.get("header_color"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "header_bold" => { + let style = color_hm.get("header_bold"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "header_style" => { + let style = color_hm.get("header_style"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } + } + "index_color" => { + let style = color_hm.get("index_color"); + match style { + Some(s) => TextStyle::with_style(Alignment::Left, *s), + None => TextStyle::basic_right(), + } } _ => TextStyle::basic(), } diff --git a/crates/nu-data/src/value.rs b/crates/nu-data/src/value.rs index 80fce47974..742d2d4a03 100644 --- a/crates/nu-data/src/value.rs +++ b/crates/nu-data/src/value.rs @@ -9,6 +9,7 @@ use nu_protocol::{Primitive, Type, UntaggedValue}; use nu_source::{DebugDocBuilder, PrettyDebug, Span, Tagged}; use nu_table::TextStyle; use num_traits::Zero; +use std::collections::HashMap; pub struct Date; @@ -245,9 +246,20 @@ pub fn format_leaf<'a>(value: impl Into<&'a UntaggedValue>) -> DebugDocBuilder { InlineShape::from_value(value.into()).format().pretty() } -pub fn style_leaf<'a>(value: impl Into<&'a UntaggedValue>) -> TextStyle { +pub fn style_leaf<'a>( + value: impl Into<&'a UntaggedValue>, + color_hash_map: &HashMap, +) -> TextStyle { match value.into() { - UntaggedValue::Primitive(p) => style_primitive(p), + UntaggedValue::Primitive(p) => { + // This is just to return the name of the type so that style_primitive + // can work on a string versus a type like String("some_text") + let str: &str = &p.to_string(); + let str_len = str.len(); + let paren_index = str.find('(').unwrap_or(str_len - 1); + let prim_type = str[0..paren_index].to_string(); + style_primitive(&prim_type, &color_hash_map) + } _ => TextStyle::basic(), } } diff --git a/crates/nu-protocol/src/value/primitive.rs b/crates/nu-protocol/src/value/primitive.rs index 34443ebf6b..fd537619b0 100644 --- a/crates/nu-protocol/src/value/primitive.rs +++ b/crates/nu-protocol/src/value/primitive.rs @@ -192,6 +192,12 @@ impl From for Primitive { } } +impl std::fmt::Display for Primitive { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + impl ShellTypeName for Primitive { /// Get the name of the type of a Primitive value fn type_name(&self) -> &'static str { diff --git a/crates/nu-table/src/main.rs b/crates/nu-table/src/main.rs index 19a8ecf6a4..5e4324e41d 100644 --- a/crates/nu-table/src/main.rs +++ b/crates/nu-table/src/main.rs @@ -1,4 +1,5 @@ use nu_table::{draw_table, StyledString, Table, TextStyle, Theme}; +use std::collections::HashMap; fn main() { let args: Vec<_> = std::env::args().collect(); @@ -23,5 +24,7 @@ fn main() { Theme::compact(), ); - draw_table(&t, width); + // FIXME: Config isn't available from here so just put these here to compile + let color_hm: HashMap = HashMap::new(); + draw_table(&t, width, &color_hm); } diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs index 6939b71117..137a1b29bf 100644 --- a/crates/nu-table/src/table.rs +++ b/crates/nu-table/src/table.rs @@ -1,4 +1,6 @@ use crate::wrap::{column_width, split_sublines, wrap, Alignment, Subline, WrappedCell}; +use ansi_term::{Color, Style}; +use std::collections::HashMap; enum SeparatorPosition { Top, @@ -23,38 +25,240 @@ impl StyledString { pub fn new(contents: String, style: TextStyle) -> StyledString { StyledString { contents, style } } + + pub fn set_style(&mut self, style: TextStyle) { + self.style = style; + } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct TextStyle { - pub is_bold: bool, pub alignment: Alignment, - pub color: Option, + pub color_style: Option