mirror of
https://github.com/nushell/nushell.git
synced 2024-11-22 08:23:24 +01:00
color_config now accepts closures as color values (#7141)
# Description Closes #6909. You can now add closures to your `color_config` themes. Whenever a value would be printed with `table`, the closure is run with the value piped-in. The closure must return either a {fg,bg,attr} record or a color name (`'light_red'` etc.). This returned style is used to colour the value. This is entirely backwards-compatible with existing config.nu files. Example code excerpt: ``` let my_theme = { header: green_bold bool: { if $in { 'light_cyan' } else { 'light_red' } } int: purple_bold filesize: { |e| if $e == 0b { 'gray' } else if $e < 1mb { 'purple_bold' } else { 'cyan_bold' } } duration: purple_bold date: { (date now) - $in | if $in > 1wk { 'cyan_bold' } else if $in > 1day { 'green_bold' } else { 'yellow_bold' } } range: yellow_bold string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } } nothing: white ``` Example output with this in effect: ![2022-11-16 12 47 23 AM - style_computer rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952558-482de05d-69c7-4bf2-91fc-d0964bf71264.png) ![2022-11-16 12 39 41 AM - style_computer rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952580-2384bb86-b680-40fe-8192-71bae396c738.png) ![2022-11-15 09 21 54 PM - run_external rs_-_nushell_-_VSCodium](https://user-images.githubusercontent.com/83939/201952601-343fc15d-e4a8-4a92-ad89-9a7d17d42748.png) Slightly important notes: * Some color_config names, namely "separator", "empty" and "hints", pipe in `null` instead of a value. * Currently, doing anything non-trivial inside a closure has an understandably big perf hit. I currently do not actually recommend something like `string: { if $in =~ '^#\w{6}$' { $in } else { 'white' } }` for serious work, mainly because of the abundance of string-type data in the world. Nevertheless, lesser-used types like "date" and "duration" work well with this. * I had to do some reorganisation in order to make it possible to call `eval_block()` that late in table rendering. I invented a new struct called "StyleComputer" which holds the engine_state and stack of the initial `table` command (implicit or explicit). * StyleComputer has a `compute()` method which takes a color_config name and a nu value, and always returns the correct Style, so you don't have to worry about A) the color_config value was set at all, B) whether it was set to a closure or not, or C) which default style to use in those cases. * Currently, errors encountered during execution of the closures are thrown in the garbage. Any other ideas are welcome. (Nonetheless, errors result in a huge perf hit when they are encountered. I think what should be done is to assume something terrible happened to the user's config and invalidate the StyleComputer for that `table` run, thus causing subsequent output to just be Style::default().) * More thorough tests are forthcoming - ran into some difficulty using `nu!` to take an alternative config, and for some reason `let-env config =` statements don't seem to work inside `nu!` pipelines(???) * The default config.nu has not been updated to make use of this yet. Do tell if you think I should incorporate that into this. # User-Facing Changes See above. # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace --features=extra` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
parent
e72cecf457
commit
774769a7ad
13
Cargo.lock
generated
13
Cargo.lock
generated
@ -2550,6 +2550,7 @@ dependencies = [
|
|||||||
"nu-parser",
|
"nu-parser",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
|
"nu-table",
|
||||||
"nu-test-support",
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
@ -2564,10 +2565,13 @@ name = "nu-color-config"
|
|||||||
version = "0.72.2"
|
version = "0.72.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-json",
|
"nu-engine",
|
||||||
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-table",
|
"nu-test-support",
|
||||||
|
"nu-utils",
|
||||||
"serde",
|
"serde",
|
||||||
|
"tabled",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2812,9 +2816,14 @@ dependencies = [
|
|||||||
name = "nu-table"
|
name = "nu-table"
|
||||||
version = "0.72.2"
|
version = "0.72.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atty",
|
||||||
"json_to_table",
|
"json_to_table",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
|
"nu-color-config",
|
||||||
|
"nu-engine",
|
||||||
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
|
"nu-test-support",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tabled",
|
"tabled",
|
||||||
|
@ -18,6 +18,7 @@ nu-path = { path = "../nu-path", version = "0.72.2" }
|
|||||||
nu-parser = { path = "../nu-parser", version = "0.72.2" }
|
nu-parser = { path = "../nu-parser", version = "0.72.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.72.2" }
|
nu-utils = { path = "../nu-utils", version = "0.72.2" }
|
||||||
|
nu-table = { path = "../nu-table", version = "0.72.2" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.72.2" }
|
nu-color-config = { path = "../nu-color-config", version = "0.72.2" }
|
||||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use super::DescriptionMenu;
|
use super::DescriptionMenu;
|
||||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use nu_color_config::lookup_ansi_color_style;
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
color_value_string, create_menus,
|
create_menus,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
|
extract_value, Config, IntoPipelineData, ParsedKeybinding, ParsedMenu, PipelineData,
|
||||||
ShellError, Span, Value,
|
ShellError, Span, Value,
|
||||||
@ -159,14 +159,11 @@ macro_rules! add_style {
|
|||||||
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
($name:expr, $cols: expr, $vals:expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
||||||
$menu = match extract_value($name, $cols, $vals, $span) {
|
$menu = match extract_value($name, $cols, $vals, $span) {
|
||||||
Ok(text) => {
|
Ok(text) => {
|
||||||
let text = match text {
|
let style = match text {
|
||||||
Value::String { val, .. } => val.clone(),
|
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
||||||
Value::Record { cols, vals, span } => {
|
Value::Record { .. } => color_record_to_nustyle(&text),
|
||||||
color_value_string(span, cols, vals, $config).into_string("", $config)
|
_ => lookup_ansi_color_style("green"),
|
||||||
}
|
|
||||||
_ => "green".to_string(),
|
|
||||||
};
|
};
|
||||||
let style = lookup_ansi_color_style(&text);
|
|
||||||
$f($menu, style)
|
$f($menu, style)
|
||||||
}
|
}
|
||||||
Err(_) => $menu,
|
Err(_) => $menu,
|
||||||
|
@ -9,7 +9,7 @@ use fancy_regex::Regex;
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{info, trace, warn};
|
use log::{info, trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -174,8 +174,6 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||||
|
|
||||||
let color_hm = get_color_config(config);
|
|
||||||
|
|
||||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
||||||
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
||||||
line_editor = line_editor
|
line_editor = line_editor
|
||||||
@ -194,10 +192,14 @@ pub fn evaluate_repl(
|
|||||||
.with_partial_completions(config.partial_completions)
|
.with_partial_completions(config.partial_completions)
|
||||||
.with_ansi_colors(config.use_ansi_coloring);
|
.with_ansi_colors(config.use_ansi_coloring);
|
||||||
|
|
||||||
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
|
|
||||||
line_editor = if config.use_ansi_coloring {
|
line_editor = if config.use_ansi_coloring {
|
||||||
line_editor.with_hinter(Box::new(
|
line_editor.with_hinter(Box::new({
|
||||||
DefaultHinter::default().with_style(color_hm["hints"]),
|
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
||||||
))
|
let style = style_computer.compute("hints", &Value::nothing(Span::unknown()));
|
||||||
|
DefaultHinter::default().with_style(style)
|
||||||
|
}))
|
||||||
} else {
|
} else {
|
||||||
line_editor.disable_hints()
|
line_editor.disable_hints()
|
||||||
};
|
};
|
||||||
@ -928,7 +930,7 @@ pub fn eval_hook(
|
|||||||
Ok(output)
|
Ok(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_hook_block(
|
fn run_hook_block(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
|
@ -8,8 +8,14 @@ name = "nu-color-config"
|
|||||||
version = "0.72.2"
|
version = "0.72.2"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
|
# used only for text_style Alignments
|
||||||
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-json = { path = "../nu-json", version = "0.72.2" }
|
nu-utils = { path = "../nu-utils", version = "0.72.2" }
|
||||||
nu-table = { path = "../nu-table", version = "0.72.2" }
|
nu-engine = { path = "../nu-engine", version = "0.72.2" }
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
nu-test-support = { path="../nu-test-support", version = "0.72.2" }
|
||||||
|
# nu-path is used only for test support
|
||||||
|
nu-path = { path="../nu-path", version = "0.72.2" }
|
@ -1,7 +1,6 @@
|
|||||||
use crate::nu_style::{color_from_hex, color_string_to_nustyle, lookup_style};
|
use crate::nu_style::{color_from_hex, lookup_style};
|
||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::Style;
|
||||||
use nu_protocol::{Config, Value};
|
use nu_protocol::Value;
|
||||||
use nu_table::{Alignment, TextStyle};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub fn lookup_ansi_color_style(s: &str) -> Style {
|
pub fn lookup_ansi_color_style(s: &str) -> Style {
|
||||||
@ -10,13 +9,12 @@ pub fn lookup_ansi_color_style(s: &str) -> Style {
|
|||||||
.ok()
|
.ok()
|
||||||
.and_then(|c| c.map(|c| c.normal()))
|
.and_then(|c| c.map(|c| c.normal()))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
} else if s.starts_with('{') {
|
|
||||||
color_string_to_nustyle(s.to_string())
|
|
||||||
} else {
|
} else {
|
||||||
lookup_style(s)
|
lookup_style(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These two are used only for Explore's very limited color config
|
||||||
fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) {
|
fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) {
|
||||||
// eprintln!("key: {}, val: {}", &key, &val);
|
// eprintln!("key: {}, val: {}", &key, &val);
|
||||||
let color = lookup_ansi_color_style(val);
|
let color = lookup_ansi_color_style(val);
|
||||||
@ -27,49 +25,6 @@ fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
|
|
||||||
let config = config;
|
|
||||||
|
|
||||||
// create the hashmap
|
|
||||||
let mut hm: HashMap<String, Style> = HashMap::new();
|
|
||||||
// set some defaults
|
|
||||||
// hm.insert("primitive_line".to_string(), Color::White.normal());
|
|
||||||
// hm.insert("primitive_pattern".to_string(), Color::White.normal());
|
|
||||||
// hm.insert("primitive_path".to_string(), Color::White.normal());
|
|
||||||
hm.insert("separator".to_string(), Color::White.normal());
|
|
||||||
hm.insert(
|
|
||||||
"leading_trailing_space_bg".to_string(),
|
|
||||||
Style::default().on(Color::Rgb(128, 128, 128)),
|
|
||||||
);
|
|
||||||
hm.insert("header".to_string(), Color::Green.bold());
|
|
||||||
hm.insert("empty".to_string(), Color::Blue.normal());
|
|
||||||
hm.insert("bool".to_string(), Color::White.normal());
|
|
||||||
hm.insert("int".to_string(), Color::White.normal());
|
|
||||||
hm.insert("filesize".to_string(), Color::White.normal());
|
|
||||||
hm.insert("duration".to_string(), Color::White.normal());
|
|
||||||
hm.insert("date".to_string(), Color::White.normal());
|
|
||||||
hm.insert("range".to_string(), Color::White.normal());
|
|
||||||
hm.insert("float".to_string(), Color::White.normal());
|
|
||||||
hm.insert("string".to_string(), Color::White.normal());
|
|
||||||
hm.insert("nothing".to_string(), Color::White.normal());
|
|
||||||
hm.insert("binary".to_string(), Color::White.normal());
|
|
||||||
hm.insert("cellpath".to_string(), Color::White.normal());
|
|
||||||
hm.insert("row_index".to_string(), Color::Green.bold());
|
|
||||||
hm.insert("record".to_string(), Color::White.normal());
|
|
||||||
hm.insert("list".to_string(), Color::White.normal());
|
|
||||||
hm.insert("block".to_string(), Color::White.normal());
|
|
||||||
hm.insert("hints".to_string(), Color::DarkGray.normal());
|
|
||||||
|
|
||||||
for (key, value) in &config.color_config {
|
|
||||||
match value.as_string() {
|
|
||||||
Ok(value) => update_hashmap(key, &value, &mut hm),
|
|
||||||
Err(_) => continue,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hm
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style> {
|
pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style> {
|
||||||
let mut hm: HashMap<String, Style> = HashMap::new();
|
let mut hm: HashMap<String, Style> = HashMap::new();
|
||||||
|
|
||||||
@ -81,189 +36,3 @@ pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style>
|
|||||||
|
|
||||||
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<String, Style>) -> TextStyle {
|
|
||||||
match primitive {
|
|
||||||
"bool" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"int" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
|
||||||
None => TextStyle::basic_right(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"filesize" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
|
||||||
None => TextStyle::basic_right(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"duration" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"date" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"range" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"float" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
|
||||||
None => TextStyle::basic_right(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"string" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"nothing" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// not sure what to do with error
|
|
||||||
// "error" => {}
|
|
||||||
"binary" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"cellpath" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"row_index" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Right, *s),
|
|
||||||
None => TextStyle::new()
|
|
||||||
.alignment(Alignment::Right)
|
|
||||||
.fg(Color::Green)
|
|
||||||
.bold(Some(true)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"record" | "list" | "block" => {
|
|
||||||
let style = color_hm.get(primitive);
|
|
||||||
match style {
|
|
||||||
Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
None => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// types in nushell but not in engine-q
|
|
||||||
// "Line" => {
|
|
||||||
// let style = color_hm.get("Primitive::Line");
|
|
||||||
// match style {
|
|
||||||
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
// None => TextStyle::basic_left(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "GlobPattern" => {
|
|
||||||
// let style = color_hm.get("Primitive::GlobPattern");
|
|
||||||
// match style {
|
|
||||||
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
// None => TextStyle::basic_left(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "FilePath" => {
|
|
||||||
// let style = color_hm.get("Primitive::FilePath");
|
|
||||||
// match style {
|
|
||||||
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
// None => TextStyle::basic_left(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "BeginningOfStream" => {
|
|
||||||
// let style = color_hm.get("Primitive::BeginningOfStream");
|
|
||||||
// match style {
|
|
||||||
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
// None => TextStyle::basic_left(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// "EndOfStream" => {
|
|
||||||
// let style = color_hm.get("Primitive::EndOfStream");
|
|
||||||
// match style {
|
|
||||||
// Some(s) => TextStyle::with_style(Alignment::Left, *s),
|
|
||||||
// None => TextStyle::basic_left(),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
_ => TextStyle::basic_left(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_hm() {
|
|
||||||
use nu_ansi_term::{Color, Style};
|
|
||||||
|
|
||||||
let mut hm: HashMap<String, Style> = HashMap::new();
|
|
||||||
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".to_string(), Color::White.normal());
|
|
||||||
hm.insert("header_align".to_string(), Color::Green.bold());
|
|
||||||
hm.insert("header".to_string(), Color::Green.bold());
|
|
||||||
hm.insert("header_style".to_string(), Style::default());
|
|
||||||
hm.insert("row_index".to_string(), Color::Green.bold());
|
|
||||||
hm.insert(
|
|
||||||
"leading_trailing_space_bg".to_string(),
|
|
||||||
Style::default().on(Color::Rgb(128, 128, 128)),
|
|
||||||
);
|
|
||||||
|
|
||||||
update_hashmap("primitive_int", "green", &mut hm);
|
|
||||||
|
|
||||||
assert_eq!(hm["primitive_int"], Color::Green.normal());
|
|
||||||
}
|
|
||||||
|
@ -2,8 +2,12 @@ mod color_config;
|
|||||||
mod matching_brackets_style;
|
mod matching_brackets_style;
|
||||||
mod nu_style;
|
mod nu_style;
|
||||||
mod shape_color;
|
mod shape_color;
|
||||||
|
mod style_computer;
|
||||||
|
mod text_style;
|
||||||
|
|
||||||
pub use color_config::*;
|
pub use color_config::*;
|
||||||
pub use matching_brackets_style::*;
|
pub use matching_brackets_style::*;
|
||||||
pub use nu_style::*;
|
pub use nu_style::*;
|
||||||
pub use shape_color::*;
|
pub use shape_color::*;
|
||||||
|
pub use style_computer::*;
|
||||||
|
pub use text_style::*;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
|
use nu_protocol::Value;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
|
#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)]
|
||||||
@ -83,22 +84,29 @@ pub fn parse_nustyle(nu_style: NuStyle) -> Style {
|
|||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color_string_to_nustyle(color_string: String) -> Style {
|
// Converts the color_config records, { fg, bg, attr }, into a Style.
|
||||||
// eprintln!("color_string: {}", &color_string);
|
pub fn color_record_to_nustyle(value: &Value) -> Style {
|
||||||
if color_string.chars().count() < 1 {
|
let mut fg = None;
|
||||||
Style::default()
|
let mut bg = None;
|
||||||
} else {
|
let mut attr = None;
|
||||||
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
|
let v = value.as_record();
|
||||||
Ok(s) => s,
|
if let Ok((cols, inner_vals)) = v {
|
||||||
Err(_) => NuStyle {
|
for (k, v) in cols.iter().zip(inner_vals) {
|
||||||
fg: None,
|
// Because config already type-checked the color_config records, this doesn't bother giving errors
|
||||||
bg: None,
|
// if there are unrecognised keys or bad values.
|
||||||
attr: None,
|
if let Ok(v) = v.as_string() {
|
||||||
},
|
match k.as_str() {
|
||||||
};
|
"fg" => fg = Some(v),
|
||||||
|
|
||||||
parse_nustyle(nu_style)
|
"bg" => bg = Some(v),
|
||||||
|
|
||||||
|
"attr" => attr = Some(v),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
parse_nustyle(NuStyle { fg, bg, attr })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color_from_hex(
|
pub fn color_from_hex(
|
||||||
|
285
crates/nu-color-config/src/style_computer.rs
Normal file
285
crates/nu-color-config/src/style_computer.rs
Normal file
@ -0,0 +1,285 @@
|
|||||||
|
use crate::{color_record_to_nustyle, lookup_ansi_color_style, TextStyle};
|
||||||
|
use nu_ansi_term::{Color, Style};
|
||||||
|
use nu_engine::eval_block;
|
||||||
|
use nu_protocol::{
|
||||||
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
|
CliError, IntoPipelineData, Value,
|
||||||
|
};
|
||||||
|
use tabled::alignment::AlignmentHorizontal;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fmt::{Debug, Formatter, Result},
|
||||||
|
};
|
||||||
|
|
||||||
|
// ComputableStyle represents the valid user style types: a single color value, or a closure which
|
||||||
|
// takes an input value and produces a color value. The latter represents a value which
|
||||||
|
// is computed at use-time.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ComputableStyle {
|
||||||
|
Static(Style),
|
||||||
|
Closure(Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
// macro used for adding initial values to the style hashmap
|
||||||
|
macro_rules! initial {
|
||||||
|
($a:expr, $b:expr) => {
|
||||||
|
($a.to_string(), ComputableStyle::Static($b))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// An alias for the mapping used internally by StyleComputer.
|
||||||
|
pub type StyleMapping = HashMap<String, ComputableStyle>;
|
||||||
|
//
|
||||||
|
// A StyleComputer is an all-in-one way to compute styles. A nu command can
|
||||||
|
// simply create it with from_config(), and then use it with compute().
|
||||||
|
// It stores the engine state and stack needed to run closures that
|
||||||
|
// may be defined as a user style.
|
||||||
|
//
|
||||||
|
pub struct StyleComputer<'a> {
|
||||||
|
engine_state: &'a EngineState,
|
||||||
|
stack: &'a Stack,
|
||||||
|
map: StyleMapping,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StyleComputer<'a> {
|
||||||
|
// This is NOT meant to be used in most cases - please use from_config() instead.
|
||||||
|
// This only exists for testing purposes.
|
||||||
|
pub fn new(
|
||||||
|
engine_state: &'a EngineState,
|
||||||
|
stack: &'a Stack,
|
||||||
|
map: StyleMapping,
|
||||||
|
) -> StyleComputer<'a> {
|
||||||
|
StyleComputer {
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The main method. Takes a string name which maps to a color_config style name,
|
||||||
|
// and a Nu value to pipe into any closures that may have been defined there.
|
||||||
|
pub fn compute(&self, style_name: &str, value: &Value) -> Style {
|
||||||
|
match self.map.get(style_name) {
|
||||||
|
// Static values require no computation.
|
||||||
|
Some(ComputableStyle::Static(s)) => *s,
|
||||||
|
// Closures are run here.
|
||||||
|
Some(ComputableStyle::Closure(Value::Closure {
|
||||||
|
val: block_id,
|
||||||
|
captures,
|
||||||
|
span,
|
||||||
|
})) => {
|
||||||
|
let block = self.engine_state.get_block(*block_id).clone();
|
||||||
|
// Because captures_to_stack() clones, we don't need to use with_env() here
|
||||||
|
// (contrast with_env() usage in `each` or `do`).
|
||||||
|
let mut stack = self.stack.captures_to_stack(captures);
|
||||||
|
|
||||||
|
// Support 1-argument blocks as well as 0-argument blocks.
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the block.
|
||||||
|
match eval_block(
|
||||||
|
self.engine_state,
|
||||||
|
&mut stack,
|
||||||
|
&block,
|
||||||
|
value.clone().into_pipeline_data(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
) {
|
||||||
|
Ok(v) => {
|
||||||
|
let value = v.into_value(*span);
|
||||||
|
// These should be the same color data forms supported by color_config.
|
||||||
|
match value {
|
||||||
|
Value::Record { .. } => color_record_to_nustyle(&value),
|
||||||
|
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
||||||
|
_ => Style::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is basically a copy of nu_cli::report_error(), but that isn't usable due to
|
||||||
|
// dependencies. While crudely spitting out a bunch of errors like this is not ideal,
|
||||||
|
// currently hook closure errors behave roughly the same.
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!(
|
||||||
|
"Error: {:?}",
|
||||||
|
CliError(&e, &StateWorkingSet::new(self.engine_state))
|
||||||
|
);
|
||||||
|
Style::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// There should be no other kinds of values (due to create_map() in config.rs filtering them out)
|
||||||
|
// so this is just a fallback.
|
||||||
|
_ => Style::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used only by the `table` command.
|
||||||
|
pub fn style_primitive(&self, value: &Value) -> TextStyle {
|
||||||
|
let s = self.compute(&value.get_type().to_string(), value);
|
||||||
|
match *value {
|
||||||
|
Value::Bool { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Int { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
||||||
|
|
||||||
|
Value::Filesize { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
||||||
|
|
||||||
|
Value::Duration { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Date { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Range { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Float { .. } => TextStyle::with_style(AlignmentHorizontal::Right, s),
|
||||||
|
|
||||||
|
Value::String { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Nothing { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Binary { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::CellPath { .. } => TextStyle::with_style(AlignmentHorizontal::Left, s),
|
||||||
|
|
||||||
|
Value::Record { .. } | Value::List { .. } | Value::Block { .. } => {
|
||||||
|
TextStyle::with_style(AlignmentHorizontal::Left, s)
|
||||||
|
}
|
||||||
|
_ => TextStyle::basic_left(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main constructor.
|
||||||
|
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
|
||||||
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
|
// Create the hashmap
|
||||||
|
let mut map: StyleMapping = HashMap::from([
|
||||||
|
initial!("separator", Color::White.normal()),
|
||||||
|
initial!(
|
||||||
|
"leading_trailing_space_bg",
|
||||||
|
Style::default().on(Color::Rgb(128, 128, 128))
|
||||||
|
),
|
||||||
|
initial!("header", Color::White.normal()),
|
||||||
|
initial!("empty", Color::White.normal()),
|
||||||
|
initial!("bool", Color::White.normal()),
|
||||||
|
initial!("int", Color::White.normal()),
|
||||||
|
initial!("filesize", Color::White.normal()),
|
||||||
|
initial!("duration", Color::White.normal()),
|
||||||
|
initial!("date", Color::White.normal()),
|
||||||
|
initial!("range", Color::White.normal()),
|
||||||
|
initial!("float", Color::White.normal()),
|
||||||
|
initial!("string", Color::White.normal()),
|
||||||
|
initial!("nothing", Color::White.normal()),
|
||||||
|
initial!("binary", Color::White.normal()),
|
||||||
|
initial!("cellpath", Color::White.normal()),
|
||||||
|
initial!("row_index", Color::Green.bold()),
|
||||||
|
initial!("record", Color::White.normal()),
|
||||||
|
initial!("list", Color::White.normal()),
|
||||||
|
initial!("block", Color::White.normal()),
|
||||||
|
initial!("hints", Color::DarkGray.normal()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (key, value) in &config.color_config {
|
||||||
|
match value {
|
||||||
|
Value::Closure { .. } => {
|
||||||
|
map.insert(key.to_string(), ComputableStyle::Closure(value.clone()));
|
||||||
|
}
|
||||||
|
Value::Record { .. } => {
|
||||||
|
map.insert(
|
||||||
|
key.to_string(),
|
||||||
|
ComputableStyle::Static(color_record_to_nustyle(value)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Value::String { val, .. } => {
|
||||||
|
// update the stylemap with the found key
|
||||||
|
let color = lookup_ansi_color_style(val.as_str());
|
||||||
|
if let Some(v) = map.get_mut(key) {
|
||||||
|
*v = ComputableStyle::Static(color);
|
||||||
|
} else {
|
||||||
|
map.insert(key.to_string(), ComputableStyle::Static(color));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This should never occur.
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StyleComputer::new(engine_state, stack, map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because EngineState doesn't have Debug (Dec 2022),
|
||||||
|
// this incomplete representation must be used.
|
||||||
|
impl<'a> Debug for StyleComputer<'a> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
|
||||||
|
f.debug_struct("StyleComputer")
|
||||||
|
.field("map", &self.map)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_computable_style_static() {
|
||||||
|
use nu_protocol::Span;
|
||||||
|
|
||||||
|
let style1 = Style::default().italic();
|
||||||
|
let style2 = Style::default().underline();
|
||||||
|
// Create a "dummy" style_computer for this test.
|
||||||
|
let dummy_engine_state = EngineState::new();
|
||||||
|
let mut dummy_stack = Stack::new();
|
||||||
|
let style_computer = StyleComputer::new(
|
||||||
|
&dummy_engine_state,
|
||||||
|
&mut dummy_stack,
|
||||||
|
HashMap::from([
|
||||||
|
("string".into(), ComputableStyle::Static(style1)),
|
||||||
|
("row_index".into(), ComputableStyle::Static(style2)),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style_computer.compute("string", &Value::nothing(Span::unknown())),
|
||||||
|
style1
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
style_computer.compute("row_index", &Value::nothing(Span::unknown())),
|
||||||
|
style2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Because each closure currently runs in a separate environment, checks that the closures have run
|
||||||
|
// must use the filesystem.
|
||||||
|
#[test]
|
||||||
|
fn test_computable_style_closure_basic() {
|
||||||
|
use nu_test_support::{nu, nu_repl_code, playground::Playground};
|
||||||
|
Playground::setup("computable_style_closure_basic", |dirs, _| {
|
||||||
|
let inp = [
|
||||||
|
r#"let-env config = {
|
||||||
|
color_config: {
|
||||||
|
string: {|e| touch ($e + '.obj'); 'red' }
|
||||||
|
}
|
||||||
|
};"#,
|
||||||
|
"[bell book candle] | table | ignore",
|
||||||
|
"ls | get name | to nuon",
|
||||||
|
];
|
||||||
|
let actual_repl = nu!(cwd: dirs.test(), nu_repl_code(&inp));
|
||||||
|
assert_eq!(actual_repl.err, "");
|
||||||
|
assert_eq!(actual_repl.out, "[bell.obj, book.obj, candle.obj]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_computable_style_closure_errors() {
|
||||||
|
use nu_test_support::{nu, nu_repl_code};
|
||||||
|
let inp = [
|
||||||
|
r#"let-env config = {
|
||||||
|
color_config: {
|
||||||
|
string: {|e| $e + 2 }
|
||||||
|
}
|
||||||
|
};"#,
|
||||||
|
"[bell] | table",
|
||||||
|
];
|
||||||
|
let actual_repl = nu!(cwd: ".", nu_repl_code(&inp));
|
||||||
|
// Check that the error was printed
|
||||||
|
assert!(actual_repl.err.contains("type mismatch for operator"));
|
||||||
|
// Check that the value was printed
|
||||||
|
assert!(actual_repl.out.contains("bell"));
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
pub type Alignment = tabled::alignment::AlignmentHorizontal;
|
pub type Alignment = tabled::alignment::AlignmentHorizontal;
|
||||||
|
|
||||||
@ -239,3 +240,23 @@ impl Default for TextStyle {
|
|||||||
Self::new()
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl tabled::papergrid::Color for TextStyle {
|
||||||
|
fn fmt_prefix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(color) = &self.color_style {
|
||||||
|
color.prefix().fmt(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fmt_suffix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
if let Some(color) = &self.color_style {
|
||||||
|
if !color.is_plain() {
|
||||||
|
f.write_str("\u{1b}[0m")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use nu_ansi_term::{
|
use nu_ansi_term::{
|
||||||
Color::{Default, Red, White},
|
Color::{Red, White},
|
||||||
Style,
|
Style,
|
||||||
};
|
};
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{get_full_help, CallExt};
|
use nu_engine::{get_full_help, CallExt};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
@ -86,13 +86,13 @@ fn help(
|
|||||||
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
|
||||||
let commands = engine_state.get_decl_ids_sorted(false);
|
let commands = engine_state.get_decl_ids_sorted(false);
|
||||||
let config = engine_state.get_config();
|
|
||||||
let color_hm = get_color_config(config);
|
// 🚩The following two-lines are copied from filters/find.rs:
|
||||||
let default_style = Style::new().fg(Default).on(Default);
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
let string_style = match color_hm.get("string") {
|
// Currently, search results all use the same style.
|
||||||
Some(style) => style,
|
// Also note that this sample string is passed into user-written code (the closure that may or may not be
|
||||||
None => &default_style,
|
// defined for "string").
|
||||||
};
|
let string_style = style_computer.compute("string", &Value::string("search result", head));
|
||||||
|
|
||||||
if let Some(f) = find {
|
if let Some(f) = find {
|
||||||
let org_search_string = f.item.clone();
|
let org_search_string = f.item.clone();
|
||||||
@ -123,7 +123,7 @@ fn help(
|
|||||||
cols.push("name".into());
|
cols.push("name".into());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
val: if key_match {
|
val: if key_match {
|
||||||
highlight_search_string(&key, &org_search_string, string_style)?
|
highlight_search_string(&key, &org_search_string, &string_style)?
|
||||||
} else {
|
} else {
|
||||||
key
|
key
|
||||||
},
|
},
|
||||||
@ -142,7 +142,7 @@ fn help(
|
|||||||
cols.push("usage".into());
|
cols.push("usage".into());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
val: if usage_match {
|
val: if usage_match {
|
||||||
highlight_search_string(&usage, &org_search_string, string_style)?
|
highlight_search_string(&usage, &org_search_string, &string_style)?
|
||||||
} else {
|
} else {
|
||||||
usage
|
usage
|
||||||
},
|
},
|
||||||
@ -172,7 +172,7 @@ fn help(
|
|||||||
match highlight_search_string(
|
match highlight_search_string(
|
||||||
term,
|
term,
|
||||||
&org_search_string,
|
&org_search_string,
|
||||||
string_style,
|
&string_style,
|
||||||
) {
|
) {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
|
@ -2,8 +2,8 @@ use crate::help::highlight_search_string;
|
|||||||
|
|
||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};
|
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};
|
||||||
use nu_ansi_term::{Color, Color::Default, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{env_to_string, CallExt};
|
use nu_engine::{env_to_string, CallExt};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
@ -308,12 +308,12 @@ fn find_with_rest_and_highlight(
|
|||||||
})
|
})
|
||||||
.collect::<Vec<Value>>();
|
.collect::<Vec<Value>>();
|
||||||
|
|
||||||
let color_hm = get_color_config(&config);
|
let style_computer = StyleComputer::from_config(&engine_state, stack);
|
||||||
let default_style = Style::new().fg(Default).on(Default);
|
// Currently, search results all use the same style.
|
||||||
let string_style = match color_hm.get("string") {
|
// Also note that this sample string is passed into user-written code (the closure that may or may not be
|
||||||
Some(style) => *style,
|
// defined for "string").
|
||||||
None => default_style,
|
let string_style = style_computer.compute("string", &Value::string("search result", span));
|
||||||
};
|
|
||||||
let ls_colors_env_str = match stack.get_env_var(&engine_state, "LS_COLORS") {
|
let ls_colors_env_str = match stack.get_env_var(&engine_state, "LS_COLORS") {
|
||||||
Some(v) => Some(env_to_string("LS_COLORS", &v, &engine_state, stack)?),
|
Some(v) => Some(env_to_string("LS_COLORS", &v, &engine_state, stack)?),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -58,7 +58,7 @@ impl Command for External {
|
|||||||
// Translate environment variables from Values to Strings
|
// Translate environment variables from Values to Strings
|
||||||
let env_vars_str = env_to_strings(engine_state, stack)?;
|
let env_vars_str = env_to_strings(engine_state, stack)?;
|
||||||
|
|
||||||
fn value_as_spanned(value: Value, name: &String) -> Result<Spanned<String>, ShellError> {
|
fn value_as_spanned(value: Value) -> Result<Spanned<String>, ShellError> {
|
||||||
let span = value.span()?;
|
let span = value.span()?;
|
||||||
|
|
||||||
value
|
value
|
||||||
@ -66,11 +66,7 @@ impl Command for External {
|
|||||||
.map(|item| Spanned { item, span })
|
.map(|item| Spanned { item, span })
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
ShellError::ExternalCommand(
|
ShellError::ExternalCommand(
|
||||||
format!(
|
format!("Cannot convert {} to a string", value.get_type()),
|
||||||
"Cannot convert {} to a string argument for '{}'",
|
|
||||||
value.get_type(),
|
|
||||||
name
|
|
||||||
),
|
|
||||||
"All arguments to an external command need to be string-compatible".into(),
|
"All arguments to an external command need to be string-compatible".into(),
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
@ -87,13 +83,13 @@ impl Command for External {
|
|||||||
// Example: one_arg may be something like ["ls" "-a"]
|
// Example: one_arg may be something like ["ls" "-a"]
|
||||||
// convert it to "ls" "-a"
|
// convert it to "ls" "-a"
|
||||||
for v in vals {
|
for v in vals {
|
||||||
spanned_args.push(value_as_spanned(v, &name.item)?);
|
spanned_args.push(value_as_spanned(v)?);
|
||||||
// for arguments in list, it's always treated as a whole arguments
|
// for arguments in list, it's always treated as a whole arguments
|
||||||
arg_keep_raw.push(true);
|
arg_keep_raw.push(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
spanned_args.push(value_as_spanned(val, &name.item)?);
|
spanned_args.push(value_as_spanned(val)?);
|
||||||
match one_arg_expr.expr {
|
match one_arg_expr.expr {
|
||||||
// refer to `parse_dollar_expr` function
|
// refer to `parse_dollar_expr` function
|
||||||
// the expression type of $variable_name, $"($variable_name)"
|
// the expression type of $variable_name, $"($variable_name)"
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use nu_ansi_term::{Color, Style};
|
use nu_ansi_term::{Color, Style};
|
||||||
use nu_color_config::{get_color_config, get_color_map};
|
use nu_color_config::{get_color_map, StyleComputer};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_explore::{
|
use nu_explore::{
|
||||||
run_pager,
|
run_pager,
|
||||||
@ -13,6 +11,7 @@ use nu_protocol::{
|
|||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// A `less` like program to render a [Value] as a table.
|
/// A `less` like program to render a [Value] as a table.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -70,7 +69,7 @@ impl Command for Explore {
|
|||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let nu_config = engine_state.get_config();
|
let nu_config = engine_state.get_config();
|
||||||
let color_hm = get_color_config(nu_config);
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
|
|
||||||
let mut config = nu_config.explore.clone();
|
let mut config = nu_config.explore.clone();
|
||||||
prepare_default_config(&mut config);
|
prepare_default_config(&mut config);
|
||||||
@ -81,7 +80,7 @@ impl Command for Explore {
|
|||||||
|
|
||||||
let style = style_from_config(&config);
|
let style = style_from_config(&config);
|
||||||
|
|
||||||
let mut config = PagerConfig::new(nu_config, &color_hm, config);
|
let mut config = PagerConfig::new(nu_config, &style_computer, config);
|
||||||
config.style = style;
|
config.style = style;
|
||||||
config.reverse = is_reverse;
|
config.reverse = is_reverse;
|
||||||
config.peek_value = peek_value;
|
config.peek_value = peek_value;
|
||||||
@ -89,7 +88,7 @@ impl Command for Explore {
|
|||||||
config.exit_esc = exit_esc;
|
config.exit_esc = exit_esc;
|
||||||
config.show_banner = show_banner;
|
config.show_banner = show_banner;
|
||||||
|
|
||||||
let result = run_pager(engine_state, stack, ctrlc, input, config);
|
let result = run_pager(engine_state, &mut stack.clone(), ctrlc, input, config);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(value)) => Ok(PipelineData::Value(value, None)),
|
Ok(Some(value)) => Ok(PipelineData::Value(value, None)),
|
||||||
@ -128,6 +127,8 @@ impl Command for Explore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For now, this doesn't use StyleComputer.
|
||||||
|
// As such, closures can't be given as styles for Explore.
|
||||||
fn is_need_banner(config: &HashMap<String, Value>) -> Option<bool> {
|
fn is_need_banner(config: &HashMap<String, Value>) -> Option<bool> {
|
||||||
config.get("help_banner").and_then(|v| v.as_bool().ok())
|
config.get("help_banner").and_then(|v| v.as_bool().ok())
|
||||||
}
|
}
|
||||||
@ -170,10 +171,6 @@ fn style_from_config(config: &HashMap<String, Value>) -> StyleConfig {
|
|||||||
style.cmd_bar_background = *s;
|
style.cmd_bar_background = *s;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = colors.get("highlight") {
|
|
||||||
style.highlight = *s;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(hm) = config.get("status").and_then(create_map) {
|
if let Some(hm) = config.get("status").and_then(create_map) {
|
||||||
let colors = get_color_map(&hm);
|
let colors = get_color_map(&hm);
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use lscolors::{LsColors, Style};
|
use lscolors::{LsColors, Style};
|
||||||
use nu_color_config::{color_from_hex, get_color_config, style_primitive};
|
use nu_color_config::color_from_hex;
|
||||||
|
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
||||||
use nu_engine::{column::get_columns, env_to_string, CallExt};
|
use nu_engine::{column::get_columns, env_to_string, CallExt};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, PathMember},
|
ast::{Call, PathMember},
|
||||||
@ -8,11 +9,12 @@ use nu_protocol::{
|
|||||||
PipelineData, PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape,
|
PipelineData, PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape,
|
||||||
TableIndexMode, Value,
|
TableIndexMode, Value,
|
||||||
};
|
};
|
||||||
use nu_table::{string_width, Alignment, Table as NuTable, TableConfig, TableTheme, TextStyle};
|
use nu_table::{string_width, Table as NuTable, TableConfig, TableTheme};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
|
use rayon::prelude::*;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{cmp::max, collections::HashMap, path::PathBuf, sync::atomic::AtomicBool};
|
use std::{cmp::max, path::PathBuf, sync::atomic::AtomicBool};
|
||||||
use terminal_size::{Height, Width};
|
use terminal_size::{Height, Width};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
@ -21,7 +23,6 @@ const STREAM_TIMEOUT_CHECK_INTERVAL: usize = 100;
|
|||||||
const INDEX_COLUMN_NAME: &str = "index";
|
const INDEX_COLUMN_NAME: &str = "index";
|
||||||
|
|
||||||
type NuText = (String, TextStyle);
|
type NuText = (String, TextStyle);
|
||||||
type NuColorMap = HashMap<String, nu_ansi_term::Style>;
|
|
||||||
|
|
||||||
fn get_width_param(width_param: Option<i64>) -> usize {
|
fn get_width_param(width_param: Option<i64>) -> usize {
|
||||||
if let Some(col) = width_param {
|
if let Some(col) = width_param {
|
||||||
@ -253,6 +254,7 @@ fn handle_table_command(
|
|||||||
metadata: None,
|
metadata: None,
|
||||||
trim_end_newline: false,
|
trim_end_newline: false,
|
||||||
}),
|
}),
|
||||||
|
// None of these two receive a StyleComputer because handle_row_stream() can produce it by itself using engine_state and stack.
|
||||||
PipelineData::Value(Value::List { vals, .. }, metadata) => handle_row_stream(
|
PipelineData::Value(Value::List { vals, .. }, metadata) => handle_row_stream(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
@ -272,10 +274,17 @@ fn handle_table_command(
|
|||||||
metadata,
|
metadata,
|
||||||
),
|
),
|
||||||
PipelineData::Value(Value::Record { cols, vals, span }, ..) => {
|
PipelineData::Value(Value::Record { cols, vals, span }, ..) => {
|
||||||
|
// Create a StyleComputer to compute styles for each value in the table.
|
||||||
|
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
||||||
let result = match table_view {
|
let result = match table_view {
|
||||||
TableView::General => {
|
TableView::General => build_general_table2(
|
||||||
build_general_table2(cols, vals, ctrlc.clone(), config, term_width)
|
style_computer,
|
||||||
}
|
cols,
|
||||||
|
vals,
|
||||||
|
ctrlc.clone(),
|
||||||
|
config,
|
||||||
|
term_width,
|
||||||
|
),
|
||||||
TableView::Expanded {
|
TableView::Expanded {
|
||||||
limit,
|
limit,
|
||||||
flatten,
|
flatten,
|
||||||
@ -288,13 +297,16 @@ fn handle_table_command(
|
|||||||
span,
|
span,
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
|
style_computer,
|
||||||
term_width,
|
term_width,
|
||||||
limit,
|
limit,
|
||||||
flatten,
|
flatten,
|
||||||
sep,
|
sep,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
TableView::Collapsed => build_collapsed_table(cols, vals, config, term_width),
|
TableView::Collapsed => {
|
||||||
|
build_collapsed_table(style_computer, cols, vals, config, term_width)
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let result = strip_output_color(result, config);
|
let result = strip_output_color(result, config);
|
||||||
@ -358,6 +370,7 @@ fn supported_table_modes() -> Vec<Value> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_collapsed_table(
|
fn build_collapsed_table(
|
||||||
|
style_computer: &StyleComputer,
|
||||||
cols: Vec<String>,
|
cols: Vec<String>,
|
||||||
vals: Vec<Value>,
|
vals: Vec<Value>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@ -369,9 +382,16 @@ fn build_collapsed_table(
|
|||||||
span: Span::new(0, 0),
|
span: Span::new(0, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
let color_hm = get_color_config(config);
|
|
||||||
let theme = load_theme_from_config(config);
|
let theme = load_theme_from_config(config);
|
||||||
let table = nu_table::NuTable::new(value, true, term_width, config, &color_hm, &theme, false);
|
let table = nu_table::NuTable::new(
|
||||||
|
value,
|
||||||
|
true,
|
||||||
|
term_width,
|
||||||
|
config,
|
||||||
|
style_computer,
|
||||||
|
&theme,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let table = table.draw();
|
let table = table.draw();
|
||||||
|
|
||||||
@ -379,6 +399,7 @@ fn build_collapsed_table(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn build_general_table2(
|
fn build_general_table2(
|
||||||
|
style_computer: &StyleComputer,
|
||||||
cols: Vec<String>,
|
cols: Vec<String>,
|
||||||
vals: Vec<Value>,
|
vals: Vec<Value>,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
@ -400,8 +421,7 @@ fn build_general_table2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data_len = data.len();
|
let data_len = data.len();
|
||||||
let color_hm = get_color_config(config);
|
let table_config = create_table_config(config, style_computer, data_len, false, false, false);
|
||||||
let table_config = create_table_config(config, &color_hm, data_len, false, false, false);
|
|
||||||
|
|
||||||
let table = NuTable::new(data, (data_len, 2));
|
let table = NuTable::new(data, (data_len, 2));
|
||||||
|
|
||||||
@ -410,6 +430,7 @@ fn build_general_table2(
|
|||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The table produced by `table -e`
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_expanded_table(
|
fn build_expanded_table(
|
||||||
cols: Vec<String>,
|
cols: Vec<String>,
|
||||||
@ -417,12 +438,12 @@ fn build_expanded_table(
|
|||||||
span: Span,
|
span: Span,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
style_computer: &StyleComputer,
|
||||||
term_width: usize,
|
term_width: usize,
|
||||||
expand_limit: Option<usize>,
|
expand_limit: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
) -> Result<Option<String>, ShellError> {
|
) -> Result<Option<String>, ShellError> {
|
||||||
let color_hm = get_color_config(config);
|
|
||||||
let theme = load_theme_from_config(config);
|
let theme = load_theme_from_config(config);
|
||||||
|
|
||||||
// calculate the width of a key part + the rest of table so we know the rest of the table width available for value.
|
// calculate the width of a key part + the rest of table so we know the rest of the table width available for value.
|
||||||
@ -431,7 +452,7 @@ fn build_expanded_table(
|
|||||||
let key_table = NuTable::new(vec![vec![key]], (1, 2));
|
let key_table = NuTable::new(vec![vec![key]], (1, 2));
|
||||||
let key_width = key_table
|
let key_width = key_table
|
||||||
.draw(
|
.draw(
|
||||||
create_table_config(config, &color_hm, 1, false, false, false),
|
create_table_config(config, style_computer, 1, false, false, false),
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
)
|
)
|
||||||
.map(|table| string_width(&table))
|
.map(|table| string_width(&table))
|
||||||
@ -454,7 +475,7 @@ fn build_expanded_table(
|
|||||||
let is_limited = matches!(expand_limit, Some(0));
|
let is_limited = matches!(expand_limit, Some(0));
|
||||||
let mut is_expanded = false;
|
let mut is_expanded = false;
|
||||||
let value = if is_limited {
|
let value = if is_limited {
|
||||||
value_to_styled_string(&value, config, &color_hm).0
|
value_to_styled_string(&value, config, style_computer).0
|
||||||
} else {
|
} else {
|
||||||
let deep = expand_limit.map(|i| i - 1);
|
let deep = expand_limit.map(|i| i - 1);
|
||||||
|
|
||||||
@ -466,7 +487,7 @@ fn build_expanded_table(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
span,
|
span,
|
||||||
&color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -476,14 +497,13 @@ fn build_expanded_table(
|
|||||||
match table {
|
match table {
|
||||||
Some((mut table, with_header, with_index)) => {
|
Some((mut table, with_header, with_index)) => {
|
||||||
// controll width via removing table columns.
|
// controll width via removing table columns.
|
||||||
let theme = load_theme_from_config(config);
|
|
||||||
table.truncate(remaining_width, &theme);
|
table.truncate(remaining_width, &theme);
|
||||||
|
|
||||||
is_expanded = true;
|
is_expanded = true;
|
||||||
|
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
&color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -499,7 +519,7 @@ fn build_expanded_table(
|
|||||||
None => {
|
None => {
|
||||||
// it means that the list is empty
|
// it means that the list is empty
|
||||||
let value = Value::List { vals, span };
|
let value = Value::List { vals, span };
|
||||||
value_to_styled_string(&value, config, &color_hm).0
|
value_to_styled_string(&value, config, style_computer).0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -510,6 +530,7 @@ fn build_expanded_table(
|
|||||||
span,
|
span,
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
|
style_computer,
|
||||||
remaining_width,
|
remaining_width,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
@ -525,7 +546,7 @@ fn build_expanded_table(
|
|||||||
let failed_value = value_to_styled_string(
|
let failed_value = value_to_styled_string(
|
||||||
&Value::Record { cols, vals, span },
|
&Value::Record { cols, vals, span },
|
||||||
config,
|
config,
|
||||||
&color_hm,
|
style_computer,
|
||||||
);
|
);
|
||||||
|
|
||||||
nu_table::wrap_string(&failed_value.0, remaining_width)
|
nu_table::wrap_string(&failed_value.0, remaining_width)
|
||||||
@ -533,7 +554,7 @@ fn build_expanded_table(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
let text = value_to_styled_string(&val, config, &color_hm).0;
|
let text = value_to_styled_string(&val, config, style_computer).0;
|
||||||
nu_table::wrap_string(&text, remaining_width)
|
nu_table::wrap_string(&text, remaining_width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -555,7 +576,7 @@ fn build_expanded_table(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let data_len = data.len();
|
let data_len = data.len();
|
||||||
let table_config = create_table_config(config, &color_hm, data_len, false, false, false);
|
let table_config = create_table_config(config, style_computer, data_len, false, false, false);
|
||||||
let table = NuTable::new(data, (data_len, 2));
|
let table = NuTable::new(data, (data_len, 2));
|
||||||
|
|
||||||
let table_s = table.clone().draw(table_config.clone(), term_width);
|
let table_s = table.clone().draw(table_config.clone(), term_width);
|
||||||
@ -700,7 +721,10 @@ fn handle_row_stream(
|
|||||||
stdout: Some(RawStream::new(
|
stdout: Some(RawStream::new(
|
||||||
Box::new(PagingTableCreator {
|
Box::new(PagingTableCreator {
|
||||||
row_offset,
|
row_offset,
|
||||||
config: engine_state.get_config().clone(),
|
// These are passed in as a way to have PagingTable create StyleComputers
|
||||||
|
// for the values it outputs. Because engine_state is passed in, config doesn't need to.
|
||||||
|
engine_state: engine_state.clone(),
|
||||||
|
stack: stack.clone(),
|
||||||
ctrlc: ctrlc.clone(),
|
ctrlc: ctrlc.clone(),
|
||||||
head,
|
head,
|
||||||
stream,
|
stream,
|
||||||
@ -742,18 +766,71 @@ fn make_clickable_link(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
// convert_to_table() defers all its style computations so that they can be run in parallel using par_extend().
|
||||||
|
// This structure holds the intermediate computations.
|
||||||
|
// Currently, the other table forms don't use this.
|
||||||
|
// Because of how table-specific this is, I don't think this can be pushed into StyleComputer itself.
|
||||||
|
enum DeferredStyleComputation {
|
||||||
|
Value { value: Value },
|
||||||
|
Header { text: String },
|
||||||
|
RowIndex { text: String },
|
||||||
|
Empty {},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeferredStyleComputation {
|
||||||
|
// This is only run inside a par_extend().
|
||||||
|
fn compute(&self, config: &Config, style_computer: &StyleComputer) -> NuText {
|
||||||
|
match self {
|
||||||
|
DeferredStyleComputation::Value { value } => {
|
||||||
|
match value {
|
||||||
|
// Float precision is required here.
|
||||||
|
Value::Float { val, .. } => (
|
||||||
|
format!("{:.prec$}", val, prec = config.float_precision as usize),
|
||||||
|
style_computer.style_primitive(value),
|
||||||
|
),
|
||||||
|
_ => (
|
||||||
|
value.into_abbreviated_string(config),
|
||||||
|
style_computer.style_primitive(value),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DeferredStyleComputation::Header { text } => (
|
||||||
|
text.clone(),
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Center,
|
||||||
|
style_computer
|
||||||
|
.compute("header", &Value::string(text.as_str(), Span::unknown())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DeferredStyleComputation::RowIndex { text } => (
|
||||||
|
text.clone(),
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Right,
|
||||||
|
style_computer
|
||||||
|
.compute("row_index", &Value::string(text.as_str(), Span::unknown())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
DeferredStyleComputation::Empty {} => (
|
||||||
|
"❎".into(),
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Right,
|
||||||
|
style_computer.compute("empty", &Value::nothing(Span::unknown())),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn convert_to_table(
|
fn convert_to_table(
|
||||||
row_offset: usize,
|
row_offset: usize,
|
||||||
input: &[Value],
|
input: &[Value],
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
) -> Result<Option<(NuTable, bool, bool)>, ShellError> {
|
) -> Result<Option<(NuTable, bool, bool)>, ShellError> {
|
||||||
let mut headers = get_columns(input);
|
let mut headers = get_columns(input);
|
||||||
let mut input = input.iter().peekable();
|
let mut input = input.iter().peekable();
|
||||||
let float_precision = config.float_precision as usize;
|
|
||||||
let with_index = match config.table_index_mode {
|
let with_index = match config.table_index_mode {
|
||||||
TableIndexMode::Always => true,
|
TableIndexMode::Always => true,
|
||||||
TableIndexMode::Never => false,
|
TableIndexMode::Never => false,
|
||||||
@ -764,7 +841,9 @@ fn convert_to_table(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !headers.is_empty() && with_index {
|
let with_header = !headers.is_empty();
|
||||||
|
|
||||||
|
if with_header && with_index {
|
||||||
headers.insert(0, "#".into());
|
headers.insert(0, "#".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,26 +852,18 @@ fn convert_to_table(
|
|||||||
let headers: Vec<_> = headers
|
let headers: Vec<_> = headers
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|header| header != INDEX_COLUMN_NAME)
|
.filter(|header| header != INDEX_COLUMN_NAME)
|
||||||
.map(|text| {
|
.map(|text| DeferredStyleComputation::Header { text })
|
||||||
NuTable::create_cell(
|
|
||||||
text,
|
|
||||||
TextStyle {
|
|
||||||
alignment: Alignment::Center,
|
|
||||||
color_style: Some(color_hm["header"]),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let with_header = !headers.is_empty();
|
|
||||||
let mut count_columns = headers.len();
|
let mut count_columns = headers.len();
|
||||||
|
|
||||||
let mut data: Vec<Vec<_>> = if headers.is_empty() {
|
let mut data: Vec<Vec<_>> = if !with_header {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
vec![headers]
|
vec![headers]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Turn each item of each row into a DeferredStyleComputation for that item.
|
||||||
for (row_num, item) in input.enumerate() {
|
for (row_num, item) in input.enumerate() {
|
||||||
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -812,26 +883,36 @@ fn convert_to_table(
|
|||||||
}
|
}
|
||||||
.unwrap_or_else(|| (row_num + row_offset).to_string());
|
.unwrap_or_else(|| (row_num + row_offset).to_string());
|
||||||
|
|
||||||
let value = make_index_string(text, color_hm);
|
row.push(DeferredStyleComputation::RowIndex { text });
|
||||||
let value = NuTable::create_cell(value.0, value.1);
|
|
||||||
|
|
||||||
row.push(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !with_header {
|
if !with_header {
|
||||||
let text = item.into_abbreviated_string(config);
|
row.push(DeferredStyleComputation::Value {
|
||||||
let text_type = item.get_type().to_string();
|
value: item.clone(),
|
||||||
let value = make_styled_string(text, &text_type, color_hm, float_precision);
|
});
|
||||||
let value = NuTable::create_cell(value.0, value.1);
|
|
||||||
|
|
||||||
row.push(value);
|
|
||||||
} else {
|
} else {
|
||||||
let skip_num = usize::from(with_index);
|
let skip_num = usize::from(with_index);
|
||||||
|
// data[0] is used here because headers (the direct reference to it) has been moved.
|
||||||
for header in data[0].iter().skip(skip_num) {
|
for header in data[0].iter().skip(skip_num) {
|
||||||
let value =
|
if let DeferredStyleComputation::Header { text } = header {
|
||||||
create_table2_entry_basic(item, header.as_ref(), head, config, color_hm);
|
row.push(match item {
|
||||||
let value = NuTable::create_cell(value.0, value.1);
|
Value::Record { .. } => {
|
||||||
row.push(value);
|
let path = PathMember::String {
|
||||||
|
val: text.clone(),
|
||||||
|
span: head,
|
||||||
|
};
|
||||||
|
let val = item.clone().follow_cell_path(&[path], false);
|
||||||
|
|
||||||
|
match val {
|
||||||
|
Ok(val) => DeferredStyleComputation::Value { value: val },
|
||||||
|
Err(_) => DeferredStyleComputation::Empty {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => DeferredStyleComputation::Value {
|
||||||
|
value: item.clone(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -840,8 +921,25 @@ fn convert_to_table(
|
|||||||
data.push(row);
|
data.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
let count_rows = data.len();
|
// All the computations are parallelised here.
|
||||||
let table = NuTable::new(data, (count_rows, count_columns));
|
// NOTE: It's currently not possible to Ctrl-C out of this...
|
||||||
|
let mut cells: Vec<Vec<_>> = Vec::with_capacity(data.len());
|
||||||
|
data.into_par_iter()
|
||||||
|
.map(|row| {
|
||||||
|
let mut new_row = Vec::with_capacity(row.len());
|
||||||
|
row.into_par_iter()
|
||||||
|
.map(|deferred| {
|
||||||
|
let pair = deferred.compute(config, style_computer);
|
||||||
|
|
||||||
|
NuTable::create_cell(pair.0, pair.1)
|
||||||
|
})
|
||||||
|
.collect_into_vec(&mut new_row);
|
||||||
|
new_row
|
||||||
|
})
|
||||||
|
.collect_into_vec(&mut cells);
|
||||||
|
|
||||||
|
let count_rows = cells.len();
|
||||||
|
let table = NuTable::new(cells, (count_rows, count_columns));
|
||||||
|
|
||||||
Ok(Some((table, with_header, with_index)))
|
Ok(Some((table, with_header, with_index)))
|
||||||
}
|
}
|
||||||
@ -854,7 +952,7 @@ fn convert_to_table2<'a>(
|
|||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -903,7 +1001,10 @@ fn convert_to_table2<'a>(
|
|||||||
let mut column_width = 0;
|
let mut column_width = 0;
|
||||||
|
|
||||||
if with_header {
|
if with_header {
|
||||||
data[0].push(NuTable::create_cell("#", header_style(color_hm)));
|
data[0].push(NuTable::create_cell(
|
||||||
|
"#",
|
||||||
|
header_style(style_computer, String::from("#")),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (row, item) in input.clone().into_iter().enumerate() {
|
for (row, item) in input.clone().into_iter().enumerate() {
|
||||||
@ -920,7 +1021,7 @@ fn convert_to_table2<'a>(
|
|||||||
.then(|| lookup_index_value(item, config).unwrap_or_else(|| index.to_string()))
|
.then(|| lookup_index_value(item, config).unwrap_or_else(|| index.to_string()))
|
||||||
.unwrap_or_else(|| index.to_string());
|
.unwrap_or_else(|| index.to_string());
|
||||||
|
|
||||||
let value = make_index_string(text, color_hm);
|
let value = make_index_string(text, style_computer);
|
||||||
|
|
||||||
let width = string_width(&value.0);
|
let width = string_width(&value.0);
|
||||||
column_width = max(column_width, width);
|
column_width = max(column_width, width);
|
||||||
@ -952,7 +1053,7 @@ fn convert_to_table2<'a>(
|
|||||||
item,
|
item,
|
||||||
config,
|
config,
|
||||||
&ctrlc,
|
&ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -994,7 +1095,10 @@ fn convert_to_table2<'a>(
|
|||||||
|
|
||||||
let mut column_width = string_width(&header);
|
let mut column_width = string_width(&header);
|
||||||
|
|
||||||
data[0].push(NuTable::create_cell(&header, header_style(color_hm)));
|
data[0].push(NuTable::create_cell(
|
||||||
|
header.clone(),
|
||||||
|
header_style(style_computer, header.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
for (row, item) in input.clone().into_iter().enumerate() {
|
for (row, item) in input.clone().into_iter().enumerate() {
|
||||||
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
||||||
@ -1007,11 +1111,11 @@ fn convert_to_table2<'a>(
|
|||||||
|
|
||||||
let value = create_table2_entry(
|
let value = create_table2_entry(
|
||||||
item,
|
item,
|
||||||
&header,
|
header.as_str(),
|
||||||
head,
|
head,
|
||||||
config,
|
config,
|
||||||
&ctrlc,
|
&ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -1039,7 +1143,7 @@ fn convert_to_table2<'a>(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = create_table2_entry_basic(item, &header, head, config, color_hm);
|
let value = create_table2_entry_basic(item, &header, head, config, style_computer);
|
||||||
let value = wrap_nu_text(value, available_width);
|
let value = wrap_nu_text(value, available_width);
|
||||||
|
|
||||||
let value_width = string_width(&value.0);
|
let value_width = string_width(&value.0);
|
||||||
@ -1064,7 +1168,7 @@ fn convert_to_table2<'a>(
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = create_table2_entry_basic(item, &header, head, config, color_hm);
|
let value = create_table2_entry_basic(item, &header, head, config, style_computer);
|
||||||
let value = wrap_nu_text(value, OK_CELL_CONTENT_WIDTH);
|
let value = wrap_nu_text(value, OK_CELL_CONTENT_WIDTH);
|
||||||
|
|
||||||
let value = NuTable::create_cell(value.0, value.1);
|
let value = NuTable::create_cell(value.0, value.1);
|
||||||
@ -1134,10 +1238,11 @@ fn lookup_index_value(item: &Value, config: &Config) -> Option<String> {
|
|||||||
.map(|value| value.into_string("", config))
|
.map(|value| value.into_string("", config))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_style(color_hm: &NuColorMap) -> TextStyle {
|
fn header_style(style_computer: &StyleComputer, header: String) -> TextStyle {
|
||||||
|
let style = style_computer.compute("header", &Value::string(header.as_str(), Span::unknown()));
|
||||||
TextStyle {
|
TextStyle {
|
||||||
alignment: Alignment::Center,
|
alignment: Alignment::Center,
|
||||||
color_style: Some(color_hm["header"]),
|
color_style: Some(style),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1147,7 +1252,7 @@ fn create_table2_entry_basic(
|
|||||||
header: &str,
|
header: &str,
|
||||||
head: Span,
|
head: Span,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
match item {
|
match item {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@ -1156,11 +1261,11 @@ fn create_table2_entry_basic(
|
|||||||
let val = item.clone().follow_cell_path(&[path], false);
|
let val = item.clone().follow_cell_path(&[path], false);
|
||||||
|
|
||||||
match val {
|
match val {
|
||||||
Ok(val) => value_to_styled_string(&val, config, color_hm),
|
Ok(val) => value_to_styled_string(&val, config, style_computer),
|
||||||
Err(_) => error_sign(color_hm),
|
Err(_) => error_sign(style_computer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => value_to_styled_string(item, config, color_hm),
|
_ => value_to_styled_string(item, config, style_computer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1171,7 +1276,7 @@ fn create_table2_entry(
|
|||||||
head: Span,
|
head: Span,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
ctrlc: &Option<Arc<AtomicBool>>,
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -1188,20 +1293,20 @@ fn create_table2_entry(
|
|||||||
&val,
|
&val,
|
||||||
config,
|
config,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
width,
|
width,
|
||||||
),
|
),
|
||||||
Err(_) => wrap_nu_text(error_sign(color_hm), width),
|
Err(_) => wrap_nu_text(error_sign(style_computer), width),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => convert_to_table2_entry(
|
_ => convert_to_table2_entry(
|
||||||
item,
|
item,
|
||||||
config,
|
config,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -1210,8 +1315,8 @@ fn create_table2_entry(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_sign(color_hm: &HashMap<String, nu_ansi_term::Style>) -> (String, TextStyle) {
|
fn error_sign(style_computer: &StyleComputer) -> (String, TextStyle) {
|
||||||
make_styled_string(String::from("❎"), "empty", color_hm, 0)
|
make_styled_string(style_computer, String::from("❎"), None, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_nu_text(mut text: NuText, width: usize) -> NuText {
|
fn wrap_nu_text(mut text: NuText, width: usize) -> NuText {
|
||||||
@ -1224,7 +1329,9 @@ fn convert_to_table2_entry(
|
|||||||
item: &Value,
|
item: &Value,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
ctrlc: &Option<Arc<AtomicBool>>,
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
color_hm: &NuColorMap,
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -1232,13 +1339,13 @@ fn convert_to_table2_entry(
|
|||||||
) -> NuText {
|
) -> NuText {
|
||||||
let is_limit_reached = matches!(deep, Some(0));
|
let is_limit_reached = matches!(deep, Some(0));
|
||||||
if is_limit_reached {
|
if is_limit_reached {
|
||||||
return wrap_nu_text(value_to_styled_string(item, config, color_hm), width);
|
return wrap_nu_text(value_to_styled_string(item, config, style_computer), width);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &item {
|
match &item {
|
||||||
Value::Record { span, cols, vals } => {
|
Value::Record { span, cols, vals } => {
|
||||||
if cols.is_empty() && vals.is_empty() {
|
if cols.is_empty() && vals.is_empty() {
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
} else {
|
} else {
|
||||||
let table = convert_to_table2(
|
let table = convert_to_table2(
|
||||||
0,
|
0,
|
||||||
@ -1246,7 +1353,7 @@ fn convert_to_table2_entry(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
*span,
|
*span,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep.map(|i| i - 1),
|
deep.map(|i| i - 1),
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -1257,7 +1364,7 @@ fn convert_to_table2_entry(
|
|||||||
table.and_then(|(table, with_header, with_index)| {
|
table.and_then(|(table, with_header, with_index)| {
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -1272,7 +1379,7 @@ fn convert_to_table2_entry(
|
|||||||
(table, TextStyle::default())
|
(table, TextStyle::default())
|
||||||
} else {
|
} else {
|
||||||
// error so back down to the default
|
// error so back down to the default
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1283,7 +1390,7 @@ fn convert_to_table2_entry(
|
|||||||
|
|
||||||
if flatten && is_simple_list {
|
if flatten && is_simple_list {
|
||||||
wrap_nu_text(
|
wrap_nu_text(
|
||||||
convert_value_list_to_string(vals, config, color_hm, flatten_sep),
|
convert_value_list_to_string(vals, config, style_computer, flatten_sep),
|
||||||
width,
|
width,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -1293,7 +1400,7 @@ fn convert_to_table2_entry(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
*span,
|
*span,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep.map(|i| i - 1),
|
deep.map(|i| i - 1),
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -1304,7 +1411,7 @@ fn convert_to_table2_entry(
|
|||||||
table.and_then(|(table, with_header, with_index)| {
|
table.and_then(|(table, with_header, with_index)| {
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -1320,23 +1427,25 @@ fn convert_to_table2_entry(
|
|||||||
} else {
|
} else {
|
||||||
// error so back down to the default
|
// error so back down to the default
|
||||||
|
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => wrap_nu_text(value_to_styled_string(item, config, color_hm), width), // unknown type.
|
_ => wrap_nu_text(value_to_styled_string(item, config, style_computer), width), // unknown type.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value_list_to_string(
|
fn convert_value_list_to_string(
|
||||||
vals: &[Value],
|
vals: &[Value],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &NuColorMap,
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
for value in vals {
|
for value in vals {
|
||||||
let (text, _) = value_to_styled_string(value, config, color_hm);
|
let (text, _) = value_to_styled_string(value, config, style_computer);
|
||||||
|
|
||||||
buf.push(text);
|
buf.push(text);
|
||||||
}
|
}
|
||||||
@ -1344,39 +1453,58 @@ fn convert_value_list_to_string(
|
|||||||
(text, TextStyle::default())
|
(text, TextStyle::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_to_styled_string(value: &Value, config: &Config, color_hm: &NuColorMap) -> NuText {
|
fn value_to_styled_string(
|
||||||
|
value: &Value,
|
||||||
|
config: &Config,
|
||||||
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
|
) -> NuText {
|
||||||
let float_precision = config.float_precision as usize;
|
let float_precision = config.float_precision as usize;
|
||||||
make_styled_string(
|
make_styled_string(
|
||||||
|
style_computer,
|
||||||
value.into_abbreviated_string(config),
|
value.into_abbreviated_string(config),
|
||||||
&value.get_type().to_string(),
|
Some(value),
|
||||||
color_hm,
|
|
||||||
float_precision,
|
float_precision,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_styled_string(
|
fn make_styled_string(
|
||||||
|
style_computer: &StyleComputer,
|
||||||
text: String,
|
text: String,
|
||||||
text_type: &str,
|
value: Option<&Value>, // None represents table holes.
|
||||||
color_hm: &NuColorMap,
|
|
||||||
float_precision: usize,
|
float_precision: usize,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
if text_type == "float" {
|
match value {
|
||||||
// set dynamic precision from config
|
Some(value) => {
|
||||||
let precise_number = match convert_with_precision(&text, float_precision) {
|
match value {
|
||||||
Ok(num) => num,
|
Value::Float { .. } => {
|
||||||
Err(e) => e.to_string(),
|
// set dynamic precision from config
|
||||||
};
|
let precise_number = match convert_with_precision(&text, float_precision) {
|
||||||
(precise_number, style_primitive(text_type, color_hm))
|
Ok(num) => num,
|
||||||
} else {
|
Err(e) => e.to_string(),
|
||||||
(text, style_primitive(text_type, color_hm))
|
};
|
||||||
|
(precise_number, style_computer.style_primitive(value))
|
||||||
|
}
|
||||||
|
_ => (text, style_computer.style_primitive(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Though holes are not the same as null, the closure for "empty" is passed a null anyway.
|
||||||
|
(
|
||||||
|
text,
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Center,
|
||||||
|
style_computer.compute("empty", &Value::nothing(Span::unknown())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_index_string(text: String, color_hm: &NuColorMap) -> NuText {
|
fn make_index_string(text: String, style_computer: &StyleComputer) -> NuText {
|
||||||
let style = TextStyle::new()
|
let style = style_computer.compute("row_index", &Value::string(text.as_str(), Span::unknown()));
|
||||||
.alignment(Alignment::Right)
|
(text, TextStyle::with_style(Alignment::Right, style))
|
||||||
.style(color_hm["row_index"]);
|
|
||||||
(text, style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellError> {
|
fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellError> {
|
||||||
@ -1399,8 +1527,9 @@ fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellEr
|
|||||||
struct PagingTableCreator {
|
struct PagingTableCreator {
|
||||||
head: Span,
|
head: Span,
|
||||||
stream: ListStream,
|
stream: ListStream,
|
||||||
|
engine_state: EngineState,
|
||||||
|
stack: Stack,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: Config,
|
|
||||||
row_offset: usize,
|
row_offset: usize,
|
||||||
width_param: Option<i64>,
|
width_param: Option<i64>,
|
||||||
view: TableView,
|
view: TableView,
|
||||||
@ -1408,7 +1537,7 @@ struct PagingTableCreator {
|
|||||||
|
|
||||||
impl PagingTableCreator {
|
impl PagingTableCreator {
|
||||||
fn build_extended(
|
fn build_extended(
|
||||||
&self,
|
&mut self,
|
||||||
batch: &[Value],
|
batch: &[Value],
|
||||||
limit: Option<usize>,
|
limit: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
@ -1418,17 +1547,18 @@ impl PagingTableCreator {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let config = self.engine_state.get_config();
|
||||||
|
let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||||
let term_width = get_width_param(self.width_param);
|
let term_width = get_width_param(self.width_param);
|
||||||
let color_hm = get_color_config(&self.config);
|
let theme = load_theme_from_config(config);
|
||||||
let theme = load_theme_from_config(&self.config);
|
|
||||||
|
|
||||||
let table = convert_to_table2(
|
let table = convert_to_table2(
|
||||||
self.row_offset,
|
self.row_offset,
|
||||||
batch.iter(),
|
batch.iter(),
|
||||||
self.ctrlc.clone(),
|
self.ctrlc.clone(),
|
||||||
&self.config,
|
config,
|
||||||
self.head,
|
self.head,
|
||||||
&color_hm,
|
&style_computer,
|
||||||
limit,
|
limit,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_separator.as_deref().unwrap_or(" "),
|
flatten_separator.as_deref().unwrap_or(" "),
|
||||||
@ -1443,8 +1573,8 @@ impl PagingTableCreator {
|
|||||||
table.truncate(term_width, &theme);
|
table.truncate(term_width, &theme);
|
||||||
|
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
&self.config,
|
config,
|
||||||
&color_hm,
|
&style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -1476,16 +1606,17 @@ impl PagingTableCreator {
|
|||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_collapsed(&self, batch: Vec<Value>) -> Result<Option<String>, ShellError> {
|
fn build_collapsed(&mut self, batch: Vec<Value>) -> Result<Option<String>, ShellError> {
|
||||||
if batch.is_empty() {
|
if batch.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let color_hm = get_color_config(&self.config);
|
let config = self.engine_state.get_config();
|
||||||
let theme = load_theme_from_config(&self.config);
|
let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||||
|
let theme = load_theme_from_config(config);
|
||||||
let term_width = get_width_param(self.width_param);
|
let term_width = get_width_param(self.width_param);
|
||||||
let need_footer = matches!(self.config.footer_mode, FooterMode::RowCount(limit) if batch.len() as u64 > limit)
|
let need_footer = matches!(config.footer_mode, FooterMode::RowCount(limit) if batch.len() as u64 > limit)
|
||||||
|| matches!(self.config.footer_mode, FooterMode::Always);
|
|| matches!(config.footer_mode, FooterMode::Always);
|
||||||
let value = Value::List {
|
let value = Value::List {
|
||||||
vals: batch,
|
vals: batch,
|
||||||
span: Span::new(0, 0),
|
span: Span::new(0, 0),
|
||||||
@ -1495,8 +1626,8 @@ impl PagingTableCreator {
|
|||||||
value,
|
value,
|
||||||
true,
|
true,
|
||||||
term_width,
|
term_width,
|
||||||
&self.config,
|
config,
|
||||||
&color_hm,
|
&style_computer,
|
||||||
&theme,
|
&theme,
|
||||||
need_footer,
|
need_footer,
|
||||||
);
|
);
|
||||||
@ -1504,17 +1635,17 @@ impl PagingTableCreator {
|
|||||||
Ok(table.draw())
|
Ok(table.draw())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_general(&self, batch: &[Value]) -> Result<Option<String>, ShellError> {
|
fn build_general(&mut self, batch: &[Value]) -> Result<Option<String>, ShellError> {
|
||||||
let term_width = get_width_param(self.width_param);
|
let term_width = get_width_param(self.width_param);
|
||||||
let color_hm = get_color_config(&self.config);
|
let config = &self.engine_state.get_config();
|
||||||
|
let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack);
|
||||||
let table = convert_to_table(
|
let table = convert_to_table(
|
||||||
self.row_offset,
|
self.row_offset,
|
||||||
batch,
|
batch,
|
||||||
self.ctrlc.clone(),
|
self.ctrlc.clone(),
|
||||||
&self.config,
|
config,
|
||||||
self.head,
|
self.head,
|
||||||
&color_hm,
|
&style_computer,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (table, with_header, with_index) = match table {
|
let (table, with_header, with_index) = match table {
|
||||||
@ -1523,8 +1654,8 @@ impl PagingTableCreator {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
&self.config,
|
config,
|
||||||
&color_hm,
|
&style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -1584,8 +1715,8 @@ impl Iterator for PagingTableCreator {
|
|||||||
|
|
||||||
match table {
|
match table {
|
||||||
Ok(Some(table)) => {
|
Ok(Some(table)) => {
|
||||||
let table =
|
let table = strip_output_color(Some(table), self.engine_state.get_config())
|
||||||
strip_output_color(Some(table), &self.config).expect("must never happen");
|
.expect("must never happen");
|
||||||
|
|
||||||
let mut bytes = table.as_bytes().to_vec();
|
let mut bytes = table.as_bytes().to_vec();
|
||||||
bytes.push(b'\n'); // nu-table tables don't come with a newline on the end
|
bytes.push(b'\n'); // nu-table tables don't come with a newline on the end
|
||||||
@ -1686,7 +1817,7 @@ fn strip_output_color(output: Option<String>, config: &Config) -> Option<String>
|
|||||||
|
|
||||||
fn create_table_config(
|
fn create_table_config(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer: &StyleComputer,
|
||||||
count_records: usize,
|
count_records: usize,
|
||||||
with_header: bool,
|
with_header: bool,
|
||||||
with_index: bool,
|
with_index: bool,
|
||||||
@ -1697,10 +1828,7 @@ fn create_table_config(
|
|||||||
|
|
||||||
let mut table_cfg = TableConfig::new(theme, with_header, with_index, append_footer);
|
let mut table_cfg = TableConfig::new(theme, with_header, with_index, append_footer);
|
||||||
|
|
||||||
let sep_color = lookup_separator_color(color_hm);
|
table_cfg = table_cfg.splitline_style(lookup_separator_color(style_computer));
|
||||||
if let Some(color) = sep_color {
|
|
||||||
table_cfg = table_cfg.splitline_style(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if expand {
|
if expand {
|
||||||
table_cfg = table_cfg.expand();
|
table_cfg = table_cfg.expand();
|
||||||
@ -1709,10 +1837,8 @@ fn create_table_config(
|
|||||||
table_cfg.trim(config.trim_strategy.clone())
|
table_cfg.trim(config.trim_strategy.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_separator_color(
|
fn lookup_separator_color(style_computer: &StyleComputer) -> nu_ansi_term::Style {
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer.compute("separator", &Value::nothing(Span::unknown()))
|
||||||
) -> Option<nu_ansi_term::Style> {
|
|
||||||
color_hm.get("separator").cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool {
|
fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool {
|
||||||
|
@ -129,7 +129,7 @@ impl ConfigView {
|
|||||||
match self.format {
|
match self.format {
|
||||||
ConfigFormat::Table => {
|
ConfigFormat::Table => {
|
||||||
let value = map_into_value(config.config.clone());
|
let value = map_into_value(config.config.clone());
|
||||||
try_build_table(None, config.nu_config, config.color_hm, value)
|
try_build_table(None, config.nu_config, config.style_computer, value)
|
||||||
}
|
}
|
||||||
ConfigFormat::Nu => nu_json::to_string(&config.config).unwrap_or_default(),
|
ConfigFormat::Nu => nu_json::to_string(&config.config).unwrap_or_default(),
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use std::{io::Result, vec};
|
use std::{io::Result, vec};
|
||||||
|
|
||||||
use nu_color_config::get_color_config;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Value,
|
Value,
|
||||||
@ -71,18 +71,18 @@ impl ViewCommand for ExpandCmd {
|
|||||||
fn spawn(
|
fn spawn(
|
||||||
&mut self,
|
&mut self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
value: Option<Value>,
|
value: Option<Value>,
|
||||||
) -> Result<Self::View> {
|
) -> Result<Self::View> {
|
||||||
let value = value
|
let value = value
|
||||||
.map(|v| convert_value_to_string(v, engine_state))
|
.map(|v| convert_value_to_string(v, engine_state, stack))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
Ok(Preview::new(&value))
|
Ok(Preview::new(&value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value_to_string(value: Value, engine_state: &EngineState) -> String {
|
fn convert_value_to_string(value: Value, engine_state: &EngineState, stack: &mut Stack) -> String {
|
||||||
let (cols, vals) = collect_input(value.clone());
|
let (cols, vals) = collect_input(value.clone());
|
||||||
|
|
||||||
let has_no_head = cols.is_empty() || (cols.len() == 1 && cols[0].is_empty());
|
let has_no_head = cols.is_empty() || (cols.len() == 1 && cols[0].is_empty());
|
||||||
@ -93,8 +93,8 @@ fn convert_value_to_string(value: Value, engine_state: &EngineState) -> String {
|
|||||||
} else {
|
} else {
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let color_hm = get_color_config(config);
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
|
|
||||||
nu_common::try_build_table(ctrlc, config, &color_hm, value)
|
nu_common::try_build_table(ctrlc, config, &style_computer, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
79
crates/nu-explore/src/commands/preview.rs
Normal file
79
crates/nu-explore/src/commands/preview.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
use nu_protocol::{
|
||||||
|
engine::{EngineState, Stack},
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
use std::io::Result;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
nu_common::{self, collect_input},
|
||||||
|
views::Preview,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{HelpManual, ViewCommand};
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct PreviewCmd;
|
||||||
|
|
||||||
|
impl PreviewCmd {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PreviewCmd {
|
||||||
|
pub const NAME: &'static str = "preview";
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewCommand for PreviewCmd {
|
||||||
|
type View = Preview;
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
Self::NAME
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &'static str {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
fn help(&self) -> Option<HelpManual> {
|
||||||
|
Some(HelpManual {
|
||||||
|
name: "preview",
|
||||||
|
description:
|
||||||
|
"View the currently selected cell's data using the `table` Nushell command",
|
||||||
|
arguments: vec![],
|
||||||
|
examples: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(&mut self, _: &str) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn(
|
||||||
|
&mut self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
value: Option<Value>,
|
||||||
|
) -> Result<Self::View> {
|
||||||
|
let value = match value {
|
||||||
|
Some(value) => {
|
||||||
|
let (cols, vals) = collect_input(value.clone());
|
||||||
|
|
||||||
|
let has_no_head = cols.is_empty() || (cols.len() == 1 && cols[0].is_empty());
|
||||||
|
let has_single_value = vals.len() == 1 && vals[0].len() == 1;
|
||||||
|
if !has_no_head && has_single_value {
|
||||||
|
let config = engine_state.get_config();
|
||||||
|
vals[0][0].into_abbreviated_string(config)
|
||||||
|
} else {
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
|
nu_common::try_build_table(engine_state, stack, ctrlc, config, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Preview::new(&value))
|
||||||
|
}
|
||||||
|
}
|
@ -3,20 +3,16 @@ mod string;
|
|||||||
mod table;
|
mod table;
|
||||||
mod value;
|
mod value;
|
||||||
|
|
||||||
use std::{
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
collections::HashMap,
|
|
||||||
sync::{atomic::AtomicBool, Arc},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
use nu_color_config::TextStyle;
|
||||||
use nu_protocol::Value;
|
use nu_protocol::Value;
|
||||||
use nu_table::TextStyle;
|
|
||||||
|
|
||||||
pub use nu_ansi_term::{Color as NuColor, Style as NuStyle};
|
pub use nu_ansi_term::{Color as NuColor, Style as NuStyle};
|
||||||
pub use nu_protocol::{Config as NuConfig, Span as NuSpan};
|
pub use nu_protocol::{Config as NuConfig, Span as NuSpan};
|
||||||
|
|
||||||
pub type NuText = (String, TextStyle);
|
pub type NuText = (String, TextStyle);
|
||||||
pub type CtrlC = Option<Arc<AtomicBool>>;
|
pub type CtrlC = Option<Arc<AtomicBool>>;
|
||||||
pub type NuStyleTable = HashMap<String, NuStyle>;
|
|
||||||
|
|
||||||
pub use command::{is_ignored_command, run_command_with_value, run_nu_command};
|
pub use command::{is_ignored_command, run_command_with_value, run_nu_command};
|
||||||
pub use string::truncate_str;
|
pub use string::truncate_str;
|
||||||
|
@ -1,33 +1,31 @@
|
|||||||
use nu_color_config::{get_color_config, style_primitive};
|
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
||||||
use nu_engine::column::get_columns;
|
use nu_engine::column::get_columns;
|
||||||
use nu_protocol::FooterMode;
|
use nu_protocol::FooterMode;
|
||||||
use nu_protocol::{ast::PathMember, Config, ShellError, Span, TableIndexMode, Value};
|
use nu_protocol::{ast::PathMember, Config, ShellError, Span, TableIndexMode, Value};
|
||||||
use nu_table::{string_width, Alignment, Table as NuTable, TableConfig, TableTheme, TextStyle};
|
use nu_table::{string_width, Table as NuTable, TableConfig, TableTheme};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
cmp::max,
|
cmp::max,
|
||||||
collections::HashMap,
|
|
||||||
sync::atomic::{AtomicBool, Ordering},
|
sync::atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
const INDEX_COLUMN_NAME: &str = "index";
|
const INDEX_COLUMN_NAME: &str = "index";
|
||||||
|
|
||||||
type NuText = (String, TextStyle);
|
type NuText = (String, TextStyle);
|
||||||
type NuColorMap = HashMap<String, nu_ansi_term::Style>;
|
use crate::nu_common::NuConfig;
|
||||||
use crate::nu_common::{NuConfig, NuStyleTable};
|
|
||||||
|
|
||||||
pub fn try_build_table(
|
pub fn try_build_table(
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &NuConfig,
|
config: &NuConfig,
|
||||||
color_hm: &NuStyleTable,
|
style_computer: &StyleComputer,
|
||||||
value: Value,
|
value: Value,
|
||||||
) -> String {
|
) -> String {
|
||||||
match value {
|
match value {
|
||||||
Value::List { vals, span } => try_build_list(vals, &ctrlc, config, span, color_hm),
|
Value::List { vals, span } => try_build_list(vals, &ctrlc, config, span, style_computer),
|
||||||
Value::Record { cols, vals, span } => {
|
Value::Record { cols, vals, span } => {
|
||||||
try_build_map(cols, vals, span, ctrlc, config, color_hm)
|
try_build_map(cols, vals, span, style_computer, ctrlc, config)
|
||||||
}
|
}
|
||||||
val => value_to_styled_string(&val, config, color_hm).0,
|
val => value_to_styled_string(&val, config, style_computer).0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,9 +33,9 @@ fn try_build_map(
|
|||||||
cols: Vec<String>,
|
cols: Vec<String>,
|
||||||
vals: Vec<Value>,
|
vals: Vec<Value>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
style_computer: &StyleComputer,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &NuConfig,
|
config: &NuConfig,
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
|
||||||
) -> String {
|
) -> String {
|
||||||
let result = build_expanded_table(
|
let result = build_expanded_table(
|
||||||
cols.clone(),
|
cols.clone(),
|
||||||
@ -45,6 +43,7 @@ fn try_build_map(
|
|||||||
span,
|
span,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
config,
|
config,
|
||||||
|
style_computer,
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
@ -53,7 +52,7 @@ fn try_build_map(
|
|||||||
match result {
|
match result {
|
||||||
Ok(Some(result)) => result,
|
Ok(Some(result)) => result,
|
||||||
Ok(None) | Err(_) => {
|
Ok(None) | Err(_) => {
|
||||||
value_to_styled_string(&Value::Record { cols, vals, span }, config, color_hm).0
|
value_to_styled_string(&Value::Record { cols, vals, span }, config, style_computer).0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +62,7 @@ fn try_build_list(
|
|||||||
ctrlc: &Option<Arc<AtomicBool>>,
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
config: &NuConfig,
|
config: &NuConfig,
|
||||||
span: Span,
|
span: Span,
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer: &StyleComputer,
|
||||||
) -> String {
|
) -> String {
|
||||||
let table = convert_to_table2(
|
let table = convert_to_table2(
|
||||||
0,
|
0,
|
||||||
@ -71,7 +70,7 @@ fn try_build_list(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
span,
|
span,
|
||||||
color_hm,
|
style_computer,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
"",
|
"",
|
||||||
@ -81,7 +80,7 @@ fn try_build_list(
|
|||||||
Ok(Some((table, with_header, with_index))) => {
|
Ok(Some((table, with_header, with_index))) => {
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -92,12 +91,14 @@ fn try_build_list(
|
|||||||
|
|
||||||
match val {
|
match val {
|
||||||
Some(result) => result,
|
Some(result) => result,
|
||||||
None => value_to_styled_string(&Value::List { vals, span }, config, color_hm).0,
|
None => {
|
||||||
|
value_to_styled_string(&Value::List { vals, span }, config, style_computer).0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(None) | Err(_) => {
|
Ok(None) | Err(_) => {
|
||||||
// it means that the list is empty
|
// it means that the list is empty
|
||||||
value_to_styled_string(&Value::List { vals, span }, config, color_hm).0
|
value_to_styled_string(&Value::List { vals, span }, config, style_computer).0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,12 +110,12 @@ fn build_expanded_table(
|
|||||||
span: Span,
|
span: Span,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
|
style_computer: &StyleComputer,
|
||||||
term_width: usize,
|
term_width: usize,
|
||||||
expand_limit: Option<usize>,
|
expand_limit: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
) -> Result<Option<String>, ShellError> {
|
) -> Result<Option<String>, ShellError> {
|
||||||
let color_hm = get_color_config(config);
|
|
||||||
let theme = load_theme_from_config(config);
|
let theme = load_theme_from_config(config);
|
||||||
|
|
||||||
// calculate the width of a key part + the rest of table so we know the rest of the table width available for value.
|
// calculate the width of a key part + the rest of table so we know the rest of the table width available for value.
|
||||||
@ -123,7 +124,7 @@ fn build_expanded_table(
|
|||||||
let key_table = NuTable::new(vec![vec![key]], (1, 2));
|
let key_table = NuTable::new(vec![vec![key]], (1, 2));
|
||||||
let key_width = key_table
|
let key_width = key_table
|
||||||
.draw(
|
.draw(
|
||||||
create_table_config(config, &color_hm, 1, false, false, false),
|
create_table_config(config, style_computer, 1, false, false, false),
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
)
|
)
|
||||||
.map(|table| string_width(&table))
|
.map(|table| string_width(&table))
|
||||||
@ -149,7 +150,7 @@ fn build_expanded_table(
|
|||||||
let is_limited = matches!(expand_limit, Some(0));
|
let is_limited = matches!(expand_limit, Some(0));
|
||||||
let mut is_expanded = false;
|
let mut is_expanded = false;
|
||||||
let value = if is_limited {
|
let value = if is_limited {
|
||||||
value_to_styled_string(&value, config, &color_hm).0
|
value_to_styled_string(&value, config, style_computer).0
|
||||||
} else {
|
} else {
|
||||||
let deep = expand_limit.map(|i| i - 1);
|
let deep = expand_limit.map(|i| i - 1);
|
||||||
|
|
||||||
@ -161,7 +162,7 @@ fn build_expanded_table(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
span,
|
span,
|
||||||
&color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -178,7 +179,7 @@ fn build_expanded_table(
|
|||||||
|
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
&color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -194,7 +195,7 @@ fn build_expanded_table(
|
|||||||
None => {
|
None => {
|
||||||
// it means that the list is empty
|
// it means that the list is empty
|
||||||
let value = Value::List { vals, span };
|
let value = Value::List { vals, span };
|
||||||
value_to_styled_string(&value, config, &color_hm).0
|
value_to_styled_string(&value, config, style_computer).0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -205,6 +206,7 @@ fn build_expanded_table(
|
|||||||
span,
|
span,
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
|
style_computer,
|
||||||
remaining_width,
|
remaining_width,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
@ -220,7 +222,7 @@ fn build_expanded_table(
|
|||||||
let failed_value = value_to_styled_string(
|
let failed_value = value_to_styled_string(
|
||||||
&Value::Record { cols, vals, span },
|
&Value::Record { cols, vals, span },
|
||||||
config,
|
config,
|
||||||
&color_hm,
|
style_computer,
|
||||||
);
|
);
|
||||||
|
|
||||||
nu_table::wrap_string(&failed_value.0, remaining_width)
|
nu_table::wrap_string(&failed_value.0, remaining_width)
|
||||||
@ -228,7 +230,7 @@ fn build_expanded_table(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val => {
|
val => {
|
||||||
let text = value_to_styled_string(&val, config, &color_hm).0;
|
let text = value_to_styled_string(&val, config, style_computer).0;
|
||||||
nu_table::wrap_string(&text, remaining_width)
|
nu_table::wrap_string(&text, remaining_width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,7 +251,7 @@ fn build_expanded_table(
|
|||||||
data.push(row);
|
data.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
let table_config = create_table_config(config, &color_hm, data.len(), false, false, false);
|
let table_config = create_table_config(config, style_computer, data.len(), false, false, false);
|
||||||
|
|
||||||
let data_len = data.len();
|
let data_len = data.len();
|
||||||
let table = NuTable::new(data, (data_len, 2));
|
let table = NuTable::new(data, (data_len, 2));
|
||||||
@ -287,7 +289,7 @@ fn convert_to_table2<'a>(
|
|||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -336,7 +338,10 @@ fn convert_to_table2<'a>(
|
|||||||
let mut column_width = 0;
|
let mut column_width = 0;
|
||||||
|
|
||||||
if with_header {
|
if with_header {
|
||||||
data[0].push(NuTable::create_cell("#", header_style(color_hm)));
|
data[0].push(NuTable::create_cell(
|
||||||
|
"#",
|
||||||
|
header_style(style_computer, String::from("#")),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (row, item) in input.clone().into_iter().enumerate() {
|
for (row, item) in input.clone().into_iter().enumerate() {
|
||||||
@ -355,7 +360,7 @@ fn convert_to_table2<'a>(
|
|||||||
.then(|| lookup_index_value(item, config).unwrap_or_else(|| index.to_string()))
|
.then(|| lookup_index_value(item, config).unwrap_or_else(|| index.to_string()))
|
||||||
.unwrap_or_else(|| index.to_string());
|
.unwrap_or_else(|| index.to_string());
|
||||||
|
|
||||||
let value = make_index_string(text, color_hm);
|
let value = make_index_string(text, style_computer);
|
||||||
|
|
||||||
let width = string_width(&value.0);
|
let width = string_width(&value.0);
|
||||||
column_width = max(column_width, width);
|
column_width = max(column_width, width);
|
||||||
@ -389,7 +394,7 @@ fn convert_to_table2<'a>(
|
|||||||
item,
|
item,
|
||||||
config,
|
config,
|
||||||
&ctrlc,
|
&ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -431,7 +436,10 @@ fn convert_to_table2<'a>(
|
|||||||
|
|
||||||
let mut column_width = string_width(&header);
|
let mut column_width = string_width(&header);
|
||||||
|
|
||||||
data[0].push(NuTable::create_cell(&header, header_style(color_hm)));
|
data[0].push(NuTable::create_cell(
|
||||||
|
&header,
|
||||||
|
header_style(style_computer, header.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
for (row, item) in input.clone().into_iter().enumerate() {
|
for (row, item) in input.clone().into_iter().enumerate() {
|
||||||
if let Some(ctrlc) = &ctrlc {
|
if let Some(ctrlc) = &ctrlc {
|
||||||
@ -450,7 +458,7 @@ fn convert_to_table2<'a>(
|
|||||||
head,
|
head,
|
||||||
config,
|
config,
|
||||||
&ctrlc,
|
&ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -480,7 +488,7 @@ fn convert_to_table2<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = create_table2_entry_basic(item, &header, head, config, color_hm);
|
let value = create_table2_entry_basic(item, &header, head, config, style_computer);
|
||||||
let value = wrap_nu_text(value, available_width);
|
let value = wrap_nu_text(value, available_width);
|
||||||
|
|
||||||
let value_width = string_width(&value.0);
|
let value_width = string_width(&value.0);
|
||||||
@ -507,7 +515,7 @@ fn convert_to_table2<'a>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = create_table2_entry_basic(item, &header, head, config, color_hm);
|
let value = create_table2_entry_basic(item, &header, head, config, style_computer);
|
||||||
let value = wrap_nu_text(value, OK_CELL_CONTENT_WIDTH);
|
let value = wrap_nu_text(value, OK_CELL_CONTENT_WIDTH);
|
||||||
|
|
||||||
let value = NuTable::create_cell(value.0, value.1);
|
let value = NuTable::create_cell(value.0, value.1);
|
||||||
@ -577,10 +585,11 @@ fn lookup_index_value(item: &Value, config: &Config) -> Option<String> {
|
|||||||
.map(|value| value.into_string("", config))
|
.map(|value| value.into_string("", config))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn header_style(color_hm: &NuColorMap) -> TextStyle {
|
fn header_style(style_computer: &StyleComputer, header: String) -> TextStyle {
|
||||||
|
let style = style_computer.compute("header", &Value::string(header.as_str(), Span::unknown()));
|
||||||
TextStyle {
|
TextStyle {
|
||||||
alignment: Alignment::Center,
|
alignment: Alignment::Center,
|
||||||
color_style: Some(color_hm["header"]),
|
color_style: Some(style),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +599,7 @@ fn create_table2_entry_basic(
|
|||||||
header: &str,
|
header: &str,
|
||||||
head: Span,
|
head: Span,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
match item {
|
match item {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@ -599,11 +608,11 @@ fn create_table2_entry_basic(
|
|||||||
let val = item.clone().follow_cell_path(&[path], false);
|
let val = item.clone().follow_cell_path(&[path], false);
|
||||||
|
|
||||||
match val {
|
match val {
|
||||||
Ok(val) => value_to_styled_string(&val, config, color_hm),
|
Ok(val) => value_to_styled_string(&val, config, style_computer),
|
||||||
Err(_) => error_sign(color_hm),
|
Err(_) => error_sign(style_computer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => value_to_styled_string(item, config, color_hm),
|
_ => value_to_styled_string(item, config, style_computer),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -614,7 +623,7 @@ fn create_table2_entry(
|
|||||||
head: Span,
|
head: Span,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
ctrlc: &Option<Arc<AtomicBool>>,
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
color_hm: &NuColorMap,
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -631,20 +640,20 @@ fn create_table2_entry(
|
|||||||
&val,
|
&val,
|
||||||
config,
|
config,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
width,
|
width,
|
||||||
),
|
),
|
||||||
Err(_) => wrap_nu_text(error_sign(color_hm), width),
|
Err(_) => wrap_nu_text(error_sign(style_computer), width),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => convert_to_table2_entry(
|
_ => convert_to_table2_entry(
|
||||||
item,
|
item,
|
||||||
config,
|
config,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep,
|
deep,
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -653,8 +662,8 @@ fn create_table2_entry(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn error_sign(color_hm: &HashMap<String, nu_ansi_term::Style>) -> (String, TextStyle) {
|
fn error_sign(style_computer: &StyleComputer) -> (String, TextStyle) {
|
||||||
make_styled_string(String::from("❎"), "empty", color_hm, 0)
|
make_styled_string(style_computer, String::from("❎"), None, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_nu_text(mut text: NuText, width: usize) -> NuText {
|
fn wrap_nu_text(mut text: NuText, width: usize) -> NuText {
|
||||||
@ -667,7 +676,9 @@ fn convert_to_table2_entry(
|
|||||||
item: &Value,
|
item: &Value,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
ctrlc: &Option<Arc<AtomicBool>>,
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
color_hm: &NuColorMap,
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
deep: Option<usize>,
|
deep: Option<usize>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
@ -675,13 +686,13 @@ fn convert_to_table2_entry(
|
|||||||
) -> NuText {
|
) -> NuText {
|
||||||
let is_limit_reached = matches!(deep, Some(0));
|
let is_limit_reached = matches!(deep, Some(0));
|
||||||
if is_limit_reached {
|
if is_limit_reached {
|
||||||
return wrap_nu_text(value_to_styled_string(item, config, color_hm), width);
|
return wrap_nu_text(value_to_styled_string(item, config, style_computer), width);
|
||||||
}
|
}
|
||||||
|
|
||||||
match &item {
|
match &item {
|
||||||
Value::Record { span, cols, vals } => {
|
Value::Record { span, cols, vals } => {
|
||||||
if cols.is_empty() && vals.is_empty() {
|
if cols.is_empty() && vals.is_empty() {
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
} else {
|
} else {
|
||||||
let table = convert_to_table2(
|
let table = convert_to_table2(
|
||||||
0,
|
0,
|
||||||
@ -689,7 +700,7 @@ fn convert_to_table2_entry(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
*span,
|
*span,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep.map(|i| i - 1),
|
deep.map(|i| i - 1),
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -700,7 +711,7 @@ fn convert_to_table2_entry(
|
|||||||
table.and_then(|(table, with_header, with_index)| {
|
table.and_then(|(table, with_header, with_index)| {
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -715,7 +726,7 @@ fn convert_to_table2_entry(
|
|||||||
(table, TextStyle::default())
|
(table, TextStyle::default())
|
||||||
} else {
|
} else {
|
||||||
// error so back down to the default
|
// error so back down to the default
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -726,7 +737,7 @@ fn convert_to_table2_entry(
|
|||||||
|
|
||||||
if flatten && is_simple_list {
|
if flatten && is_simple_list {
|
||||||
wrap_nu_text(
|
wrap_nu_text(
|
||||||
convert_value_list_to_string(vals, config, color_hm, flatten_sep),
|
convert_value_list_to_string(vals, config, style_computer, flatten_sep),
|
||||||
width,
|
width,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@ -736,7 +747,7 @@ fn convert_to_table2_entry(
|
|||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
config,
|
config,
|
||||||
*span,
|
*span,
|
||||||
color_hm,
|
style_computer,
|
||||||
deep.map(|i| i - 1),
|
deep.map(|i| i - 1),
|
||||||
flatten,
|
flatten,
|
||||||
flatten_sep,
|
flatten_sep,
|
||||||
@ -747,7 +758,7 @@ fn convert_to_table2_entry(
|
|||||||
table.and_then(|(table, with_header, with_index)| {
|
table.and_then(|(table, with_header, with_index)| {
|
||||||
let table_config = create_table_config(
|
let table_config = create_table_config(
|
||||||
config,
|
config,
|
||||||
color_hm,
|
style_computer,
|
||||||
table.count_rows(),
|
table.count_rows(),
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
@ -763,23 +774,25 @@ fn convert_to_table2_entry(
|
|||||||
} else {
|
} else {
|
||||||
// error so back down to the default
|
// error so back down to the default
|
||||||
|
|
||||||
wrap_nu_text(value_to_styled_string(item, config, color_hm), width)
|
wrap_nu_text(value_to_styled_string(item, config, style_computer), width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => wrap_nu_text(value_to_styled_string(item, config, color_hm), width), // unknown type.
|
_ => wrap_nu_text(value_to_styled_string(item, config, style_computer), width), // unknown type.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_value_list_to_string(
|
fn convert_value_list_to_string(
|
||||||
vals: &[Value],
|
vals: &[Value],
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &NuColorMap,
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
flatten_sep: &str,
|
flatten_sep: &str,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
for value in vals {
|
for value in vals {
|
||||||
let (text, _) = value_to_styled_string(value, config, color_hm);
|
let (text, _) = value_to_styled_string(value, config, style_computer);
|
||||||
|
|
||||||
buf.push(text);
|
buf.push(text);
|
||||||
}
|
}
|
||||||
@ -787,39 +800,58 @@ fn convert_value_list_to_string(
|
|||||||
(text, TextStyle::default())
|
(text, TextStyle::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_to_styled_string(value: &Value, config: &Config, color_hm: &NuColorMap) -> NuText {
|
fn value_to_styled_string(
|
||||||
|
value: &Value,
|
||||||
|
config: &Config,
|
||||||
|
// This is passed in, even though it could be retrieved from config,
|
||||||
|
// to save reallocation (because it's presumably being used upstream).
|
||||||
|
style_computer: &StyleComputer,
|
||||||
|
) -> NuText {
|
||||||
let float_precision = config.float_precision as usize;
|
let float_precision = config.float_precision as usize;
|
||||||
make_styled_string(
|
make_styled_string(
|
||||||
|
style_computer,
|
||||||
value.into_abbreviated_string(config),
|
value.into_abbreviated_string(config),
|
||||||
&value.get_type().to_string(),
|
Some(value),
|
||||||
color_hm,
|
|
||||||
float_precision,
|
float_precision,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_styled_string(
|
fn make_styled_string(
|
||||||
|
style_computer: &StyleComputer,
|
||||||
text: String,
|
text: String,
|
||||||
text_type: &str,
|
value: Option<&Value>, // None represents table holes.
|
||||||
color_hm: &NuColorMap,
|
|
||||||
float_precision: usize,
|
float_precision: usize,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
if text_type == "float" {
|
match value {
|
||||||
// set dynamic precision from config
|
Some(value) => {
|
||||||
let precise_number = match convert_with_precision(&text, float_precision) {
|
match value {
|
||||||
Ok(num) => num,
|
Value::Float { .. } => {
|
||||||
Err(e) => e.to_string(),
|
// set dynamic precision from config
|
||||||
};
|
let precise_number = match convert_with_precision(&text, float_precision) {
|
||||||
(precise_number, style_primitive(text_type, color_hm))
|
Ok(num) => num,
|
||||||
} else {
|
Err(e) => e.to_string(),
|
||||||
(text, style_primitive(text_type, color_hm))
|
};
|
||||||
|
(precise_number, style_computer.style_primitive(value))
|
||||||
|
}
|
||||||
|
_ => (text, style_computer.style_primitive(value)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
// Though holes are not the same as null, the closure for "empty" is passed a null anyway.
|
||||||
|
(
|
||||||
|
text,
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Center,
|
||||||
|
style_computer.compute("empty", &Value::nothing(Span::unknown())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_index_string(text: String, color_hm: &NuColorMap) -> NuText {
|
fn make_index_string(text: String, style_computer: &StyleComputer) -> NuText {
|
||||||
let style = TextStyle::new()
|
let style = style_computer.compute("row_index", &Value::string(text.as_str(), Span::unknown()));
|
||||||
.alignment(Alignment::Right)
|
(text, TextStyle::with_style(Alignment::Right, style))
|
||||||
.style(color_hm["row_index"]);
|
|
||||||
(text, style)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellError> {
|
fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellError> {
|
||||||
@ -857,7 +889,7 @@ fn load_theme_from_config(config: &Config) -> TableTheme {
|
|||||||
|
|
||||||
fn create_table_config(
|
fn create_table_config(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer: &StyleComputer,
|
||||||
count_records: usize,
|
count_records: usize,
|
||||||
with_header: bool,
|
with_header: bool,
|
||||||
with_index: bool,
|
with_index: bool,
|
||||||
@ -868,10 +900,7 @@ fn create_table_config(
|
|||||||
|
|
||||||
let mut table_cfg = TableConfig::new(theme, with_header, with_index, append_footer);
|
let mut table_cfg = TableConfig::new(theme, with_header, with_index, append_footer);
|
||||||
|
|
||||||
let sep_color = lookup_separator_color(color_hm);
|
table_cfg = table_cfg.splitline_style(lookup_separator_color(style_computer));
|
||||||
if let Some(color) = sep_color {
|
|
||||||
table_cfg = table_cfg.splitline_style(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
if expand {
|
if expand {
|
||||||
table_cfg = table_cfg.expand();
|
table_cfg = table_cfg.expand();
|
||||||
@ -880,10 +909,8 @@ fn create_table_config(
|
|||||||
table_cfg.trim(config.trim_strategy.clone())
|
table_cfg.trim(config.trim_strategy.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lookup_separator_color(
|
fn lookup_separator_color(style_computer: &StyleComputer) -> nu_ansi_term::Style {
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer.compute("separator", &Value::nothing(Span::unknown()))
|
||||||
) -> Option<nu_ansi_term::Style> {
|
|
||||||
color_hm.get("separator").cloned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool {
|
fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool {
|
||||||
|
@ -18,7 +18,7 @@ use crossterm::{
|
|||||||
LeaveAlternateScreen,
|
LeaveAlternateScreen,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use nu_color_config::lookup_ansi_color_style;
|
use nu_color_config::{lookup_ansi_color_style, StyleComputer};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Value,
|
Value,
|
||||||
@ -26,7 +26,7 @@ use nu_protocol::{
|
|||||||
use tui::{backend::CrosstermBackend, layout::Rect, widgets::Block};
|
use tui::{backend::CrosstermBackend, layout::Rect, widgets::Block};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
nu_common::{CtrlC, NuColor, NuConfig, NuSpan, NuStyle, NuStyleTable},
|
nu_common::{CtrlC, NuColor, NuConfig, NuSpan, NuStyle},
|
||||||
registry::{Command, CommandRegistry},
|
registry::{Command, CommandRegistry},
|
||||||
util::map_into_value,
|
util::map_into_value,
|
||||||
views::{util::nu_style_to_tui, ViewConfig},
|
views::{util::nu_style_to_tui, ViewConfig},
|
||||||
@ -140,7 +140,7 @@ impl<'a> Pager<'a> {
|
|||||||
if let Some(page) = &mut view {
|
if let Some(page) = &mut view {
|
||||||
page.view.setup(ViewConfig::new(
|
page.view.setup(ViewConfig::new(
|
||||||
self.config.nu_config,
|
self.config.nu_config,
|
||||||
self.config.color_hm,
|
self.config.style_computer,
|
||||||
&self.config.config,
|
&self.config.config,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ pub enum Transition {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PagerConfig<'a> {
|
pub struct PagerConfig<'a> {
|
||||||
pub nu_config: &'a NuConfig,
|
pub nu_config: &'a NuConfig,
|
||||||
pub color_hm: &'a NuStyleTable,
|
pub style_computer: &'a StyleComputer<'a>,
|
||||||
pub config: ConfigMap,
|
pub config: ConfigMap,
|
||||||
pub style: StyleConfig,
|
pub style: StyleConfig,
|
||||||
pub peek_value: bool,
|
pub peek_value: bool,
|
||||||
@ -170,10 +170,14 @@ pub struct PagerConfig<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> PagerConfig<'a> {
|
impl<'a> PagerConfig<'a> {
|
||||||
pub fn new(nu_config: &'a NuConfig, color_hm: &'a NuStyleTable, config: ConfigMap) -> Self {
|
pub fn new(
|
||||||
|
nu_config: &'a NuConfig,
|
||||||
|
style_computer: &'a StyleComputer,
|
||||||
|
config: ConfigMap,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
nu_config,
|
nu_config,
|
||||||
color_hm,
|
style_computer,
|
||||||
config,
|
config,
|
||||||
peek_value: false,
|
peek_value: false,
|
||||||
exit_esc: false,
|
exit_esc: false,
|
||||||
@ -261,7 +265,7 @@ fn render_ui(
|
|||||||
if let Some(page) = &mut view {
|
if let Some(page) = &mut view {
|
||||||
let cfg = ViewConfig::new(
|
let cfg = ViewConfig::new(
|
||||||
pager.config.nu_config,
|
pager.config.nu_config,
|
||||||
pager.config.color_hm,
|
pager.config.style_computer,
|
||||||
&pager.config.config,
|
&pager.config.config,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -416,7 +420,7 @@ fn run_command(
|
|||||||
if let Some(page) = view.as_mut() {
|
if let Some(page) = view.as_mut() {
|
||||||
page.view.setup(ViewConfig::new(
|
page.view.setup(ViewConfig::new(
|
||||||
pager.config.nu_config,
|
pager.config.nu_config,
|
||||||
pager.config.color_hm,
|
pager.config.style_computer,
|
||||||
&pager.config.config,
|
&pager.config.config,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -424,7 +428,7 @@ fn run_command(
|
|||||||
for page in view_stack {
|
for page in view_stack {
|
||||||
page.view.setup(ViewConfig::new(
|
page.view.setup(ViewConfig::new(
|
||||||
pager.config.nu_config,
|
pager.config.nu_config,
|
||||||
pager.config.color_hm,
|
pager.config.style_computer,
|
||||||
&pager.config.config,
|
&pager.config.config,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -452,7 +456,7 @@ fn run_command(
|
|||||||
|
|
||||||
new_view.setup(ViewConfig::new(
|
new_view.setup(ViewConfig::new(
|
||||||
pager.config.nu_config,
|
pager.config.nu_config,
|
||||||
pager.config.color_hm,
|
pager.config.style_computer,
|
||||||
&pager.config.config,
|
&pager.config.config,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
|
use nu_color_config::TextStyle;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use nu_table::TextStyle;
|
|
||||||
use tui::{layout::Rect, widgets::Paragraph};
|
use tui::{layout::Rect, widgets::Paragraph};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -7,16 +7,14 @@ mod record;
|
|||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
|
use nu_color_config::StyleComputer;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
|
||||||
use crate::{
|
use crate::{nu_common::NuConfig, pager::ConfigMap};
|
||||||
nu_common::{NuConfig, NuStyleTable},
|
|
||||||
pager::ConfigMap,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
nu_common::NuText,
|
nu_common::NuText,
|
||||||
@ -61,15 +59,19 @@ impl ElementInfo {
|
|||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ViewConfig<'a> {
|
pub struct ViewConfig<'a> {
|
||||||
pub nu_config: &'a NuConfig,
|
pub nu_config: &'a NuConfig,
|
||||||
pub color_hm: &'a NuStyleTable,
|
pub style_computer: &'a StyleComputer<'a>,
|
||||||
pub config: &'a ConfigMap,
|
pub config: &'a ConfigMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ViewConfig<'a> {
|
impl<'a> ViewConfig<'a> {
|
||||||
pub fn new(nu_config: &'a NuConfig, color_hm: &'a NuStyleTable, config: &'a ConfigMap) -> Self {
|
pub fn new(
|
||||||
|
nu_config: &'a NuConfig,
|
||||||
|
style_computer: &'a StyleComputer<'a>,
|
||||||
|
config: &'a ConfigMap,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
nu_config,
|
nu_config,
|
||||||
color_hm,
|
style_computer,
|
||||||
config,
|
config,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
use nu_color_config::TextStyle;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Value,
|
Value,
|
||||||
};
|
};
|
||||||
use nu_table::TextStyle;
|
|
||||||
use tui::layout::Rect;
|
use tui::layout::Rect;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -3,7 +3,7 @@ mod tablew;
|
|||||||
use std::{borrow::Cow, collections::HashMap};
|
use std::{borrow::Cow, collections::HashMap};
|
||||||
|
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
use nu_color_config::get_color_map;
|
use nu_color_config::{get_color_map, StyleComputer};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Value,
|
Value,
|
||||||
@ -11,7 +11,7 @@ use nu_protocol::{
|
|||||||
use tui::{layout::Rect, widgets::Block};
|
use tui::{layout::Rect, widgets::Block};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
nu_common::{collect_input, NuConfig, NuSpan, NuStyle, NuStyleTable, NuText},
|
nu_common::{collect_input, NuConfig, NuSpan, NuStyle, NuText},
|
||||||
pager::{
|
pager::{
|
||||||
report::{Report, Severity},
|
report::{Report, Severity},
|
||||||
ConfigMap, Frame, Transition, ViewInfo,
|
ConfigMap, Frame, Transition, ViewInfo,
|
||||||
@ -208,16 +208,16 @@ impl<'a> RecordView<'a> {
|
|||||||
|
|
||||||
fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableW<'a> {
|
fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableW<'a> {
|
||||||
let layer = self.get_layer_last();
|
let layer = self.get_layer_last();
|
||||||
let data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.color_hm);
|
let data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer);
|
||||||
|
|
||||||
let headers = layer.columns.as_ref();
|
let headers = layer.columns.as_ref();
|
||||||
let color_hm = cfg.color_hm;
|
let style_computer = cfg.style_computer;
|
||||||
let (row, column) = self.get_current_offset();
|
let (row, column) = self.get_current_offset();
|
||||||
|
|
||||||
TableW::new(
|
TableW::new(
|
||||||
headers,
|
headers,
|
||||||
data,
|
data,
|
||||||
color_hm,
|
style_computer,
|
||||||
row,
|
row,
|
||||||
column,
|
column,
|
||||||
self.theme.table,
|
self.theme.table,
|
||||||
@ -301,10 +301,15 @@ impl View for RecordView<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn collect_data(&self) -> Vec<NuText> {
|
fn collect_data(&self) -> Vec<NuText> {
|
||||||
|
// Create a "dummy" style_computer.
|
||||||
|
let dummy_engine_state = EngineState::new();
|
||||||
|
let dummy_stack = Stack::new();
|
||||||
|
let style_computer = StyleComputer::new(&dummy_engine_state, &dummy_stack, HashMap::new());
|
||||||
|
|
||||||
let data = convert_records_to_string(
|
let data = convert_records_to_string(
|
||||||
&self.get_layer_last().records,
|
&self.get_layer_last().records,
|
||||||
&NuConfig::default(),
|
&NuConfig::default(),
|
||||||
&HashMap::default(),
|
&style_computer,
|
||||||
);
|
);
|
||||||
|
|
||||||
data.iter().flatten().cloned().collect()
|
data.iter().flatten().cloned().collect()
|
||||||
@ -595,7 +600,7 @@ fn state_reverse_data(state: &mut RecordView<'_>, page_size: usize) {
|
|||||||
fn convert_records_to_string(
|
fn convert_records_to_string(
|
||||||
records: &[Vec<Value>],
|
records: &[Vec<Value>],
|
||||||
cfg: &NuConfig,
|
cfg: &NuConfig,
|
||||||
color_hm: &NuStyleTable,
|
style_computer: &StyleComputer,
|
||||||
) -> Vec<Vec<NuText>> {
|
) -> Vec<Vec<NuText>> {
|
||||||
records
|
records
|
||||||
.iter()
|
.iter()
|
||||||
@ -603,10 +608,9 @@ fn convert_records_to_string(
|
|||||||
row.iter()
|
row.iter()
|
||||||
.map(|value| {
|
.map(|value| {
|
||||||
let text = value.clone().into_abbreviated_string(cfg);
|
let text = value.clone().into_abbreviated_string(cfg);
|
||||||
let tp = value.get_type().to_string();
|
|
||||||
let float_precision = cfg.float_precision as usize;
|
let float_precision = cfg.float_precision as usize;
|
||||||
|
|
||||||
make_styled_string(text, &tp, 0, false, color_hm, float_precision)
|
make_styled_string(style_computer, text, Some(value), float_precision)
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,9 @@ use std::{
|
|||||||
cmp::{max, Ordering},
|
cmp::{max, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use nu_table::{string_width, Alignment, TextStyle};
|
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
||||||
|
use nu_protocol::Value;
|
||||||
|
use nu_table::string_width;
|
||||||
use tui::{
|
use tui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
@ -12,7 +14,7 @@ use tui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
nu_common::{truncate_str, NuStyle, NuStyleTable, NuText},
|
nu_common::{truncate_str, NuStyle, NuText},
|
||||||
views::util::{nu_style_to_tui, text_style_to_tui_style},
|
views::util::{nu_style_to_tui, text_style_to_tui_style},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,7 +28,7 @@ pub struct TableW<'a> {
|
|||||||
index_column: usize,
|
index_column: usize,
|
||||||
style: TableStyle,
|
style: TableStyle,
|
||||||
head_position: Orientation,
|
head_position: Orientation,
|
||||||
color_hm: &'a NuStyleTable,
|
style_computer: &'a StyleComputer<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
@ -58,7 +60,7 @@ impl<'a> TableW<'a> {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
columns: impl Into<Cow<'a, [String]>>,
|
columns: impl Into<Cow<'a, [String]>>,
|
||||||
data: impl Into<Cow<'a, [Vec<NuText>]>>,
|
data: impl Into<Cow<'a, [Vec<NuText>]>>,
|
||||||
color_hm: &'a NuStyleTable,
|
style_computer: &'a StyleComputer<'a>,
|
||||||
index_row: usize,
|
index_row: usize,
|
||||||
index_column: usize,
|
index_column: usize,
|
||||||
style: TableStyle,
|
style: TableStyle,
|
||||||
@ -67,11 +69,11 @@ impl<'a> TableW<'a> {
|
|||||||
Self {
|
Self {
|
||||||
columns: columns.into(),
|
columns: columns.into(),
|
||||||
data: data.into(),
|
data: data.into(),
|
||||||
|
style_computer,
|
||||||
index_row,
|
index_row,
|
||||||
index_column,
|
index_column,
|
||||||
style,
|
style,
|
||||||
head_position,
|
head_position,
|
||||||
color_hm,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,7 +192,7 @@ impl<'a> TableW<'a> {
|
|||||||
width += render_index(
|
width += render_index(
|
||||||
buf,
|
buf,
|
||||||
area,
|
area,
|
||||||
self.color_hm,
|
self.style_computer,
|
||||||
self.index_row,
|
self.index_row,
|
||||||
padding_index_l,
|
padding_index_l,
|
||||||
padding_index_r,
|
padding_index_r,
|
||||||
@ -246,7 +248,7 @@ impl<'a> TableW<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if show_head {
|
if show_head {
|
||||||
let mut header = [head_row_text(&head, self.color_hm)];
|
let mut header = [head_row_text(&head, self.style_computer)];
|
||||||
if head_width > use_space as usize {
|
if head_width > use_space as usize {
|
||||||
truncate_str(&mut header[0].0, use_space as usize)
|
truncate_str(&mut header[0].0, use_space as usize)
|
||||||
}
|
}
|
||||||
@ -330,7 +332,7 @@ impl<'a> TableW<'a> {
|
|||||||
left_w += render_index(
|
left_w += render_index(
|
||||||
buf,
|
buf,
|
||||||
area,
|
area,
|
||||||
self.color_hm,
|
self.style_computer,
|
||||||
self.index_row,
|
self.index_row,
|
||||||
padding_index_l,
|
padding_index_l,
|
||||||
padding_index_r,
|
padding_index_r,
|
||||||
@ -358,7 +360,7 @@ impl<'a> TableW<'a> {
|
|||||||
|
|
||||||
let columns = columns
|
let columns = columns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|s| head_row_text(s, self.color_hm))
|
.map(|s| head_row_text(s, self.style_computer))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
if is_head_left {
|
if is_head_left {
|
||||||
@ -540,13 +542,16 @@ fn check_column_width(
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct IndexColumn<'a> {
|
struct IndexColumn<'a> {
|
||||||
color_hm: &'a NuStyleTable,
|
style_computer: &'a StyleComputer<'a>,
|
||||||
start: usize,
|
start: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IndexColumn<'a> {
|
impl<'a> IndexColumn<'a> {
|
||||||
fn new(color_hm: &'a NuStyleTable, start: usize) -> Self {
|
fn new(style_computer: &'a StyleComputer, start: usize) -> Self {
|
||||||
Self { color_hm, start }
|
Self {
|
||||||
|
style_computer,
|
||||||
|
start,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn estimate_width(&self, height: u16) -> usize {
|
fn estimate_width(&self, height: u16) -> usize {
|
||||||
@ -557,11 +562,13 @@ impl<'a> IndexColumn<'a> {
|
|||||||
|
|
||||||
impl Widget for IndexColumn<'_> {
|
impl Widget for IndexColumn<'_> {
|
||||||
fn render(self, area: Rect, buf: &mut Buffer) {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
let style = nu_style_to_tui(self.color_hm["row_index"]);
|
|
||||||
|
|
||||||
for row in 0..area.height {
|
for row in 0..area.height {
|
||||||
let i = 1 + row as usize + self.start;
|
let i = 1 + row as usize + self.start;
|
||||||
let text = i.to_string();
|
let text = i.to_string();
|
||||||
|
let style = nu_style_to_tui(self.style_computer.compute(
|
||||||
|
"row_index",
|
||||||
|
&Value::string(text.as_str(), nu_protocol::Span::unknown()),
|
||||||
|
));
|
||||||
|
|
||||||
let p = Paragraph::new(text)
|
let p = Paragraph::new(text)
|
||||||
.style(style)
|
.style(style)
|
||||||
@ -606,15 +613,17 @@ fn render_header_borders(
|
|||||||
|
|
||||||
fn render_index(
|
fn render_index(
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
|
|
||||||
area: Rect,
|
area: Rect,
|
||||||
color_hm: &NuStyleTable,
|
|
||||||
|
style_computer: &StyleComputer,
|
||||||
start_index: usize,
|
start_index: usize,
|
||||||
padding_left: u16,
|
padding_left: u16,
|
||||||
padding_right: u16,
|
padding_right: u16,
|
||||||
) -> u16 {
|
) -> u16 {
|
||||||
let mut width = render_space(buf, area.x, area.y, area.height, padding_left);
|
let mut width = render_space(buf, area.x, area.y, area.height, padding_left);
|
||||||
|
|
||||||
let index = IndexColumn::new(color_hm, start_index);
|
let index = IndexColumn::new(style_computer, start_index);
|
||||||
let w = index.estimate_width(area.height) as u16;
|
let w = index.estimate_width(area.height) as u16;
|
||||||
let area = Rect::new(area.x + width, area.y, w, area.height);
|
let area = Rect::new(area.x + width, area.y, w, area.height);
|
||||||
|
|
||||||
@ -769,12 +778,12 @@ fn strip_string(text: &str) -> String {
|
|||||||
.unwrap_or_else(|| text.to_owned())
|
.unwrap_or_else(|| text.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn head_row_text(head: &str, color_hm: &NuStyleTable) -> NuText {
|
fn head_row_text(head: &str, style_computer: &StyleComputer) -> NuText {
|
||||||
(
|
(
|
||||||
String::from(head),
|
String::from(head),
|
||||||
TextStyle {
|
TextStyle::with_style(
|
||||||
alignment: Alignment::Center,
|
Alignment::Center,
|
||||||
color_style: Some(color_hm["header"]),
|
style_computer.compute("header", &Value::string(head, nu_protocol::Span::unknown())),
|
||||||
},
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use nu_color_config::style_primitive;
|
use nu_color_config::{Alignment, StyleComputer};
|
||||||
use nu_table::{string_width, Alignment, TextStyle};
|
use nu_protocol::{ShellError, Value};
|
||||||
|
use nu_table::{string_width, TextStyle};
|
||||||
use tui::{
|
use tui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::Span,
|
text::Span,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::nu_common::{truncate_str, NuColor, NuStyle, NuStyleTable, NuText};
|
use crate::nu_common::{truncate_str, NuColor, NuStyle, NuText};
|
||||||
|
|
||||||
pub fn set_span(
|
pub fn set_span(
|
||||||
buf: &mut Buffer,
|
buf: &mut Buffer,
|
||||||
@ -118,39 +119,53 @@ pub fn text_style_to_tui_style(style: TextStyle) -> tui::style::Style {
|
|||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is identical to the same function in nu-explore/src/nu_common
|
||||||
pub fn make_styled_string(
|
pub fn make_styled_string(
|
||||||
|
style_computer: &StyleComputer,
|
||||||
text: String,
|
text: String,
|
||||||
text_type: &str,
|
value: Option<&Value>, // None represents table holes.
|
||||||
col: usize,
|
|
||||||
with_index: bool,
|
|
||||||
color_hm: &NuStyleTable,
|
|
||||||
float_precision: usize,
|
float_precision: usize,
|
||||||
) -> NuText {
|
) -> NuText {
|
||||||
if col == 0 && with_index {
|
match value {
|
||||||
return (text, index_text_style(color_hm));
|
Some(value) => {
|
||||||
}
|
match value {
|
||||||
|
Value::Float { .. } => {
|
||||||
let style = style_primitive(text_type, color_hm);
|
// set dynamic precision from config
|
||||||
|
let precise_number = match convert_with_precision(&text, float_precision) {
|
||||||
let mut text = text;
|
Ok(num) => num,
|
||||||
if text_type == "float" {
|
Err(e) => e.to_string(),
|
||||||
text = convert_with_precision(&text, float_precision);
|
};
|
||||||
}
|
(precise_number, style_computer.style_primitive(value))
|
||||||
|
}
|
||||||
(text, style)
|
_ => (text, style_computer.style_primitive(value)),
|
||||||
}
|
}
|
||||||
|
}
|
||||||
fn index_text_style(color_hm: &std::collections::HashMap<String, NuStyle>) -> TextStyle {
|
None => {
|
||||||
TextStyle {
|
// Though holes are not the same as null, the closure for "empty" is passed a null anyway.
|
||||||
alignment: Alignment::Right,
|
(
|
||||||
color_style: Some(color_hm["row_index"]),
|
text,
|
||||||
|
TextStyle::with_style(
|
||||||
|
Alignment::Center,
|
||||||
|
style_computer.compute("empty", &Value::nothing(nu_protocol::Span::unknown())),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_with_precision(val: &str, precision: usize) -> String {
|
fn convert_with_precision(val: &str, precision: usize) -> Result<String, ShellError> {
|
||||||
// vall will always be a f64 so convert it with precision formatting
|
// vall will always be a f64 so convert it with precision formatting
|
||||||
match val.trim().parse::<f64>() {
|
let val_float = match val.trim().parse::<f64>() {
|
||||||
Ok(f) => format!("{:.prec$}", f, prec = precision),
|
Ok(f) => f,
|
||||||
Err(err) => format!("error converting string [{}] to f64; {}", &val, err),
|
Err(e) => {
|
||||||
}
|
return Err(ShellError::GenericError(
|
||||||
|
format!("error converting string [{}] to f64", &val),
|
||||||
|
"".to_string(),
|
||||||
|
None,
|
||||||
|
Some(e.to_string()),
|
||||||
|
Vec::new(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(format!("{:.prec$}", val_float, prec = precision))
|
||||||
}
|
}
|
||||||
|
@ -712,7 +712,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"trim" => {
|
"trim" => {
|
||||||
match try_parse_trim_strategy(value, &config, &mut errors) {
|
match try_parse_trim_strategy(value, &mut errors) {
|
||||||
Ok(v) => config.trim_strategy = v,
|
Ok(v) => config.trim_strategy = v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// try_parse_trim_strategy() already adds its own errors
|
// try_parse_trim_strategy() already adds its own errors
|
||||||
@ -792,7 +792,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"explore" => {
|
"explore" => {
|
||||||
if let Ok(map) = create_map(value, &config) {
|
if let Ok(map) = create_map(value) {
|
||||||
config.explore = map;
|
config.explore = map;
|
||||||
} else {
|
} else {
|
||||||
invalid!(vals[index].span().ok(), "should be a record");
|
invalid!(vals[index].span().ok(), "should be a record");
|
||||||
@ -802,7 +802,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
// Misc. options
|
// Misc. options
|
||||||
"color_config" => {
|
"color_config" => {
|
||||||
if let Ok(map) = create_map(value, &config) {
|
if let Ok(map) = create_map(value) {
|
||||||
config.color_config = map;
|
config.color_config = map;
|
||||||
} else {
|
} else {
|
||||||
invalid!(vals[index].span().ok(), "should be a record");
|
invalid!(vals[index].span().ok(), "should be a record");
|
||||||
@ -1118,7 +1118,7 @@ impl Value {
|
|||||||
}
|
}
|
||||||
"table_trim" => {
|
"table_trim" => {
|
||||||
legacy_options_used = true;
|
legacy_options_used = true;
|
||||||
match try_parse_trim_strategy(value, &config, &mut errors) {
|
match try_parse_trim_strategy(value, &mut errors) {
|
||||||
Ok(v) => config.trim_strategy = v,
|
Ok(v) => config.trim_strategy = v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
// try_parse_trim_strategy() already calls eprintln!() on error
|
// try_parse_trim_strategy() already calls eprintln!() on error
|
||||||
@ -1205,10 +1205,9 @@ Please consult https://www.nushell.sh/blog/2022-11-29-nushell-0.72.html for deta
|
|||||||
|
|
||||||
fn try_parse_trim_strategy(
|
fn try_parse_trim_strategy(
|
||||||
value: &Value,
|
value: &Value,
|
||||||
config: &Config,
|
|
||||||
errors: &mut Vec<ShellError>,
|
errors: &mut Vec<ShellError>,
|
||||||
) -> Result<TrimStrategy, ShellError> {
|
) -> Result<TrimStrategy, ShellError> {
|
||||||
let map = create_map(value, config).map_err(|e| {
|
let map = create_map(value).map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error while applying config changes".into(),
|
"Error while applying config changes".into(),
|
||||||
"$env.config.table.trim is not a record".into(),
|
"$env.config.table.trim is not a record".into(),
|
||||||
@ -1288,55 +1287,17 @@ fn try_parse_trim_methodology(value: &Value) -> Option<TrimStrategy> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_map(value: &Value, config: &Config) -> Result<HashMap<String, Value>, ShellError> {
|
fn create_map(value: &Value) -> Result<HashMap<String, Value>, ShellError> {
|
||||||
let (cols, inner_vals) = value.as_record()?;
|
let (cols, inner_vals) = value.as_record()?;
|
||||||
let mut hm: HashMap<String, Value> = HashMap::new();
|
let mut hm: HashMap<String, Value> = HashMap::new();
|
||||||
|
|
||||||
for (k, v) in cols.iter().zip(inner_vals) {
|
for (k, v) in cols.iter().zip(inner_vals) {
|
||||||
match &v {
|
hm.insert(k.to_string(), v.clone());
|
||||||
Value::Record {
|
|
||||||
cols: inner_cols,
|
|
||||||
vals: inner_vals,
|
|
||||||
span,
|
|
||||||
} => {
|
|
||||||
let val = color_value_string(span, inner_cols, inner_vals, config);
|
|
||||||
hm.insert(k.to_string(), val);
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
hm.insert(k.to_string(), v.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(hm)
|
Ok(hm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn color_value_string(
|
|
||||||
span: &Span,
|
|
||||||
inner_cols: &[String],
|
|
||||||
inner_vals: &[Value],
|
|
||||||
config: &Config,
|
|
||||||
) -> Value {
|
|
||||||
// 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 val: String = inner_cols
|
|
||||||
.iter()
|
|
||||||
.zip(inner_vals)
|
|
||||||
.map(|(x, y)| format!("{}: \"{}\" ", x, y.into_string(", ", config)))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// now insert the braces at the front and the back to fake the json string
|
|
||||||
Value::String {
|
|
||||||
val: format!("{{{}}}", val),
|
|
||||||
span: *span,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the hooks to find the blocks to run when the hooks fire
|
// Parse the hooks to find the blocks to run when the hooks fire
|
||||||
fn create_hooks(value: &Value) -> Result<Hooks, ShellError> {
|
fn create_hooks(value: &Value) -> Result<Hooks, ShellError> {
|
||||||
match value {
|
match value {
|
||||||
|
@ -20,7 +20,7 @@ static PWD_ENV: &str = "PWD";
|
|||||||
|
|
||||||
// TODO: move to different file? where?
|
// TODO: move to different file? where?
|
||||||
/// An operation to be performed with the current buffer of the interactive shell.
|
/// An operation to be performed with the current buffer of the interactive shell.
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ReplOperation {
|
pub enum ReplOperation {
|
||||||
Append(String),
|
Append(String),
|
||||||
Insert(String),
|
Insert(String),
|
||||||
|
@ -11,6 +11,12 @@ version = "0.72.2"
|
|||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
nu-protocol = { path = "../nu-protocol", version = "0.72.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.72.2" }
|
nu-utils = { path = "../nu-utils", version = "0.72.2" }
|
||||||
|
nu-engine = { path = "../nu-engine", version = "0.72.2" }
|
||||||
|
nu-color-config = { path = "../nu-color-config", version = "0.72.2" }
|
||||||
|
nu-test-support = { path="../nu-test-support", version = "0.72.2" }
|
||||||
|
# nu-path is used only for test support
|
||||||
|
nu-path = { path="../nu-path", version = "0.72.2" }
|
||||||
|
atty = "0.2.14"
|
||||||
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
json_to_table = { version = "0.3.1", features = ["color"] }
|
json_to_table = { version = "0.3.1", features = ["color"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use nu_table::{Table, TableConfig, TableTheme, TextStyle};
|
use nu_color_config::TextStyle;
|
||||||
|
use nu_table::{Table, TableConfig, TableTheme};
|
||||||
use tabled::papergrid::records::{cell_info::CellInfo, tcell::TCell};
|
use tabled::papergrid::records::{cell_info::CellInfo, tcell::TCell};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
mod nu_protocol_table;
|
mod nu_protocol_table;
|
||||||
mod table;
|
mod table;
|
||||||
mod table_theme;
|
mod table_theme;
|
||||||
mod textstyle;
|
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
pub use nu_color_config::TextStyle;
|
||||||
pub use nu_protocol_table::NuTable;
|
pub use nu_protocol_table::NuTable;
|
||||||
pub use table::{Alignments, Table, TableConfig};
|
pub use table::{Alignments, Table, TableConfig};
|
||||||
pub use table_theme::TableTheme;
|
pub use table_theme::TableTheme;
|
||||||
pub use textstyle::{Alignment, TextStyle};
|
|
||||||
pub use util::*;
|
pub use util::*;
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{table::TrimStrategyModifier, Alignments, TableTheme};
|
||||||
|
use nu_color_config::StyleComputer;
|
||||||
use nu_protocol::{Config, Span, Value};
|
use nu_protocol::{Config, Span, Value};
|
||||||
use tabled::{
|
use tabled::{
|
||||||
color::Color, formatting::AlignmentStrategy, object::Segment, papergrid::records::Records,
|
color::Color, formatting::AlignmentStrategy, object::Segment, papergrid::records::Records,
|
||||||
Alignment, Modify, Table,
|
Alignment, Modify, Table,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{table::TrimStrategyModifier, Alignments, TableTheme};
|
/// NuTable has a recursive table representation of nu_protocol::Value.
|
||||||
|
|
||||||
/// NuTable has a recursive table representation of nu_prorocol::Value.
|
|
||||||
///
|
///
|
||||||
/// It doesn't support alignement and a proper width controll.
|
/// It doesn't support alignement and a proper width control.
|
||||||
pub struct NuTable {
|
pub struct NuTable {
|
||||||
inner: tabled::Table,
|
inner: tabled::Table,
|
||||||
}
|
}
|
||||||
@ -21,12 +21,12 @@ impl NuTable {
|
|||||||
collapse: bool,
|
collapse: bool,
|
||||||
termwidth: usize,
|
termwidth: usize,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
style_computer: &StyleComputer,
|
||||||
theme: &TableTheme,
|
theme: &TableTheme,
|
||||||
with_footer: bool,
|
with_footer: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut table = tabled::Table::new([""]);
|
let mut table = tabled::Table::new([""]);
|
||||||
load_theme(&mut table, color_hm, theme);
|
load_theme(&mut table, style_computer, theme);
|
||||||
let cfg = table.get_config().clone();
|
let cfg = table.get_config().clone();
|
||||||
|
|
||||||
let val = nu_protocol_value_to_json(value, config, with_footer);
|
let val = nu_protocol_value_to_json(value, config, with_footer);
|
||||||
@ -200,11 +200,9 @@ fn connect_maps(map: &mut serde_json::Map<String, serde_json::Value>, value: ser
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_theme<R>(
|
//
|
||||||
table: &mut tabled::Table<R>,
|
fn load_theme<R>(table: &mut tabled::Table<R>, style_computer: &StyleComputer, theme: &TableTheme)
|
||||||
color_hm: &HashMap<String, nu_ansi_term::Style>,
|
where
|
||||||
theme: &TableTheme,
|
|
||||||
) where
|
|
||||||
R: Records,
|
R: Records,
|
||||||
{
|
{
|
||||||
let mut theme = theme.theme.clone();
|
let mut theme = theme.theme.clone();
|
||||||
@ -212,11 +210,11 @@ fn load_theme<R>(
|
|||||||
|
|
||||||
table.with(theme);
|
table.with(theme);
|
||||||
|
|
||||||
if let Some(color) = color_hm.get("separator") {
|
// color_config closures for "separator" are just given a null.
|
||||||
let color = color.paint(" ").to_string();
|
let color = style_computer.compute("separator", &Value::nothing(Span::unknown()));
|
||||||
if let Ok(color) = Color::try_from(color) {
|
let color = color.paint(" ").to_string();
|
||||||
table.with(color);
|
if let Ok(color) = Color::try_from(color) {
|
||||||
}
|
table.with(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
table.with(
|
table.with(
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use std::{cmp::min, collections::HashMap, fmt::Display};
|
use crate::table_theme::TableTheme;
|
||||||
|
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
|
use nu_color_config::TextStyle;
|
||||||
use nu_protocol::TrimStrategy;
|
use nu_protocol::TrimStrategy;
|
||||||
|
use std::{cmp::min, collections::HashMap};
|
||||||
use tabled::{
|
use tabled::{
|
||||||
alignment::AlignmentHorizontal,
|
alignment::AlignmentHorizontal,
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
@ -9,7 +10,6 @@ use tabled::{
|
|||||||
formatting::AlignmentStrategy,
|
formatting::AlignmentStrategy,
|
||||||
object::{Cell, Columns, Rows, Segment},
|
object::{Cell, Columns, Rows, Segment},
|
||||||
papergrid::{
|
papergrid::{
|
||||||
self,
|
|
||||||
records::{
|
records::{
|
||||||
cell_info::CellInfo, tcell::TCell, vec_records::VecRecords, Records, RecordsMut,
|
cell_info::CellInfo, tcell::TCell, vec_records::VecRecords, Records, RecordsMut,
|
||||||
},
|
},
|
||||||
@ -21,8 +21,6 @@ use tabled::{
|
|||||||
Alignment, Modify, ModifyObject, TableOption, Width,
|
Alignment, Modify, ModifyObject, TableOption, Width,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{table_theme::TableTheme, TextStyle};
|
|
||||||
|
|
||||||
/// Table represent a table view.
|
/// Table represent a table view.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
@ -577,26 +575,6 @@ fn truncate_columns_by_columns(data: &mut Data, theme: &TableTheme, termwidth: u
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
impl papergrid::Color for TextStyle {
|
|
||||||
fn fmt_prefix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if let Some(color) = &self.color_style {
|
|
||||||
color.prefix().fmt(f)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fmt_suffix(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
if let Some(color) = &self.color_style {
|
|
||||||
if !color.is_plain() {
|
|
||||||
f.write_str("\u{1b}[0m")?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The same as [`tabled::peaker::PriorityMax`] but prioritizes left columns first in case of equal width.
|
/// The same as [`tabled::peaker::PriorityMax`] but prioritizes left columns first in case of equal width.
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct PriorityMax;
|
pub struct PriorityMax;
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
|
use common::{create_row, styled_str, test_table, TestCase, VecCells};
|
||||||
use nu_protocol::TrimStrategy;
|
use nu_protocol::TrimStrategy;
|
||||||
use nu_table::{Table, TableConfig, TableTheme as theme};
|
use nu_table::{Table, TableConfig, TableTheme as theme};
|
||||||
|
|
||||||
use common::{create_row, styled_str, test_table, TestCase, VecCells};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn data_and_header_has_different_size() {
|
fn data_and_header_has_different_size() {
|
||||||
let table = Table::new(vec![create_row(3), create_row(5), create_row(5)], (3, 5));
|
let table = Table::new(vec![create_row(3), create_row(5), create_row(5)], (3, 5));
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::{create_row, create_table};
|
use common::{create_row, create_table};
|
||||||
|
|
||||||
use nu_table::{TableConfig, TableTheme as theme};
|
use nu_table::{TableConfig, TableTheme as theme};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use nu_table::{TableConfig, TableTheme as theme};
|
|
||||||
|
|
||||||
use common::{create_row as row, VecCells};
|
use common::{create_row as row, VecCells};
|
||||||
|
use nu_table::{TableConfig, TableTheme as theme};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rounded() {
|
fn test_rounded() {
|
||||||
|
@ -3,7 +3,6 @@ mod deansi;
|
|||||||
pub mod locale;
|
pub mod locale;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub use ctrl_c::was_pressed;
|
|
||||||
pub use locale::get_system_locale;
|
pub use locale::get_system_locale;
|
||||||
pub use utils::{
|
pub use utils::{
|
||||||
enable_vt_processing, get_default_config, get_default_env, get_ls_colors,
|
enable_vt_processing, get_default_config, get_default_env, get_ls_colors,
|
||||||
|
@ -127,7 +127,7 @@ module completions {
|
|||||||
# Get just the extern definitions without the custom completion commands
|
# Get just the extern definitions without the custom completion commands
|
||||||
use completions *
|
use completions *
|
||||||
|
|
||||||
# for more information on themes see
|
# For more information on themes, see
|
||||||
# https://www.nushell.sh/book/coloring_and_theming.html
|
# https://www.nushell.sh/book/coloring_and_theming.html
|
||||||
let dark_theme = {
|
let dark_theme = {
|
||||||
# color for nushell primitives
|
# color for nushell primitives
|
||||||
@ -135,11 +135,35 @@ let dark_theme = {
|
|||||||
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
||||||
header: green_bold
|
header: green_bold
|
||||||
empty: blue
|
empty: blue
|
||||||
bool: white
|
# Closures can be used to choose colors for specific values.
|
||||||
|
# The value (in this case, a bool) is piped into the closure.
|
||||||
|
bool: { if $in { 'light_cyan' } else { 'light_gray' } }
|
||||||
int: white
|
int: white
|
||||||
filesize: white
|
filesize: {|e|
|
||||||
|
if $e == 0b {
|
||||||
|
'white'
|
||||||
|
} else if $e < 1mb {
|
||||||
|
'cyan'
|
||||||
|
} else { 'blue' }
|
||||||
|
}
|
||||||
duration: white
|
duration: white
|
||||||
date: white
|
date: { (date now) - $in |
|
||||||
|
if $in < 1hr {
|
||||||
|
'#e61919'
|
||||||
|
} else if $in < 6hr {
|
||||||
|
'#e68019'
|
||||||
|
} else if $in < 1day {
|
||||||
|
'#e5e619'
|
||||||
|
} else if $in < 3day {
|
||||||
|
'#80e619'
|
||||||
|
} else if $in < 1wk {
|
||||||
|
'#19e619'
|
||||||
|
} else if $in < 6wk {
|
||||||
|
'#19e5e6'
|
||||||
|
} else if $in < 52wk {
|
||||||
|
'#197fe6'
|
||||||
|
} else { 'light_gray' }
|
||||||
|
}
|
||||||
range: white
|
range: white
|
||||||
float: white
|
float: white
|
||||||
string: white
|
string: white
|
||||||
@ -192,11 +216,35 @@ let light_theme = {
|
|||||||
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
leading_trailing_space_bg: { attr: n } # no fg, no bg, attr none effectively turns this off
|
||||||
header: green_bold
|
header: green_bold
|
||||||
empty: blue
|
empty: blue
|
||||||
bool: dark_gray
|
# Closures can be used to choose colors for specific values.
|
||||||
|
# The value (in this case, a bool) is piped into the closure.
|
||||||
|
bool: { if $in { 'dark_cyan' } else { 'dark_gray' } }
|
||||||
int: dark_gray
|
int: dark_gray
|
||||||
filesize: dark_gray
|
filesize: {|e|
|
||||||
|
if $e == 0b {
|
||||||
|
'dark_gray'
|
||||||
|
} else if $e < 1mb {
|
||||||
|
'cyan_bold'
|
||||||
|
} else { 'blue_bold' }
|
||||||
|
}
|
||||||
duration: dark_gray
|
duration: dark_gray
|
||||||
date: dark_gray
|
date: { (date now) - $in |
|
||||||
|
if $in < 1hr {
|
||||||
|
'#e61919'
|
||||||
|
} else if $in < 6hr {
|
||||||
|
'#e68019'
|
||||||
|
} else if $in < 1day {
|
||||||
|
'#e5e619'
|
||||||
|
} else if $in < 3day {
|
||||||
|
'#80e619'
|
||||||
|
} else if $in < 1wk {
|
||||||
|
'#19e619'
|
||||||
|
} else if $in < 6wk {
|
||||||
|
'#19e5e6'
|
||||||
|
} else if $in < 52wk {
|
||||||
|
'#197fe6'
|
||||||
|
} else { 'dark_gray' }
|
||||||
|
}
|
||||||
range: dark_gray
|
range: dark_gray
|
||||||
float: dark_gray
|
float: dark_gray
|
||||||
string: dark_gray
|
string: dark_gray
|
||||||
|
Loading…
Reference in New Issue
Block a user