From 7162289d776fe8e9d4c690ab8e4bd710c0a48b68 Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Thu, 3 Aug 2023 22:03:20 +0300 Subject: [PATCH] Add an option to move header on borders (#9796) A patch to play with. Need to make a few tests after all. The question is what shall be done with `table.mode = none`, as it has no borders. ```nu $env.config.table.move_header = true ``` ![image](https://github.com/nushell/nushell/assets/20165848/cdcffa6d-989c-4368-a436-fdf7d3400e31) cc: @fdncred --------- Signed-off-by: Maxim Zhiburt --- Cargo.lock | 23 +- crates/nu-command/Cargo.toml | 2 +- crates/nu-command/src/viewers/table.rs | 124 +++----- crates/nu-explore/src/nu_common/table.rs | 33 +- crates/nu-protocol/src/config.rs | 5 + crates/nu-table/Cargo.toml | 2 +- crates/nu-table/examples/table_demo.rs | 9 +- crates/nu-table/src/common.rs | 175 ++++++++++ crates/nu-table/src/lib.rs | 10 +- crates/nu-table/src/table.rs | 265 ++++++++------- crates/nu-table/src/table_theme.rs | 4 + crates/nu-table/src/types/collapse.rs | 26 +- crates/nu-table/src/types/expanded.rs | 389 +++++++++++------------ crates/nu-table/src/types/general.rs | 104 ++---- crates/nu-table/src/types/mod.rs | 207 ++---------- crates/nu-table/src/util.rs | 5 + crates/nu-table/tests/common.rs | 8 +- crates/nu-table/tests/constrains.rs | 78 +++-- crates/nu-table/tests/expand.rs | 12 +- crates/nu-table/tests/style.rs | 20 +- 20 files changed, 758 insertions(+), 743 deletions(-) create mode 100644 crates/nu-table/src/common.rs diff --git a/Cargo.lock b/Cargo.lock index 61eaf53b28..8e5673e253 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2737,7 +2737,7 @@ dependencies = [ "sha2", "sqlparser", "sysinfo", - "tabled", + "tabled 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "terminal_size 0.2.6", "titlecase", "toml", @@ -2904,7 +2904,7 @@ dependencies = [ "nu-engine", "nu-protocol", "nu-utils", - "tabled", + "tabled 0.13.0 (git+https://github.com/zhiburt/tabled/?rev=6c51e3eaa362914a71b868ccb78e7addbebd3657)", ] [[package]] @@ -3289,9 +3289,9 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" [[package]] name = "papergrid" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae7891b22598926e4398790c8fe6447930c72a67d36d983a49d6ce682ce83290" +checksum = "a2ccbe15f2b6db62f9a9871642746427e297b0ceb85f9a7f1ee5ff47d184d0c8" dependencies = [ "ansi-str", "ansitok", @@ -4947,9 +4947,20 @@ dependencies = [ [[package]] name = "tabled" -version = "0.12.2" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce69a5028cd9576063ec1f48edb2c75339fd835e6094ef3e05b3a079bf594a6" +checksum = "4d38d39c754ae037a9bc3ca1580a985db7371cd14f1229172d1db9093feb6739" +dependencies = [ + "ansi-str", + "ansitok", + "papergrid", + "unicode-width", +] + +[[package]] +name = "tabled" +version = "0.13.0" +source = "git+https://github.com/zhiburt/tabled/?rev=6c51e3eaa362914a71b868ccb78e7addbebd3657#6c51e3eaa362914a71b868ccb78e7addbebd3657" dependencies = [ "ansi-str", "ansitok", diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 7bc23b0b10..27ae027495 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -84,7 +84,7 @@ serde_yaml = "0.9" sha2 = "0.10" sqlparser = { version = "0.34", features = ["serde"], optional = true } sysinfo = "0.29" -tabled = { version = "0.12.2", features = ["color"], default-features = false } +tabled = { version = "0.13.0", features = ["color"], default-features = false } terminal_size = "0.2" titlecase = "2.0" toml = "0.7" diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index b41e290ded..dd7ddb4235 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -6,12 +6,13 @@ use nu_engine::{env::get_config, env_to_string, CallExt}; use nu_protocol::{ ast::Call, engine::{Command, EngineState, Stack}, - Category, Config, DataSource, Example, FooterMode, IntoPipelineData, ListStream, PipelineData, + Category, Config, DataSource, Example, IntoPipelineData, ListStream, PipelineData, PipelineMetadata, RawStream, ShellError, Signature, Span, SyntaxShape, Type, Value, }; +use nu_table::common::create_nu_table_config; use nu_table::{ - BuildConfig, Cell, CollapsedTable, ExpandedTable, JustTable, NuTable, StringResult, - TableConfig, TableOutput, TableTheme, + CollapsedTable, ExpandedTable, JustTable, NuTable, NuTableCell, StringResult, TableOpts, + TableOutput, }; use nu_utils::get_ls_colors; use std::sync::Arc; @@ -361,8 +362,8 @@ fn handle_record( let result = if cols.is_empty() { create_empty_placeholder("record", term_width, engine_state, stack) } else { - let opts = BuildConfig::new(ctrlc, config, style_computer, span, term_width); - let result = build_table_kv(cols, vals, table_view, opts)?; + let opts = TableOpts::new(config, style_computer, ctrlc, span, 0, term_width); + let result = build_table_kv(cols, vals, table_view, opts, span)?; match result { Some(output) => maybe_strip_color(output, config), None => report_unsuccessful_output(ctrlc1, term_width), @@ -391,7 +392,8 @@ fn build_table_kv( cols: Vec, vals: Vec, table_view: TableView, - opts: BuildConfig<'_>, + opts: TableOpts<'_>, + span: Span, ) -> StringResult { match table_view { TableView::General => JustTable::kv_table(&cols, &vals, opts), @@ -404,7 +406,6 @@ fn build_table_kv( ExpandedTable::new(limit, flatten, sep).build_map(&cols, &vals, opts) } TableView::Collapsed => { - let span = opts.span(); let value = Value::Record { cols, vals, span }; CollapsedTable::build(value, opts) } @@ -414,21 +415,20 @@ fn build_table_kv( fn build_table_batch( vals: Vec, table_view: TableView, - row_offset: usize, - opts: BuildConfig<'_>, + opts: TableOpts<'_>, + span: Span, ) -> StringResult { match table_view { - TableView::General => JustTable::table(&vals, row_offset, opts), + TableView::General => JustTable::table(&vals, opts), TableView::Expanded { limit, flatten, flatten_separator, } => { let sep = flatten_separator.unwrap_or_else(|| String::from(' ')); - ExpandedTable::new(limit, flatten, sep).build_list(&vals, opts, row_offset) + ExpandedTable::new(limit, flatten, sep).build_list(&vals, opts) } TableView::Collapsed => { - let span = opts.span(); let value = Value::List { vals, span }; CollapsedTable::build(value, opts) } @@ -647,20 +647,16 @@ impl PagingTableCreator { return Ok(None); } - let config = get_config(&self.engine_state, &self.stack); - let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack); - let term_width = get_width_param(self.width_param); - - let ctrlc = self.ctrlc.clone(); - let span = self.head; - let opts = BuildConfig::new(ctrlc, &config, &style_computer, span, term_width); + let cfg = get_config(&self.engine_state, &self.stack); + let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack); + let opts = self.create_table_opts(&cfg, &style_comp); let view = TableView::Expanded { limit, flatten, flatten_separator, }; - build_table_batch(batch, view, self.row_offset, opts) + build_table_batch(batch, view, opts, self.head) } fn build_collapsed(&mut self, batch: Vec) -> StringResult { @@ -668,26 +664,34 @@ impl PagingTableCreator { return Ok(None); } - let config = get_config(&self.engine_state, &self.stack); - let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack); - let term_width = get_width_param(self.width_param); - let ctrlc = self.ctrlc.clone(); - let span = self.head; - let opts = BuildConfig::new(ctrlc, &config, &style_computer, span, term_width); + let cfg = get_config(&self.engine_state, &self.stack); + let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack); + let opts = self.create_table_opts(&cfg, &style_comp); - build_table_batch(batch, TableView::Collapsed, self.row_offset, opts) + build_table_batch(batch, TableView::Collapsed, opts, self.head) } fn build_general(&mut self, batch: Vec) -> StringResult { - let term_width = get_width_param(self.width_param); - let config = get_config(&self.engine_state, &self.stack); - let style_computer = StyleComputer::from_config(&self.engine_state, &self.stack); - let ctrlc = self.ctrlc.clone(); - let span = self.head; - let row_offset = self.row_offset; - let opts = BuildConfig::new(ctrlc, &config, &style_computer, span, term_width); + let cfg = get_config(&self.engine_state, &self.stack); + let style_comp = StyleComputer::from_config(&self.engine_state, &self.stack); + let opts = self.create_table_opts(&cfg, &style_comp); - build_table_batch(batch, TableView::General, row_offset, opts) + build_table_batch(batch, TableView::General, opts, self.head) + } + + fn create_table_opts<'a>( + &self, + cfg: &'a Config, + style_comp: &'a StyleComputer<'a>, + ) -> TableOpts<'a> { + TableOpts::new( + cfg, + style_comp, + self.ctrlc.clone(), + self.head, + self.row_offset, + get_width_param(self.width_param), + ) } } @@ -780,22 +784,6 @@ impl Iterator for PagingTableCreator { } } -fn load_theme_from_config(config: &Config) -> TableTheme { - match config.table_mode.as_str() { - "basic" => TableTheme::basic(), - "thin" => TableTheme::thin(), - "light" => TableTheme::light(), - "compact" => TableTheme::compact(), - "with_love" => TableTheme::with_love(), - "compact_double" => TableTheme::compact_double(), - "rounded" => TableTheme::rounded(), - "reinforced" => TableTheme::reinforced(), - "heavy" => TableTheme::heavy(), - "none" => TableTheme::none(), - _ => TableTheme::rounded(), - } -} - fn render_path_name( path: &str, config: &Config, @@ -859,34 +847,6 @@ fn maybe_strip_color(output: String, config: &Config) -> String { } } -fn create_table_config(config: &Config, comp: &StyleComputer, out: &TableOutput) -> TableConfig { - let theme = load_theme_from_config(config); - let footer = with_footer(config, out.with_header, out.table.count_rows()); - let line_style = lookup_separator_color(comp); - let trim = config.trim_strategy.clone(); - - TableConfig::new() - .theme(theme) - .with_footer(footer) - .with_header(out.with_header) - .with_index(out.with_index) - .line_style(line_style) - .trim(trim) -} - -fn lookup_separator_color(style_computer: &StyleComputer) -> nu_ansi_term::Style { - style_computer.compute("separator", &Value::nothing(Span::unknown())) -} - -fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool { - with_header && need_footer(config, count_records as u64) -} - -fn need_footer(config: &Config, count_records: u64) -> bool { - matches!(config.footer_mode, FooterMode::RowCount(limit) if count_records > limit) - || matches!(config.footer_mode, FooterMode::Always) -} - fn create_empty_placeholder( value_type_name: &str, termwidth: usize, @@ -898,14 +858,14 @@ fn create_empty_placeholder( return String::new(); } - let cell = Cell::new(format!("empty {}", value_type_name)); + let cell = NuTableCell::new(format!("empty {}", value_type_name)); let data = vec![vec![cell]]; let mut table = NuTable::from(data); - table.set_cell_style((0, 0), TextStyle::default().dimmed()); + table.set_data_style(TextStyle::default().dimmed()); let out = TableOutput::new(table, false, false); let style_computer = &StyleComputer::from_config(engine_state, stack); - let config = create_table_config(&config, style_computer, &out); + let config = create_nu_table_config(&config, style_computer, &out, false); out.table .draw(config, termwidth) diff --git a/crates/nu-explore/src/nu_common/table.rs b/crates/nu-explore/src/nu_common/table.rs index 5286216d3f..345e8a1113 100644 --- a/crates/nu-explore/src/nu_common/table.rs +++ b/crates/nu-explore/src/nu_common/table.rs @@ -1,6 +1,9 @@ use nu_color_config::StyleComputer; use nu_protocol::{Span, Value}; -use nu_table::{value_to_clean_styled_string, value_to_styled_string, BuildConfig, ExpandedTable}; +use nu_table::{ + common::{nu_value_to_string, nu_value_to_string_clean}, + ExpandedTable, TableOpts, +}; use std::sync::atomic::AtomicBool; use std::sync::Arc; @@ -18,9 +21,9 @@ pub fn try_build_table( try_build_map(cols, vals, span, style_computer, ctrlc, config) } val if matches!(val, Value::String { .. }) => { - value_to_clean_styled_string(&val, config, style_computer).0 + nu_value_to_string_clean(&val, config, style_computer).0 } - val => value_to_styled_string(&val, config, style_computer).0, + val => nu_value_to_string(&val, config, style_computer).0, } } @@ -32,12 +35,19 @@ fn try_build_map( ctrlc: Option>, config: &NuConfig, ) -> String { - let opts = BuildConfig::new(ctrlc, config, style_computer, Span::unknown(), usize::MAX); + let opts = TableOpts::new( + config, + style_computer, + ctrlc, + Span::unknown(), + 0, + usize::MAX, + ); let result = ExpandedTable::new(None, false, String::new()).build_map(&cols, &vals, opts); match result { Ok(Some(result)) => result, Ok(None) | Err(_) => { - value_to_styled_string(&Value::Record { cols, vals, span }, config, style_computer).0 + nu_value_to_string(&Value::Record { cols, vals, span }, config, style_computer).0 } } } @@ -49,13 +59,20 @@ fn try_build_list( span: Span, style_computer: &StyleComputer, ) -> String { - let opts = BuildConfig::new(ctrlc, config, style_computer, Span::unknown(), usize::MAX); - let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts, 0); + let opts = TableOpts::new( + config, + style_computer, + ctrlc, + Span::unknown(), + 0, + usize::MAX, + ); + let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts); match result { Ok(Some(out)) => out, Ok(None) | Err(_) => { // it means that the list is empty - value_to_styled_string(&Value::List { vals, span }, config, style_computer).0 + nu_value_to_string(&Value::List { vals, span }, config, style_computer).0 } } } diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index c11c0f5b3b..03c03c0beb 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -70,6 +70,7 @@ pub struct Config { pub external_completer: Option, pub filesize_metric: bool, pub table_mode: String, + pub table_move_header: bool, pub table_show_empty: bool, pub use_ls_colors: bool, pub color_config: HashMap, @@ -126,6 +127,7 @@ impl Default for Config { table_index_mode: TableIndexMode::Always, table_show_empty: true, trim_strategy: TRIM_STRATEGY_DEFAULT, + table_move_header: false, datetime_normal_format: None, datetime_table_format: None, @@ -926,6 +928,9 @@ impl Value { Value::string(config.table_mode.clone(), span); } } + "header_on_separator" => { + try_bool!(cols, vals, index, span, table_move_header) + } "index_mode" => { if let Ok(b) = value.as_string() { let val_str = b.to_lowercase(); diff --git a/crates/nu-table/Cargo.toml b/crates/nu-table/Cargo.toml index 83e8117bdf..b6d94d93ad 100644 --- a/crates/nu-table/Cargo.toml +++ b/crates/nu-table/Cargo.toml @@ -16,7 +16,7 @@ nu-utils = { path = "../nu-utils", version = "0.83.2" } nu-engine = { path = "../nu-engine", version = "0.83.2" } nu-color-config = { path = "../nu-color-config", version = "0.83.2" } nu-ansi-term = "0.49.0" -tabled = { version = "0.12.2", features = ["color"], default-features = false } +tabled = { git = "https://github.com/zhiburt/tabled/", rev = "6c51e3eaa362914a71b868ccb78e7addbebd3657", features = ["color"], default-features = false } [dev-dependencies] # nu-test-support = { path="../nu-test-support", version = "0.83.2" } diff --git a/crates/nu-table/examples/table_demo.rs b/crates/nu-table/examples/table_demo.rs index 2160411006..c36f5d78dc 100644 --- a/crates/nu-table/examples/table_demo.rs +++ b/crates/nu-table/examples/table_demo.rs @@ -1,6 +1,6 @@ use nu_ansi_term::{Color, Style}; use nu_color_config::TextStyle; -use nu_table::{NuTable, TableConfig, TableTheme}; +use nu_table::{NuTable, NuTableConfig, TableTheme}; use tabled::grid::records::vec_records::CellInfo; fn main() { @@ -29,8 +29,11 @@ fn main() { table.set_data_style(TextStyle::basic_left()); table.set_header_style(TextStyle::basic_center().style(Style::new().on(Color::Blue))); - let theme = TableTheme::rounded(); - let table_cfg = TableConfig::new().theme(theme).with_header(true); + let table_cfg = NuTableConfig { + theme: TableTheme::rounded(), + with_header: true, + ..Default::default() + }; let output_table = table .draw(table_cfg, width) diff --git a/crates/nu-table/src/common.rs b/crates/nu-table/src/common.rs new file mode 100644 index 0000000000..257c66749c --- /dev/null +++ b/crates/nu-table/src/common.rs @@ -0,0 +1,175 @@ +use nu_color_config::{Alignment, StyleComputer, TextStyle}; +use nu_protocol::TrimStrategy; +use nu_protocol::{Config, FooterMode, ShellError, Span, Value}; + +use crate::{clean_charset, string_wrap, NuTableConfig, TableOutput, TableTheme}; + +pub type NuText = (String, TextStyle); +pub type TableResult = Result, ShellError>; +pub type StringResult = Result, ShellError>; + +pub const INDEX_COLUMN_NAME: &str = "index"; + +pub fn create_nu_table_config( + config: &Config, + comp: &StyleComputer, + out: &TableOutput, + expand: bool, +) -> NuTableConfig { + NuTableConfig { + theme: load_theme_from_config(config), + with_footer: with_footer(config, out.with_header, out.table.count_rows()), + with_index: out.with_index, + with_header: out.with_header, + split_color: Some(lookup_separator_color(comp)), + trim: config.trim_strategy.clone(), + header_on_border: config.table_move_header, + expand, + } +} + +pub fn nu_value_to_string(val: &Value, cfg: &Config, style: &StyleComputer) -> NuText { + let float_precision = cfg.float_precision as usize; + let text = val.into_abbreviated_string(cfg); + make_styled_string(style, text, Some(val), float_precision) +} + +pub fn nu_value_to_string_clean(val: &Value, cfg: &Config, style: &StyleComputer) -> NuText { + let (text, style) = nu_value_to_string(val, cfg, style); + let text = clean_charset(&text); + (text, style) +} + +pub fn error_sign(style_computer: &StyleComputer) -> (String, TextStyle) { + make_styled_string(style_computer, String::from("❎"), None, 0) +} + +pub fn wrap_text(text: &str, width: usize, config: &Config) -> String { + string_wrap(text, width, is_cfg_trim_keep_words(config)) +} + +pub fn get_header_style(style_computer: &StyleComputer) -> TextStyle { + TextStyle::with_style( + Alignment::Center, + style_computer.compute("header", &Value::string("", Span::unknown())), + ) +} + +pub fn get_index_style(style_computer: &StyleComputer) -> TextStyle { + TextStyle::with_style( + Alignment::Right, + style_computer.compute("row_index", &Value::string("", Span::unknown())), + ) +} + +pub fn get_value_style(value: &Value, config: &Config, style_computer: &StyleComputer) -> NuText { + match value { + // Float precision is required here. + Value::Float { val, .. } => ( + format!("{:.prec$}", val, prec = config.float_precision as usize), + style_computer.style_primitive(value), + ), + _ => ( + value.into_abbreviated_string(config), + style_computer.style_primitive(value), + ), + } +} + +pub fn get_empty_style(style_computer: &StyleComputer) -> NuText { + ( + String::from("❎"), + TextStyle::with_style( + Alignment::Right, + style_computer.compute("empty", &Value::nothing(Span::unknown())), + ), + ) +} + +fn make_styled_string( + style_computer: &StyleComputer, + text: String, + value: Option<&Value>, // None represents table holes. + float_precision: usize, +) -> NuText { + match value { + Some(value) => { + match value { + Value::Float { .. } => { + // set dynamic precision from config + let precise_number = match convert_with_precision(&text, float_precision) { + Ok(num) => num, + Err(e) => e.to_string(), + }; + (precise_number, style_computer.style_primitive(value)) + } + _ => (text, style_computer.style_primitive(value)), + } + } + None => { + // Though holes are not the same as null, the closure for "empty" is passed a null anyway. + ( + text, + TextStyle::with_style( + Alignment::Center, + style_computer.compute("empty", &Value::nothing(Span::unknown())), + ), + ) + } + } +} + +fn convert_with_precision(val: &str, precision: usize) -> Result { + // vall will always be a f64 so convert it with precision formatting + let val_float = match val.trim().parse::() { + Ok(f) => f, + Err(e) => { + return Err(ShellError::GenericError( + format!("error converting string [{}] to f64", &val), + "".to_string(), + None, + Some(e.to_string()), + Vec::new(), + )); + } + }; + Ok(format!("{val_float:.precision$}")) +} + +fn is_cfg_trim_keep_words(config: &Config) -> bool { + matches!( + config.trim_strategy, + TrimStrategy::Wrap { + try_to_keep_words: true + } + ) +} + +pub fn load_theme_from_config(config: &Config) -> TableTheme { + match config.table_mode.as_str() { + "basic" => TableTheme::basic(), + "thin" => TableTheme::thin(), + "light" => TableTheme::light(), + "compact" => TableTheme::compact(), + "with_love" => TableTheme::with_love(), + "compact_double" => TableTheme::compact_double(), + "rounded" => TableTheme::rounded(), + "reinforced" => TableTheme::reinforced(), + "heavy" => TableTheme::heavy(), + "none" => TableTheme::none(), + _ => TableTheme::rounded(), + } +} + +fn lookup_separator_color(style_computer: &StyleComputer) -> nu_ansi_term::Style { + style_computer.compute("separator", &Value::nothing(Span::unknown())) +} + +fn with_footer(config: &Config, with_header: bool, count_records: usize) -> bool { + with_header && need_footer(config, count_records as u64) +} + +fn need_footer(config: &Config, count_records: u64) -> bool { + matches!(config.footer_mode, FooterMode::RowCount(limit) if count_records > limit) + || matches!(config.footer_mode, FooterMode::Always) +} diff --git a/crates/nu-table/src/lib.rs b/crates/nu-table/src/lib.rs index 219fc435e9..66cc2a3a82 100644 --- a/crates/nu-table/src/lib.rs +++ b/crates/nu-table/src/lib.rs @@ -4,12 +4,12 @@ mod types; mod unstructured_table; mod util; +pub mod common; + +pub use common::{StringResult, TableResult}; pub use nu_color_config::TextStyle; -pub use table::{Alignments, Cell, NuTable, TableConfig}; +pub use table::{NuTable, NuTableCell, NuTableConfig}; pub use table_theme::TableTheme; -pub use types::{ - clean_charset, value_to_clean_styled_string, value_to_styled_string, BuildConfig, - CollapsedTable, ExpandedTable, JustTable, NuText, StringResult, TableOutput, TableResult, -}; +pub use types::{CollapsedTable, ExpandedTable, JustTable, TableOpts, TableOutput}; pub use unstructured_table::UnstructuredTable; pub use util::*; diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs index 153d6de31d..50b1e69010 100644 --- a/crates/nu-table/src/table.rs +++ b/crates/nu-table/src/table.rs @@ -12,25 +12,27 @@ use tabled::{ dimension::CompleteDimensionVecRecords, records::{ vec_records::{CellInfo, VecRecords}, - ExactRecords, Records, + ExactRecords, PeekableRecords, Records, Resizable, }, }, settings::{ - formatting::AlignmentStrategy, object::Segment, peaker::Peaker, Color, Modify, Settings, - TableOption, Width, + formatting::AlignmentStrategy, object::Segment, peaker::Peaker, themes::ColumnNames, Color, + Modify, Settings, TableOption, Width, }, Table, }; -/// Table represent a table view. +/// NuTable is a table rendering implementation. #[derive(Debug, Clone)] pub struct NuTable { - data: Data, + data: NuTableData, styles: Styles, alignments: Alignments, - size: (usize, usize), } +type NuTableData = VecRecords; +pub type NuTableCell = CellInfo; + #[derive(Debug, Default, Clone)] struct Styles { index: AnsiColor<'static>, @@ -39,27 +41,39 @@ struct Styles { data_is_set: bool, } -type Data = VecRecords; -pub type Cell = CellInfo; +#[derive(Debug, Clone)] +struct Alignments { + data: AlignmentHorizontal, + index: AlignmentHorizontal, + header: AlignmentHorizontal, + columns: HashMap, + cells: HashMap, +} impl NuTable { /// Creates an empty [Table] instance. pub fn new(count_rows: usize, count_columns: usize) -> Self { - let data = VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]); Self { - data, - size: (count_rows, count_columns), + data: VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]), styles: Styles::default(), - alignments: Alignments::default(), + alignments: Alignments { + data: AlignmentHorizontal::Left, + index: AlignmentHorizontal::Right, + header: AlignmentHorizontal::Center, + columns: HashMap::default(), + cells: HashMap::default(), + }, } } + /// Return amount of rows. pub fn count_rows(&self) -> usize { - self.size.0 + self.data.count_rows() } + /// Return amount of columns. pub fn count_columns(&self) -> usize { - self.size.1 + self.data.count_columns() } pub fn insert(&mut self, pos: Position, text: String) { @@ -79,7 +93,7 @@ impl NuTable { } } - pub fn set_cell_style(&mut self, pos: Position, style: TextStyle) { + pub fn insert_style(&mut self, pos: Position, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Cell(pos.0, pos.1), style); @@ -123,12 +137,12 @@ impl NuTable { /// Converts a table to a String. /// /// It returns None in case where table cannot be fit to a terminal width. - pub fn draw(self, config: TableConfig, termwidth: usize) -> Option { + pub fn draw(self, config: NuTableConfig, termwidth: usize) -> Option { build_table(self.data, config, self.alignments, self.styles, termwidth) } /// Return a total table width. - pub fn total_width(&self, config: &TableConfig) -> usize { + pub fn total_width(&self, config: &NuTableConfig) -> usize { let config = get_config(&config.theme, false, None); let widths = build_width(&self.data); get_total_width2(&widths, &config) @@ -137,107 +151,43 @@ impl NuTable { impl From>>> for NuTable { fn from(value: Vec>>) -> Self { - let data = VecRecords::new(value); - let size = (data.count_rows(), data.count_columns()); - Self { - data, - size, - alignments: Alignments::default(), - styles: Styles::default(), - } + let mut nutable = Self::new(0, 0); + nutable.data = VecRecords::new(value); + + nutable } } #[derive(Debug, Clone)] -pub struct TableConfig { - theme: TableTheme, - trim: TrimStrategy, - split_color: Option