nu-table/ Add table.indent configuration (#9983)

Hi there.

Am I got it right?

ref: https://github.com/zhiburt/tabled/issues/358
cc: @fdncred

---------

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
Maxim Zhiburt 2023-08-11 16:37:16 +03:00 committed by GitHub
parent a0cecf7658
commit aa37572ddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 226 additions and 13 deletions

View file

@ -362,7 +362,8 @@ fn handle_record(
let result = if cols.is_empty() { let result = if cols.is_empty() {
create_empty_placeholder("record", term_width, engine_state, stack) create_empty_placeholder("record", term_width, engine_state, stack)
} else { } else {
let opts = TableOpts::new(config, style_computer, ctrlc, span, 0, term_width); let indent = (config.table_indent.left, config.table_indent.right);
let opts = TableOpts::new(config, style_computer, ctrlc, span, 0, term_width, indent);
let result = build_table_kv(cols, vals, table_view, opts, span)?; let result = build_table_kv(cols, vals, table_view, opts, span)?;
match result { match result {
Some(output) => maybe_strip_color(output, config), Some(output) => maybe_strip_color(output, config),
@ -691,6 +692,7 @@ impl PagingTableCreator {
self.head, self.head,
self.row_offset, self.row_offset,
get_width_param(self.width_param), get_width_param(self.width_param),
(cfg.table_indent.left, cfg.table_indent.right),
) )
} }
} }

View file

@ -2560,3 +2560,107 @@ fn theme_cmd(theme: &str, footer: bool, then: &str) -> String {
format!("$env.config.table.mode = {theme}; $env.config.table.header_on_separator = true; {with_foorter}; {then}") format!("$env.config.table.mode = {theme}; $env.config.table.header_on_separator = true; {with_foorter}; {then}")
} }
#[test]
fn table_padding_not_default() {
let actual = nu!("$env.config.table.padding = 5; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table");
assert_eq!(
actual.out,
"╭───────────┬───────────┬───────────┬────────────────────────╮\
# a b c \
\
0 1 2 3 \
1 4 5 [list 3 items] \
"
);
}
#[test]
fn table_padding_zero() {
let actual = nu!(
"$env.config.table.padding = {left: 0, right: 0}; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table"
);
assert_eq!(
actual.out,
"╭─┬─┬─┬──────────────╮\
#ab c \
\
012 3\
145[list 3 items]\
"
);
}
#[test]
fn table_expand_padding_not_default() {
let actual = nu!("$env.config.table.padding = 5; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table -e");
assert_eq!(
actual.out,
"╭───────────┬───────────┬───────────┬───────────────────────────────────╮\
# a b c \
\
0 1 2 3 \
1 4 5 \
0 1 \
1 2 \
2 3 \
\
"
);
}
#[test]
fn table_expand_padding_zero() {
let actual = nu!("$env.config.table.padding = {left: 0, right: 0}; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table -e");
assert_eq!(
actual.out,
"╭─┬─┬─┬─────╮\
#ab c \
\
012 3\
145\
01\
12\
23\
\
"
);
}
#[test]
fn table_collapse_padding_not_default() {
let actual = nu!("$env.config.table.padding = 5; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table -c");
assert_eq!(
actual.out,
"╭───────────┬───────────┬───────────╮\
a b c \
\
1 2 3 \
\
4 5 1 \
\
2 \
\
3 \
"
);
}
#[test]
fn table_collapse_padding_zero() {
let actual = nu!("$env.config.table.padding = {left: 0, right: 0}; [[a b, c]; [1 2 3] [4 5 [1 2 3]]] | table -c");
assert_eq!(
actual.out,
"╭─┬─┬─╮\
abc\
\
123\
\
451\
\
2\
\
3\
"
);
}

View file

@ -42,6 +42,7 @@ fn try_build_map(
Span::unknown(), Span::unknown(),
0, 0,
usize::MAX, usize::MAX,
(config.table_indent.left, config.table_indent.right),
); );
let result = ExpandedTable::new(None, false, String::new()).build_map(&cols, &vals, opts); let result = ExpandedTable::new(None, false, String::new()).build_map(&cols, &vals, opts);
match result { match result {
@ -66,6 +67,7 @@ fn try_build_list(
Span::unknown(), Span::unknown(),
0, 0,
usize::MAX, usize::MAX,
(config.table_indent.left, config.table_indent.right),
); );
let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts); let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts);
match result { match result {

View file

@ -75,6 +75,7 @@ pub struct Config {
pub table_mode: String, pub table_mode: String,
pub table_move_header: bool, pub table_move_header: bool,
pub table_show_empty: bool, pub table_show_empty: bool,
pub table_indent: TableIndent,
pub use_ls_colors: bool, pub use_ls_colors: bool,
pub color_config: HashMap<String, Value>, pub color_config: HashMap<String, Value>,
pub use_grid_icons: bool, pub use_grid_icons: bool,
@ -131,6 +132,7 @@ impl Default for Config {
table_show_empty: true, table_show_empty: true,
trim_strategy: TRIM_STRATEGY_DEFAULT, trim_strategy: TRIM_STRATEGY_DEFAULT,
table_move_header: false, table_move_header: false,
table_indent: TableIndent { left: 1, right: 1 },
datetime_normal_format: None, datetime_normal_format: None,
datetime_table_format: None, datetime_table_format: None,
@ -243,6 +245,12 @@ impl TrimStrategy {
} }
} }
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TableIndent {
pub left: usize,
pub right: usize,
}
impl Value { impl Value {
pub fn into_config(&mut self, config: &Config) -> (Config, Option<ShellError>) { pub fn into_config(&mut self, config: &Config) -> (Config, Option<ShellError>) {
// Clone the passed-in config rather than mutating it. // Clone the passed-in config rather than mutating it.
@ -934,6 +942,55 @@ impl Value {
"header_on_separator" => { "header_on_separator" => {
try_bool!(cols, vals, index, span, table_move_header) try_bool!(cols, vals, index, span, table_move_header)
} }
"padding" => match value {
Value::Int { val, .. } => {
if *val < 0 {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} '{val}'; expected a unsigned integer");
}
config.table_indent.left = *val as usize;
config.table_indent.right = *val as usize;
}
Value::Record { vals, cols, .. } => {
let left = cols.iter().position(|e| e == "left");
let right = cols.iter().position(|e| e == "right");
if let Some(i) = left {
let value = vals[i].as_int();
match value {
Ok(val) => {
if val < 0 {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} '{val}'; expected a unsigned integer");
}
config.table_indent.left = val as usize;
}
Err(_) => {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} value; expected a unsigned integer or a record");
}
}
}
if let Some(i) = right {
let value = vals[i].as_int();
match value {
Ok(val) => {
if val < 0 {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} '{val}'; expected a unsigned integer");
}
config.table_indent.right = val as usize;
}
Err(_) => {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} value; expected a unsigned integer or a record");
}
}
}
}
_ => {
invalid!(Some(span), "unexpected $env.config.{key}.{key2} value; expected a unsigned integer or a record");
}
},
"index_mode" => { "index_mode" => {
if let Ok(b) = value.as_string() { if let Ok(b) = value.as_string() {
let val_str = b.to_lowercase(); let val_str = b.to_lowercase();

View file

@ -18,7 +18,7 @@ use tabled::{
}, },
settings::{ settings::{
formatting::AlignmentStrategy, object::Segment, peaker::Peaker, themes::ColumnNames, Color, formatting::AlignmentStrategy, object::Segment, peaker::Peaker, themes::ColumnNames, Color,
Modify, Settings, TableOption, Width, Modify, Padding, Settings, TableOption, Width,
}, },
Table, Table,
}; };
@ -29,6 +29,7 @@ pub struct NuTable {
data: NuTableData, data: NuTableData,
styles: Styles, styles: Styles,
alignments: Alignments, alignments: Alignments,
indent: (usize, usize),
} }
type NuTableData = VecRecords<NuTableCell>; type NuTableData = VecRecords<NuTableCell>;
@ -57,6 +58,7 @@ impl NuTable {
Self { Self {
data: VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]), data: VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]),
styles: Styles::default(), styles: Styles::default(),
indent: (1, 1),
alignments: Alignments { alignments: Alignments {
data: AlignmentHorizontal::Left, data: AlignmentHorizontal::Left,
index: AlignmentHorizontal::Right, index: AlignmentHorizontal::Right,
@ -135,11 +137,22 @@ impl NuTable {
self.alignments.data = convert_alignment(style.alignment); self.alignments.data = convert_alignment(style.alignment);
} }
pub fn set_indent(&mut self, left: usize, right: usize) {
self.indent = (left, right);
}
/// Converts a table to a String. /// Converts a table to a String.
/// ///
/// It returns None in case where table cannot be fit to a terminal width. /// It returns None in case where table cannot be fit to a terminal width.
pub fn draw(self, config: NuTableConfig, termwidth: usize) -> Option<String> { pub fn draw(self, config: NuTableConfig, termwidth: usize) -> Option<String> {
build_table(self.data, config, self.alignments, self.styles, termwidth) build_table(
self.data,
config,
self.alignments,
self.styles,
termwidth,
self.indent,
)
} }
/// Return a total table width. /// Return a total table width.
@ -192,6 +205,7 @@ fn build_table(
alignments: Alignments, alignments: Alignments,
styles: Styles, styles: Styles,
termwidth: usize, termwidth: usize,
indent: (usize, usize),
) -> Option<String> { ) -> Option<String> {
if data.count_columns() == 0 || data.count_rows() == 0 { if data.count_columns() == 0 || data.count_rows() == 0 {
return Some(String::new()); return Some(String::new());
@ -206,7 +220,7 @@ fn build_table(
duplicate_row(&mut data, 0); duplicate_row(&mut data, 0);
} }
draw_table(data, alignments, styles, widths, cfg, termwidth) draw_table(data, alignments, styles, widths, cfg, termwidth, indent)
} }
fn draw_table( fn draw_table(
@ -216,6 +230,7 @@ fn draw_table(
widths: Vec<usize>, widths: Vec<usize>,
cfg: NuTableConfig, cfg: NuTableConfig,
termwidth: usize, termwidth: usize,
indent: (usize, usize),
) -> Option<String> { ) -> Option<String> {
let with_index = cfg.with_index; let with_index = cfg.with_index;
let with_header = cfg.with_header && data.count_rows() > 1; let with_header = cfg.with_header && data.count_rows() > 1;
@ -226,6 +241,7 @@ fn draw_table(
let data: Vec<Vec<_>> = data.into(); let data: Vec<Vec<_>> = data.into();
let mut table = Builder::from(data).build(); let mut table = Builder::from(data).build();
set_indent(&mut table, indent.0, indent.1);
load_theme(&mut table, &cfg.theme, with_footer, with_header, sep_color); load_theme(&mut table, &cfg.theme, with_footer, with_header, sep_color);
align_table(&mut table, alignments, with_index, with_header, with_footer); align_table(&mut table, alignments, with_index, with_header, with_footer);
colorize_table(&mut table, styles, with_index, with_header, with_footer); colorize_table(&mut table, styles, with_index, with_header, with_footer);
@ -240,6 +256,10 @@ fn draw_table(
build_table_with_width_check(table, total_width, termwidth) build_table_with_width_check(table, total_width, termwidth)
} }
fn set_indent(table: &mut Table, left: usize, right: usize) {
table.with(Padding::new(left, right, 0, 0));
}
fn set_border_head(table: &mut Table, with_footer: bool) { fn set_border_head(table: &mut Table, with_footer: bool) {
let count_rows = table.count_rows(); let count_rows = table.count_rows();

View file

@ -32,7 +32,8 @@ fn collapsed_table(
return Ok(None); return Ok(None);
} }
let table = table.draw(style_computer, &theme); let indent = (config.table_indent.left, config.table_indent.right);
let table = table.draw(style_computer, &theme, indent);
Ok(Some(table)) Ok(Some(table))
} }

View file

@ -174,6 +174,7 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
} }
let mut table = NuTable::from(data); let mut table = NuTable::from(data);
table.set_indent(cfg.opts.indent.0, cfg.opts.indent.1);
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);
@ -333,6 +334,7 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
let mut table = NuTable::from(data); let mut table = NuTable::from(data);
table.set_index_style(get_index_style(cfg.opts.style_computer)); table.set_index_style(get_index_style(cfg.opts.style_computer));
table.set_header_style(get_header_style(cfg.opts.style_computer)); table.set_header_style(get_header_style(cfg.opts.style_computer));
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))) Ok(Some(TableOutput::new(table, true, with_index)))
@ -378,6 +380,7 @@ fn expanded_table_kv(cols: &[String], vals: &[Value], cfg: Cfg<'_>) -> StringRes
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);
let out = TableOutput::new(table, false, true); let out = TableOutput::new(table, false, true);

View file

@ -25,7 +25,11 @@ impl JustTable {
fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, ShellError> { fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, ShellError> {
match table(input, opts.row_offset, opts.clone())? { match table(input, opts.row_offset, opts.clone())? {
Some(out) => { Some(mut out) => {
let left = opts.config.table_indent.left;
let right = opts.config.table_indent.right;
out.table.set_indent(left, right);
let table_config = let table_config =
create_nu_table_config(opts.config, opts.style_computer, &out, false); create_nu_table_config(opts.config, opts.style_computer, &out, false);
Ok(out.table.draw(table_config, opts.width)) Ok(out.table.draw(table_config, opts.width))
@ -56,7 +60,12 @@ fn kv_table(cols: &[String], vals: &[Value], opts: TableOpts<'_>) -> StringResul
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 out = TableOutput::new(table, false, true); let mut out = TableOutput::new(table, false, true);
let left = opts.config.table_indent.left;
let right = opts.config.table_indent.right;
out.table.set_indent(left, right);
let table_config = create_nu_table_config(opts.config, opts.style_computer, &out, false); let table_config = create_nu_table_config(opts.config, opts.style_computer, &out, false);
let table = out.table.draw(table_config, opts.width); let table = out.table.draw(table_config, opts.width);

View file

@ -36,6 +36,7 @@ pub struct TableOpts<'a> {
span: Span, span: Span,
row_offset: usize, row_offset: usize,
width: usize, width: usize,
indent: (usize, usize),
} }
impl<'a> TableOpts<'a> { impl<'a> TableOpts<'a> {
@ -45,7 +46,8 @@ impl<'a> TableOpts<'a> {
ctrlc: Option<Arc<AtomicBool>>, ctrlc: Option<Arc<AtomicBool>>,
span: Span, span: Span,
row_offset: usize, row_offset: usize,
available_width: usize, width: usize,
indent: (usize, usize),
) -> Self { ) -> Self {
Self { Self {
ctrlc, ctrlc,
@ -53,7 +55,8 @@ impl<'a> TableOpts<'a> {
style_computer, style_computer,
span, span,
row_offset, row_offset,
width: available_width, indent,
width,
} }
} }
} }

View file

@ -6,7 +6,7 @@ use tabled::{
config::{AlignmentHorizontal, Borders, CompactMultilineConfig}, config::{AlignmentHorizontal, Borders, CompactMultilineConfig},
dimension::{DimensionPriority, PoolTableDimension}, dimension::{DimensionPriority, PoolTableDimension},
}, },
settings::{style::RawStyle, Color, TableOption}, settings::{style::RawStyle, Color, Padding, TableOption},
tables::{PoolTable, TableValue}, tables::{PoolTable, TableValue},
}; };
@ -35,17 +35,28 @@ impl UnstructuredTable {
truncate_table_value(&mut self.value, has_vertical, available).is_none() truncate_table_value(&mut self.value, has_vertical, available).is_none()
} }
pub fn draw(self, style_computer: &StyleComputer, theme: &TableTheme) -> String { pub fn draw(
build_table(self.value, style_computer, theme) self,
style_computer: &StyleComputer,
theme: &TableTheme,
indent: (usize, usize),
) -> String {
build_table(self.value, style_computer, theme, indent)
} }
} }
fn build_table(val: TableValue, style_computer: &StyleComputer, theme: &TableTheme) -> String { fn build_table(
val: TableValue,
style_computer: &StyleComputer,
theme: &TableTheme,
indent: (usize, usize),
) -> String {
let mut table = PoolTable::from(val); let mut table = PoolTable::from(val);
let mut theme = theme.get_theme_full(); let mut theme = theme.get_theme_full();
theme.set_horizontals(std::collections::HashMap::default()); theme.set_horizontals(std::collections::HashMap::default());
table.with(Padding::new(indent.0, indent.1, 0, 0));
table.with(SetRawStyle(theme)); table.with(SetRawStyle(theme));
table.with(SetAlignment(AlignmentHorizontal::Left)); table.with(SetAlignment(AlignmentHorizontal::Left));
table.with(PoolTableDimension::new( table.with(PoolTableDimension::new(

View file

@ -158,6 +158,7 @@ $env.config = {
mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other mode: rounded # basic, compact, compact_double, light, thin, with_love, rounded, reinforced, heavy, none, other
index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column index_mode: always # "always" show indexes, "never" show indexes, "auto" = show indexes when a table has "index" column
show_empty: true # show 'empty list' and 'empty record' placeholders for command output show_empty: true # show 'empty list' and 'empty record' placeholders for command output
padding: { left: 1, right: 1 } # a left right padding of each column in a table
trim: { trim: {
methodology: wrapping # wrapping or truncating methodology: wrapping # wrapping or truncating
wrapping_try_keep_words: true # A strategy used by the 'wrapping' methodology wrapping_try_keep_words: true # A strategy used by the 'wrapping' methodology