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 {
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| {

View File

@ -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<String, nu_ansi_term::Style> = HashMap::new();
// get the default config

View File

@ -19,14 +19,14 @@ use crate::{
#[derive(Debug)]
pub struct Table {
pub headers: Vec<StyledString>,
pub headers: Option<Vec<StyledString>>,
pub data: Vec<Vec<StyledString>>,
pub theme: TableTheme,
}
impl Table {
pub fn new(
headers: Vec<StyledString>,
headers: Option<Vec<StyledString>>,
data: Vec<Vec<StyledString>>,
theme: TableTheme,
) -> Table {
@ -46,22 +46,18 @@ pub fn draw_table(
) -> Option<String> {
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<StyledString>) -> Vec<String> {
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<Vec<StyledString>>) -> Option<Vec<String>> {
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<StyledString>]) -> Vec<Vec<Alignment>> {
@ -320,14 +318,17 @@ impl tabled::TableOption for &TrimStrategyModifier<'_> {
}
fn table_fix_lengths(
headers: &[StyledString],
headers: Option<&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 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<StyledString>]) -> usize {
let mut length = headers.len();
fn table_find_max_length(headers: Option<&Vec<StyledString>>, data: &[Vec<StyledString>]) -> usize {
let mut length = headers.map_or(0, |h| h.len());
for row in data {
length = std::cmp::max(length, row.len());
}

View File

@ -3,23 +3,26 @@ use crate::{StyledString, TableTheme};
use std::iter::Iterator;
pub(crate) fn maybe_truncate_columns(
headers: &mut Vec<StyledString>,
headers: &mut Option<Vec<StyledString>>,
data: &mut [Vec<StyledString>],
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<StyledString>>,
data: &[Vec<StyledString>],
count_columns: usize,
termwidth: 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)
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<usiz
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;
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 {