From d8c721282b16f2f6d2a2fa88111e687c4b8f9d0f Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Wed, 1 Dec 2021 13:20:23 -0600 Subject: [PATCH] add optional footer to table (#392) * add optional footer to table * missed a draw_table --- Cargo.lock | 1 + crates/nu-command/src/viewers/table.rs | 9 ++--- crates/nu-protocol/src/config.rs | 26 ++++++++++++ crates/nu-table/Cargo.toml | 1 + crates/nu-table/src/main.rs | 5 ++- crates/nu-table/src/table.rs | 55 ++++++++++++++++++++++++-- crates/nu-table/src/wrap.rs | 4 +- 7 files changed, 89 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 88193c2ba..ca6149d8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1549,6 +1549,7 @@ version = "0.36.0" dependencies = [ "ansi-cut", "nu-ansi-term 0.39.0", + "nu-protocol", "regex", "strip-ansi-escapes", "unicode-width", diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index 871ff3735..d883230aa 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -1,3 +1,4 @@ +use super::color_config::style_primitive; use crate::viewers::color_config::get_color_config; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; @@ -9,8 +10,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use terminal_size::{Height, Width}; -use super::color_config::style_primitive; - #[derive(Clone)] pub struct Table; @@ -50,7 +49,7 @@ impl Command for Table { let table = convert_to_table(vals, ctrlc, &config)?; if let Some(table) = table { - let result = nu_table::draw_table(&table, term_width, &color_hm); + let result = nu_table::draw_table(&table, term_width, &color_hm, &config); Ok(Value::String { val: result, @@ -65,7 +64,7 @@ impl Command for Table { let table = convert_to_table(stream, ctrlc, &config)?; if let Some(table) = table { - let result = nu_table::draw_table(&table, term_width, &color_hm); + let result = nu_table::draw_table(&table, term_width, &color_hm, &config); Ok(Value::String { val: result, @@ -98,7 +97,7 @@ impl Command for Table { theme: load_theme_from_config(&config), }; - let result = nu_table::draw_table(&table, term_width, &color_hm); + let result = nu_table::draw_table(&table, term_width, &color_hm, &config); Ok(Value::String { val: result, diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 1dc904566..4d5024a20 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -9,6 +9,7 @@ pub struct Config { pub use_ls_colors: bool, pub color_config: HashMap, pub use_grid_icons: bool, + pub footer_mode: FooterMode, } impl Default for Config { @@ -19,10 +20,23 @@ impl Default for Config { use_ls_colors: true, color_config: HashMap::new(), use_grid_icons: false, + footer_mode: FooterMode::Never, } } } +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum FooterMode { + /// Never show the footer + Never, + /// Always show the footer + Always, + /// Only show the footer if there are more than RowCount rows + RowCount(u64), + /// Calculate the screen height, calculate row count, if display will be bigger than screen, add the footer + Auto, +} + impl Value { pub fn into_config(self) -> Result { let v = self.as_record()?; @@ -51,6 +65,18 @@ impl Value { "use_grid_icons" => { config.use_grid_icons = value.as_bool()?; } + "footer_mode" => { + let val_str = value.as_string()?; + config.footer_mode = match val_str.as_ref() { + "auto" => FooterMode::Auto, + "never" => FooterMode::Never, + "always" => FooterMode::Always, + _ => match &val_str.parse::() { + Ok(number) => FooterMode::RowCount(*number), + _ => FooterMode::Never, + }, + }; + } _ => {} } } diff --git a/crates/nu-table/Cargo.toml b/crates/nu-table/Cargo.toml index dc8a8cf37..18cb07631 100644 --- a/crates/nu-table/Cargo.toml +++ b/crates/nu-table/Cargo.toml @@ -14,6 +14,7 @@ path = "src/main.rs" [dependencies] # nu-ansi-term = "0.39.0" nu-ansi-term = { path = "../nu-ansi-term" } +nu-protocol = { path = "../nu-protocol"} regex = "1.4" unicode-width = "0.1.8" strip-ansi-escapes = "0.1.1" diff --git a/crates/nu-table/src/main.rs b/crates/nu-table/src/main.rs index 638582a1f..75401b888 100644 --- a/crates/nu-table/src/main.rs +++ b/crates/nu-table/src/main.rs @@ -1,3 +1,4 @@ +use nu_protocol::Config; use nu_table::{draw_table, StyledString, Table, TextStyle, Theme}; use std::collections::HashMap; @@ -25,8 +26,10 @@ fn main() { let table = Table::new(headers, vec![rows; 3], Theme::rounded()); // FIXME: Config isn't available from here so just put these here to compile let color_hm: HashMap = HashMap::new(); + // get the default config + let config = Config::default(); // Capture the table as a string - let output_table = draw_table(&table, width, &color_hm); + let output_table = draw_table(&table, width, &color_hm, &config); // Draw the table println!("{}", output_table) } diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs index 8843c38a4..e3132f394 100644 --- a/crates/nu-table/src/table.rs +++ b/crates/nu-table/src/table.rs @@ -1,5 +1,6 @@ use crate::wrap::{column_width, split_sublines, wrap, Alignment, Subline, WrappedCell}; use nu_ansi_term::{Color, Style}; +use nu_protocol::{Config, FooterMode}; use std::collections::HashMap; use std::fmt::Write; @@ -629,6 +630,7 @@ pub struct WrappedTable { pub headers: Vec, pub data: Vec>, pub theme: Theme, + pub footer: Vec, } impl WrappedTable { @@ -878,7 +880,7 @@ impl WrappedTable { total_output } - fn print_table(&self, color_hm: &HashMap) -> String { + fn print_table(&self, color_hm: &HashMap, config: &Config) -> String { let mut output = String::new(); #[cfg(windows)] @@ -890,10 +892,12 @@ impl WrappedTable { return output; } + // The top border if self.theme.print_top_border { output.push_str(&self.print_separator(SeparatorPosition::Top, color_hm)); } + // The header let skip_headers = (self.headers.len() == 2 && self.headers[1].max_width == 0) || (self.headers.len() == 1 && self.headers[0].max_width == 0); @@ -901,8 +905,8 @@ impl WrappedTable { output.push_str(&self.print_cell_contents(&self.headers, color_hm)); } + // The middle section let mut first_row = true; - for row in &self.data { if !first_row { if self.theme.separate_rows { @@ -919,6 +923,31 @@ impl WrappedTable { output.push_str(&self.print_cell_contents(row, color_hm)); } + match config.footer_mode { + FooterMode::Always => { + if self.theme.separate_header && !self.headers.is_empty() && !skip_headers { + output.push_str(&self.print_separator(SeparatorPosition::Middle, color_hm)); + } + + if !self.headers.is_empty() && !skip_headers { + output.push_str(&self.print_cell_contents(&self.footer, color_hm)); + } + } + FooterMode::RowCount(r) => { + if self.data.len() as u64 > r { + if self.theme.separate_header && !self.headers.is_empty() && !skip_headers { + output.push_str(&self.print_separator(SeparatorPosition::Middle, color_hm)); + } + + if !self.headers.is_empty() && !skip_headers { + output.push_str(&self.print_cell_contents(&self.footer, color_hm)); + } + } + } + _ => {} // Never and Auto aka auto get eaten and nothing happens + } + + // The table finish if self.theme.print_bottom_border { output.push_str(&self.print_separator(SeparatorPosition::Bottom, color_hm)); } @@ -1013,7 +1042,12 @@ pub fn maybe_truncate_columns(termwidth: usize, processed_table: &mut ProcessedT } } -pub fn draw_table(table: &Table, termwidth: usize, color_hm: &HashMap) -> String { +pub fn draw_table( + table: &Table, + termwidth: usize, + color_hm: &HashMap, + config: &Config, +) -> String { // Remove the edges, if used let termwidth = if table.theme.print_left_border && table.theme.print_right_border { termwidth - 2 @@ -1073,7 +1107,7 @@ pub fn draw_table(table: &Table, termwidth: usize, color_hm: &HashMap, pub max_width: usize,