use crate::{convert_style, table_theme::TableTheme}; use nu_ansi_term::Style; use nu_color_config::TextStyle; use nu_protocol::TrimStrategy; use nu_utils::strip_ansi_unlikely; use std::cmp::min; use std::collections::HashMap; use tabled::{ builder::Builder, grid::{ color::AnsiColor, colors::Colors, config::{AlignmentHorizontal, ColoredConfig, Entity, EntityMap, Position}, dimension::CompleteDimensionVecRecords, records::{ vec_records::{CellInfo, VecRecords}, ExactRecords, PeekableRecords, Records, Resizable, }, }, settings::{ formatting::AlignmentStrategy, object::Segment, peaker::Peaker, themes::ColumnNames, Color, Modify, Padding, Settings, TableOption, Width, }, Table, }; /// NuTable is a table rendering implementation. #[derive(Debug, Clone)] pub struct NuTable { data: NuRecords, styles: Styles, alignments: Alignments, indent: (usize, usize), } pub type NuRecords = VecRecords; pub type NuTableCell = CellInfo; #[derive(Debug, Default, Clone)] struct Styles { index: AnsiColor<'static>, header: AnsiColor<'static>, data: EntityMap>, data_is_set: bool, } #[derive(Debug, Clone)] struct Alignments { data: AlignmentHorizontal, index: AlignmentHorizontal, header: AlignmentHorizontal, columns: HashMap, cells: HashMap, } impl NuTable { /// Creates an empty [Table] instance. pub fn new(count_rows: usize, count_columns: usize) -> Self { Self { data: VecRecords::new(vec![vec![CellInfo::default(); count_columns]; count_rows]), styles: Styles::default(), indent: (1, 1), alignments: Alignments { data: AlignmentHorizontal::Left, index: AlignmentHorizontal::Right, header: AlignmentHorizontal::Center, columns: HashMap::default(), cells: HashMap::default(), }, } } /// Return amount of rows. pub fn count_rows(&self) -> usize { self.data.count_rows() } /// Return amount of columns. pub fn count_columns(&self) -> usize { self.data.count_columns() } pub fn insert(&mut self, pos: Position, text: String) { self.data[pos.0][pos.1] = CellInfo::new(text); } pub fn set_column_style(&mut self, column: usize, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Column(column), style); self.styles.data_is_set = true; } let alignment = convert_alignment(style.alignment); if alignment != self.alignments.data { self.alignments.columns.insert(column, alignment); } } pub fn insert_style(&mut self, pos: Position, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Cell(pos.0, pos.1), style); self.styles.data_is_set = true; } let alignment = convert_alignment(style.alignment); if alignment != self.alignments.data { self.alignments.cells.insert(pos, alignment); } } pub fn set_header_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.header = style; } self.alignments.header = convert_alignment(style.alignment); } pub fn set_index_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.index = style; } self.alignments.index = convert_alignment(style.alignment); } pub fn set_data_style(&mut self, style: TextStyle) { if let Some(style) = style.color_style { let style = AnsiColor::from(convert_style(style)); self.styles.data.insert(Entity::Global, style); self.styles.data_is_set = true; } self.alignments.data = convert_alignment(style.alignment); } pub fn set_indent(&mut self, left: usize, right: usize) { self.indent = (left, right); } pub fn get_records_mut(&mut self) -> &mut NuRecords { &mut self.data } /// Converts a table to a String. /// /// It returns None in case where table cannot be fit to a terminal width. pub fn draw(self, config: NuTableConfig, termwidth: usize) -> Option { build_table( self.data, config, self.alignments, self.styles, termwidth, self.indent, ) } /// Return a total table width. pub fn total_width(&self, config: &NuTableConfig) -> usize { let config = get_config(&config.theme, false, None); let widths = build_width(&self.data, self.indent.0 + self.indent.1); get_total_width2(&widths, &config) } } impl From>>> for NuTable { fn from(value: Vec>>) -> Self { let mut nutable = Self::new(0, 0); nutable.data = VecRecords::new(value); nutable } } #[derive(Debug, Clone)] pub struct NuTableConfig { pub theme: TableTheme, pub trim: TrimStrategy, pub split_color: Option