mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
nu_table: Fix style of tables with no header (#6025)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
parent
217c2bae99
commit
93a965e3e2
4 changed files with 76 additions and 64 deletions
|
@ -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| {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue