From d8bec8668f5c6dfd51256c5bc5bd89f501d2174a Mon Sep 17 00:00:00 2001 From: Bahex Date: Sun, 27 Apr 2025 23:58:39 +0300 Subject: [PATCH] feat(table): make missing value symbol configurable (#15647) Co-authored-by: Bahex <17417311+Bahex@users.noreply.github.com> --- crates/nu-command/tests/commands/table.rs | 33 +++++++++++++++++++ crates/nu-protocol/src/config/table.rs | 7 ++++ crates/nu-table/src/common.rs | 7 ++-- crates/nu-table/src/types/expanded.rs | 5 ++- crates/nu-table/src/types/general.rs | 5 ++- .../nu-utils/src/default_files/doc_config.nu | 3 ++ 6 files changed, 54 insertions(+), 6 deletions(-) diff --git a/crates/nu-command/tests/commands/table.rs b/crates/nu-command/tests/commands/table.rs index 6bd7dfbc00..fa0acad897 100644 --- a/crates/nu-command/tests/commands/table.rs +++ b/crates/nu-command/tests/commands/table.rs @@ -3359,3 +3359,36 @@ fn table_expand_big_header() { ╰───┴──────────────────────────────────────────────────────────────────────────╯" ); } + +#[test] +fn table_missing_value() { + let actual = nu!(r###"[{foo: null} {} {}] | table"###); + assert_eq!( + actual.out, + "╭───┬─────╮\ + │ # │ foo │\ + ├───┼─────┤\ + │ 0 │ │\ + │ 1 │ ❎ │\ + │ 2 │ ❎ │\ + ╰───┴─────╯", + ) +} + +#[test] +fn table_missing_value_custom() { + let actual = nu!(r###" + $env.config.table.missing_value_symbol = "NULL"; + [{foo: null} {} {}] | table + "###); + assert_eq!( + actual.out, + "╭───┬──────╮\ + │ # │ foo │\ + ├───┼──────┤\ + │ 0 │ │\ + │ 1 │ NULL │\ + │ 2 │ NULL │\ + ╰───┴──────╯", + ) +} diff --git a/crates/nu-protocol/src/config/table.rs b/crates/nu-protocol/src/config/table.rs index 5c6aaf07d6..baf4b65412 100644 --- a/crates/nu-protocol/src/config/table.rs +++ b/crates/nu-protocol/src/config/table.rs @@ -340,6 +340,7 @@ pub struct TableConfig { pub header_on_separator: bool, pub abbreviated_row_count: Option, pub footer_inheritance: bool, + pub missing_value_symbol: String, } impl IntoValue for TableConfig { @@ -358,6 +359,7 @@ impl IntoValue for TableConfig { "header_on_separator" => self.header_on_separator.into_value(span), "abbreviated_row_count" => abbv_count, "footer_inheritance" => self.footer_inheritance.into_value(span), + "missing_value_symbol" => self.missing_value_symbol.into_value(span), } .into_value(span) } @@ -374,6 +376,7 @@ impl Default for TableConfig { padding: TableIndent::default(), abbreviated_row_count: None, footer_inheritance: false, + missing_value_symbol: "❎".into(), } } } @@ -411,6 +414,10 @@ impl UpdateFromValue for TableConfig { _ => errors.type_mismatch(path, Type::custom("int or nothing"), val), }, "footer_inheritance" => self.footer_inheritance.update(val, path, errors), + "missing_value_symbol" => match val.as_str() { + Ok(val) => self.missing_value_symbol = val.to_string(), + Err(_) => errors.type_mismatch(path, Type::String, val), + }, _ => errors.unknown_option(path, val), } } diff --git a/crates/nu-table/src/common.rs b/crates/nu-table/src/common.rs index 2f08e8f966..2df8815bdc 100644 --- a/crates/nu-table/src/common.rs +++ b/crates/nu-table/src/common.rs @@ -71,10 +71,9 @@ pub fn nu_value_to_string_clean(val: &Value, cfg: &Config, style_comp: &StyleCom (text, style) } -pub fn error_sign(style_computer: &StyleComputer) -> (String, TextStyle) { +pub fn error_sign(text: String, style_computer: &StyleComputer) -> (String, TextStyle) { // Though holes are not the same as null, the closure for "empty" is passed a null anyway. - let text = String::from("❎"); let style = style_computer.compute("empty", &Value::nothing(Span::unknown())); (text, TextStyle::with_style(Alignment::Center, style)) } @@ -122,9 +121,9 @@ pub fn get_value_style(value: &Value, config: &Config, style_computer: &StyleCom } } -pub fn get_empty_style(style_computer: &StyleComputer) -> NuText { +pub fn get_empty_style(text: String, style_computer: &StyleComputer) -> NuText { ( - String::from("❎"), + text, TextStyle::with_style( Alignment::Right, style_computer.compute("empty", &Value::nothing(Span::unknown())), diff --git a/crates/nu-table/src/types/expanded.rs b/crates/nu-table/src/types/expanded.rs index c93832fc19..423cf65aec 100644 --- a/crates/nu-table/src/types/expanded.rs +++ b/crates/nu-table/src/types/expanded.rs @@ -507,7 +507,10 @@ fn expand_entry_with_header(item: &Value, header: &str, cfg: Cfg<'_>) -> CellOut match item { Value::Record { val, .. } => match val.get(header) { Some(val) => expand_entry(val, cfg), - None => CellOutput::styled(error_sign(&cfg.opts.style_computer)), + None => CellOutput::styled(error_sign( + cfg.opts.config.table.missing_value_symbol.clone(), + &cfg.opts.style_computer, + )), }, _ => expand_entry(item, cfg), } diff --git a/crates/nu-table/src/types/general.rs b/crates/nu-table/src/types/general.rs index 81d4906783..e7d9fea8dd 100644 --- a/crates/nu-table/src/types/general.rs +++ b/crates/nu-table/src/types/general.rs @@ -206,7 +206,10 @@ fn get_string_value_with_header(item: &Value, header: &str, opts: &TableOpts) -> match item { Value::Record { val, .. } => match val.get(header) { Some(value) => get_string_value(value, opts), - None => get_empty_style(&opts.style_computer), + None => get_empty_style( + opts.config.table.missing_value_symbol.clone(), + &opts.style_computer, + ), }, value => get_string_value(value, opts), } diff --git a/crates/nu-utils/src/default_files/doc_config.nu b/crates/nu-utils/src/default_files/doc_config.nu index 5b53d67de2..ae1de9145f 100644 --- a/crates/nu-utils/src/default_files/doc_config.nu +++ b/crates/nu-utils/src/default_files/doc_config.nu @@ -355,6 +355,9 @@ $env.config.table.abbreviated_row_count = null # false: Always apply `footer_mode` rules to the parent table $env.config.table.footer_inheritance = false +# missing_value_symbol (string): The symbol shown for missing values +$env.config.table.missing_value_symbol = "❎" + # ---------------- # Datetime Display # ----------------