From 93a965e3e23781be474052fe54219ff3c3bef8ff Mon Sep 17 00:00:00 2001 From: Maxim Zhiburt Date: Tue, 12 Jul 2022 20:56:36 +0300 Subject: [PATCH] nu_table: Fix style of tables with no header (#6025) Signed-off-by: Maxim Zhiburt --- crates/nu-command/src/viewers/table.rs | 24 +++++----- crates/nu-table/src/main.rs | 2 +- crates/nu-table/src/table.rs | 65 +++++++++++++------------- crates/nu-table/src/width_control.rs | 49 +++++++++++-------- 4 files changed, 76 insertions(+), 64 deletions(-) diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index 941bed5732..e1e140b740 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -168,7 +168,7 @@ impl Command for Table { } let table = nu_table::Table { - headers: vec![], + headers: None, data: output, theme: load_theme_from_config(config), }; @@ -430,16 +430,18 @@ fn convert_to_table( } Ok(Some(nu_table::Table { - headers: headers - .into_iter() - .map(|x| StyledString { - contents: x, - style: TextStyle { - alignment: nu_table::Alignment::Center, - color_style: Some(color_hm["header"]), - }, - }) - .collect(), + headers: Some( + headers + .into_iter() + .map(|x| StyledString { + contents: x, + style: TextStyle { + alignment: nu_table::Alignment::Center, + color_style: Some(color_hm["header"]), + }, + }) + .collect(), + ), data: data .into_iter() .map(|x| { diff --git a/crates/nu-table/src/main.rs b/crates/nu-table/src/main.rs index f85ada3924..79736e75a6 100644 --- a/crates/nu-table/src/main.rs +++ b/crates/nu-table/src/main.rs @@ -23,7 +23,7 @@ fn main() { // The table rows let rows = vec_of_str_to_vec_of_styledstr(&row_data, false); // The table itself - let table = Table::new(headers, vec![rows; 3], TableTheme::rounded()); + let table = Table::new(Some(headers), vec![rows; 3], TableTheme::rounded()); // FIXME: Config isn't available from here so just put these here to compile let color_hm: HashMap = HashMap::new(); // get the default config diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs index 4754b4e9e6..e2e2e5bd99 100644 --- a/crates/nu-table/src/table.rs +++ b/crates/nu-table/src/table.rs @@ -19,14 +19,14 @@ use crate::{ #[derive(Debug)] pub struct Table { - pub headers: Vec, + pub headers: Option>, pub data: Vec>, pub theme: TableTheme, } impl Table { pub fn new( - headers: Vec, + headers: Option>, data: Vec>, theme: TableTheme, ) -> Table { @@ -46,22 +46,18 @@ pub fn draw_table( ) -> Option { let termwidth = fix_termwidth(termwidth, &table.theme)?; - let (mut headers, mut data) = table_fix_lengths(&table.headers, &table.data); + let (mut headers, mut data, count_columns) = + table_fix_lengths(table.headers.as_ref(), &table.data); - maybe_truncate_columns(&mut headers, &mut data, termwidth); + maybe_truncate_columns(&mut headers, &mut data, count_columns, termwidth); - let max_column_width = estimate_max_column_width(&headers, &data, termwidth)?; + let max_column_width = + estimate_max_column_width(headers.as_ref(), &data, count_columns, termwidth)?; let alignments = build_alignment_map(&table.data); let headers = table_header_to_strings(headers); - let data = table_data_to_strings(data, headers.len()); - - let headers = if headers.is_empty() { - None - } else { - Some(headers) - }; + let data = table_data_to_strings(data, count_columns); let theme = &table.theme; let with_header = headers.is_some(); @@ -111,20 +107,22 @@ fn table_data_to_strings( data } -fn table_header_to_strings(table_headers: Vec) -> Vec { - let mut headers = Vec::with_capacity(table_headers.len()); - for cell in table_headers { - let colored_text = cell - .style - .color_style - .as_ref() - .map(|color| color.paint(&cell.contents).to_string()) - .unwrap_or(cell.contents); +fn table_header_to_strings(table_headers: Option>) -> Option> { + table_headers.map(|table_headers| { + let mut headers = Vec::with_capacity(table_headers.len()); + for cell in table_headers { + let colored_text = cell + .style + .color_style + .as_ref() + .map(|color| color.paint(&cell.contents).to_string()) + .unwrap_or(cell.contents); - headers.push(colored_text) - } + headers.push(colored_text) + } - headers + headers + }) } fn build_alignment_map(data: &[Vec]) -> Vec> { @@ -320,14 +318,17 @@ impl tabled::TableOption for &TrimStrategyModifier<'_> { } fn table_fix_lengths( - headers: &[StyledString], + headers: Option<&Vec>, data: &[Vec], -) -> (Vec, Vec>) { +) -> (Option>, Vec>, usize) { let length = table_find_max_length(headers, data); - let mut headers_fixed = Vec::with_capacity(length); - headers_fixed.extend(headers.iter().cloned()); - headers_fixed.extend(std::iter::repeat(StyledString::default()).take(length - headers.len())); + let headers_fixed = headers.map(|h| { + let mut headers_fixed = Vec::with_capacity(length); + headers_fixed.extend(h.iter().cloned()); + headers_fixed.extend(std::iter::repeat(StyledString::default()).take(length - h.len())); + headers_fixed + }); let mut data_fixed = Vec::with_capacity(data.len()); for row in data { @@ -337,11 +338,11 @@ fn table_fix_lengths( data_fixed.push(row_fixed); } - (headers_fixed, data_fixed) + (headers_fixed, data_fixed, length) } -fn table_find_max_length(headers: &[StyledString], data: &[Vec]) -> usize { - let mut length = headers.len(); +fn table_find_max_length(headers: Option<&Vec>, data: &[Vec]) -> usize { + let mut length = headers.map_or(0, |h| h.len()); for row in data { length = std::cmp::max(length, row.len()); } diff --git a/crates/nu-table/src/width_control.rs b/crates/nu-table/src/width_control.rs index ef01cbc857..a42741852a 100644 --- a/crates/nu-table/src/width_control.rs +++ b/crates/nu-table/src/width_control.rs @@ -3,23 +3,26 @@ use crate::{StyledString, TableTheme}; use std::iter::Iterator; pub(crate) fn maybe_truncate_columns( - headers: &mut Vec, + headers: &mut Option>, data: &mut [Vec], + length: usize, termwidth: usize, ) { // Make sure we have enough space for the columns we have let max_num_of_columns = termwidth / 10; // If we have too many columns, truncate the table - if max_num_of_columns < headers.len() { - headers.truncate(max_num_of_columns); - headers.push(StyledString::new( - String::from("..."), - TextStyle::basic_center(), - )); + if let Some(headers) = headers { + if max_num_of_columns < length { + headers.truncate(max_num_of_columns); + headers.push(StyledString::new( + String::from("..."), + TextStyle::basic_center(), + )); + } } - if max_num_of_columns < headers.len() { + if max_num_of_columns < length { for entry in data.iter_mut() { entry.truncate(max_num_of_columns); entry.push(StyledString::new( @@ -31,17 +34,17 @@ pub(crate) fn maybe_truncate_columns( } pub(crate) fn estimate_max_column_width( - headers: &[StyledString], + headers: Option<&Vec>, data: &[Vec], + count_columns: usize, termwidth: usize, ) -> Option { - let max_per_column = get_max_column_widths(headers, data); + let max_per_column = get_max_column_widths(headers, data, count_columns); - let headers_len = headers.len(); // Measure how big our columns need to be (accounting for separators also) - let max_naive_column_width = (termwidth - 3 * (headers_len - 1)) / headers_len; + let max_naive_column_width = (termwidth - 3 * (count_columns - 1)) / count_columns; - let column_space = ColumnSpace::measure(&max_per_column, max_naive_column_width, headers_len); + let column_space = ColumnSpace::measure(&max_per_column, max_naive_column_width, count_columns); // This gives us the max column width let max_column_width = column_space.max_width(termwidth)?; @@ -51,7 +54,7 @@ pub(crate) fn estimate_max_column_width( &max_per_column, max_naive_column_width, max_column_width, - headers_len, + count_columns, ); // This should give us the final max column width @@ -76,15 +79,21 @@ pub(crate) fn fix_termwidth(termwidth: usize, theme: &TableTheme) -> Option]) -> Vec { +fn get_max_column_widths( + headers: Option<&Vec>, + data: &[Vec], + count_columns: usize, +) -> Vec { use std::cmp::max; - let mut output = vec![0; headers.len()]; + let mut output = vec![0; count_columns]; - for (col, content) in headers.iter().enumerate() { - let content = clean(&content.contents); - let content_width = tabled::papergrid::string_width_multiline(&content); - output[col] = max(output[col], content_width); + if let Some(headers) = headers { + for (col, content) in headers.iter().enumerate() { + let content = clean(&content.contents); + let content_width = tabled::papergrid::string_width_multiline(&content); + output[col] = max(output[col], content_width); + } } for row in data {