[DRAFT] Check fix for emojie, wrap issues (#13430)

Hi there

Here I am using latest tabled.

My tests shows it does fixes panics, but I am wanna be sure.

@fdncred could you verify that it does fixes those panics/errors?

Closes #13405 
Closes #12786
This commit is contained in:
Maxim Zhiburt 2024-08-24 01:35:42 +03:00 committed by GitHub
parent 7003b007d5
commit 525eac1afd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 917 additions and 822 deletions

1292
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -156,7 +156,7 @@ sha2 = "0.10"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
syn = "2.0" syn = "2.0"
sysinfo = "0.30" sysinfo = "0.30"
tabled = { version = "0.14.0", default-features = false } tabled = { version = "0.16.0", default-features = false }
tempfile = "3.10" tempfile = "3.10"
terminal_size = "0.3" terminal_size = "0.3"
titlecase = "2.0" titlecase = "2.0"

View File

@ -83,7 +83,7 @@ serde_urlencoded = { workspace = true }
serde_yaml = { workspace = true } serde_yaml = { workspace = true }
sha2 = { workspace = true } sha2 = { workspace = true }
sysinfo = { workspace = true } sysinfo = { workspace = true }
tabled = { workspace = true, features = ["color"], default-features = false } tabled = { workspace = true, features = ["ansi"], default-features = false }
terminal_size = { workspace = true } terminal_size = { workspace = true }
titlecase = { workspace = true } titlecase = { workspace = true }
toml = { workspace = true, features = ["preserve_order"] } toml = { workspace = true, features = ["preserve_order"] }

View File

@ -57,7 +57,7 @@ pub fn build_table(value: Value, description: String, termsize: usize) -> String
Settings::default() Settings::default()
.with(Style::rounded().corner_top_left('├').corner_top_right('┤')) .with(Style::rounded().corner_top_left('├').corner_top_right('┤'))
.with(SetWidths(widths)) .with(SetWidths(widths))
.with(Wrap::new(width).priority::<PriorityMax>()) .with(Wrap::new(width).priority(PriorityMax))
.with(SetHorizontalChar::new('┼', '┴', 11 + 2 + 1)), .with(SetHorizontalChar::new('┼', '┴', 11 + 2 + 1)),
); );
@ -308,7 +308,7 @@ mod global_horizontal_char {
} }
} }
impl<R: Records + ExactRecords> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig> impl<R: Records + ExactRecords> TableOption<R, ColoredConfig, CompleteDimensionVecRecords<'_>>
for SetHorizontalChar for SetHorizontalChar
{ {
fn change( fn change(
@ -377,7 +377,7 @@ mod set_widths {
pub struct SetWidths(pub Vec<usize>); pub struct SetWidths(pub Vec<usize>);
impl<R> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetWidths { impl<R> TableOption<R, ColoredConfig, CompleteDimensionVecRecords<'_>> for SetWidths {
fn change( fn change(
self, self,
_: &mut R, _: &mut R,

View File

@ -18,7 +18,7 @@ nu-color-config = { path = "../nu-color-config", version = "0.97.2" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
once_cell = { workspace = true } once_cell = { workspace = true }
fancy-regex = { workspace = true } fancy-regex = { workspace = true }
tabled = { workspace = true, features = ["color"], default-features = false } tabled = { workspace = true, features = ["ansi"], default-features = false }
[dev-dependencies] [dev-dependencies]
# nu-test-support = { path="../nu-test-support", version = "0.97.2" } # nu-test-support = { path="../nu-test-support", version = "0.97.2" }

View File

@ -1,7 +1,7 @@
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_color_config::TextStyle; use nu_color_config::TextStyle;
use nu_table::{NuTable, NuTableConfig, TableTheme}; use nu_table::{NuTable, NuTableConfig, TableTheme};
use tabled::grid::records::vec_records::CellInfo; use tabled::grid::records::vec_records::Text;
fn main() { fn main() {
let args: Vec<_> = std::env::args().collect(); let args: Vec<_> = std::env::args().collect();
@ -80,10 +80,10 @@ fn make_table_data() -> (Vec<&'static str>, Vec<&'static str>) {
(table_headers, row_data) (table_headers, row_data)
} }
fn to_cell_info_vec(data: &[&str]) -> Vec<CellInfo<String>> { fn to_cell_info_vec(data: &[&str]) -> Vec<Text<String>> {
let mut v = vec![]; let mut v = vec![];
for x in data { for x in data {
v.push(CellInfo::new(String::from(*x))); v.push(Text::new(String::from(*x)));
} }
v v

View File

@ -7,12 +7,12 @@ use std::{cmp::min, collections::HashMap};
use tabled::{ use tabled::{
builder::Builder, builder::Builder,
grid::{ grid::{
color::AnsiColor, ansi::ANSIBuf,
colors::Colors, colors::Colors,
config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position}, config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position},
dimension::CompleteDimensionVecRecords, dimension::CompleteDimensionVecRecords,
records::{ records::{
vec_records::{Cell, CellInfo, VecRecords}, vec_records::{Cell, Text, VecRecords},
ExactRecords, PeekableRecords, Records, Resizable, ExactRecords, PeekableRecords, Records, Resizable,
}, },
}, },
@ -22,7 +22,7 @@ use tabled::{
peaker::Peaker, peaker::Peaker,
themes::ColumnNames, themes::ColumnNames,
width::Truncate, width::Truncate,
Color, Modify, Padding, Settings, TableOption, Width, Alignment, Color, Modify, Padding, Settings, TableOption, Width,
}, },
Table, Table,
}; };
@ -37,13 +37,13 @@ pub struct NuTable {
} }
pub type NuRecords = VecRecords<NuTableCell>; pub type NuRecords = VecRecords<NuTableCell>;
pub type NuTableCell = CellInfo<String>; pub type NuTableCell = Text<String>;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct Styles { struct Styles {
index: AnsiColor<'static>, index: ANSIBuf,
header: AnsiColor<'static>, header: ANSIBuf,
data: EntityMap<AnsiColor<'static>>, data: EntityMap<ANSIBuf>,
data_is_set: bool, data_is_set: bool,
} }
@ -60,7 +60,7 @@ impl NuTable {
/// Creates an empty [`NuTable`] instance. /// Creates an empty [`NuTable`] instance.
pub fn new(count_rows: usize, count_columns: usize) -> Self { pub fn new(count_rows: usize, count_columns: usize) -> Self {
Self { Self {
data: VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]), data: VecRecords::new(vec![vec![Text::default(); count_columns]; count_rows]),
styles: Styles::default(), styles: Styles::default(),
indent: (1, 1), indent: (1, 1),
alignments: Alignments { alignments: Alignments {
@ -84,12 +84,12 @@ impl NuTable {
} }
pub fn insert(&mut self, pos: Position, text: String) { pub fn insert(&mut self, pos: Position, text: String) {
self.data[pos.0][pos.1] = CellInfo::new(text); self.data[pos.0][pos.1] = Text::new(text);
} }
pub fn set_column_style(&mut self, column: usize, style: TextStyle) { pub fn set_column_style(&mut self, column: usize, style: TextStyle) {
if let Some(style) = style.color_style { if let Some(style) = style.color_style {
let style = AnsiColor::from(convert_style(style)); let style = ANSIBuf::from(convert_style(style));
self.styles.data.insert(Entity::Column(column), style); self.styles.data.insert(Entity::Column(column), style);
self.styles.data_is_set = true; self.styles.data_is_set = true;
} }
@ -102,7 +102,7 @@ impl NuTable {
pub fn insert_style(&mut self, pos: Position, style: TextStyle) { pub fn insert_style(&mut self, pos: Position, style: TextStyle) {
if let Some(style) = style.color_style { if let Some(style) = style.color_style {
let style = AnsiColor::from(convert_style(style)); let style = ANSIBuf::from(convert_style(style));
self.styles.data.insert(Entity::Cell(pos.0, pos.1), style); self.styles.data.insert(Entity::Cell(pos.0, pos.1), style);
self.styles.data_is_set = true; self.styles.data_is_set = true;
} }
@ -115,7 +115,7 @@ impl NuTable {
pub fn set_header_style(&mut self, style: TextStyle) { pub fn set_header_style(&mut self, style: TextStyle) {
if let Some(style) = style.color_style { if let Some(style) = style.color_style {
let style = AnsiColor::from(convert_style(style)); let style = ANSIBuf::from(convert_style(style));
self.styles.header = style; self.styles.header = style;
} }
@ -124,7 +124,7 @@ impl NuTable {
pub fn set_index_style(&mut self, style: TextStyle) { pub fn set_index_style(&mut self, style: TextStyle) {
if let Some(style) = style.color_style { if let Some(style) = style.color_style {
let style = AnsiColor::from(convert_style(style)); let style = ANSIBuf::from(convert_style(style));
self.styles.index = style; self.styles.index = style;
} }
@ -133,7 +133,7 @@ impl NuTable {
pub fn set_data_style(&mut self, style: TextStyle) { pub fn set_data_style(&mut self, style: TextStyle) {
if let Some(style) = style.color_style { if let Some(style) = style.color_style {
let style = AnsiColor::from(convert_style(style)); let style = ANSIBuf::from(convert_style(style));
self.styles.data.insert(Entity::Global, style); self.styles.data.insert(Entity::Global, style);
self.styles.data_is_set = true; self.styles.data_is_set = true;
} }
@ -171,8 +171,8 @@ impl NuTable {
} }
} }
impl From<Vec<Vec<CellInfo<String>>>> for NuTable { impl From<Vec<Vec<Text<String>>>> for NuTable {
fn from(value: Vec<Vec<CellInfo<String>>>) -> Self { fn from(value: Vec<Vec<Text<String>>>) -> Self {
let mut nutable = Self::new(0, 0); let mut nutable = Self::new(0, 0);
nutable.data = VecRecords::new(value); nutable.data = VecRecords::new(value);
@ -352,7 +352,7 @@ impl TableWidthCtrl {
} }
} }
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for TableWidthCtrl { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for TableWidthCtrl {
fn change( fn change(
self, self,
rec: &mut NuRecords, rec: &mut NuRecords,
@ -374,8 +374,8 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
) )
.change(rec, cfg, dim); .change(rec, cfg, dim);
} else if self.cfg.expand && self.width_max > total_width { } else if self.cfg.expand && self.width_max > total_width {
Settings::new(SetDimensions(self.width), Width::increase(self.width_max)) let opt = (SetDimensions(self.width), Width::increase(self.width_max));
.change(rec, cfg, dim) TableOption::<VecRecords<_>, _, _>::change(opt, rec, cfg, dim)
} else { } else {
SetDimensions(self.width).change(rec, cfg, dim); SetDimensions(self.width).change(rec, cfg, dim);
} }
@ -408,7 +408,7 @@ impl TableTrim {
} }
} }
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for TableTrim { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for TableTrim {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -424,27 +424,28 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
match self.strategy { match self.strategy {
TrimStrategy::Wrap { try_to_keep_words } => { TrimStrategy::Wrap { try_to_keep_words } => {
let mut wrap = Width::wrap(self.width_max).priority::<PriorityMax>(); let wrap = Width::wrap(self.width_max)
if try_to_keep_words { .keep_words(try_to_keep_words)
wrap = wrap.keep_words(); .priority(PriorityMax);
}
Settings::new(SetDimensions(self.width), wrap).change(recs, cfg, dims); let opt = (SetDimensions(self.width), wrap);
TableOption::<NuRecords, _, _>::change(opt, recs, cfg, dims);
} }
TrimStrategy::Truncate { suffix } => { TrimStrategy::Truncate { suffix } => {
let mut truncate = Width::truncate(self.width_max).priority::<PriorityMax>(); let mut truncate = Width::truncate(self.width_max).priority(PriorityMax);
if let Some(suffix) = suffix { if let Some(suffix) = suffix {
truncate = truncate.suffix(suffix).suffix_try_color(true); truncate = truncate.suffix(suffix).suffix_try_color(true);
} }
Settings::new(SetDimensions(self.width), truncate).change(recs, cfg, dims); let opt = (SetDimensions(self.width), truncate);
TableOption::<NuRecords, _, _>::change(opt, recs, cfg, dims);
} }
} }
} }
} }
fn trim_as_header( fn trim_as_header(
recs: &mut VecRecords<CellInfo<String>>, recs: &mut VecRecords<Text<String>>,
cfg: &mut ColoredConfig, cfg: &mut ColoredConfig,
dims: &mut CompleteDimensionVecRecords, dims: &mut CompleteDimensionVecRecords,
trim: TableTrim, trim: TableTrim,
@ -456,7 +457,7 @@ fn trim_as_header(
let headers = recs[0].to_owned(); let headers = recs[0].to_owned();
let headers_widths = headers let headers_widths = headers
.iter() .iter()
.map(CellInfo::width) .map(Text::width)
.map(|v| v + trim.pad) .map(|v| v + trim.pad)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let min_width_use = get_total_width2(&headers_widths, cfg); let min_width_use = get_total_width2(&headers_widths, cfg);
@ -481,14 +482,10 @@ fn trim_as_header(
match &trim.strategy { match &trim.strategy {
TrimStrategy::Wrap { try_to_keep_words } => { TrimStrategy::Wrap { try_to_keep_words } => {
let mut wrap = Width::wrap(use_width); let wrap = Width::wrap(use_width).keep_words(*try_to_keep_words);
if *try_to_keep_words {
wrap = wrap.keep_words();
}
Modify::new(Columns::single(i)) let opt = Modify::new(Columns::single(i)).with(wrap);
.with(wrap) TableOption::<VecRecords<Text<String>>, _, _>::change(opt, recs, cfg, dims);
.change(recs, cfg, dims);
} }
TrimStrategy::Truncate { suffix } => { TrimStrategy::Truncate { suffix } => {
let mut truncate = Width::truncate(use_width); let mut truncate = Width::truncate(use_width);
@ -496,9 +493,8 @@ fn trim_as_header(
truncate = truncate.suffix(suffix).suffix_try_color(true); truncate = truncate.suffix(suffix).suffix_try_color(true);
} }
Modify::new(Columns::single(i)) let opt = Modify::new(Columns::single(i)).with(truncate);
.with(truncate) TableOption::<VecRecords<Text<String>>, _, _>::change(opt, recs, cfg, dims);
.change(recs, cfg, dims);
} }
} }
} }
@ -579,19 +575,21 @@ fn load_theme(
let mut theme = theme.get_theme(); let mut theme = theme.get_theme();
if !with_header { if !with_header {
theme.set_horizontals(std::collections::HashMap::new()); theme.set_horizontal_lines(Default::default());
} else if with_footer && table.count_rows() > 2 {
if let Some(line) = theme.get_horizontal(1) {
theme.insert_horizontal(table.count_rows() - 1, line);
}
} }
table.with(theme); table.with(theme);
if let Some(style) = sep_color { if let Some(style) = sep_color {
let color = convert_style(style); let color = convert_style(style);
let color = AnsiColor::from(color); let color = ANSIBuf::from(color);
table.get_config_mut().set_border_color_global(color); table.get_config_mut().set_border_color_default(color);
}
if !with_header {
table.with(RemoveHorizontalLine);
} else if with_footer {
table.with(CopyFirstHorizontalLineAtLast);
} }
} }
@ -853,10 +851,6 @@ fn truncate_columns_by_head(
pub struct PriorityMax; pub struct PriorityMax;
impl Peaker for PriorityMax { impl Peaker for PriorityMax {
fn create() -> Self {
Self
}
fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> { fn peak(&mut self, _: &[usize], widths: &[usize]) -> Option<usize> {
let col = (0..widths.len()).rev().max_by_key(|&i| widths[i]); let col = (0..widths.len()).rev().max_by_key(|&i| widths[i]);
col.filter(|&col| widths[col] != 0) col.filter(|&col| widths[col] != 0)
@ -881,7 +875,7 @@ fn push_empty_column(data: &mut NuRecords) {
let records = std::mem::take(data); let records = std::mem::take(data);
let mut inner: Vec<Vec<_>> = records.into(); let mut inner: Vec<Vec<_>> = records.into();
let empty_cell = CellInfo::new(String::from("...")); let empty_cell = Text::new(String::from("..."));
for row in &mut inner { for row in &mut inner {
row.push(empty_cell.clone()); row.push(empty_cell.clone());
} }
@ -920,7 +914,7 @@ fn convert_alignment(alignment: nu_color_config::Alignment) -> AlignmentHorizont
struct SetAlignment(AlignmentHorizontal, Entity); struct SetAlignment(AlignmentHorizontal, Entity);
impl<R, D> TableOption<R, D, ColoredConfig> for SetAlignment { impl<R, D> TableOption<R, ColoredConfig, D> for SetAlignment {
fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
cfg.set_alignment_horizontal(self.1, self.0); cfg.set_alignment_horizontal(self.1, self.0);
} }
@ -928,7 +922,7 @@ impl<R, D> TableOption<R, D, ColoredConfig> for SetAlignment {
struct SetDimensions(Vec<usize>); struct SetDimensions(Vec<usize>);
impl<R> TableOption<R, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetDimensions { impl<R> TableOption<R, ColoredConfig, CompleteDimensionVecRecords<'_>> for SetDimensions {
fn change(self, _: &mut R, _: &mut ColoredConfig, dims: &mut CompleteDimensionVecRecords<'_>) { fn change(self, _: &mut R, _: &mut ColoredConfig, dims: &mut CompleteDimensionVecRecords<'_>) {
dims.set_widths(self.0); dims.set_widths(self.0);
} }
@ -951,7 +945,7 @@ fn build_width(records: &NuRecords, pad: usize) -> Vec<usize> {
struct GetRow(usize, Vec<String>); struct GetRow(usize, Vec<String>);
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for &mut GetRow { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for &mut GetRow {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -965,7 +959,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
struct GetRowSettings(usize, AlignmentHorizontal, Option<Color>); struct GetRowSettings(usize, AlignmentHorizontal, Option<Color>);
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>>
for &mut GetRowSettings for &mut GetRowSettings
{ {
fn change( fn change(
@ -1007,7 +1001,7 @@ impl SetLineHeaders {
} }
} }
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for SetLineHeaders { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for SetLineHeaders {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -1020,7 +1014,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
columns = columns columns = columns
.into_iter() .into_iter()
.zip(widths.iter().map(|w| w.checked_sub(2).unwrap_or(*w))) // exclude padding; which is generally 2 .zip(widths.iter().map(|w| w.checked_sub(2).unwrap_or(*w))) // exclude padding; which is generally 2
.map(|(s, width)| Truncate::truncate_text(&s, width).into_owned()) .map(|(s, width)| Truncate::truncate(&s, width).into_owned())
.collect(); .collect();
} }
None => { None => {
@ -1065,7 +1059,7 @@ impl MoveRowPrev {
} }
} }
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for MoveRowNext { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for MoveRowNext {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -1076,7 +1070,7 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
} }
} }
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for MoveRowPrev { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for MoveRowPrev {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -1193,9 +1187,11 @@ fn set_column_names(
align: AlignmentHorizontal, align: AlignmentHorizontal,
color: Option<Color>, color: Option<Color>,
) { ) {
let mut names = ColumnNames::new(head).set_line(line).set_alignment(align); let mut names = ColumnNames::new(head)
.line(line)
.alignment(Alignment::from(align));
if let Some(color) = color { if let Some(color) = color {
names = names.set_color(color); names = names.color(color);
} }
ColumnNames::change(names, records, cfg, dims) ColumnNames::change(names, records, cfg, dims)
@ -1214,7 +1210,7 @@ fn remove_row(recs: &mut NuRecords, row: usize) -> Vec<String> {
struct StripColorFromRow(usize); struct StripColorFromRow(usize);
impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for StripColorFromRow { impl TableOption<NuRecords, ColoredConfig, CompleteDimensionVecRecords<'_>> for StripColorFromRow {
fn change( fn change(
self, self,
recs: &mut NuRecords, recs: &mut NuRecords,
@ -1222,7 +1218,32 @@ impl TableOption<NuRecords, CompleteDimensionVecRecords<'_>, ColoredConfig> for
_: &mut CompleteDimensionVecRecords<'_>, _: &mut CompleteDimensionVecRecords<'_>,
) { ) {
for cell in &mut recs[self.0] { for cell in &mut recs[self.0] {
*cell = CellInfo::new(strip_ansi_unlikely(cell.as_ref()).into_owned()); *cell = Text::new(strip_ansi_unlikely(cell.as_ref()).into_owned());
} }
} }
} }
struct RemoveHorizontalLine;
impl<D> TableOption<NuRecords, ColoredConfig, D> for RemoveHorizontalLine {
fn change(self, recs: &mut NuRecords, cfg: &mut ColoredConfig, _: &mut D) {
cfg.remove_horizontal_line(1, recs.count_rows());
}
}
struct CopyFirstHorizontalLineAtLast;
impl<D> TableOption<NuRecords, ColoredConfig, D> for CopyFirstHorizontalLineAtLast {
fn change(self, recs: &mut NuRecords, cfg: &mut ColoredConfig, _: &mut D) {
if recs.count_rows() <= 2 {
return;
}
let line = match cfg.get_horizontal_line(1) {
Some(line) => *line,
None => return,
};
cfg.insert_horizontal_line(recs.count_rows() - 1, line);
}
}

View File

@ -1,107 +1,78 @@
use tabled::settings::style::{HorizontalLine, Line, RawStyle, Style}; use tabled::settings::style::{HorizontalLine, Style};
use tabled::settings::themes::Theme;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct TableTheme { pub struct TableTheme {
theme: RawStyle, theme: Theme,
full_theme: RawStyle, full_theme: Theme,
has_inner: bool, has_inner: bool,
} }
impl TableTheme { impl TableTheme {
pub fn basic() -> TableTheme { pub fn new(theme: impl Into<Theme>, full_theme: impl Into<Theme>, has_inner: bool) -> Self {
Self { Self {
theme: Style::ascii().into(), theme: theme.into(),
full_theme: Style::ascii().into(), full_theme: full_theme.into(),
has_inner: true, has_inner,
} }
} }
pub fn basic() -> TableTheme {
Self::new(Style::ascii(), Style::ascii(), true)
}
pub fn thin() -> TableTheme { pub fn thin() -> TableTheme {
Self { Self::new(Style::modern(), Style::modern(), true)
theme: Style::modern().into(),
full_theme: Style::modern().into(),
has_inner: true,
}
} }
pub fn light() -> TableTheme { pub fn light() -> TableTheme {
let theme = Style::blank() let mut theme = Theme::from_style(Style::blank());
.horizontals([HorizontalLine::new( theme.insert_horizontal_line(1, HorizontalLine::new('─').intersection('─'));
1,
Line::new(Some('─'), Some('─'), None, None), Self::new(theme, Style::modern(), true)
)])
.into();
Self {
theme,
full_theme: Style::modern().into(),
has_inner: true,
}
} }
pub fn psql() -> TableTheme { pub fn psql() -> TableTheme {
Self { Self::new(Style::psql(), Style::psql(), true)
theme: Style::psql().into(),
full_theme: Style::psql().into(),
has_inner: true,
}
} }
pub fn markdown() -> TableTheme { pub fn markdown() -> TableTheme {
Self { Self::new(Style::markdown(), Style::markdown(), true)
theme: Style::markdown().into(),
full_theme: Style::markdown().into(),
has_inner: true,
}
} }
pub fn dots() -> TableTheme { pub fn dots() -> TableTheme {
let theme = Style::dots().remove_horizontal().into(); let theme = Style::dots().remove_horizontal();
Self {
theme, Self::new(theme, Style::dots(), true)
full_theme: Style::dots().into(),
has_inner: true,
}
} }
pub fn restructured() -> TableTheme { pub fn restructured() -> TableTheme {
Self { Self::new(
theme: Style::re_structured_text().into(), Style::re_structured_text(),
full_theme: Style::re_structured_text().into(), Style::re_structured_text(),
has_inner: true, true,
} )
} }
pub fn ascii_rounded() -> TableTheme { pub fn ascii_rounded() -> TableTheme {
Self { Self::new(Style::ascii_rounded(), Style::ascii_rounded(), true)
theme: Style::ascii_rounded().into(),
full_theme: Style::ascii_rounded().into(),
has_inner: true,
}
} }
pub fn basic_compact() -> TableTheme { pub fn basic_compact() -> TableTheme {
let theme = Style::ascii().remove_horizontal().into(); let theme = Style::ascii().remove_horizontal();
Self {
theme, Self::new(theme, Style::ascii(), true)
full_theme: Style::ascii().into(),
has_inner: true,
}
} }
pub fn compact() -> TableTheme { pub fn compact() -> TableTheme {
let hline = HorizontalLine::inherit(Style::modern().remove_left().remove_right());
let theme = Style::modern() let theme = Style::modern()
.remove_left() .remove_left()
.remove_right() .remove_right()
.remove_horizontal() .remove_horizontal()
.horizontals([HorizontalLine::new(1, Style::modern().get_horizontal()) .horizontals([(1, hline)]);
.left(None)
.right(None)]) Self::new(theme, Style::modern(), true)
.into();
Self {
theme,
full_theme: Style::modern().into(),
has_inner: true,
}
} }
pub fn with_love() -> TableTheme { pub fn with_love() -> TableTheme {
@ -109,10 +80,7 @@ impl TableTheme {
.top('❤') .top('❤')
.bottom('❤') .bottom('❤')
.vertical('❤') .vertical('❤')
.horizontals([HorizontalLine::new( .horizontals([(1, HorizontalLine::new('❤').intersection('❤'))]);
1,
Line::new(Some('❤'), Some('❤'), None, None),
)]);
let full_theme = Style::empty() let full_theme = Style::empty()
.top('❤') .top('❤')
@ -131,53 +99,40 @@ impl TableTheme {
.intersection_left('❤') .intersection_left('❤')
.intersection('❤'); .intersection('❤');
Self { Self::new(theme, full_theme, true)
theme: theme.into(),
full_theme: full_theme.into(),
has_inner: true,
}
} }
pub fn compact_double() -> TableTheme { pub fn compact_double() -> TableTheme {
let hline = HorizontalLine::inherit(Style::extended())
.remove_left()
.remove_right();
let theme = Style::extended() let theme = Style::extended()
.remove_left() .remove_left()
.remove_right() .remove_right()
.remove_horizontal() .remove_horizontal()
.horizontals([HorizontalLine::new(1, Style::extended().get_horizontal()) .horizontals([(1, hline)]);
.left(None)
.right(None)]) Self::new(theme, Style::extended(), true)
.into();
Self {
theme,
full_theme: Style::extended().into(),
has_inner: true,
}
} }
pub fn rounded() -> TableTheme { pub fn rounded() -> TableTheme {
Self { let full = Style::modern()
theme: Style::rounded().into(), .corner_top_left('╭')
full_theme: Style::modern() .corner_top_right('╮')
.corner_top_left('╭') .corner_bottom_left('╰')
.corner_top_right('╮') .corner_bottom_right('╯');
.corner_bottom_left('╰')
.corner_bottom_right('╯') Self::new(Style::rounded(), full, true)
.into(),
has_inner: true,
}
} }
pub fn reinforced() -> TableTheme { pub fn reinforced() -> TableTheme {
let full_theme = Style::modern() let full = Style::modern()
.corner_top_left('┏') .corner_top_left('┏')
.corner_top_right('┓') .corner_top_right('┓')
.corner_bottom_left('┗') .corner_bottom_left('┗')
.corner_bottom_right('┛'); .corner_bottom_right('┛');
Self {
theme: full_theme.clone().remove_horizontal().into(), Self::new(full.clone().remove_horizontal(), full, true)
full_theme: full_theme.into(),
has_inner: true,
}
} }
pub fn heavy() -> TableTheme { pub fn heavy() -> TableTheme {
@ -193,48 +148,32 @@ impl TableTheme {
.corner_top_right('┓') .corner_top_right('┓')
.corner_bottom_left('┗') .corner_bottom_left('┗')
.corner_bottom_right('┛') .corner_bottom_right('┛')
.horizontals([HorizontalLine::new(1, Line::full('━', '╋', '┣', '┫'))]); .horizontals([(1, HorizontalLine::full('━', '╋', '┣', '┫'))]);
let full_theme = theme let full = theme
.clone() .clone()
.remove_horizontals() .remove_horizontals()
.horizontal('━') .horizontal('━')
.intersection_left('┣') .intersection_left('┣')
.intersection_right('┫') .intersection_right('┫')
.intersection('╋'); .intersection('╋');
Self {
theme: theme.into(), Self::new(theme, full, true)
full_theme: full_theme.into(),
has_inner: true,
}
} }
pub fn none() -> TableTheme { pub fn none() -> TableTheme {
Self { Self::new(Style::blank(), Style::blank(), true)
theme: Style::blank().into(),
full_theme: Style::blank().into(),
has_inner: true,
}
} }
pub fn has_top_line(&self) -> bool { pub fn has_top(&self) -> bool {
self.theme.get_top().is_some() self.theme.borders_has_top()
|| self.theme.get_top_intersection().is_some()
|| self.theme.get_top_left().is_some()
|| self.theme.get_top_right().is_some()
} }
pub fn has_left(&self) -> bool { pub fn has_left(&self) -> bool {
self.theme.get_left().is_some() self.theme.borders_has_left()
|| self.theme.get_left_intersection().is_some()
|| self.theme.get_top_left().is_some()
|| self.theme.get_bottom_left().is_some()
} }
pub fn has_right(&self) -> bool { pub fn has_right(&self) -> bool {
self.theme.get_right().is_some() self.theme.borders_has_right()
|| self.theme.get_right_intersection().is_some()
|| self.theme.get_top_right().is_some()
|| self.theme.get_bottom_right().is_some()
} }
pub fn has_inner(&self) -> bool { pub fn has_inner(&self) -> bool {
@ -245,11 +184,11 @@ impl TableTheme {
self.full_theme.get_borders().has_horizontal() self.full_theme.get_borders().has_horizontal()
} }
pub fn get_theme_full(&self) -> RawStyle { pub fn get_theme_full(&self) -> Theme {
self.full_theme.clone() self.full_theme.clone()
} }
pub fn get_theme(&self) -> RawStyle { pub fn get_theme(&self) -> Theme {
self.theme.clone() self.theme.clone()
} }
} }

View File

@ -358,7 +358,7 @@ fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
// we could use Padding for it but, // we could use Padding for it but,
// the easiest way to do so is just push a new_line char before // the easiest way to do so is just push a new_line char before
let mut key = key.to_owned(); let mut key = key.to_owned();
if !key.is_empty() && is_expanded && theme.has_top_line() { if !key.is_empty() && is_expanded && theme.has_top() {
key.insert(0, '\n'); key.insert(0, '\n');
} }

View File

@ -3,11 +3,11 @@ use nu_color_config::StyleComputer;
use nu_protocol::{Config, Record, Span, Value}; use nu_protocol::{Config, Record, Span, Value};
use tabled::{ use tabled::{
grid::{ grid::{
color::{AnsiColor, StaticColor}, ansi::{ANSIBuf, ANSIStr},
config::{AlignmentHorizontal, Borders, CompactMultilineConfig}, config::{AlignmentHorizontal, Borders, CompactMultilineConfig},
dimension::{DimensionPriority, PoolTableDimension}, dimension::{DimensionPriority, PoolTableDimension},
}, },
settings::{style::RawStyle, Color, Padding, TableOption}, settings::{Color, Padding, TableOption, Theme},
tables::{PoolTable, TableValue}, tables::{PoolTable, TableValue},
}; };
@ -53,7 +53,7 @@ fn build_table(
let mut table = PoolTable::from(val); let mut table = PoolTable::from(val);
let mut theme = theme.get_theme_full(); let mut theme = theme.get_theme_full();
theme.set_horizontals(std::collections::HashMap::default()); theme.set_horizontal_lines(Default::default());
table.with(Padding::new(indent.0, indent.1, 0, 0)); table.with(Padding::new(indent.0, indent.1, 0, 0));
table.with(SetRawStyle(theme)); table.with(SetRawStyle(theme));
@ -73,12 +73,13 @@ fn build_table(
// We just need this unsafe section to cope with some limitations of [`PoolTable`]. // We just need this unsafe section to cope with some limitations of [`PoolTable`].
// Mitigation of this is definitely on a todo list. // Mitigation of this is definitely on a todo list.
let color: AnsiColor<'_> = color.into(); let color: ANSIBuf = color.into();
let prefix = color.get_prefix(); let prefix = color.get_prefix();
let suffix = color.get_suffix(); let suffix = color.get_suffix();
let prefix: &'static str = unsafe { std::mem::transmute(prefix) }; let prefix: &'static str = unsafe { std::mem::transmute(prefix) };
let suffix: &'static str = unsafe { std::mem::transmute(suffix) }; let suffix: &'static str = unsafe { std::mem::transmute(suffix) };
table.with(SetBorderColor(StaticColor::new(prefix, suffix)));
table.with(SetBorderColor(ANSIStr::new(prefix, suffix)));
let table = table.to_string(); let table = table.to_string();
return table; return table;
@ -217,29 +218,29 @@ fn get_columns_in_record(vals: &[Value]) -> Vec<String> {
} }
} }
struct SetRawStyle(RawStyle); struct SetRawStyle(Theme);
impl<R, D> TableOption<R, D, CompactMultilineConfig> for SetRawStyle { impl<R, D> TableOption<R, CompactMultilineConfig, D> for SetRawStyle {
fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) { fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
let borders = self.0.get_borders(); let borders = *self.0.get_borders();
*cfg = cfg.set_borders(borders); cfg.set_borders(borders);
} }
} }
struct SetBorderColor(StaticColor); struct SetBorderColor(ANSIStr<'static>);
impl<R, D> TableOption<R, D, CompactMultilineConfig> for SetBorderColor { impl<R, D> TableOption<R, CompactMultilineConfig, D> for SetBorderColor {
fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) { fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
let borders = Borders::filled(self.0); let borders = Borders::filled(self.0);
*cfg = cfg.set_borders_color(borders); cfg.set_borders_color(borders);
} }
} }
struct SetAlignment(AlignmentHorizontal); struct SetAlignment(AlignmentHorizontal);
impl<R, D> TableOption<R, D, CompactMultilineConfig> for SetAlignment { impl<R, D> TableOption<R, CompactMultilineConfig, D> for SetAlignment {
fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) { fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
*cfg = cfg.set_alignment_horizontal(self.0); cfg.set_alignment_horizontal(self.0);
} }
} }

View File

@ -2,7 +2,9 @@ use nu_color_config::StyleComputer;
use tabled::{ use tabled::{
builder::Builder, builder::Builder,
grid::{ grid::{
color::AnsiColor, records::vec_records::CellInfo, util::string::string_width_multiline, ansi::{ANSIBuf, ANSIStr},
records::vec_records::Text,
util::string::get_text_width,
}, },
settings::{width::Truncate, Color, Modify, Padding, Style, Width}, settings::{width::Truncate, Color, Modify, Padding, Style, Width},
}; };
@ -10,7 +12,7 @@ use tabled::{
use crate::common::get_leading_trailing_space_style; use crate::common::get_leading_trailing_space_style;
pub fn string_width(text: &str) -> usize { pub fn string_width(text: &str) -> usize {
string_width_multiline(text) get_text_width(text)
} }
pub fn string_wrap(text: &str, width: usize, keep_words: bool) -> String { pub fn string_wrap(text: &str, width: usize, keep_words: bool) -> String {
@ -24,7 +26,7 @@ pub fn string_wrap(text: &str, width: usize, keep_words: bool) -> String {
} }
let wrap = if keep_words { let wrap = if keep_words {
Width::wrap(width).keep_words() Width::wrap(width).keep_words(true)
} else { } else {
Width::wrap(width) Width::wrap(width)
}; };
@ -45,7 +47,7 @@ pub fn string_truncate(text: &str, width: usize) -> String {
None => return String::new(), None => return String::new(),
}; };
Truncate::truncate_text(line, width).into_owned() Truncate::truncate(line, width).into_owned()
} }
pub fn clean_charset(text: &str) -> String { pub fn clean_charset(text: &str) -> String {
@ -53,24 +55,26 @@ pub fn clean_charset(text: &str) -> String {
text.replace('\t', " ").replace('\r', "") text.replace('\t', " ").replace('\r', "")
} }
pub fn colorize_space(data: &mut [Vec<CellInfo<String>>], style_computer: &StyleComputer<'_>) { pub fn colorize_space(data: &mut [Vec<Text<String>>], style_computer: &StyleComputer<'_>) {
if let Some(style) = get_leading_trailing_space_style(style_computer).color_style { if let Some(style) = get_leading_trailing_space_style(style_computer).color_style {
let style = convert_style(style).into(); let style = ANSIBuf::from(convert_style(style));
colorize_lead_trail_space(data, Some(&style), Some(&style)); let style = style.as_ref();
colorize_lead_trail_space(data, Some(style), Some(style));
} }
} }
pub fn colorize_space_str(text: &mut String, style_computer: &StyleComputer<'_>) { pub fn colorize_space_str(text: &mut String, style_computer: &StyleComputer<'_>) {
if let Some(style) = get_leading_trailing_space_style(style_computer).color_style { if let Some(style) = get_leading_trailing_space_style(style_computer).color_style {
let style = convert_style(style).into(); let style = ANSIBuf::from(convert_style(style));
*text = colorize_space_one(text, Some(&style), Some(&style)); let style = style.as_ref();
*text = colorize_space_one(text, Some(style), Some(style));
} }
} }
fn colorize_lead_trail_space( fn colorize_lead_trail_space(
data: &mut [Vec<CellInfo<String>>], data: &mut [Vec<Text<String>>],
lead: Option<&AnsiColor<'_>>, lead: Option<ANSIStr<'_>>,
trail: Option<&AnsiColor<'_>>, trail: Option<ANSIStr<'_>>,
) { ) {
if lead.is_none() && trail.is_none() { if lead.is_none() && trail.is_none() {
return; return;
@ -79,16 +83,12 @@ fn colorize_lead_trail_space(
for row in data.iter_mut() { for row in data.iter_mut() {
for cell in row { for cell in row {
let buf = colorize_space_one(cell.as_ref(), lead, trail); let buf = colorize_space_one(cell.as_ref(), lead, trail);
*cell = CellInfo::new(buf); *cell = Text::new(buf);
} }
} }
} }
fn colorize_space_one( fn colorize_space_one(text: &str, lead: Option<ANSIStr<'_>>, trail: Option<ANSIStr<'_>>) -> String {
text: &str,
lead: Option<&AnsiColor<'_>>,
trail: Option<&AnsiColor<'_>>,
) -> String {
use fancy_regex::Captures; use fancy_regex::Captures;
use fancy_regex::Regex; use fancy_regex::Regex;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;

View File

@ -1,7 +1,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use nu_table::{string_width, NuTable, NuTableConfig}; use nu_table::{string_width, NuTable, NuTableConfig};
use tabled::grid::records::vec_records::CellInfo; use tabled::grid::records::vec_records::Text;
pub struct TestCase { pub struct TestCase {
cfg: NuTableConfig, cfg: NuTableConfig,
@ -19,7 +19,7 @@ impl TestCase {
} }
} }
type Data = Vec<Vec<CellInfo<String>>>; type Data = Vec<Vec<Text<String>>>;
pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) { pub fn test_table<I: IntoIterator<Item = TestCase>>(data: Data, tests: I) {
for (i, test) in tests.into_iter().enumerate() { for (i, test) in tests.into_iter().enumerate() {
@ -42,15 +42,15 @@ pub fn create_table(data: Data, config: NuTableConfig, termwidth: usize) -> Opti
table.draw(config, termwidth) table.draw(config, termwidth)
} }
pub fn create_row(count_columns: usize) -> Vec<CellInfo<String>> { pub fn create_row(count_columns: usize) -> Vec<Text<String>> {
let mut row = Vec::with_capacity(count_columns); let mut row = Vec::with_capacity(count_columns);
for i in 0..count_columns { for i in 0..count_columns {
row.push(CellInfo::new(i.to_string())); row.push(Text::new(i.to_string()));
} }
row row
} }
pub fn cell(text: &str) -> CellInfo<String> { pub fn cell(text: &str) -> Text<String> {
CellInfo::new(text.to_string()) Text::new(text.to_string())
} }

View File

@ -3,7 +3,7 @@ mod common;
use common::{create_row, test_table, TestCase}; use common::{create_row, test_table, TestCase};
use nu_protocol::TrimStrategy; use nu_protocol::TrimStrategy;
use nu_table::{NuTable, NuTableConfig, TableTheme as theme}; use nu_table::{NuTable, NuTableConfig, TableTheme as theme};
use tabled::grid::records::vec_records::CellInfo; use tabled::grid::records::vec_records::Text;
#[test] #[test]
fn data_and_header_has_different_size_doesnt_work() { fn data_and_header_has_different_size_doesnt_work() {
@ -194,7 +194,7 @@ fn width_control_test_0() {
test_width(data, &tests); test_width(data, &tests);
} }
fn test_width(data: Vec<Vec<CellInfo<String>>>, tests: &[(usize, &str)]) { fn test_width(data: Vec<Vec<Text<String>>>, tests: &[(usize, &str)]) {
let config = NuTableConfig { let config = NuTableConfig {
theme: theme::heavy(), theme: theme::heavy(),
trim: TrimStrategy::truncate(Some(String::from("..."))), trim: TrimStrategy::truncate(Some(String::from("..."))),

View File

@ -2,7 +2,7 @@ mod common;
use common::create_row as row; use common::create_row as row;
use nu_table::{NuTable, NuTableConfig, TableTheme as theme}; use nu_table::{NuTable, NuTableConfig, TableTheme as theme};
use tabled::grid::records::vec_records::CellInfo; use tabled::grid::records::vec_records::Text;
#[test] #[test]
fn test_rounded() { fn test_rounded() {
@ -451,7 +451,7 @@ fn test_with_love() {
assert_eq!(create_table_with_size(vec![], true, theme::with_love()), ""); assert_eq!(create_table_with_size(vec![], true, theme::with_love()), "");
} }
fn create_table(data: Vec<Vec<CellInfo<String>>>, with_header: bool, theme: theme) -> String { fn create_table(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
let config = NuTableConfig { let config = NuTableConfig {
theme, theme,
with_header, with_header,
@ -463,11 +463,7 @@ fn create_table(data: Vec<Vec<CellInfo<String>>>, with_header: bool, theme: them
out.expect("not expected to get None") out.expect("not expected to get None")
} }
fn create_table_with_size( fn create_table_with_size(data: Vec<Vec<Text<String>>>, with_header: bool, theme: theme) -> String {
data: Vec<Vec<CellInfo<String>>>,
with_header: bool,
theme: theme,
) -> String {
let config = NuTableConfig { let config = NuTableConfig {
theme, theme,
with_header, with_header,