mirror of
https://github.com/nushell/nushell
synced 2024-12-27 21:43:09 +00:00
nu-table: Remove width estimation logic (#6037)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
This commit is contained in:
parent
2b2117173c
commit
58ab5aa887
3 changed files with 4 additions and 246 deletions
|
@ -11,11 +11,7 @@ use tabled::{
|
||||||
Alignment, Modify, TableOption, Width,
|
Alignment, Modify, TableOption, Width,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{table_theme::TableTheme, width_control::maybe_truncate_columns, StyledString};
|
||||||
table_theme::TableTheme,
|
|
||||||
width_control::{estimate_max_column_width, fix_termwidth, maybe_truncate_columns},
|
|
||||||
StyledString,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
|
@ -49,16 +45,11 @@ pub fn draw_table(
|
||||||
color_hm: &HashMap<String, Style>,
|
color_hm: &HashMap<String, Style>,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Option<String> {
|
) -> Option<String> {
|
||||||
let termwidth = fix_termwidth(termwidth, &table.theme)?;
|
|
||||||
|
|
||||||
let (mut headers, mut data, count_columns) =
|
let (mut headers, mut data, count_columns) =
|
||||||
table_fix_lengths(table.headers.as_ref(), &table.data);
|
table_fix_lengths(table.headers.as_ref(), &table.data);
|
||||||
|
|
||||||
maybe_truncate_columns(&mut headers, &mut data, count_columns, termwidth);
|
maybe_truncate_columns(&mut headers, &mut data, count_columns, 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);
|
||||||
|
@ -71,15 +62,7 @@ pub fn draw_table(
|
||||||
let table = build_table(data, headers, Some(alignments), config, with_footer);
|
let table = build_table(data, headers, Some(alignments), config, with_footer);
|
||||||
let table = load_theme(table, color_hm, theme, with_footer, with_header);
|
let table = load_theme(table, color_hm, theme, with_footer, with_header);
|
||||||
|
|
||||||
let (count_columns, table) = count_columns_on_table(table);
|
let table = table_trim_columns(table, termwidth, &config.trim_strategy);
|
||||||
|
|
||||||
let table = table_trim_columns(
|
|
||||||
table,
|
|
||||||
count_columns,
|
|
||||||
termwidth,
|
|
||||||
max_column_width,
|
|
||||||
&config.trim_strategy,
|
|
||||||
);
|
|
||||||
|
|
||||||
Some(print_table(table, config))
|
Some(print_table(table, config))
|
||||||
}
|
}
|
||||||
|
@ -100,13 +83,6 @@ fn print_table(table: tabled::Table, config: &Config) -> String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count_columns_on_table(mut table: tabled::Table) -> (usize, tabled::Table) {
|
|
||||||
let mut c = CountColumns(0);
|
|
||||||
table = table.with(&mut c);
|
|
||||||
|
|
||||||
(c.0, table)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn table_data_to_strings(
|
fn table_data_to_strings(
|
||||||
table_data: Vec<Vec<StyledString>>,
|
table_data: Vec<Vec<StyledString>>,
|
||||||
count_headers: usize,
|
count_headers: usize,
|
||||||
|
@ -292,18 +268,11 @@ impl TableOption for &mut CountColumns {
|
||||||
|
|
||||||
fn table_trim_columns(
|
fn table_trim_columns(
|
||||||
table: tabled::Table,
|
table: tabled::Table,
|
||||||
count_columns: usize,
|
|
||||||
termwidth: usize,
|
termwidth: usize,
|
||||||
max_column_width: usize,
|
|
||||||
trim_strategy: &TrimStrategy,
|
trim_strategy: &TrimStrategy,
|
||||||
) -> tabled::Table {
|
) -> tabled::Table {
|
||||||
let mut table_width = max_column_width * count_columns;
|
|
||||||
if table_width > termwidth {
|
|
||||||
table_width = termwidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.with(&TrimStrategyModifier {
|
table.with(&TrimStrategyModifier {
|
||||||
termwidth: table_width,
|
termwidth,
|
||||||
trim_strategy,
|
trim_strategy,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,32 +3,24 @@ use tabled::{style::StyleConfig, Style};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct TableTheme {
|
pub struct TableTheme {
|
||||||
pub(crate) theme: StyleConfig,
|
pub(crate) theme: StyleConfig,
|
||||||
pub(crate) is_left_set: bool,
|
|
||||||
pub(crate) is_right_set: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableTheme {
|
impl TableTheme {
|
||||||
pub fn basic() -> TableTheme {
|
pub fn basic() -> TableTheme {
|
||||||
Self {
|
Self {
|
||||||
theme: Style::ascii().into(),
|
theme: Style::ascii().into(),
|
||||||
is_left_set: true,
|
|
||||||
is_right_set: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thin() -> TableTheme {
|
pub fn thin() -> TableTheme {
|
||||||
Self {
|
Self {
|
||||||
theme: Style::modern().into(),
|
theme: Style::modern().into(),
|
||||||
is_left_set: true,
|
|
||||||
is_right_set: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn light() -> TableTheme {
|
pub fn light() -> TableTheme {
|
||||||
Self {
|
Self {
|
||||||
theme: Style::blank().header('─').into(),
|
theme: Style::blank().header('─').into(),
|
||||||
is_left_set: false,
|
|
||||||
is_right_set: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,8 +31,6 @@ impl TableTheme {
|
||||||
.right_off()
|
.right_off()
|
||||||
.horizontal_off()
|
.horizontal_off()
|
||||||
.into(),
|
.into(),
|
||||||
is_left_set: false,
|
|
||||||
is_right_set: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,8 +42,6 @@ impl TableTheme {
|
||||||
.bottom('❤')
|
.bottom('❤')
|
||||||
.vertical('❤')
|
.vertical('❤')
|
||||||
.into(),
|
.into(),
|
||||||
is_left_set: false,
|
|
||||||
is_right_set: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,16 +56,12 @@ impl TableTheme {
|
||||||
.bottom_intersection('╩')
|
.bottom_intersection('╩')
|
||||||
.header_intersection('╬')
|
.header_intersection('╬')
|
||||||
.into(),
|
.into(),
|
||||||
is_left_set: false,
|
|
||||||
is_right_set: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rounded() -> TableTheme {
|
pub fn rounded() -> TableTheme {
|
||||||
Self {
|
Self {
|
||||||
theme: Style::rounded().into(),
|
theme: Style::rounded().into(),
|
||||||
is_left_set: true,
|
|
||||||
is_right_set: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,8 +74,6 @@ impl TableTheme {
|
||||||
.bottom_right_corner('┛')
|
.bottom_right_corner('┛')
|
||||||
.horizontal_off()
|
.horizontal_off()
|
||||||
.into(),
|
.into(),
|
||||||
is_left_set: true,
|
|
||||||
is_right_set: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,16 +97,12 @@ impl TableTheme {
|
||||||
.header_intersection('╋')
|
.header_intersection('╋')
|
||||||
.horizontal_off()
|
.horizontal_off()
|
||||||
.into(),
|
.into(),
|
||||||
is_left_set: true,
|
|
||||||
is_right_set: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn none() -> TableTheme {
|
pub fn none() -> TableTheme {
|
||||||
Self {
|
Self {
|
||||||
theme: Style::blank().into(),
|
theme: Style::blank().into(),
|
||||||
is_left_set: false,
|
|
||||||
is_right_set: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::textstyle::TextStyle;
|
use crate::textstyle::TextStyle;
|
||||||
use crate::{StyledString, TableTheme};
|
use crate::StyledString;
|
||||||
use std::iter::Iterator;
|
|
||||||
|
|
||||||
pub(crate) fn maybe_truncate_columns(
|
pub(crate) fn maybe_truncate_columns(
|
||||||
headers: &mut Option<Vec<StyledString>>,
|
headers: &mut Option<Vec<StyledString>>,
|
||||||
|
@ -32,191 +31,3 @@ pub(crate) fn maybe_truncate_columns(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn estimate_max_column_width(
|
|
||||||
headers: Option<&Vec<StyledString>>,
|
|
||||||
data: &[Vec<StyledString>],
|
|
||||||
count_columns: usize,
|
|
||||||
termwidth: usize,
|
|
||||||
) -> Option<usize> {
|
|
||||||
let max_per_column = get_max_column_widths(headers, data, count_columns);
|
|
||||||
|
|
||||||
// Measure how big our columns need to be (accounting for separators also)
|
|
||||||
let max_naive_column_width = (termwidth - 3 * (count_columns - 1)) / count_columns;
|
|
||||||
|
|
||||||
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)?;
|
|
||||||
|
|
||||||
// This width isn't quite right, as we're rounding off some of our space
|
|
||||||
let column_space = column_space.fix_almost_column_width(
|
|
||||||
&max_per_column,
|
|
||||||
max_naive_column_width,
|
|
||||||
max_column_width,
|
|
||||||
count_columns,
|
|
||||||
);
|
|
||||||
|
|
||||||
// This should give us the final max column width
|
|
||||||
let max_column_width = column_space.max_width(termwidth)?;
|
|
||||||
|
|
||||||
Some(max_column_width)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn fix_termwidth(termwidth: usize, theme: &TableTheme) -> Option<usize> {
|
|
||||||
let edges_width = if theme.is_left_set && theme.is_right_set {
|
|
||||||
3
|
|
||||||
} else if theme.is_left_set || theme.is_right_set {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if termwidth < edges_width {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(termwidth - edges_width - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
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; count_columns];
|
|
||||||
|
|
||||||
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 {
|
|
||||||
for (col, content) in row.iter().enumerate() {
|
|
||||||
let content = clean(&content.contents);
|
|
||||||
let content_width = tabled::papergrid::string_width_multiline(&content);
|
|
||||||
output[col] = max(output[col], content_width);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ColumnSpace {
|
|
||||||
num_overages: usize,
|
|
||||||
underage_sum: usize,
|
|
||||||
overage_separator_sum: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ColumnSpace {
|
|
||||||
/// Measure how much space we have once we subtract off the columns who are small enough
|
|
||||||
fn measure(
|
|
||||||
max_per_column: &[usize],
|
|
||||||
max_naive_column_width: usize,
|
|
||||||
headers_len: usize,
|
|
||||||
) -> ColumnSpace {
|
|
||||||
let mut num_overages = 0;
|
|
||||||
let mut underage_sum = 0;
|
|
||||||
let mut overage_separator_sum = 0;
|
|
||||||
let iter = max_per_column.iter().enumerate().take(headers_len);
|
|
||||||
|
|
||||||
for (i, &column_max) in iter {
|
|
||||||
if column_max > max_naive_column_width {
|
|
||||||
num_overages += 1;
|
|
||||||
if i != (headers_len - 1) {
|
|
||||||
overage_separator_sum += 3;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
overage_separator_sum += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
underage_sum += column_max;
|
|
||||||
// if column isn't last, add 3 for its separator
|
|
||||||
if i != (headers_len - 1) {
|
|
||||||
underage_sum += 3;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
underage_sum += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnSpace {
|
|
||||||
num_overages,
|
|
||||||
underage_sum,
|
|
||||||
overage_separator_sum,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fix_almost_column_width(
|
|
||||||
self,
|
|
||||||
max_per_column: &[usize],
|
|
||||||
max_naive_column_width: usize,
|
|
||||||
max_column_width: usize,
|
|
||||||
headers_len: usize,
|
|
||||||
) -> ColumnSpace {
|
|
||||||
let mut num_overages = 0;
|
|
||||||
let mut overage_separator_sum = 0;
|
|
||||||
let mut underage_sum = self.underage_sum;
|
|
||||||
let iter = max_per_column.iter().enumerate().take(headers_len);
|
|
||||||
|
|
||||||
for (i, &column_max) in iter {
|
|
||||||
if column_max > max_naive_column_width {
|
|
||||||
if column_max <= max_column_width {
|
|
||||||
underage_sum += column_max;
|
|
||||||
// if column isn't last, add 3 for its separator
|
|
||||||
if i != (headers_len - 1) {
|
|
||||||
underage_sum += 3;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
underage_sum += 1;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Column is still too large, so let's count it
|
|
||||||
num_overages += 1;
|
|
||||||
if i != (headers_len - 1) {
|
|
||||||
overage_separator_sum += 3;
|
|
||||||
}
|
|
||||||
if i == 0 {
|
|
||||||
overage_separator_sum += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ColumnSpace {
|
|
||||||
num_overages,
|
|
||||||
underage_sum,
|
|
||||||
overage_separator_sum,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn max_width(&self, termwidth: usize) -> Option<usize> {
|
|
||||||
let ColumnSpace {
|
|
||||||
num_overages,
|
|
||||||
underage_sum,
|
|
||||||
overage_separator_sum,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
if *num_overages > 0 {
|
|
||||||
termwidth
|
|
||||||
.checked_sub(1)?
|
|
||||||
.checked_sub(*underage_sum)?
|
|
||||||
.checked_sub(*overage_separator_sum)?
|
|
||||||
.checked_div(*num_overages)
|
|
||||||
} else {
|
|
||||||
Some(99999)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clean(input: &str) -> String {
|
|
||||||
let input = input.replace('\r', "");
|
|
||||||
|
|
||||||
input.replace('\t', " ")
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue