[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
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(),
full_theme: 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('╯');
.into(),
has_inner: true, Self::new(Style::rounded(), full, 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,