Refactroings

This commit is contained in:
Maxim Zhiburt 2024-12-17 23:47:10 +03:00
parent 25a5fca41d
commit 91130104f8
13 changed files with 301 additions and 273 deletions

View file

@ -18,8 +18,8 @@ use nu_protocol::{
ByteStream, Config, DataSource, ListStream, PipelineMetadata, Signals, TableMode, ValueIterator, ByteStream, Config, DataSource, ListStream, PipelineMetadata, Signals, TableMode, ValueIterator,
}; };
use nu_table::{ use nu_table::{
common::create_nu_table_config, CollapsedTable, ExpandedTable, JustTable, NuRecordsValue, common::configure_table, CollapsedTable, ExpandedTable, JustTable, NuRecordsValue, NuTable,
NuTable, StringResult, TableOpts, TableOutput, StringResult, TableOpts, TableOutput,
}; };
use nu_utils::{get_ls_colors, terminal_size}; use nu_utils::{get_ls_colors, terminal_size};
@ -1070,13 +1070,13 @@ 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::from_table(table, false, false); let mut out = TableOutput::from_table(table, 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()); configure_table(&mut out, &config, style_computer, TableMode::default());
out.table out.table
.draw(config, termwidth) .draw(termwidth)
.expect("Could not create empty table placeholder") .expect("Could not create empty table placeholder")
} }

View file

@ -1,6 +1,6 @@
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_color_config::TextStyle; use nu_color_config::TextStyle;
use nu_table::{NuTable, NuTableConfig, TableTheme}; use nu_table::{NuTable, TableTheme};
use tabled::grid::records::vec_records::Text; use tabled::grid::records::vec_records::Text;
fn main() { fn main() {
@ -28,15 +28,11 @@ fn main() {
table.set_data_style(TextStyle::basic_left()); table.set_data_style(TextStyle::basic_left());
table.set_header_style(TextStyle::basic_center().style(Style::new().on(Color::Blue))); table.set_header_style(TextStyle::basic_center().style(Style::new().on(Color::Blue)));
table.set_theme(TableTheme::rounded());
let table_cfg = NuTableConfig { table.set_structure(false, true, false);
theme: TableTheme::rounded(),
with_header: true,
..Default::default()
};
let output_table = table let output_table = table
.draw(table_cfg, width) .draw(width)
.unwrap_or_else(|| format!("Couldn't fit table into {width} columns!")); .unwrap_or_else(|| format!("Couldn't fit table into {width} columns!"));
println!("{output_table}") println!("{output_table}")

View file

@ -3,9 +3,7 @@ use nu_protocol::{Config, FooterMode, ShellError, Span, TableMode, TrimStrategy,
use terminal_size::{terminal_size, Height, Width}; use terminal_size::{terminal_size, Height, Width};
use crate::{ use crate::{clean_charset, colorize_space_str, string_wrap, TableOutput, TableTheme};
clean_charset, colorize_space_str, string_wrap, NuTableConfig, TableOutput, TableTheme,
};
pub type NuText = (String, TextStyle); pub type NuText = (String, TextStyle);
pub type TableResult = Result<Option<TableOutput>, ShellError>; pub type TableResult = Result<Option<TableOutput>, ShellError>;
@ -13,30 +11,31 @@ pub type StringResult = Result<Option<String>, ShellError>;
pub const INDEX_COLUMN_NAME: &str = "index"; pub const INDEX_COLUMN_NAME: &str = "index";
pub fn create_nu_table_config( pub fn configure_table(
out: &mut TableOutput,
config: &Config, config: &Config,
comp: &StyleComputer, comp: &StyleComputer,
out: &TableOutput,
expand: bool,
mode: TableMode, mode: TableMode,
) -> NuTableConfig { ) {
let with_footer = is_footer_needed(config, out);
let theme = load_theme(mode);
out.table.set_theme(theme);
out.table
.set_structure(out.with_index, out.with_header, with_footer);
out.table.set_trim(config.table.trim.clone());
out.table
.set_border_header(config.table.header_on_separator);
out.table.set_border_color(lookup_separator_color(comp));
}
fn is_footer_needed(config: &Config, out: &TableOutput) -> bool {
let mut count_rows = out.table.count_rows(); let mut count_rows = out.table.count_rows();
if config.table.footer_inheritance { if config.table.footer_inheritance {
count_rows = out.count_rows; count_rows = out.count_rows;
} }
let with_footer = with_footer(config, out.with_header, count_rows); with_footer(config, out.with_header, count_rows)
NuTableConfig {
theme: load_theme(mode),
with_footer,
with_index: out.with_index,
with_header: out.with_header,
split_color: Some(lookup_separator_color(comp)),
trim: config.table.trim.clone(),
header_on_border: config.table.header_on_separator,
expand,
}
} }
pub fn nu_value_to_string_colored(val: &Value, cfg: &Config, comp: &StyleComputer) -> String { pub fn nu_value_to_string_colored(val: &Value, cfg: &Config, comp: &StyleComputer) -> String {

View file

@ -10,7 +10,7 @@ pub mod common;
pub use common::{StringResult, TableResult}; pub use common::{StringResult, TableResult};
pub use nu_color_config::TextStyle; pub use nu_color_config::TextStyle;
pub use table::{NuRecordsValue, NuTable, NuTableConfig}; pub use table::{NuRecordsValue, NuTable};
pub use table_theme::TableTheme; pub use table_theme::TableTheme;
pub use types::{CollapsedTable, ExpandedTable, JustTable, TableOpts, TableOutput}; pub use types::{CollapsedTable, ExpandedTable, JustTable, TableOpts, TableOutput};
pub use unstructured_table::UnstructuredTable; pub use unstructured_table::UnstructuredTable;

View file

@ -2,7 +2,7 @@ use std::{cmp::min, collections::HashMap};
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_color_config::TextStyle; use nu_color_config::TextStyle;
use nu_protocol::TrimStrategy; use nu_protocol::{TableIndent, TrimStrategy};
use nu_utils::strip_ansi_unlikely; use nu_utils::strip_ansi_unlikely;
use tabled::{ use tabled::{
@ -40,29 +40,15 @@ pub struct NuTable {
data: NuRecords, data: NuRecords,
styles: Styles, styles: Styles,
alignments: Alignments, alignments: Alignments,
indent: (usize, usize), config: TableConfig,
} }
#[derive(Debug, Default, Clone)]
struct TableConfig<Value> {
data: Value,
index: Value,
header: Value,
columns: HashMap<usize, Value>,
cells: HashMap<Position, Value>,
}
type Alignments = TableConfig<AlignmentHorizontal>;
type Styles = TableConfig<Color>;
impl NuTable { impl NuTable {
/// Creates an empty [`NuTable`] instance. /// Creates an empty [`NuTable`] instance.
pub fn new(count_rows: usize, count_columns: usize) -> Self { pub fn new(count_rows: usize, count_columns: usize) -> Self {
Self { Self {
data: VecRecords::new(vec![vec![Text::default(); count_columns]; count_rows]), data: VecRecords::new(vec![vec![Text::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,
@ -70,6 +56,15 @@ impl NuTable {
columns: HashMap::default(), columns: HashMap::default(),
cells: HashMap::default(), cells: HashMap::default(),
}, },
config: TableConfig {
theme: TableTheme::basic(),
trim: TrimStrategy::truncate(None),
structure: TableStructure::new(false, false, false),
indent: TableIndent::new(1, 1),
header_on_border: false,
expand: false,
border_color: None,
},
} }
} }
@ -151,8 +146,32 @@ 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) { pub fn set_indent(&mut self, indent: TableIndent) {
self.indent = (left, right); self.config.indent = indent;
}
pub fn set_theme(&mut self, theme: TableTheme) {
self.config.theme = theme;
}
pub fn set_structure(&mut self, index: bool, header: bool, footer: bool) {
self.config.structure = TableStructure::new(index, header, footer);
}
pub fn set_border_header(&mut self, on: bool) {
self.config.header_on_border = on;
}
pub fn set_trim(&mut self, strategy: TrimStrategy) {
self.config.trim = strategy;
}
pub fn set_strategy(&mut self, expand: bool) {
self.config.expand = expand;
}
pub fn set_border_color(&mut self, color: Style) {
self.config.border_color = (!color.is_plain()).then_some(color);
} }
pub fn get_records_mut(&mut self) -> &mut NuRecords { pub fn get_records_mut(&mut self) -> &mut NuRecords {
@ -162,21 +181,15 @@ impl NuTable {
/// 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, termwidth: usize) -> Option<String> {
build_table( build_table(self, termwidth)
self.data,
config,
self.alignments,
self.styles,
termwidth,
self.indent,
)
} }
/// Return a total table width. /// Return a total table width.
pub fn total_width(&self, config: &NuTableConfig) -> usize { pub fn total_width(&self) -> usize {
let config = get_config(&config.theme, false, None); let config = create_config(&self.config.theme, false, None);
let widths = build_width(&self.data, self.indent.0 + self.indent.1); let pad = indent_sum(self.config.indent);
let widths = build_width(&self.data, pad);
get_total_width2(&widths, &config) get_total_width2(&widths, &config)
} }
} }
@ -190,33 +203,31 @@ impl From<Vec<Vec<Text<String>>>> for NuTable {
} }
} }
type Alignments = CellConfiguration<AlignmentHorizontal>;
type Styles = CellConfiguration<Color>;
#[derive(Debug, Default, Clone)]
struct CellConfiguration<Value> {
data: Value,
index: Value,
header: Value,
columns: HashMap<usize, Value>,
cells: HashMap<Position, Value>,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct NuTableConfig { pub struct TableConfig {
pub theme: TableTheme, theme: TableTheme,
pub trim: TrimStrategy, trim: TrimStrategy,
pub split_color: Option<Style>, border_color: Option<Style>,
pub expand: bool, expand: bool,
pub with_index: bool, structure: TableStructure,
pub with_header: bool, header_on_border: bool,
pub with_footer: bool, indent: TableIndent,
pub header_on_border: bool,
}
impl Default for NuTableConfig {
fn default() -> Self {
Self {
theme: TableTheme::basic(),
trim: TrimStrategy::truncate(None),
with_header: false,
with_index: false,
with_footer: false,
expand: false,
split_color: None,
header_on_border: false,
}
}
} }
#[derive(Debug, Clone)]
struct TableStructure { struct TableStructure {
with_index: bool, with_index: bool,
with_header: bool, with_header: bool,
@ -233,64 +244,62 @@ impl TableStructure {
} }
} }
fn build_table( fn build_table(mut t: NuTable, termwidth: usize) -> Option<String> {
mut data: NuRecords, if t.count_columns() == 0 || t.count_rows() == 0 {
cfg: NuTableConfig,
alignments: Alignments,
styles: Styles,
termwidth: usize,
indent: (usize, usize),
) -> Option<String> {
if data.count_columns() == 0 || data.count_rows() == 0 {
return Some(String::new()); return Some(String::new());
} }
let pad = indent.0 + indent.1; let widths = table_truncate(&mut t, termwidth)?;
let widths = maybe_truncate_columns(&mut data, &cfg, termwidth, pad); table_insert_footer(&mut t);
draw_table(t, widths, termwidth)
}
fn table_insert_footer(t: &mut NuTable) {
if t.config.structure.with_header && t.config.structure.with_footer {
duplicate_row(&mut t.data, 0);
}
}
fn table_truncate(t: &mut NuTable, termwidth: usize) -> Option<Vec<usize>> {
let pad = t.config.indent.left + t.config.indent.right;
let widths = maybe_truncate_columns(&mut t.data, &t.config, termwidth, pad);
if widths.is_empty() { if widths.is_empty() {
return None; return None;
} }
if cfg.with_header && cfg.with_footer { Some(widths)
duplicate_row(&mut data, 0);
}
draw_table(data, alignments, styles, widths, cfg, termwidth, indent)
} }
fn draw_table( fn draw_table(t: NuTable, widths: Vec<usize>, termwidth: usize) -> Option<String> {
data: NuRecords, let structure = get_table_structure(&t.data, &t.config);
alignments: Alignments, let sep_color = t.config.border_color;
styles: Styles, let border_header = structure.with_header && t.config.header_on_border;
widths: Vec<usize>,
cfg: NuTableConfig,
termwidth: usize,
indent: (usize, usize),
) -> Option<String> {
let structure = get_table_structure(&data, &cfg);
let sep_color = cfg.split_color;
let border_header = structure.with_header && cfg.header_on_border;
let data: Vec<Vec<_>> = data.into(); // TODO: Optimize in tabled
let data: Vec<Vec<_>> = t.data.into();
let mut table = Builder::from(data).build(); let mut table = Builder::from(data).build();
set_indent(&mut table, indent.0, indent.1); set_indent(&mut table, t.config.indent);
load_theme(&mut table, &cfg.theme, &structure, sep_color); load_theme(&mut table, &t.config.theme, &structure, sep_color);
align_table(&mut table, alignments, &structure); align_table(&mut table, t.alignments, &structure);
colorize_table(&mut table, styles, &structure); colorize_table(&mut table, t.styles, &structure);
let pad = indent.0 + indent.1; let pad = indent_sum(t.config.indent);
let width_ctrl = WidthCtrl::new(widths, cfg, termwidth, pad); let width_ctrl = WidthCtrl::new(widths, t.config, termwidth, pad);
adjust_table(&mut table, width_ctrl, border_header, structure.with_footer); adjust_table(&mut table, width_ctrl, border_header, structure.with_footer);
table_to_string(table, termwidth) table_to_string(table, termwidth)
} }
fn get_table_structure(data: &VecRecords<Text<String>>, cfg: &NuTableConfig) -> TableStructure { fn indent_sum(indent: TableIndent) -> usize {
let with_index = cfg.with_index; indent.left + indent.right
let with_header = cfg.with_header && data.count_rows() > 1; }
let with_footer = with_header && cfg.with_footer;
fn get_table_structure(data: &VecRecords<Text<String>>, cfg: &TableConfig) -> TableStructure {
let with_index = cfg.structure.with_index;
let with_header = cfg.structure.with_header && data.count_rows() > 1;
let with_footer = with_header && cfg.structure.with_footer;
TableStructure::new(with_index, with_header, with_footer) TableStructure::new(with_index, with_header, with_footer)
} }
@ -307,8 +316,8 @@ fn adjust_table(table: &mut Table, width_ctrl: WidthCtrl, border_header: bool, w
} }
} }
fn set_indent(table: &mut Table, left: usize, right: usize) { fn set_indent(table: &mut Table, indent: TableIndent) {
table.with(Padding::new(left, right, 0, 0)); table.with(Padding::new(indent.left, indent.right, 0, 0));
} }
fn set_border_head(table: &mut Table, wctrl: WidthCtrl) { fn set_border_head(table: &mut Table, wctrl: WidthCtrl) {
@ -377,13 +386,13 @@ fn table_to_string(table: Table, termwidth: usize) -> Option<String> {
struct WidthCtrl { struct WidthCtrl {
width: Vec<usize>, width: Vec<usize>,
cfg: NuTableConfig, cfg: TableConfig,
width_max: usize, width_max: usize,
pad: usize, pad: usize,
} }
impl WidthCtrl { impl WidthCtrl {
fn new(width: Vec<usize>, cfg: NuTableConfig, max: usize, pad: usize) -> Self { fn new(width: Vec<usize>, cfg: TableConfig, max: usize, pad: usize) -> Self {
Self { Self {
width, width,
cfg, cfg,
@ -404,7 +413,7 @@ impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for
let need_truncation = total_width > self.width_max; let need_truncation = total_width > self.width_max;
if need_truncation { if need_truncation {
let has_header = self.cfg.with_header && rec.count_rows() > 1; let has_header = self.cfg.structure.with_header && rec.count_rows() > 1;
let as_head = has_header && self.cfg.header_on_border; let as_head = has_header && self.cfg.header_on_border;
let trim = TableTrim::new(self.width, self.width_max, self.cfg.trim, as_head, self.pad); let trim = TableTrim::new(self.width, self.width_max, self.cfg.trim, as_head, self.pad);
@ -654,14 +663,14 @@ fn load_theme(
fn maybe_truncate_columns( fn maybe_truncate_columns(
data: &mut NuRecords, data: &mut NuRecords,
cfg: &NuTableConfig, cfg: &TableConfig,
termwidth: usize, termwidth: usize,
pad: usize, pad: usize,
) -> Vec<usize> { ) -> Vec<usize> {
const TERMWIDTH_THRESHOLD: usize = 120; const TERMWIDTH_THRESHOLD: usize = 120;
let preserve_content = termwidth > TERMWIDTH_THRESHOLD; let preserve_content = termwidth > TERMWIDTH_THRESHOLD;
let has_header = cfg.with_header && data.count_rows() > 1; let has_header = cfg.structure.with_header && data.count_rows() > 1;
let is_header_on_border = has_header && cfg.header_on_border; let is_header_on_border = has_header && cfg.header_on_border;
let truncate = if is_header_on_border { let truncate = if is_header_on_border {
@ -685,7 +694,7 @@ fn truncate_columns_by_content(
const MIN_ACCEPTABLE_WIDTH: usize = 3; const MIN_ACCEPTABLE_WIDTH: usize = 3;
const TRAILING_COLUMN_WIDTH: usize = 5; const TRAILING_COLUMN_WIDTH: usize = 5;
let config = get_config(theme, false, None); let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad); let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config); let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth { if total_width <= termwidth {
@ -765,7 +774,7 @@ fn truncate_columns_by_columns(
let acceptable_width = 10 + pad; let acceptable_width = 10 + pad;
let trailing_column_width = 3 + pad; let trailing_column_width = 3 + pad;
let config = get_config(theme, false, None); let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad); let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config); let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth { if total_width <= termwidth {
@ -836,7 +845,7 @@ fn truncate_columns_by_head(
) -> Vec<usize> { ) -> Vec<usize> {
const TRAILING_COLUMN_WIDTH: usize = 5; const TRAILING_COLUMN_WIDTH: usize = 5;
let config = get_config(theme, false, None); let config = create_config(theme, false, None);
let mut widths = build_width(&*data, pad); let mut widths = build_width(&*data, pad);
let total_width = get_total_width2(&widths, &config); let total_width = get_total_width2(&widths, &config);
if total_width <= termwidth { if total_width <= termwidth {
@ -912,7 +921,7 @@ fn get_total_width2(widths: &[usize], cfg: &ColoredConfig) -> usize {
total + countv + margin.left.size + margin.right.size total + countv + margin.left.size + margin.right.size
} }
fn get_config(theme: &TableTheme, with_header: bool, color: Option<Style>) -> ColoredConfig { fn create_config(theme: &TableTheme, with_header: bool, color: Option<Style>) -> ColoredConfig {
let structure = TableStructure::new(false, with_header, false); let structure = TableStructure::new(false, with_header, false);
let mut table = Table::new([[""]]); let mut table = Table::new([[""]]);
load_theme(&mut table, theme, &structure, color); load_theme(&mut table, theme, &structure, color);

View file

@ -8,9 +8,9 @@ use tabled::grid::config::Position;
use crate::{ use crate::{
common::{ common::{
check_value, create_nu_table_config, error_sign, get_header_style, get_index_style, check_value, configure_table, error_sign, get_header_style, get_index_style, load_theme,
load_theme, nu_value_to_string, nu_value_to_string_clean, nu_value_to_string_colored, nu_value_to_string, nu_value_to_string_clean, nu_value_to_string_colored, wrap_text,
wrap_text, NuText, StringResult, TableResult, INDEX_COLUMN_NAME, NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
}, },
string_width, string_width,
types::has_index, types::has_index,
@ -47,12 +47,19 @@ impl ExpandedTable {
pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult { pub fn build_list(self, vals: &[Value], opts: TableOpts<'_>) -> StringResult {
let cfg = Cfg { opts, format: self }; let cfg = Cfg { opts, format: self };
let output = expand_list(vals, cfg.clone())?; let output = expand_list(vals, cfg.clone())?;
let output = match output { let mut output = match output {
Some(out) => out, Some(out) => out,
None => return Ok(None), None => return Ok(None),
}; };
maybe_expand_table(output, cfg.opts.width, &cfg.opts) configure_table(
&mut output,
cfg.opts.config,
&cfg.opts.style_computer,
cfg.opts.mode,
);
maybe_expand_table(output, cfg.opts.width)
} }
} }
@ -200,10 +207,7 @@ fn expand_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
} }
let mut table = NuTable::from(data); let mut table = NuTable::from(data);
table.set_indent( table.set_indent(cfg.opts.config.table.padding);
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
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);
@ -363,10 +367,7 @@ fn expand_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( table.set_indent(cfg.opts.config.table.padding);
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
set_data_styles(&mut table, data_styles); set_data_styles(&mut table, data_styles);
Ok(Some(TableOutput::new(table, true, with_index, rows_count))) Ok(Some(TableOutput::new(table, true, with_index, rows_count)))
@ -421,14 +422,11 @@ fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> CellResult {
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( table.set_indent(cfg.opts.config.table.padding);
cfg.opts.config.table.padding.left,
cfg.opts.config.table.padding.right,
);
let out = TableOutput::new(table, false, true, count_rows); let out = TableOutput::new(table, false, true, count_rows);
maybe_expand_table(out, cfg.opts.width, &cfg.opts) maybe_expand_table(out, cfg.opts.width)
.map(|value| value.map(|value| CellOutput::clean(value, count_rows, false))) .map(|value| value.map(|value| CellOutput::clean(value, count_rows, false)))
} }
@ -446,9 +444,9 @@ fn expand_value(value: &Value, width: usize, cfg: &Cfg<'_>) -> CellResult {
let table = expand_list(vals, inner_cfg)?; let table = expand_list(vals, inner_cfg)?;
match table { match table {
Some(out) => { Some(mut out) => {
let cfg = create_table_cfg(cfg, &out); table_apply_config(&mut out, cfg);
let value = out.table.draw(cfg, width); let value = out.table.draw(width);
match value { match value {
Some(value) => Ok(Some(CellOutput::clean(value, out.count_rows, true))), Some(value) => Ok(Some(CellOutput::clean(value, out.count_rows, true))),
None => Ok(None), None => Ok(None),
@ -539,7 +537,7 @@ fn expand_entry(item: &Value, cfg: Cfg<'_>) -> CellOutput {
let inner_cfg = cfg_expand_next_level(cfg.clone(), span); let inner_cfg = cfg_expand_next_level(cfg.clone(), span);
let table = expand_list(vals, inner_cfg); let table = expand_list(vals, inner_cfg);
let out = match table { let mut out = match table {
Ok(Some(out)) => out, Ok(Some(out)) => out,
_ => { _ => {
let value = nu_value_to_string(item, cfg.opts.config, &cfg.opts.style_computer); let value = nu_value_to_string(item, cfg.opts.config, &cfg.opts.style_computer);
@ -547,8 +545,9 @@ fn expand_entry(item: &Value, cfg: Cfg<'_>) -> CellOutput {
} }
}; };
let table_config = create_table_cfg(&cfg, &out); table_apply_config(&mut out, &cfg);
let table = out.table.draw(table_config, usize::MAX);
let table = out.table.draw(usize::MAX);
match table { match table {
Some(table) => CellOutput::clean(table, out.count_rows, false), Some(table) => CellOutput::clean(table, out.count_rows, false),
None => { None => {
@ -592,20 +591,18 @@ fn list_to_string(
buf buf
} }
fn maybe_expand_table(out: TableOutput, term_width: usize, opts: &TableOpts<'_>) -> StringResult { fn maybe_expand_table(mut out: TableOutput, term_width: usize) -> StringResult {
let mut table_config = let total_width = out.table.total_width();
create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode);
let total_width = out.table.total_width(&table_config);
if total_width < term_width { if total_width < term_width {
const EXPAND_THRESHOLD: f32 = 0.80; const EXPAND_THRESHOLD: f32 = 0.80;
let used_percent = total_width as f32 / term_width as f32; let used_percent = total_width as f32 / term_width as f32;
let need_expansion = total_width < term_width && used_percent > EXPAND_THRESHOLD; let need_expansion = total_width < term_width && used_percent > EXPAND_THRESHOLD;
if need_expansion { if need_expansion {
table_config.expand = true; out.table.set_strategy(true);
} }
} }
let table = out.table.draw(table_config, term_width); let table = out.table.draw(term_width);
Ok(table) Ok(table)
} }
@ -616,12 +613,11 @@ fn set_data_styles(table: &mut NuTable, styles: HashMap<Position, TextStyle>) {
} }
} }
fn create_table_cfg(cfg: &Cfg<'_>, out: &TableOutput) -> crate::NuTableConfig { fn table_apply_config(out: &mut TableOutput, cfg: &Cfg<'_>) {
create_nu_table_config( configure_table(
out,
cfg.opts.config, cfg.opts.config,
&cfg.opts.style_computer, &cfg.opts.style_computer,
out,
false,
cfg.opts.mode, cfg.opts.mode,
) )
} }

View file

@ -5,7 +5,7 @@ use nu_protocol::{Config, Record, ShellError, Value};
use crate::{ use crate::{
clean_charset, colorize_space, clean_charset, colorize_space,
common::{ common::{
check_value, create_nu_table_config, get_empty_style, get_header_style, get_index_style, check_value, configure_table, get_empty_style, get_header_style, get_index_style,
get_value_style, nu_value_to_string_colored, NuText, INDEX_COLUMN_NAME, get_value_style, nu_value_to_string_colored, NuText, INDEX_COLUMN_NAME,
}, },
types::has_index, types::has_index,
@ -30,15 +30,12 @@ fn list_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, Sh
None => return Ok(None), None => return Ok(None),
}; };
out.table.set_indent( out.table.set_indent(opts.config.table.padding);
opts.config.table.padding.left,
opts.config.table.padding.right,
);
colorize_space(out.table.get_records_mut(), &opts.style_computer); colorize_space(out.table.get_records_mut(), &opts.style_computer);
let config = create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode); configure_table(&mut out, opts.config, &opts.style_computer, opts.mode);
let table = out.table.draw(config, opts.width); let table = out.table.draw(opts.width);
Ok(table) Ok(table)
} }
@ -60,15 +57,11 @@ 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());
table.set_indent( table.set_indent(opts.config.table.padding);
opts.config.table.padding.left,
opts.config.table.padding.right,
);
let out = TableOutput::from_table(table, false, true); let mut out = TableOutput::from_table(table, false, true);
let table_config = configure_table(&mut out, opts.config, &opts.style_computer, opts.mode);
create_nu_table_config(opts.config, &opts.style_computer, &out, false, opts.mode); let table = out.table.draw(opts.width);
let table = out.table.draw(table_config, opts.width);
Ok(table) Ok(table)
} }

View file

@ -12,8 +12,11 @@ pub use expanded::ExpandedTable;
pub use general::JustTable; pub use general::JustTable;
pub struct TableOutput { pub struct TableOutput {
/// A table structure.
pub table: NuTable, pub table: NuTable,
/// A flag whether a header was present in the table.
pub with_header: bool, pub with_header: bool,
/// A flag whether a index was present in the table.
pub with_index: bool, pub with_index: bool,
/// The value may be bigger then table.count_rows(), /// The value may be bigger then table.count_rows(),
/// Specifically in case of expanded table we collect the whole structure size here. /// Specifically in case of expanded table we collect the whole structure size here.

View file

@ -69,9 +69,11 @@ pub fn clean_charset(text: &str) -> String {
continue; continue;
} }
if c < ' ' { // note: Overall maybe we shall delete this check?
continue; // it was made in order to cope with emojie issue.
} // if c < ' ' && c != '\u{1b}' {
// continue;
// }
buf.push(c); buf.push(c);
} }

View file

@ -1,29 +1,79 @@
#![allow(dead_code)] #![allow(dead_code)]
use nu_table::{string_width, NuTable, NuTableConfig}; use nu_protocol::TrimStrategy;
use nu_table::{string_width, NuTable, TableTheme};
use tabled::grid::records::vec_records::Text; use tabled::grid::records::vec_records::Text;
#[derive(Debug, Clone)]
pub struct TestCase { pub struct TestCase {
cfg: NuTableConfig, theme: TableTheme,
with_header: bool,
with_footer: bool,
with_index: bool,
expand: bool,
strategy: TrimStrategy,
termwidth: usize, termwidth: usize,
expected: Option<String>, expected: Option<String>,
} }
impl TestCase { impl TestCase {
pub fn new(cfg: NuTableConfig, termwidth: usize, expected: Option<String>) -> Self { pub fn new(termwidth: usize) -> Self {
Self { Self {
cfg,
termwidth, termwidth,
expected, expected: None,
theme: TableTheme::basic(),
with_header: false,
with_footer: false,
with_index: false,
expand: false,
strategy: TrimStrategy::truncate(None),
} }
} }
pub fn expected(mut self, value: Option<String>) -> Self {
self.expected = value;
self
}
pub fn theme(mut self, theme: TableTheme) -> Self {
self.theme = theme;
self
}
pub fn expand(mut self) -> Self {
self.expand = true;
self
}
pub fn header(mut self) -> Self {
self.with_header = true;
self
}
pub fn footer(mut self) -> Self {
self.with_footer = true;
self
}
pub fn index(mut self) -> Self {
self.with_index = true;
self
}
pub fn trim(mut self, trim: TrimStrategy) -> Self {
self.strategy = trim;
self
}
} }
type Data = Vec<Vec<Text<String>>>; type Data = Vec<Vec<Text<String>>>;
pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) { pub fn test_table<I>(data: Data, tests: I)
where
I: IntoIterator<Item = TestCase>,
{
for (i, test) in tests.into_iter().enumerate() { for (i, test) in tests.into_iter().enumerate() {
let actual = create_table(data.clone(), test.cfg.clone(), test.termwidth); let actual = create_table(data.clone(), test.clone());
assert_eq!( assert_eq!(
actual, test.expected, actual, test.expected,
@ -37,9 +87,14 @@ pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) {
} }
} }
pub fn create_table(data: Data, config: NuTableConfig, termwidth: usize) -> Option<String> { pub fn create_table(data: Data, case: TestCase) -> Option<String> {
let table = NuTable::from(data); let mut table = NuTable::from(data);
table.draw(config, termwidth) table.set_theme(case.theme);
table.set_structure(case.with_index, case.with_header, case.with_footer);
table.set_trim(case.strategy);
table.set_strategy(case.expand);
table.draw(case.termwidth)
} }
pub fn create_row(count_columns: usize) -> Vec<Text<String>> { pub fn create_row(count_columns: usize) -> Vec<Text<String>> {

View file

@ -1,20 +1,19 @@
mod common; mod common;
use common::{create_row, test_table, TestCase};
use nu_protocol::TrimStrategy; use nu_protocol::TrimStrategy;
use nu_table::{NuTable, NuTableConfig, TableTheme as theme}; use nu_table::{NuTable, TableTheme as theme};
use common::{create_row, test_table, TestCase};
use tabled::grid::records::vec_records::Text; use tabled::grid::records::vec_records::Text;
#[test] #[test]
fn data_and_header_has_different_size_doesnt_work() { fn data_and_header_has_different_size_doesnt_work() {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]); let mut table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let cfg = NuTableConfig { table.set_theme(theme::heavy());
theme: theme::heavy(), table.set_structure(false, true, false);
with_header: true,
..Default::default()
};
let table = table.draw(cfg.clone(), usize::MAX); let table = table.draw(usize::MAX);
assert_eq!( assert_eq!(
table.as_deref(), table.as_deref(),
@ -30,7 +29,7 @@ fn data_and_header_has_different_size_doesnt_work() {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]); let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let table = table.draw(cfg, usize::MAX); let table = table.draw(usize::MAX);
assert_eq!( assert_eq!(
table.as_deref(), table.as_deref(),
@ -47,30 +46,27 @@ fn data_and_header_has_different_size_doesnt_work() {
#[test] #[test]
fn termwidth_too_small() { fn termwidth_too_small() {
let test_loop = |config: NuTableConfig| { let tests = [
for i in 0..10 {
let table = NuTable::from(vec![create_row(5), create_row(5), create_row(5)]);
let table = table.draw(config.clone(), i);
assert!(table.is_none());
}
};
let mut cfg = NuTableConfig {
theme: theme::heavy(),
with_header: true,
..Default::default()
};
for case in [
TrimStrategy::truncate(None), TrimStrategy::truncate(None),
TrimStrategy::truncate(Some(String::from("**"))), TrimStrategy::truncate(Some(String::from("**"))),
TrimStrategy::truncate(Some(String::from(""))), TrimStrategy::truncate(Some(String::from(""))),
TrimStrategy::wrap(false), TrimStrategy::wrap(false),
TrimStrategy::wrap(true), TrimStrategy::wrap(true),
] { ];
cfg.trim = case;
test_loop(cfg.clone()); let data = vec![create_row(5), create_row(5), create_row(5)];
for case in tests {
for i in 0..10 {
let mut table = NuTable::from(data.clone());
table.set_theme(theme::heavy());
table.set_structure(false, true, false);
table.set_trim(case.clone());
let table = table.draw(i);
assert!(table.is_none());
}
} }
} }
@ -195,30 +191,24 @@ fn width_control_test_0() {
} }
fn test_width(data: Vec<Vec<Text<String>>>, tests: &[(usize, &str)]) { fn test_width(data: Vec<Vec<Text<String>>>, tests: &[(usize, &str)]) {
let config = NuTableConfig {
theme: theme::heavy(),
trim: TrimStrategy::truncate(Some(String::from("..."))),
with_header: true,
..Default::default()
};
let tests = tests.iter().map(|&(termwidth, expected)| { let tests = tests.iter().map(|&(termwidth, expected)| {
TestCase::new(config.clone(), termwidth, Some(expected.to_owned())) TestCase::new(termwidth)
.theme(theme::heavy())
.trim(TrimStrategy::truncate(Some(String::from("..."))))
.header()
.expected(Some(expected.to_owned()))
}); });
test_table(data, tests); test_table(data, tests);
} }
fn test_trim(tests: &[(usize, Option<&str>)], trim: TrimStrategy) { fn test_trim(tests: &[(usize, Option<&str>)], trim: TrimStrategy) {
let config = NuTableConfig {
theme: theme::heavy(),
with_header: true,
trim,
..Default::default()
};
let tests = tests.iter().map(|&(termwidth, expected)| { let tests = tests.iter().map(|&(termwidth, expected)| {
TestCase::new(config.clone(), termwidth, expected.map(|s| s.to_string())) TestCase::new(termwidth)
.theme(theme::heavy())
.trim(trim.clone())
.header()
.expected(expected.map(|s| s.to_string()))
}); });
let data = vec![ let data = vec![

View file

@ -1,20 +1,14 @@
mod common; mod common;
use common::{create_row, create_table}; use common::{create_row, create_table, TestCase};
use nu_table::{NuTableConfig, TableTheme as theme}; use nu_table::TableTheme as theme;
#[test] #[test]
fn test_expand() { fn test_expand() {
let table = create_table( let table = create_table(
vec![create_row(4); 3], vec![create_row(4); 3],
NuTableConfig { TestCase::new(50).theme(theme::rounded()).header().expand(),
theme: theme::rounded(),
with_header: true,
expand: true,
..Default::default()
},
50,
); );
assert_eq!( assert_eq!(

View file

@ -1,7 +1,7 @@
mod common; mod common;
use common::create_row as row; use common::{create_row as row, TestCase};
use nu_table::{NuTable, NuTableConfig, TableTheme as theme}; use nu_table::{NuTable, TableTheme as theme};
use tabled::grid::records::vec_records::Text; use tabled::grid::records::vec_records::Text;
#[test] #[test]
@ -452,27 +452,18 @@ fn test_with_love() {
} }
fn create_table(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String { fn create_table(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
let config = NuTableConfig { let mut case = TestCase::new(usize::MAX).theme(theme);
theme, if with_header {
with_header, case = case.header();
..Default::default() }
};
let out = common::create_table(data, config, usize::MAX); common::create_table(data, case).expect("not expected to get None")
out.expect("not expected to get None")
} }
fn create_table_with_size(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String { fn create_table_with_size(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
let config = NuTableConfig { let mut table = NuTable::from(data);
theme, table.set_theme(theme);
with_header, table.set_structure(false, with_header, false);
..Default::default()
};
let table = NuTable::from(data); table.draw(usize::MAX).expect("not expected to get None")
table
.draw(config, usize::MAX)
.expect("not expected to get None")
} }