Fix unused space when truncation is used and header on border is configured (#13353)

Somehow this logic was missed on my end. ( I mean I was not even
thinking about it in original patch 😄 )

Please recheck

Added a regression test too.

close #13336

cc: @fdncred
This commit is contained in:
Maxim Zhiburt 2024-07-11 23:13:16 +03:00 committed by GitHub
parent ccd0160c32
commit 4bd87d0496
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 76 additions and 19 deletions

View file

@ -2895,3 +2895,9 @@ fn table_kv_header_on_separator_trim_algorithm() {
let actual = nu!("$env.config.table.header_on_separator = true; {key1: '111111111111111111111111111111111111111111111111111111111111'} | table --width=60 --theme basic");
assert_eq!(actual.out, "+------+---------------------------------------------------+| key1 | 1111111111111111111111111111111111111111111111111 || | 11111111111 |+------+---------------------------------------------------+");
}
#[test]
fn table_general_header_on_separator_trim_algorithm() {
let actual = nu!("$env.config.table.header_on_separator = true; [[a b]; ['11111111111111111111111111111111111111' 2] ] | table --width=20 --theme basic");
assert_eq!(actual.out, "+-#-+----a-----+-b-+| 0 | 11111111 | 2 || | 11111111 | || | 11111111 | || | 11111111 | || | 111111 | |+---+----------+---+");
}

View file

@ -255,7 +255,8 @@ fn draw_table(
align_table(&mut table, alignments, with_index, with_header, with_footer);
colorize_table(&mut table, styles, with_index, with_header, with_footer);
let width_ctrl = TableWidthCtrl::new(widths, cfg, termwidth);
let pad = indent.0 + indent.1;
let width_ctrl = TableWidthCtrl::new(widths, cfg, termwidth, pad);
if with_header && border_header {
set_border_head(&mut table, with_footer, width_ctrl);
@ -336,12 +337,18 @@ fn table_to_string(table: Table, termwidth: usize) -> Option<String> {
struct TableWidthCtrl {
width: Vec<usize>,
cfg: NuTableConfig,
max: usize,
width_max: usize,
pad: usize,
}
impl TableWidthCtrl {
fn new(width: Vec<usize>, cfg: NuTableConfig, max: usize) -> Self {
Self { width, cfg, max }
fn new(width: Vec<usize>, cfg: NuTableConfig, max: usize, pad: usize) -> Self {
Self {
width,
cfg,
width_max: max,
pad,
}
}
}
@ -354,19 +361,20 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
) {
let total_width = get_total_width2(&self.width, cfg);
if total_width > self.max {
if total_width > self.width_max {
let has_header = self.cfg.with_header && rec.count_rows() > 1;
let trim_as_head = has_header && self.cfg.header_on_border;
TableTrim {
max: self.max,
strategy: self.cfg.trim,
width: self.width,
TableTrim::new(
self.width,
self.width_max,
self.cfg.trim,
trim_as_head,
}
self.pad,
)
.change(rec, cfg, dim);
} else if self.cfg.expand && self.max > total_width {
Settings::new(SetDimensions(self.width), Width::increase(self.max))
} else if self.cfg.expand && self.width_max > total_width {
Settings::new(SetDimensions(self.width), Width::increase(self.width_max))
.change(rec, cfg, dim)
} else {
SetDimensions(self.width).change(rec, cfg, dim);
@ -376,9 +384,28 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
struct TableTrim {
width: Vec<usize>,
width_max: usize,
strategy: TrimStrategy,
max: usize,
trim_as_head: bool,
pad: usize,
}
impl TableTrim {
fn new(
width: Vec<usize>,
width_max: usize,
strategy: TrimStrategy,
trim_as_head: bool,
pad: usize,
) -> Self {
Self {
width,
strategy,
pad,
width_max,
trim_as_head,
}
}
}
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for TableTrim {
@ -395,13 +422,37 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
return;
}
// even though it's safe to trim columns by header there might be left unused space
// so we do use it if possible prioritizing left columns
let headers = recs[0].to_owned();
for (i, head) in headers.into_iter().enumerate() {
let head_width = CellInfo::width(&head);
let headers_widths = headers
.iter()
.map(CellInfo::width)
.map(|v| v + self.pad)
.collect::<Vec<_>>();
let min_width_use = get_total_width2(&headers_widths, cfg);
let mut free_width = self.width_max.saturating_sub(min_width_use);
for (i, head_width) in headers_widths.into_iter().enumerate() {
let head_width = head_width - self.pad;
let column_width = self.width[i] - self.pad; // safe to assume width is bigger then paddding
let mut use_width = head_width;
if free_width > 0 {
// it's safe to assume that column_width is always bigger or equal to head_width
debug_assert!(column_width >= head_width);
let additional_width = min(free_width, column_width - head_width);
free_width -= additional_width;
use_width += additional_width;
}
match &self.strategy {
TrimStrategy::Wrap { try_to_keep_words } => {
let mut wrap = Width::wrap(head_width);
let mut wrap = Width::wrap(use_width);
if *try_to_keep_words {
wrap = wrap.keep_words();
}
@ -411,7 +462,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
.change(recs, cfg, dims);
}
TrimStrategy::Truncate { suffix } => {
let mut truncate = Width::truncate(self.max);
let mut truncate = Width::truncate(use_width);
if let Some(suffix) = suffix {
truncate = truncate.suffix(suffix).suffix_try_color(true);
}
@ -428,7 +479,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
match self.strategy {
TrimStrategy::Wrap { try_to_keep_words } => {
let mut wrap = Width::wrap(self.max).priority::<PriorityMax>();
let mut wrap = Width::wrap(self.width_max).priority::<PriorityMax>();
if try_to_keep_words {
wrap = wrap.keep_words();
}
@ -436,7 +487,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
Settings::new(SetDimensions(self.width), wrap).change(recs, cfg, dims);
}
TrimStrategy::Truncate { suffix } => {
let mut truncate = Width::truncate(self.max).priority::<PriorityMax>();
let mut truncate = Width::truncate(self.width_max).priority::<PriorityMax>();
if let Some(suffix) = suffix {
truncate = truncate.suffix(suffix).suffix_try_color(true);
}