diff --git a/Cargo.lock b/Cargo.lock index d15ecdf729..f3b8c0ec5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4168,15 +4168,15 @@ checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" [[package]] name = "papergrid" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7419ad52a7de9b60d33e11085a0fe3df1fbd5926aa3f93d3dd53afbc9e86725" +checksum = "d2b0f8def1f117e13c895f3eda65a7b5650688da29d6ad04635f61bc7b92eebd" dependencies = [ "ansi-str", "ansitok", "bytecount", "fnv", - "unicode-width 0.1.11", + "unicode-width 0.2.0", ] [[package]] @@ -6317,9 +6317,9 @@ dependencies = [ [[package]] name = "tabled" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c9303ee60b9bedf722012ea29ae3711ba13a67c9b9ae28993838b63057cb1b" +checksum = "c6709222f3973137427ce50559cd564dc187a95b9cfe01613d2f4e93610e510a" dependencies = [ "ansi-str", "ansitok", diff --git a/Cargo.toml b/Cargo.toml index cf836ea6e4..cb417c5bd3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,7 @@ sha2 = "0.10" strip-ansi-escapes = "0.2.0" syn = "2.0" sysinfo = "0.32" -tabled = { version = "0.16.0", default-features = false } +tabled = { version = "0.17.0", default-features = false } tempfile = "3.14" terminal_size = "0.4" titlecase = "2.0" diff --git a/crates/nu-command/src/debug/inspect_table.rs b/crates/nu-command/src/debug/inspect_table.rs index f7195992a8..e2a2483484 100644 --- a/crates/nu-command/src/debug/inspect_table.rs +++ b/crates/nu-command/src/debug/inspect_table.rs @@ -1,14 +1,17 @@ -use crate::debug::inspect_table::{ - global_horizontal_char::SetHorizontalChar, set_widths::SetWidths, -}; +// note: Seems like could be simplified +// IMHO: it shall not take 300+ lines :) + use nu_protocol::Value; use nu_table::{string_width, string_wrap}; + use tabled::{ grid::config::ColoredConfig, - settings::{peaker::PriorityMax, width::Wrap, Settings, Style}, + settings::{peaker::Priority, width::Wrap, Settings, Style}, Table, }; +use self::{global_horizontal_char::SetHorizontalChar, set_widths::SetWidths}; + pub fn build_table(value: Value, description: String, termsize: usize) -> String { let (head, mut data) = util::collect_input(value); let count_columns = head.len(); @@ -57,7 +60,7 @@ pub fn build_table(value: Value, description: String, termsize: usize) -> String Settings::default() .with(Style::rounded().corner_top_left('├').corner_top_right('┤')) .with(SetWidths(widths)) - .with(Wrap::new(width).priority(PriorityMax)) + .with(Wrap::new(width).priority(Priority::max(true))) .with(SetHorizontalChar::new('┼', '┴', 11 + 2 + 1)), ); diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs index bf75e14380..fbb7c2d7a5 100644 --- a/crates/nu-table/src/table.rs +++ b/crates/nu-table/src/table.rs @@ -1,32 +1,36 @@ -use crate::{convert_style, table_theme::TableTheme}; +use std::{cmp::min, collections::HashMap}; + use nu_ansi_term::Style; use nu_color_config::TextStyle; use nu_protocol::TrimStrategy; use nu_utils::strip_ansi_unlikely; -use std::{cmp::min, collections::HashMap}; + use tabled::{ builder::Builder, grid::{ ansi::ANSIBuf, colors::Colors, - config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position}, + config::{AlignmentHorizontal, ColoredConfig, Entity, Position}, dimension::CompleteDimensionVecRecords, records::{ vec_records::{Cell, Text, VecRecords}, - ExactRecords, PeekableRecords, Records, Resizable, + ExactRecords, Records, Resizable, }, }, settings::{ + format::FormatContent, formatting::AlignmentStrategy, - object::{Columns, Segment}, - peaker::Peaker, + object::{Columns, Row, Rows}, + peaker::Priority, themes::ColumnNames, width::Truncate, - Alignment, Color, Modify, Padding, Settings, TableOption, Width, + Alignment, Color, Format, Modify, ModifyList, Padding, Settings, TableOption, Width, }, Table, }; +use crate::{convert_style, table_theme::TableTheme}; + /// NuTable is a table rendering implementation. #[derive(Debug, Clone)] pub struct NuTable { @@ -41,12 +45,14 @@ pub type NuTableCell = Text; #[derive(Debug, Default, Clone)] struct Styles { - index: ANSIBuf, - header: ANSIBuf, - data: EntityMap, - data_is_set: bool, + data: Color, + index: Color, + header: Color, + columns: HashMap, + cells: HashMap, } +// todo: generic #[derive(Debug, Clone)] struct Alignments { data: AlignmentHorizontal, @@ -89,9 +95,8 @@ impl NuTable { pub fn set_column_style(&mut self, column: usize, style: TextStyle) { if let Some(style) = style.color_style { - let style = ANSIBuf::from(convert_style(style)); - self.styles.data.insert(Entity::Column(column), style); - self.styles.data_is_set = true; + let style = convert_style(style); + self.styles.columns.insert(column, style); } let alignment = convert_alignment(style.alignment); @@ -102,9 +107,8 @@ impl NuTable { pub fn insert_style(&mut self, pos: Position, style: TextStyle) { if let Some(style) = style.color_style { - let style = ANSIBuf::from(convert_style(style)); - self.styles.data.insert(Entity::Cell(pos.0, pos.1), style); - self.styles.data_is_set = true; + let style = convert_style(style); + self.styles.cells.insert(pos, style); } let alignment = convert_alignment(style.alignment); @@ -115,7 +119,7 @@ impl NuTable { pub fn set_header_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { - let style = ANSIBuf::from(convert_style(style)); + let style = convert_style(style); self.styles.header = style; } @@ -124,7 +128,7 @@ impl NuTable { pub fn set_index_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { - let style = ANSIBuf::from(convert_style(style)); + let style = convert_style(style); self.styles.index = style; } @@ -133,9 +137,8 @@ impl NuTable { pub fn set_data_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { - let style = ANSIBuf::from(convert_style(style)); - self.styles.data.insert(Entity::Global, style); - self.styles.data_is_set = true; + let style = convert_style(style); + self.styles.data = style; } self.alignments.data = convert_alignment(style.alignment); @@ -207,6 +210,22 @@ impl Default for NuTableConfig { } } +struct TableStructure { + with_index: bool, + with_header: bool, + with_footer: bool, +} + +impl TableStructure { + fn new(with_index: bool, with_header: bool, with_footer: bool) -> Self { + Self { + with_index, + with_header, + with_footer, + } + } +} + fn build_table( mut data: NuRecords, cfg: NuTableConfig, @@ -241,9 +260,7 @@ fn draw_table( termwidth: usize, indent: (usize, usize), ) -> Option { - let with_index = cfg.with_index; - let with_header = cfg.with_header && data.count_rows() > 1; - let with_footer = with_header && cfg.with_footer; + let structure = get_table_structure(&data, &cfg); let sep_color = cfg.split_color; let border_header = cfg.header_on_border; @@ -251,77 +268,99 @@ fn draw_table( 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); - align_table(&mut table, alignments, with_index, with_header, with_footer); - colorize_table(&mut table, styles, with_index, with_header, with_footer); + load_theme(&mut table, &cfg.theme, &structure, sep_color); + align_table(&mut table, alignments, &structure); + colorize_table(&mut table, styles, &structure); let pad = indent.0 + indent.1; - let width_ctrl = TableWidthCtrl::new(widths, cfg, termwidth, pad); + let width_ctrl = WidthCtrl::new(widths, cfg, termwidth, pad); - if with_header && border_header { - set_border_head(&mut table, with_footer, width_ctrl); + let need_border_header = structure.with_header && border_header; + adjust_table( + &mut table, + width_ctrl, + need_border_header, + structure.with_footer, + ); + + table_to_string(table, termwidth) +} + +fn get_table_structure(data: &VecRecords>, cfg: &NuTableConfig) -> TableStructure { + let with_index = cfg.with_index; + let with_header = cfg.with_header && data.count_rows() > 1; + let with_footer = with_header && cfg.with_footer; + + TableStructure::new(with_index, with_header, with_footer) +} + +fn adjust_table(table: &mut Table, width_ctrl: WidthCtrl, border_header: bool, with_footer: bool) { + if border_header { + if with_footer { + set_border_head_with_footer(table, width_ctrl); + } else { + set_border_head(table, width_ctrl); + } } else { table.with(width_ctrl); } - - table_to_string(table, 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, wctrl: TableWidthCtrl) { - if with_footer { - let count_rows = table.count_rows(); - let last_row_index = count_rows - 1; +fn set_border_head(table: &mut Table, wctrl: WidthCtrl) { + let mut row = GetRow(0, Vec::new()); + let mut row_opts = GetRowSettings(0, AlignmentHorizontal::Left, None); - // note: funnily last and row must be equal at this point but we do not rely on it just in case. + table.with(&mut row); + table.with(&mut row_opts); - let mut first_row = GetRow(0, Vec::new()); - let mut head_settings = GetRowSettings(0, AlignmentHorizontal::Left, None); - let mut last_row = GetRow(last_row_index, Vec::new()); + table.with( + Settings::default() + .with(strip_color_from_row(0)) + .with(wctrl) + .with(MoveRowNext::new(0, 0)) + .with(SetLineHeaders::new(0, row.1, row_opts.1, row_opts.2)), + ); +} - table.with(&mut first_row); - table.with(&mut head_settings); - table.with(&mut last_row); +fn set_border_head_with_footer(table: &mut Table, wctrl: WidthCtrl) { + // note: funnily last and row must be equal at this point but we do not rely on it just in case. - let head = first_row.1; - let footer = last_row.1; - let alignment = head_settings.1; - let head_color = head_settings.2.clone(); - let footer_color = head_settings.2; + let count_rows = table.count_rows(); + let last_row_index = count_rows - 1; - table.with( - Settings::default() - .with(StripColorFromRow(0)) - .with(StripColorFromRow(count_rows - 1)) - .with(wctrl) - .with(MoveRowNext::new(0, 0)) - .with(MoveRowPrev::new(last_row_index - 1, last_row_index)) - .with(SetLineHeaders::new(0, head, alignment, head_color)) - .with(SetLineHeaders::new( - last_row_index - 1, - footer, - alignment, - footer_color, - )), - ); - } else { - let mut row = GetRow(0, Vec::new()); - let mut row_opts = GetRowSettings(0, AlignmentHorizontal::Left, None); + let mut first_row = GetRow(0, Vec::new()); + let mut head_settings = GetRowSettings(0, AlignmentHorizontal::Left, None); + let mut last_row = GetRow(last_row_index, Vec::new()); - table.with(&mut row); - table.with(&mut row_opts); + table.with(&mut first_row); + table.with(&mut head_settings); + table.with(&mut last_row); - table.with( - Settings::default() - .with(StripColorFromRow(0)) - .with(wctrl) - .with(MoveRowNext::new(0, 0)) - .with(SetLineHeaders::new(0, row.1, row_opts.1, row_opts.2)), - ); - } + let head = first_row.1; + let footer = last_row.1; + let alignment = head_settings.1; + let head_color = head_settings.2.clone(); + let footer_color = head_settings.2; + + table.with( + Settings::default() + .with(strip_color_from_row(0)) + .with(strip_color_from_row(count_rows - 1)) + .with(wctrl) + .with(MoveRowNext::new(0, 0)) + .with(MoveRowPrev::new(last_row_index - 1, last_row_index)) + .with(SetLineHeaders::new(0, head, alignment, head_color)) + .with(SetLineHeaders::new( + last_row_index - 1, + footer, + alignment, + footer_color, + )), + ); } fn table_to_string(table: Table, termwidth: usize) -> Option { @@ -335,14 +374,14 @@ fn table_to_string(table: Table, termwidth: usize) -> Option { } } -struct TableWidthCtrl { +struct WidthCtrl { width: Vec, cfg: NuTableConfig, width_max: usize, pad: usize, } -impl TableWidthCtrl { +impl WidthCtrl { fn new(width: Vec, cfg: NuTableConfig, max: usize, pad: usize) -> Self { Self { width, @@ -353,7 +392,7 @@ impl TableWidthCtrl { } } -impl TableOption> for TableWidthCtrl { +impl TableOption> for WidthCtrl { fn change( self, rec: &mut NuRecords, @@ -362,24 +401,24 @@ impl TableOption> for ) { let total_width = get_total_width2(&self.width, cfg); - if total_width > self.width_max { + let need_truncation = total_width > self.width_max; + if need_truncation { let has_header = self.cfg.with_header && rec.count_rows() > 1; - let trim_as_head = has_header && self.cfg.header_on_border; + let as_head = has_header && self.cfg.header_on_border; - TableTrim::new( - self.width, - self.width_max, - self.cfg.trim, - trim_as_head, - self.pad, - ) - .change(rec, cfg, dim); - } else if self.cfg.expand && self.width_max > total_width { - let opt = (SetDimensions(self.width), Width::increase(self.width_max)); - TableOption::, _, _>::change(opt, rec, cfg, dim) - } else { - SetDimensions(self.width).change(rec, cfg, dim); + let trim = TableTrim::new(self.width, self.width_max, self.cfg.trim, as_head, self.pad); + trim.change(rec, cfg, dim); + return; } + + let need_expantion = self.cfg.expand && self.width_max > total_width; + if need_expantion { + let opt = (SetDimensions(self.width), Width::increase(self.width_max)); + TableOption::, _, _>::change(opt, rec, cfg, dim); + return; + } + + SetDimensions(self.width).change(rec, cfg, dim); } } @@ -427,13 +466,13 @@ impl TableOption> for TrimStrategy::Wrap { try_to_keep_words } => { let wrap = Width::wrap(self.width_max) .keep_words(try_to_keep_words) - .priority(PriorityMax); + .priority(Priority::max(false)); let opt = (SetDimensions(self.width), wrap); TableOption::::change(opt, recs, cfg, dims); } TrimStrategy::Truncate { suffix } => { - let mut truncate = Width::truncate(self.width_max).priority(PriorityMax); + let mut truncate = Width::truncate(self.width_max).priority(Priority::max(false)); if let Some(suffix) = suffix { truncate = truncate.suffix(suffix).suffix_try_color(true); } @@ -515,7 +554,7 @@ fn trim_as_header( let wrap = Width::wrap(width).keep_words(*try_to_keep_words); let opt = Modify::new(Columns::single(i)).with(wrap); - TableOption::>, _, _>::change(opt, recs, cfg, dims); + TableOption::, _, _>::change(opt, recs, cfg, dims); } TrimStrategy::Truncate { suffix } => { let mut truncate = Width::truncate(width); @@ -524,7 +563,7 @@ fn trim_as_header( } let opt = Modify::new(Columns::single(i)).with(truncate); - TableOption::>, _, _>::change(opt, recs, cfg, dims); + TableOption::, _, _>::change(opt, recs, cfg, dims); } } } @@ -532,82 +571,74 @@ fn trim_as_header( TableOption::change(SetDimensions(widths), recs, cfg, dims); } -fn align_table( - table: &mut Table, - alignments: Alignments, - with_index: bool, - with_header: bool, - with_footer: bool, -) { - table - .with(Modify::new(Segment::all()).with(AlignmentStrategy::PerLine)) - .with(SetAlignment(alignments.data, Entity::Global)); +fn align_table(table: &mut Table, alignments: Alignments, structure: &TableStructure) { + table.with(AlignmentStrategy::PerLine); + table.with(Alignment::from(alignments.data)); for (column, alignment) in alignments.columns { - table.with(SetAlignment(alignment, Entity::Column(column))); + table.modify(Columns::single(column), Alignment::from(alignment)); } for (pos, alignment) in alignments.cells { - table.with(SetAlignment(alignment, Entity::Cell(pos.0, pos.1))); + table.modify(pos, Alignment::from(alignment)); } - if with_header { - table.with(SetAlignment(alignments.header, Entity::Row(0))); + if structure.with_header { + table.modify(Rows::first(), Alignment::from(alignments.header)); - if with_footer { - table.with(SetAlignment( - alignments.header, - Entity::Row(table.count_rows() - 1), - )); + if structure.with_footer { + table.modify(Rows::last(), Alignment::from(alignments.header)); } } - if with_index { - table.with(SetAlignment(alignments.index, Entity::Column(0))); + if structure.with_index { + table.modify(Columns::first(), Alignment::from(alignments.index)); } } -fn colorize_table( - table: &mut Table, - mut styles: Styles, - with_index: bool, - with_header: bool, - with_footer: bool, -) { - if with_index { - styles.data.insert(Entity::Column(0), styles.index); - styles.data_is_set = true; +fn colorize_table(table: &mut Table, styles: Styles, structure: &TableStructure) { + if !is_color_empty(&styles.data) { + table.with(styles.data); } - if with_header { - styles.data.insert(Entity::Row(0), styles.header.clone()); - styles.data_is_set = true; - - if with_footer { - let count_rows = table.count_rows(); - if count_rows > 1 { - let last_row = count_rows - 1; - styles.data.insert(Entity::Row(last_row), styles.header); - } + for (column, color) in styles.columns { + if !is_color_empty(&color) { + table.modify(Columns::single(column), color); } } - if styles.data_is_set { - table.get_config_mut().set_colors(styles.data); + for (pos, color) in styles.cells { + if !is_color_empty(&color) { + table.modify(pos, color); + } + } + + if structure.with_index && !is_color_empty(&styles.index) { + table.modify(Columns::first(), styles.index); + } + + if structure.with_header && !is_color_empty(&styles.header) { + table.modify(Rows::first(), styles.header.clone()); + } + + if structure.with_footer && !is_color_empty(&styles.header) { + table.modify(Rows::last(), styles.header); } } fn load_theme( table: &mut Table, theme: &TableTheme, - with_footer: bool, - with_header: bool, + structure: &TableStructure, sep_color: Option