nu_table: Fix style of tables with no header (#6025)

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
Maxim Zhiburt 2022-07-12 20:56:36 +03:00 committed by GitHub
parent 217c2bae99
commit 93a965e3e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 64 deletions

View file

@ -168,7 +168,7 @@ impl Command for Table {
} }
let table = nu_table::Table { let table = nu_table::Table {
headers: vec![], headers: None,
data: output, data: output,
theme: load_theme_from_config(config), theme: load_theme_from_config(config),
}; };
@ -430,16 +430,18 @@ fn convert_to_table(
} }
Ok(Some(nu_table::Table { Ok(Some(nu_table::Table {
headers: headers headers: Some(
.into_iter() headers
.map(|x| StyledString { .into_iter()
contents: x, .map(|x| StyledString {
style: TextStyle { contents: x,
alignment: nu_table::Alignment::Center, style: TextStyle {
color_style: Some(color_hm["header"]), alignment: nu_table::Alignment::Center,
}, color_style: Some(color_hm["header"]),
}) },
.collect(), })
.collect(),
),
data: data data: data
.into_iter() .into_iter()
.map(|x| { .map(|x| {

View file

@ -23,7 +23,7 @@ fn main() {
// The table rows // The table rows
let rows = vec_of_str_to_vec_of_styledstr(&row_data, false); let rows = vec_of_str_to_vec_of_styledstr(&row_data, false);
// The table itself // 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 // FIXME: Config isn't available from here so just put these here to compile
let color_hm: HashMap<String, nu_ansi_term::Style> = HashMap::new(); let color_hm: HashMap<String, nu_ansi_term::Style> = HashMap::new();
// get the default config // get the default config

View file

@ -19,14 +19,14 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
pub struct Table { pub struct Table {
pub headers: Vec<StyledString>, pub headers: Option<Vec<StyledString>>,
pub data: Vec<Vec<StyledString>>, pub data: Vec<Vec<StyledString>>,
pub theme: TableTheme, pub theme: TableTheme,
} }
impl Table { impl Table {
pub fn new( pub fn new(
headers: Vec<StyledString>, headers: Option<Vec<StyledString>>,
data: Vec<Vec<StyledString>>, data: Vec<Vec<StyledString>>,
theme: TableTheme, theme: TableTheme,
) -> Table { ) -> Table {
@ -46,22 +46,18 @@ pub fn draw_table(
) -> Option<String> { ) -> Option<String> {
let termwidth = fix_termwidth(termwidth, &table.theme)?; 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 alignments = build_alignment_map(&table.data);
let headers = table_header_to_strings(headers); let headers = table_header_to_strings(headers);
let data = table_data_to_strings(data, headers.len()); let data = table_data_to_strings(data, count_columns);
let headers = if headers.is_empty() {
None
} else {
Some(headers)
};
let theme = &table.theme; let theme = &table.theme;
let with_header = headers.is_some(); let with_header = headers.is_some();
@ -111,20 +107,22 @@ fn table_data_to_strings(
data data
} }
fn table_header_to_strings(table_headers: Vec<StyledString>) -> Vec<String> { fn table_header_to_strings(table_headers: Option<Vec<StyledString>>) -> Option<Vec<String>> {
let mut headers = Vec::with_capacity(table_headers.len()); table_headers.map(|table_headers| {
for cell in table_headers { let mut headers = Vec::with_capacity(table_headers.len());
let colored_text = cell for cell in table_headers {
.style let colored_text = cell
.color_style .style
.as_ref() .color_style
.map(|color| color.paint(&cell.contents).to_string()) .as_ref()
.unwrap_or(cell.contents); .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<StyledString>]) -> Vec<Vec<Alignment>> { fn build_alignment_map(data: &[Vec<StyledString>]) -> Vec<Vec<Alignment>> {
@ -320,14 +318,17 @@ impl tabled::TableOption for &TrimStrategyModifier<'_> {
} }
fn table_fix_lengths( fn table_fix_lengths(
headers: &[StyledString], headers: Option<&Vec<StyledString>>,
data: &[Vec<StyledString>], data: &[Vec<StyledString>],
) -> (Vec<StyledString>, Vec<Vec<StyledString>>) { ) -> (Option<Vec<StyledString>>, Vec<Vec<StyledString>>, usize) {
let length = table_find_max_length(headers, data); let length = table_find_max_length(headers, data);
let mut headers_fixed = Vec::with_capacity(length); let headers_fixed = headers.map(|h| {
headers_fixed.extend(headers.iter().cloned()); let mut headers_fixed = Vec::with_capacity(length);
headers_fixed.extend(std::iter::repeat(StyledString::default()).take(length - headers.len())); 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()); let mut data_fixed = Vec::with_capacity(data.len());
for row in data { for row in data {
@ -337,11 +338,11 @@ fn table_fix_lengths(
data_fixed.push(row_fixed); data_fixed.push(row_fixed);
} }
(headers_fixed, data_fixed) (headers_fixed, data_fixed, length)
} }
fn table_find_max_length(headers: &[StyledString], data: &[Vec<StyledString>]) -> usize { fn table_find_max_length(headers: Option<&Vec<StyledString>>, data: &[Vec<StyledString>]) -> usize {
let mut length = headers.len(); let mut length = headers.map_or(0, |h| h.len());
for row in data { for row in data {
length = std::cmp::max(length, row.len()); length = std::cmp::max(length, row.len());
} }

View file

@ -3,23 +3,26 @@ use crate::{StyledString, TableTheme};
use std::iter::Iterator; use std::iter::Iterator;
pub(crate) fn maybe_truncate_columns( pub(crate) fn maybe_truncate_columns(
headers: &mut Vec<StyledString>, headers: &mut Option<Vec<StyledString>>,
data: &mut [Vec<StyledString>], data: &mut [Vec<StyledString>],
length: usize,
termwidth: usize, termwidth: usize,
) { ) {
// Make sure we have enough space for the columns we have // Make sure we have enough space for the columns we have
let max_num_of_columns = termwidth / 10; let max_num_of_columns = termwidth / 10;
// If we have too many columns, truncate the table // If we have too many columns, truncate the table
if max_num_of_columns < headers.len() { if let Some(headers) = headers {
headers.truncate(max_num_of_columns); if max_num_of_columns < length {
headers.push(StyledString::new( headers.truncate(max_num_of_columns);
String::from("..."), headers.push(StyledString::new(
TextStyle::basic_center(), String::from("..."),
)); TextStyle::basic_center(),
));
}
} }
if max_num_of_columns < headers.len() { if max_num_of_columns < length {
for entry in data.iter_mut() { for entry in data.iter_mut() {
entry.truncate(max_num_of_columns); entry.truncate(max_num_of_columns);
entry.push(StyledString::new( entry.push(StyledString::new(
@ -31,17 +34,17 @@ pub(crate) fn maybe_truncate_columns(
} }
pub(crate) fn estimate_max_column_width( pub(crate) fn estimate_max_column_width(
headers: &[StyledString], headers: Option<&Vec<StyledString>>,
data: &[Vec<StyledString>], data: &[Vec<StyledString>],
count_columns: usize,
termwidth: usize, termwidth: usize,
) -> Option<usize> { ) -> Option<usize> {
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) // 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 // This gives us the max column width
let max_column_width = column_space.max_width(termwidth)?; let max_column_width = column_space.max_width(termwidth)?;
@ -51,7 +54,7 @@ pub(crate) fn estimate_max_column_width(
&max_per_column, &max_per_column,
max_naive_column_width, max_naive_column_width,
max_column_width, max_column_width,
headers_len, count_columns,
); );
// This should give us the final max column width // This should give us the final max column width
@ -76,15 +79,21 @@ pub(crate) fn fix_termwidth(termwidth: usize, theme: &TableTheme) -> Option<usiz
Some(termwidth - edges_width - 1) Some(termwidth - edges_width - 1)
} }
fn get_max_column_widths(headers: &[StyledString], data: &[Vec<StyledString>]) -> Vec<usize> { fn get_max_column_widths(
headers: Option<&Vec<StyledString>>,
data: &[Vec<StyledString>],
count_columns: usize,
) -> Vec<usize> {
use std::cmp::max; 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() { if let Some(headers) = headers {
let content = clean(&content.contents); for (col, content) in headers.iter().enumerate() {
let content_width = tabled::papergrid::string_width_multiline(&content); let content = clean(&content.contents);
output[col] = max(output[col], content_width); let content_width = tabled::papergrid::string_width_multiline(&content);
output[col] = max(output[col], content_width);
}
} }
for row in data { for row in data {