add optional footer to table (#392)

* add optional footer to table

* missed a draw_table
This commit is contained in:
Darren Schroeder 2021-12-01 13:20:23 -06:00 committed by GitHub
parent d2a1564b94
commit d8c721282b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 89 additions and 12 deletions

1
Cargo.lock generated
View File

@ -1549,6 +1549,7 @@ version = "0.36.0"
dependencies = [ dependencies = [
"ansi-cut", "ansi-cut",
"nu-ansi-term 0.39.0", "nu-ansi-term 0.39.0",
"nu-protocol",
"regex", "regex",
"strip-ansi-escapes", "strip-ansi-escapes",
"unicode-width", "unicode-width",

View File

@ -1,3 +1,4 @@
use super::color_config::style_primitive;
use crate::viewers::color_config::get_color_config; use crate::viewers::color_config::get_color_config;
use nu_protocol::ast::{Call, PathMember}; use nu_protocol::ast::{Call, PathMember};
use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::engine::{Command, EngineState, Stack};
@ -9,8 +10,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use terminal_size::{Height, Width}; use terminal_size::{Height, Width};
use super::color_config::style_primitive;
#[derive(Clone)] #[derive(Clone)]
pub struct Table; pub struct Table;
@ -50,7 +49,7 @@ impl Command for Table {
let table = convert_to_table(vals, ctrlc, &config)?; let table = convert_to_table(vals, ctrlc, &config)?;
if let Some(table) = table { 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 { Ok(Value::String {
val: result, val: result,
@ -65,7 +64,7 @@ impl Command for Table {
let table = convert_to_table(stream, ctrlc, &config)?; let table = convert_to_table(stream, ctrlc, &config)?;
if let Some(table) = table { 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 { Ok(Value::String {
val: result, val: result,
@ -98,7 +97,7 @@ impl Command for Table {
theme: load_theme_from_config(&config), 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 { Ok(Value::String {
val: result, val: result,

View File

@ -9,6 +9,7 @@ pub struct Config {
pub use_ls_colors: bool, pub use_ls_colors: bool,
pub color_config: HashMap<String, String>, pub color_config: HashMap<String, String>,
pub use_grid_icons: bool, pub use_grid_icons: bool,
pub footer_mode: FooterMode,
} }
impl Default for Config { impl Default for Config {
@ -19,10 +20,23 @@ impl Default for Config {
use_ls_colors: true, use_ls_colors: true,
color_config: HashMap::new(), color_config: HashMap::new(),
use_grid_icons: false, 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 { impl Value {
pub fn into_config(self) -> Result<Config, ShellError> { pub fn into_config(self) -> Result<Config, ShellError> {
let v = self.as_record()?; let v = self.as_record()?;
@ -51,6 +65,18 @@ impl Value {
"use_grid_icons" => { "use_grid_icons" => {
config.use_grid_icons = value.as_bool()?; 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::<u64>() {
Ok(number) => FooterMode::RowCount(*number),
_ => FooterMode::Never,
},
};
}
_ => {} _ => {}
} }
} }

View File

@ -14,6 +14,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
# nu-ansi-term = "0.39.0" # nu-ansi-term = "0.39.0"
nu-ansi-term = { path = "../nu-ansi-term" } nu-ansi-term = { path = "../nu-ansi-term" }
nu-protocol = { path = "../nu-protocol"}
regex = "1.4" regex = "1.4"
unicode-width = "0.1.8" unicode-width = "0.1.8"
strip-ansi-escapes = "0.1.1" strip-ansi-escapes = "0.1.1"

View File

@ -1,3 +1,4 @@
use nu_protocol::Config;
use nu_table::{draw_table, StyledString, Table, TextStyle, Theme}; use nu_table::{draw_table, StyledString, Table, TextStyle, Theme};
use std::collections::HashMap; use std::collections::HashMap;
@ -25,8 +26,10 @@ fn main() {
let table = Table::new(headers, vec![rows; 3], Theme::rounded()); let table = Table::new(headers, vec![rows; 3], Theme::rounded());
// FIXME: Config isn't available from here so just put these here to compile // FIXME: Config isn't available from here so just put these here to compile
let color_hm: HashMap<String, nu_ansi_term::Style> = HashMap::new(); let color_hm: HashMap<String, nu_ansi_term::Style> = HashMap::new();
// get the default config
let config = Config::default();
// Capture the table as a string // 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 // Draw the table
println!("{}", output_table) println!("{}", output_table)
} }

View File

@ -1,5 +1,6 @@
use crate::wrap::{column_width, split_sublines, wrap, Alignment, Subline, WrappedCell}; use crate::wrap::{column_width, split_sublines, wrap, Alignment, Subline, WrappedCell};
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_protocol::{Config, FooterMode};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::Write; use std::fmt::Write;
@ -629,6 +630,7 @@ pub struct WrappedTable {
pub headers: Vec<WrappedCell>, pub headers: Vec<WrappedCell>,
pub data: Vec<Vec<WrappedCell>>, pub data: Vec<Vec<WrappedCell>>,
pub theme: Theme, pub theme: Theme,
pub footer: Vec<WrappedCell>,
} }
impl WrappedTable { impl WrappedTable {
@ -878,7 +880,7 @@ impl WrappedTable {
total_output total_output
} }
fn print_table(&self, color_hm: &HashMap<String, Style>) -> String { fn print_table(&self, color_hm: &HashMap<String, Style>, config: &Config) -> String {
let mut output = String::new(); let mut output = String::new();
#[cfg(windows)] #[cfg(windows)]
@ -890,10 +892,12 @@ impl WrappedTable {
return output; return output;
} }
// The top border
if self.theme.print_top_border { if self.theme.print_top_border {
output.push_str(&self.print_separator(SeparatorPosition::Top, color_hm)); 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) let skip_headers = (self.headers.len() == 2 && self.headers[1].max_width == 0)
|| (self.headers.len() == 1 && self.headers[0].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)); output.push_str(&self.print_cell_contents(&self.headers, color_hm));
} }
// The middle section
let mut first_row = true; let mut first_row = true;
for row in &self.data { for row in &self.data {
if !first_row { if !first_row {
if self.theme.separate_rows { if self.theme.separate_rows {
@ -919,6 +923,31 @@ impl WrappedTable {
output.push_str(&self.print_cell_contents(row, color_hm)); 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 { if self.theme.print_bottom_border {
output.push_str(&self.print_separator(SeparatorPosition::Bottom, color_hm)); 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, Style>) -> String { pub fn draw_table(
table: &Table,
termwidth: usize,
color_hm: &HashMap<String, Style>,
config: &Config,
) -> String {
// Remove the edges, if used // Remove the edges, if used
let termwidth = if table.theme.print_left_border && table.theme.print_right_border { let termwidth = if table.theme.print_left_border && table.theme.print_right_border {
termwidth - 2 termwidth - 2
@ -1073,7 +1107,7 @@ pub fn draw_table(table: &Table, termwidth: usize, color_hm: &HashMap<String, St
&re_trailing, &re_trailing,
); );
wrapped_table.print_table(color_hm) wrapped_table.print_table(color_hm, config)
} }
fn wrap_cells( fn wrap_cells(
@ -1151,11 +1185,24 @@ fn wrap_cells(
output_data.push(output_row); output_data.push(output_row);
} }
let mut footer = vec![
WrappedCell {
lines: vec![],
max_width: 0,
style: TextStyle {
..Default::default()
},
};
output_headers.len()
];
footer.clone_from_slice(&output_headers[..]);
WrappedTable { WrappedTable {
column_widths, column_widths,
headers: output_headers, headers: output_headers,
data: output_data, data: output_data,
theme: processed_table.theme, theme: processed_table.theme,
footer,
} }
} }

View File

@ -24,13 +24,13 @@ pub struct Line {
pub width: usize, pub width: usize,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct WrappedLine { pub struct WrappedLine {
pub line: String, pub line: String,
pub width: usize, pub width: usize,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct WrappedCell { pub struct WrappedCell {
pub lines: Vec<WrappedLine>, pub lines: Vec<WrappedLine>,
pub max_width: usize, pub max_width: usize,