mirror of
https://github.com/nushell/nushell.git
synced 2024-11-24 17:34:00 +01:00
Introduce footer_inheritance option (#14070)
```nu $env.config.table.footer_inheritance = true ``` close #14060
This commit is contained in:
parent
619211c1bf
commit
3ec1c40320
@ -1088,7 +1088,7 @@ fn create_empty_placeholder(
|
|||||||
let data = vec![vec![cell]];
|
let data = vec![vec![cell]];
|
||||||
let mut table = NuTable::from(data);
|
let mut table = NuTable::from(data);
|
||||||
table.set_data_style(TextStyle::default().dimmed());
|
table.set_data_style(TextStyle::default().dimmed());
|
||||||
let out = TableOutput::new(table, false, false);
|
let out = TableOutput::new(table, false, false, false);
|
||||||
|
|
||||||
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
||||||
let config = create_nu_table_config(&config, style_computer, &out, false, TableMode::default());
|
let config = create_nu_table_config(&config, style_computer, &out, false, TableMode::default());
|
||||||
|
@ -2905,3 +2905,39 @@ fn table_general_header_on_separator_issue1() {
|
|||||||
let actual = nu!("$env.config.table.header_on_separator = true; [['Llll oo Bbbbbbbb' 'Bbbbbbbb Aaaa' Nnnnnn Ggggg 'Xxxxx Llllllll #' Bbb 'Pppp Ccccc' 'Rrrrrrrr Dddd' Rrrrrr 'Rrrrrr Ccccc II' 'Rrrrrr Ccccc Ppppppp II' 'Pppppp Dddddddd Tttt' 'Pppppp Dddddddd Dddd' 'Rrrrrrrrr Trrrrrr' 'Pppppp Ppppp Dddd' 'Ppppp Dddd' Hhhh]; [RRRRRRR FFFFFFFF UUUU VV 202407160001 BBB 1 '7/16/2024' '' AAA-1111 AAA-1111-11 '7 YEARS' 2555 'RRRRRRRR DDDD' '7/16/2031' '7/16/2031' NN]] | table --width=87 --theme basic");
|
let actual = nu!("$env.config.table.header_on_separator = true; [['Llll oo Bbbbbbbb' 'Bbbbbbbb Aaaa' Nnnnnn Ggggg 'Xxxxx Llllllll #' Bbb 'Pppp Ccccc' 'Rrrrrrrr Dddd' Rrrrrr 'Rrrrrr Ccccc II' 'Rrrrrr Ccccc Ppppppp II' 'Pppppp Dddddddd Tttt' 'Pppppp Dddddddd Dddd' 'Rrrrrrrrr Trrrrrr' 'Pppppp Ppppp Dddd' 'Ppppp Dddd' Hhhh]; [RRRRRRR FFFFFFFF UUUU VV 202407160001 BBB 1 '7/16/2024' '' AAA-1111 AAA-1111-11 '7 YEARS' 2555 'RRRRRRRR DDDD' '7/16/2031' '7/16/2031' NN]] | table --width=87 --theme basic");
|
||||||
assert_eq!(actual.out, "+-#-+-Llll oo Bbbbbbbb-+-Bbbbbbbb Aaaa-+-Nnnnnn-+-Ggggg-+-Xxxxx Llllllll #-+-...-+| 0 | RRRRRRR | FFFFFFFF | UUUU | VV | 202407160001 | ... |+---+------------------+---------------+--------+-------+------------------+-----+");
|
assert_eq!(actual.out, "+-#-+-Llll oo Bbbbbbbb-+-Bbbbbbbb Aaaa-+-Nnnnnn-+-Ggggg-+-Xxxxx Llllllll #-+-...-+| 0 | RRRRRRR | FFFFFFFF | UUUU | VV | 202407160001 | ... |+---+------------------+---------------+--------+-------+------------------+-----+");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_footer_inheritance() {
|
||||||
|
let table1 = format!(
|
||||||
|
"[ [ head1, head2, head3 ]; {} ]",
|
||||||
|
(0..212)
|
||||||
|
.map(|_| "[ 79 79 79 ]")
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ")
|
||||||
|
);
|
||||||
|
|
||||||
|
let structure = format!(
|
||||||
|
"{{\
|
||||||
|
field0: [ [ y1, y2, y3 ]; [ 1 2 3 ] [ 79 79 79 ] [ {{ f1: 'a string', f2: 1000 }}, 1, 2 ] ],\
|
||||||
|
field1: [ a, b, c ],\
|
||||||
|
field2: [ 123, 234, 345 ],\
|
||||||
|
field3: {},\
|
||||||
|
field4: {{ f1: 1, f2: 3, f3: {{ f1: f1, f2: f2, f3: f3 }} }},\
|
||||||
|
field5: [ [ x1, x2, x3 ]; [ 1 2 3 ] [ 79 79 79 ] [ {{ f1: 'a string', f2: 1000 }}, 1, 2 ] ],\
|
||||||
|
}}",
|
||||||
|
table1
|
||||||
|
);
|
||||||
|
let actual = nu!(format!(
|
||||||
|
"$env.config.table.footer_inheritance = true; {structure} | table --width=80 --expand"
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(actual.out.match_indices("head1").count(), 2);
|
||||||
|
assert_eq!(actual.out.match_indices("head2").count(), 2);
|
||||||
|
assert_eq!(actual.out.match_indices("head3").count(), 2);
|
||||||
|
assert_eq!(actual.out.match_indices("y1").count(), 1);
|
||||||
|
assert_eq!(actual.out.match_indices("y2").count(), 1);
|
||||||
|
assert_eq!(actual.out.match_indices("y3").count(), 1);
|
||||||
|
assert_eq!(actual.out.match_indices("x1").count(), 1);
|
||||||
|
assert_eq!(actual.out.match_indices("x2").count(), 1);
|
||||||
|
assert_eq!(actual.out.match_indices("x3").count(), 1);
|
||||||
|
}
|
||||||
|
@ -333,6 +333,7 @@ pub struct TableConfig {
|
|||||||
pub trim: TrimStrategy,
|
pub trim: TrimStrategy,
|
||||||
pub header_on_separator: bool,
|
pub header_on_separator: bool,
|
||||||
pub abbreviated_row_count: Option<usize>,
|
pub abbreviated_row_count: Option<usize>,
|
||||||
|
pub footer_inheritance: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IntoValue for TableConfig {
|
impl IntoValue for TableConfig {
|
||||||
@ -350,6 +351,7 @@ impl IntoValue for TableConfig {
|
|||||||
"trim" => self.trim.into_value(span),
|
"trim" => self.trim.into_value(span),
|
||||||
"header_on_separator" => self.header_on_separator.into_value(span),
|
"header_on_separator" => self.header_on_separator.into_value(span),
|
||||||
"abbreviated_row_count" => abbv_count,
|
"abbreviated_row_count" => abbv_count,
|
||||||
|
"footer_inheritance" => self.footer_inheritance.into_value(span),
|
||||||
}
|
}
|
||||||
.into_value(span)
|
.into_value(span)
|
||||||
}
|
}
|
||||||
@ -365,6 +367,7 @@ impl Default for TableConfig {
|
|||||||
header_on_separator: false,
|
header_on_separator: false,
|
||||||
padding: TableIndent::default(),
|
padding: TableIndent::default(),
|
||||||
abbreviated_row_count: None,
|
abbreviated_row_count: None,
|
||||||
|
footer_inheritance: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,6 +404,7 @@ impl UpdateFromValue for TableConfig {
|
|||||||
}
|
}
|
||||||
_ => errors.type_mismatch(path, Type::custom("int or nothing"), val),
|
_ => errors.type_mismatch(path, Type::custom("int or nothing"), val),
|
||||||
},
|
},
|
||||||
|
"footer_inheritance" => self.footer_inheritance.update(val, path, errors),
|
||||||
_ => errors.unknown_option(path, val),
|
_ => errors.unknown_option(path, val),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,12 @@ pub fn create_nu_table_config(
|
|||||||
expand: bool,
|
expand: bool,
|
||||||
mode: TableMode,
|
mode: TableMode,
|
||||||
) -> NuTableConfig {
|
) -> NuTableConfig {
|
||||||
|
let with_footer = (config.table.footer_inheritance && out.with_footer)
|
||||||
|
|| with_footer(config, out.with_header, out.table.count_rows());
|
||||||
|
|
||||||
NuTableConfig {
|
NuTableConfig {
|
||||||
theme: load_theme(mode),
|
theme: load_theme(mode),
|
||||||
with_footer: with_footer(config, out.with_header, out.table.count_rows()),
|
with_footer,
|
||||||
with_index: out.with_index,
|
with_index: out.with_index,
|
||||||
with_header: out.with_header,
|
with_header: out.with_header,
|
||||||
split_color: Some(lookup_separator_color(comp)),
|
split_color: Some(lookup_separator_color(comp)),
|
||||||
|
@ -5,7 +5,7 @@ use crate::{
|
|||||||
NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
|
NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
|
||||||
},
|
},
|
||||||
string_width,
|
string_width,
|
||||||
types::has_index,
|
types::{has_footer, has_index},
|
||||||
NuTable, NuTableCell, TableOpts, TableOutput,
|
NuTable, NuTableCell, TableOpts, TableOutput,
|
||||||
};
|
};
|
||||||
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
||||||
@ -31,11 +31,12 @@ impl ExpandedTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_value(self, item: &Value, opts: TableOpts<'_>) -> NuText {
|
pub fn build_value(self, item: &Value, opts: TableOpts<'_>) -> NuText {
|
||||||
expanded_table_entry2(item, Cfg { opts, format: self })
|
let cell = expanded_table_entry2(item, Cfg { opts, format: self });
|
||||||
|
(cell.text, cell.style)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_map(self, record: &Record, opts: TableOpts<'_>) -> StringResult {
|
pub fn build_map(self, record: &Record, opts: TableOpts<'_>) -> StringResult {
|
||||||
expanded_table_kv(record, Cfg { opts, format: self })
|
expanded_table_kv(record, Cfg { opts, format: self }).map(|cell| cell.map(|cell| cell.text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult {
|
pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult {
|
||||||
@ -58,6 +59,39 @@ struct Cfg<'a> {
|
|||||||
format: ExpandedTable,
|
format: ExpandedTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct CellOutput {
|
||||||
|
text: String,
|
||||||
|
style: TextStyle,
|
||||||
|
is_big: bool,
|
||||||
|
is_expanded: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CellOutput {
|
||||||
|
fn new(text: String, style: TextStyle, is_big: bool, is_expanded: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
style,
|
||||||
|
is_big,
|
||||||
|
is_expanded,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clean(text: String, is_big: bool, is_expanded: bool) -> Self {
|
||||||
|
Self::new(text, Default::default(), is_big, is_expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text(text: String) -> Self {
|
||||||
|
Self::styled((text, Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn styled(text: NuText) -> Self {
|
||||||
|
Self::new(text.0, text.1, false, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type CellResult = Result<Option<CellOutput>, ShellError>;
|
||||||
|
|
||||||
fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
||||||
const PADDING_SPACE: usize = 2;
|
const PADDING_SPACE: usize = 2;
|
||||||
const SPLIT_LINE_SPACE: usize = 1;
|
const SPLIT_LINE_SPACE: usize = 1;
|
||||||
@ -83,6 +117,7 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
|||||||
|
|
||||||
let with_index = has_index(&cfg.opts, &headers);
|
let with_index = has_index(&cfg.opts, &headers);
|
||||||
let row_offset = cfg.opts.index_offset;
|
let row_offset = cfg.opts.index_offset;
|
||||||
|
let mut is_footer_used = false;
|
||||||
|
|
||||||
// The header with the INDEX is removed from the table headers since
|
// The header with the INDEX is removed from the table headers since
|
||||||
// it is added to the natural table index
|
// it is added to the natural table index
|
||||||
@ -148,21 +183,25 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inner_cfg = update_config(cfg.clone(), available_width);
|
let inner_cfg = update_config(cfg.clone(), available_width);
|
||||||
let (mut text, style) = expanded_table_entry2(item, inner_cfg);
|
let mut cell = expanded_table_entry2(item, inner_cfg);
|
||||||
|
|
||||||
let value_width = string_width(&text);
|
let value_width = string_width(&cell.text);
|
||||||
if value_width > available_width {
|
if value_width > available_width {
|
||||||
// it must only happen when a string is produced, so we can safely wrap it.
|
// it must only happen when a string is produced, so we can safely wrap it.
|
||||||
// (it might be string table representation as well) (I guess I mean default { table ...} { list ...})
|
// (it might be string table representation as well) (I guess I mean default { table ...} { list ...})
|
||||||
//
|
//
|
||||||
// todo: Maybe convert_to_table2_entry could do for strings to not mess caller code?
|
// todo: Maybe convert_to_table2_entry could do for strings to not mess caller code?
|
||||||
|
|
||||||
text = wrap_text(&text, available_width, cfg.opts.config);
|
cell.text = wrap_text(&cell.text, available_width, cfg.opts.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = NuTableCell::new(text);
|
let value = NuTableCell::new(cell.text);
|
||||||
data[row].push(value);
|
data[row].push(value);
|
||||||
data_styles.insert((row, with_index as usize), style);
|
data_styles.insert((row, with_index as usize), cell.style);
|
||||||
|
|
||||||
|
if cell.is_big {
|
||||||
|
is_footer_used = cell.is_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = NuTable::from(data);
|
let mut table = NuTable::from(data);
|
||||||
@ -170,7 +209,12 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
|||||||
table.set_index_style(get_index_style(cfg.opts.style_computer));
|
table.set_index_style(get_index_style(cfg.opts.style_computer));
|
||||||
set_data_styles(&mut table, data_styles);
|
set_data_styles(&mut table, data_styles);
|
||||||
|
|
||||||
return Ok(Some(TableOutput::new(table, false, with_index)));
|
return Ok(Some(TableOutput::new(
|
||||||
|
table,
|
||||||
|
false,
|
||||||
|
with_index,
|
||||||
|
is_footer_used,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if !headers.is_empty() {
|
if !headers.is_empty() {
|
||||||
@ -233,22 +277,26 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let inner_cfg = update_config(cfg.clone(), available);
|
let inner_cfg = update_config(cfg.clone(), available);
|
||||||
let (mut text, style) = expanded_table_entry(item, header.as_str(), inner_cfg);
|
let mut cell = expanded_table_entry(item, header.as_str(), inner_cfg);
|
||||||
|
|
||||||
let mut value_width = string_width(&text);
|
let mut value_width = string_width(&cell.text);
|
||||||
if value_width > available {
|
if value_width > available {
|
||||||
// it must only happen when a string is produced, so we can safely wrap it.
|
// it must only happen when a string is produced, so we can safely wrap it.
|
||||||
// (it might be string table representation as well)
|
// (it might be string table representation as well)
|
||||||
|
|
||||||
text = wrap_text(&text, available, cfg.opts.config);
|
cell.text = wrap_text(&cell.text, available, cfg.opts.config);
|
||||||
value_width = available;
|
value_width = available;
|
||||||
}
|
}
|
||||||
|
|
||||||
column_width = max(column_width, value_width);
|
column_width = max(column_width, value_width);
|
||||||
|
|
||||||
let value = NuTableCell::new(text);
|
let value = NuTableCell::new(cell.text);
|
||||||
data[row + 1].push(value);
|
data[row + 1].push(value);
|
||||||
data_styles.insert((row + 1, col + with_index as usize), style);
|
data_styles.insert((row + 1, col + with_index as usize), cell.style);
|
||||||
|
|
||||||
|
if cell.is_big {
|
||||||
|
is_footer_used = cell.is_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let head_cell = NuTableCell::new(header);
|
let head_cell = NuTableCell::new(header);
|
||||||
@ -326,10 +374,12 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
|||||||
table.set_indent(cfg.opts.indent.0, cfg.opts.indent.1);
|
table.set_indent(cfg.opts.indent.0, cfg.opts.indent.1);
|
||||||
set_data_styles(&mut table, data_styles);
|
set_data_styles(&mut table, data_styles);
|
||||||
|
|
||||||
Ok(Some(TableOutput::new(table, true, with_index)))
|
let has_footer = is_footer_used || has_footer(&cfg.opts, table.count_rows() as u64);
|
||||||
|
|
||||||
|
Ok(Some(TableOutput::new(table, true, with_index, has_footer)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
|
fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> CellResult {
|
||||||
let theme = load_theme(cfg.opts.mode);
|
let theme = load_theme(cfg.opts.mode);
|
||||||
let key_width = record
|
let key_width = record
|
||||||
.columns()
|
.columns()
|
||||||
@ -345,11 +395,13 @@ fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
|
|||||||
|
|
||||||
let value_width = cfg.opts.width - key_width - count_borders - padding - padding;
|
let value_width = cfg.opts.width - key_width - count_borders - padding - padding;
|
||||||
|
|
||||||
|
let mut with_footer = false;
|
||||||
|
|
||||||
let mut data = Vec::with_capacity(record.len());
|
let mut data = Vec::with_capacity(record.len());
|
||||||
for (key, value) in record {
|
for (key, value) in record {
|
||||||
cfg.opts.signals.check(cfg.opts.span)?;
|
cfg.opts.signals.check(cfg.opts.span)?;
|
||||||
|
|
||||||
let (value, is_expanded) = match expand_table_value(value, value_width, &cfg)? {
|
let cell = match expand_table_value(value, value_width, &cfg)? {
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
@ -358,35 +410,40 @@ fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
|
|||||||
// we could use Padding for it but,
|
// we could use Padding for it but,
|
||||||
// the easiest way to do so is just push a new_line char before
|
// the easiest way to do so is just push a new_line char before
|
||||||
let mut key = key.to_owned();
|
let mut key = key.to_owned();
|
||||||
if !key.is_empty() && is_expanded && theme.has_top() {
|
if !key.is_empty() && cell.is_expanded && theme.has_top() {
|
||||||
key.insert(0, '\n');
|
key.insert(0, '\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
let key = NuTableCell::new(key);
|
let key = NuTableCell::new(key);
|
||||||
let val = NuTableCell::new(value);
|
let val = NuTableCell::new(cell.text);
|
||||||
let row = vec![key, val];
|
let row = vec![key, val];
|
||||||
|
|
||||||
data.push(row);
|
data.push(row);
|
||||||
|
|
||||||
|
if cell.is_big {
|
||||||
|
with_footer = cell.is_big;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = NuTable::from(data);
|
let mut table = NuTable::from(data);
|
||||||
table.set_index_style(get_key_style(&cfg));
|
table.set_index_style(get_key_style(&cfg));
|
||||||
table.set_indent(cfg.opts.indent.0, cfg.opts.indent.1);
|
table.set_indent(cfg.opts.indent.0, cfg.opts.indent.1);
|
||||||
|
|
||||||
let out = TableOutput::new(table, false, true);
|
let out = TableOutput::new(table, false, true, with_footer);
|
||||||
|
|
||||||
maybe_expand_table(out, cfg.opts.width, &cfg.opts)
|
maybe_expand_table(out, cfg.opts.width, &cfg.opts)
|
||||||
|
.map(|value| value.map(|value| CellOutput::clean(value, with_footer, false)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// the flag is used as an optimization to not do `value.lines().count()` search.
|
// the flag is used as an optimization to not do `value.lines().count()` search.
|
||||||
fn expand_table_value(
|
fn expand_table_value(value: &Value, value_width: usize, cfg: &Cfg<'_>) -> CellResult {
|
||||||
value: &Value,
|
|
||||||
value_width: usize,
|
|
||||||
cfg: &Cfg<'_>,
|
|
||||||
) -> Result<Option<(String, bool)>, ShellError> {
|
|
||||||
let is_limited = matches!(cfg.format.expand_limit, Some(0));
|
let is_limited = matches!(cfg.format.expand_limit, Some(0));
|
||||||
if is_limited {
|
if is_limited {
|
||||||
return Ok(Some((value_to_string_clean(value, cfg), false)));
|
return Ok(Some(CellOutput::clean(
|
||||||
|
value_to_string_clean(value, cfg),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = value.span();
|
let span = value.span();
|
||||||
@ -400,42 +457,46 @@ fn expand_table_value(
|
|||||||
let cfg = create_table_cfg(cfg, &out);
|
let cfg = create_table_cfg(cfg, &out);
|
||||||
let value = out.table.draw(cfg, value_width);
|
let value = out.table.draw(cfg, value_width);
|
||||||
match value {
|
match value {
|
||||||
Some(result) => Ok(Some((result, true))),
|
Some(value) => Ok(Some(CellOutput::clean(value, out.with_footer, true))),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// it means that the list is empty
|
// it means that the list is empty
|
||||||
Ok(Some((
|
Ok(Some(CellOutput::text(value_to_wrapped_string(
|
||||||
value_to_wrapped_string(value, cfg, value_width),
|
value,
|
||||||
false,
|
cfg,
|
||||||
)))
|
value_width,
|
||||||
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => {
|
||||||
if record.is_empty() {
|
if record.is_empty() {
|
||||||
// Like list case return styled string instead of empty value
|
// Like list case return styled string instead of empty value
|
||||||
return Ok(Some((
|
return Ok(Some(CellOutput::text(value_to_wrapped_string(
|
||||||
value_to_wrapped_string(value, cfg, value_width),
|
value,
|
||||||
false,
|
cfg,
|
||||||
)));
|
value_width,
|
||||||
|
))));
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner_cfg = update_config(dive_options(cfg, span), value_width);
|
let inner_cfg = update_config(dive_options(cfg, span), value_width);
|
||||||
let result = expanded_table_kv(record, inner_cfg)?;
|
let result = expanded_table_kv(record, inner_cfg)?;
|
||||||
match result {
|
match result {
|
||||||
Some(result) => Ok(Some((result, true))),
|
Some(result) => Ok(Some(CellOutput::clean(result.text, result.is_big, true))),
|
||||||
None => Ok(Some((
|
None => Ok(Some(CellOutput::text(value_to_wrapped_string(
|
||||||
value_to_wrapped_string(value, cfg, value_width),
|
value,
|
||||||
false,
|
cfg,
|
||||||
))),
|
value_width,
|
||||||
|
)))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => Ok(Some(CellOutput::text(value_to_wrapped_string_clean(
|
||||||
let text = value_to_wrapped_string_clean(value, cfg, value_width);
|
value,
|
||||||
Ok(Some((text, false)))
|
cfg,
|
||||||
}
|
value_width,
|
||||||
|
)))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,27 +504,35 @@ fn get_key_style(cfg: &Cfg<'_>) -> TextStyle {
|
|||||||
get_header_style(cfg.opts.style_computer).alignment(Alignment::Left)
|
get_header_style(cfg.opts.style_computer).alignment(Alignment::Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expanded_table_entry(item: &Value, header: &str, cfg: Cfg<'_>) -> NuText {
|
fn expanded_table_entry(item: &Value, header: &str, cfg: Cfg<'_>) -> CellOutput {
|
||||||
match item {
|
match item {
|
||||||
Value::Record { val, .. } => match val.get(header) {
|
Value::Record { val, .. } => match val.get(header) {
|
||||||
Some(val) => expanded_table_entry2(val, cfg),
|
Some(val) => expanded_table_entry2(val, cfg),
|
||||||
None => error_sign(cfg.opts.style_computer),
|
None => CellOutput::styled(error_sign(cfg.opts.style_computer)),
|
||||||
},
|
},
|
||||||
_ => expanded_table_entry2(item, cfg),
|
_ => expanded_table_entry2(item, cfg),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expanded_table_entry2(item: &Value, cfg: Cfg<'_>) -> NuText {
|
fn expanded_table_entry2(item: &Value, cfg: Cfg<'_>) -> CellOutput {
|
||||||
let is_limit_reached = matches!(cfg.format.expand_limit, Some(0));
|
let is_limit_reached = matches!(cfg.format.expand_limit, Some(0));
|
||||||
if is_limit_reached {
|
if is_limit_reached {
|
||||||
return nu_value_to_string_clean(item, cfg.opts.config, cfg.opts.style_computer);
|
return CellOutput::styled(nu_value_to_string_clean(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = item.span();
|
let span = item.span();
|
||||||
match &item {
|
match &item {
|
||||||
Value::Record { val: record, .. } => {
|
Value::Record { val: record, .. } => {
|
||||||
if record.is_empty() {
|
if record.is_empty() {
|
||||||
return nu_value_to_string(item, cfg.opts.config, cfg.opts.style_computer);
|
return CellOutput::styled(nu_value_to_string(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// we verify what is the structure of a Record cause it might represent
|
// we verify what is the structure of a Record cause it might represent
|
||||||
@ -471,18 +540,22 @@ fn expanded_table_entry2(item: &Value, cfg: Cfg<'_>) -> NuText {
|
|||||||
let table = expanded_table_kv(record, inner_cfg);
|
let table = expanded_table_kv(record, inner_cfg);
|
||||||
|
|
||||||
match table {
|
match table {
|
||||||
Ok(Some(table)) => (table, TextStyle::default()),
|
Ok(Some(table)) => table,
|
||||||
_ => nu_value_to_string(item, cfg.opts.config, cfg.opts.style_computer),
|
_ => CellOutput::styled(nu_value_to_string(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
if cfg.format.flatten && is_simple_list(vals) {
|
if cfg.format.flatten && is_simple_list(vals) {
|
||||||
return value_list_to_string(
|
return CellOutput::styled(value_list_to_string(
|
||||||
vals,
|
vals,
|
||||||
cfg.opts.config,
|
cfg.opts.config,
|
||||||
cfg.opts.style_computer,
|
cfg.opts.style_computer,
|
||||||
&cfg.format.flatten_sep,
|
&cfg.format.flatten_sep,
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let inner_cfg = dive_options(&cfg, span);
|
let inner_cfg = dive_options(&cfg, span);
|
||||||
@ -490,17 +563,31 @@ fn expanded_table_entry2(item: &Value, cfg: Cfg<'_>) -> NuText {
|
|||||||
|
|
||||||
let out = match table {
|
let out = match table {
|
||||||
Ok(Some(out)) => out,
|
Ok(Some(out)) => out,
|
||||||
_ => return nu_value_to_string(item, cfg.opts.config, cfg.opts.style_computer),
|
_ => {
|
||||||
|
return CellOutput::styled(nu_value_to_string(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let table_config = create_table_cfg(&cfg, &out);
|
let table_config = create_table_cfg(&cfg, &out);
|
||||||
let table = out.table.draw(table_config, usize::MAX);
|
let table = out.table.draw(table_config, usize::MAX);
|
||||||
match table {
|
match table {
|
||||||
Some(table) => (table, TextStyle::default()),
|
Some(table) => CellOutput::clean(table, out.with_footer, false),
|
||||||
None => nu_value_to_string(item, cfg.opts.config, cfg.opts.style_computer),
|
None => CellOutput::styled(nu_value_to_string(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => nu_value_to_string_clean(item, cfg.opts.config, cfg.opts.style_computer),
|
_ => CellOutput::styled(nu_value_to_string_clean(
|
||||||
|
item,
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult {
|
|||||||
let mut table = NuTable::from(data);
|
let mut table = NuTable::from(data);
|
||||||
table.set_index_style(TextStyle::default_field());
|
table.set_index_style(TextStyle::default_field());
|
||||||
|
|
||||||
let mut out = TableOutput::new(table, false, true);
|
let mut out = TableOutput::new(table, false, true, false);
|
||||||
|
|
||||||
let left = opts.config.table.padding.left;
|
let left = opts.config.table.padding.left;
|
||||||
let right = opts.config.table.padding.right;
|
let right = opts.config.table.padding.right;
|
||||||
@ -82,7 +82,7 @@ fn table(input: &[Value], opts: &TableOpts<'_>) -> TableResult {
|
|||||||
let with_header = !headers.is_empty();
|
let with_header = !headers.is_empty();
|
||||||
if !with_header {
|
if !with_header {
|
||||||
let table = to_table_with_no_header(input, with_index, row_offset, opts)?;
|
let table = to_table_with_no_header(input, with_index, row_offset, opts)?;
|
||||||
let table = table.map(|table| TableOutput::new(table, false, with_index));
|
let table = table.map(|table| TableOutput::new(table, false, with_index, false));
|
||||||
return Ok(table);
|
return Ok(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ fn table(input: &[Value], opts: &TableOpts<'_>) -> TableResult {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let table = to_table_with_header(input, &headers, with_index, row_offset, opts)?;
|
let table = to_table_with_header(input, &headers, with_index, row_offset, opts)?;
|
||||||
let table = table.map(|table| TableOutput::new(table, true, with_index));
|
let table = table.map(|table| TableOutput::new(table, true, with_index, false));
|
||||||
|
|
||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
use terminal_size::{terminal_size, Height, Width};
|
||||||
|
|
||||||
|
use crate::{common::INDEX_COLUMN_NAME, NuTable};
|
||||||
|
use nu_color_config::StyleComputer;
|
||||||
|
use nu_protocol::{Config, FooterMode, Signals, Span, TableIndexMode, TableMode};
|
||||||
|
|
||||||
mod collapse;
|
mod collapse;
|
||||||
mod expanded;
|
mod expanded;
|
||||||
mod general;
|
mod general;
|
||||||
@ -6,22 +12,20 @@ pub use collapse::CollapsedTable;
|
|||||||
pub use expanded::ExpandedTable;
|
pub use expanded::ExpandedTable;
|
||||||
pub use general::JustTable;
|
pub use general::JustTable;
|
||||||
|
|
||||||
use crate::{common::INDEX_COLUMN_NAME, NuTable};
|
|
||||||
use nu_color_config::StyleComputer;
|
|
||||||
use nu_protocol::{Config, Signals, Span, TableIndexMode, TableMode};
|
|
||||||
|
|
||||||
pub struct TableOutput {
|
pub struct TableOutput {
|
||||||
pub table: NuTable,
|
pub table: NuTable,
|
||||||
pub with_header: bool,
|
pub with_header: bool,
|
||||||
pub with_index: bool,
|
pub with_index: bool,
|
||||||
|
pub with_footer: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableOutput {
|
impl TableOutput {
|
||||||
pub fn new(table: NuTable, with_header: bool, with_index: bool) -> Self {
|
pub fn new(table: NuTable, with_header: bool, with_index: bool, with_footer: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
table,
|
table,
|
||||||
with_header,
|
with_header,
|
||||||
with_index,
|
with_index,
|
||||||
|
with_footer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,3 +79,23 @@ fn has_index(opts: &TableOpts<'_>, headers: &[String]) -> bool {
|
|||||||
|
|
||||||
with_index && !opts.index_remove
|
with_index && !opts.index_remove
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_footer(opts: &TableOpts<'_>, count_records: u64) -> bool {
|
||||||
|
match opts.config.footer_mode {
|
||||||
|
// Only show the footer if there are more than RowCount rows
|
||||||
|
FooterMode::RowCount(limit) => count_records > limit,
|
||||||
|
// Always show the footer
|
||||||
|
FooterMode::Always => true,
|
||||||
|
// Never show the footer
|
||||||
|
FooterMode::Never => false,
|
||||||
|
// Calculate the screen height and row count, if screen height is larger than row count, don't show footer
|
||||||
|
FooterMode::Auto => {
|
||||||
|
let (_width, height) = match terminal_size() {
|
||||||
|
Some((w, h)) => (Width(w.0).0 as u64, Height(h.0).0 as u64),
|
||||||
|
None => (Width(0).0 as u64, Height(0).0 as u64),
|
||||||
|
};
|
||||||
|
|
||||||
|
height <= count_records
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -169,6 +169,7 @@ $env.config = {
|
|||||||
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
|
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
|
||||||
}
|
}
|
||||||
header_on_separator: false # show header text on separator/border line
|
header_on_separator: false # show header text on separator/border line
|
||||||
|
footer_inheritance: false # render footer in parent table if child is big enough (extended table option)
|
||||||
# abbreviated_row_count: 10 # limit data rows from top and bottom after reaching a set point
|
# abbreviated_row_count: 10 # limit data rows from top and bottom after reaching a set point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user