From 89c737f4564479e31bb1b7c2b816085e1f07fe28 Mon Sep 17 00:00:00 2001
From: Jonathan Turner <jonathandturner@users.noreply.github.com>
Date: Sat, 20 Jun 2020 12:25:07 -0700
Subject: [PATCH] Finish move to nu-table (#2025)

---
 Cargo.lock                                |  23 +---
 crates/nu-cli/Cargo.toml                  |   1 -
 crates/nu-cli/src/commands/autoview.rs    | 101 +++--------------
 crates/nu-cli/src/commands/table.rs       |  15 ++-
 crates/nu-table/src/table.rs              | 128 +++++++++++++---------
 crates/nu_plugin_textview/src/textview.rs |   4 -
 6 files changed, 111 insertions(+), 161 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 95e328633..5a44529ad 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1899,9 +1899,9 @@ dependencies = [
 
 [[package]]
 name = "kstring"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6382df53100fd22e149030b6634720c94a151076db8d727b3274d7070975d609"
+checksum = "fbbc30beb80d56ddf6346e935c7abcba96329ee5c5a4cde8984a4e6b6f18b58e"
 dependencies = [
  "serde 1.0.110",
 ]
@@ -2038,9 +2038,9 @@ dependencies = [
 
 [[package]]
 name = "liquid-core"
-version = "0.20.1"
+version = "0.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ea59709d9530bab4e9cc0ece12f20fe4999fdef90f7d89391b0fc9ff563b62b"
+checksum = "4dc58422728185d54cd044bba4d45a2ef2a7111a421f84d344f65629949de4f1"
 dependencies = [
  "anymap",
  "chrono",
@@ -2481,7 +2481,6 @@ dependencies = [
  "pin-utils",
  "pretty-hex",
  "pretty_env_logger",
- "prettytable-rs",
  "ptree",
  "query_interface",
  "quickcheck",
@@ -3235,20 +3234,6 @@ dependencies = [
  "log",
 ]
 
-[[package]]
-name = "prettytable-rs"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e"
-dependencies = [
- "atty",
- "csv",
- "encode_unicode",
- "lazy_static 1.4.0",
- "term",
- "unicode-width",
-]
-
 [[package]]
 name = "proc-macro-error"
 version = "1.0.2"
diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml
index d9c8c3638..070e8f04b 100644
--- a/crates/nu-cli/Cargo.toml
+++ b/crates/nu-cli/Cargo.toml
@@ -62,7 +62,6 @@ parking_lot = "0.10.2"
 pin-utils = "0.1.0"
 pretty-hex = "0.1.1"
 pretty_env_logger = "0.4.0"
-prettytable-rs = "0.8.0"
 ptree = {version = "0.2" }
 query_interface = "0.3.5"
 rand = "0.7"
diff --git a/crates/nu-cli/src/commands/autoview.rs b/crates/nu-cli/src/commands/autoview.rs
index c587d603b..eac2317a6 100644
--- a/crates/nu-cli/src/commands/autoview.rs
+++ b/crates/nu-cli/src/commands/autoview.rs
@@ -6,10 +6,7 @@ use nu_errors::ShellError;
 use nu_protocol::{hir, hir::Expression, hir::Literal, hir::SpannedExpression};
 use nu_protocol::{Primitive, Scope, Signature, UntaggedValue, Value};
 use parking_lot::Mutex;
-use prettytable::format::{FormatBuilder, LinePosition, LineSeparator};
-use prettytable::{color, Attr, Cell, Row, Table};
 use std::sync::atomic::AtomicBool;
-use textwrap::fill;
 
 pub struct Autoview;
 
@@ -268,90 +265,28 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
                                 + row.entries.iter().count() * 2)
                                 > textwrap::termwidth()) =>
                     {
-                        let termwidth = std::cmp::max(textwrap::termwidth(), 20);
-
-                        enum TableMode {
-                            Light,
-                            Normal,
-                        }
-
-                        let mut table = Table::new();
-                        let table_mode = crate::data::config::config(Tag::unknown());
-
-                        let table_mode = if let Some(s) = table_mode?.get("table_mode") {
-                            match s.as_string() {
-                                Ok(typ) if typ == "light" => TableMode::Light,
-                                _ => TableMode::Normal,
-                            }
-                        } else {
-                            TableMode::Normal
-                        };
-
-                        match table_mode {
-                            TableMode::Light => {
-                                table.set_format(
-                                    FormatBuilder::new()
-                                        .separator(
-                                            LinePosition::Title,
-                                            LineSeparator::new('─', '─', ' ', ' '),
-                                        )
-                                        .separator(
-                                            LinePosition::Bottom,
-                                            LineSeparator::new(' ', ' ', ' ', ' '),
-                                        )
-                                        .padding(1, 1)
-                                        .build(),
-                                );
-                            }
-                            _ => {
-                                table.set_format(
-                                    FormatBuilder::new()
-                                        .column_separator('│')
-                                        .separator(
-                                            LinePosition::Top,
-                                            LineSeparator::new('─', '┬', ' ', ' '),
-                                        )
-                                        .separator(
-                                            LinePosition::Title,
-                                            LineSeparator::new('─', '┼', ' ', ' '),
-                                        )
-                                        .separator(
-                                            LinePosition::Bottom,
-                                            LineSeparator::new('─', '┴', ' ', ' '),
-                                        )
-                                        .padding(1, 1)
-                                        .build(),
-                                );
-                            }
-                        }
-
-                        let mut max_key_len = 0;
-                        for (key, _) in row.entries.iter() {
-                            max_key_len = std::cmp::max(max_key_len, key.chars().count());
-                        }
-
-                        if max_key_len > (termwidth / 2 - 1) {
-                            max_key_len = termwidth / 2 - 1;
-                        }
-
-                        let max_val_len = termwidth - max_key_len - 5;
-
+                        let mut entries = vec![];
                         for (key, value) in row.entries.iter() {
-                            table.add_row(Row::new(vec![
-                                Cell::new(&fill(&key, max_key_len))
-                                    .with_style(Attr::ForegroundColor(color::GREEN))
-                                    .with_style(Attr::Bold),
-                                Cell::new(&fill(
-                                    &format_leaf(value).plain_string(100_000),
-                                    max_val_len,
-                                )),
-                            ]));
+                            entries.push(vec![
+                                nu_table::StyledString::new(
+                                    key.to_string(),
+                                    nu_table::TextStyle {
+                                        alignment: nu_table::Alignment::Left,
+                                        color: Some(ansi_term::Color::Green),
+                                        is_bold: true,
+                                    },
+                                ),
+                                nu_table::StyledString::new(
+                                    format_leaf(value).plain_string(100_000),
+                                    nu_table::TextStyle::basic(),
+                                ),
+                            ]);
                         }
 
-                        table.printstd();
+                        let table =
+                            nu_table::Table::new(vec![], entries, nu_table::Theme::compact());
 
-                        // table.print_term(&mut *context.host.lock().out_terminal().ok_or_else(|| ShellError::untagged_runtime_error("Could not open terminal for output"))?)
-                        //     .map_err(|_| ShellError::untagged_runtime_error("Internal error: could not print to terminal (for unix systems check to make sure TERM is set)"))?;
+                        nu_table::draw_table(&table, textwrap::termwidth());
                     }
 
                     Value {
diff --git a/crates/nu-cli/src/commands/table.rs b/crates/nu-cli/src/commands/table.rs
index 9953dbda9..ea6386428 100644
--- a/crates/nu-cli/src/commands/table.rs
+++ b/crates/nu-cli/src/commands/table.rs
@@ -56,7 +56,7 @@ fn str_to_color(s: String) -> Option<ansi_term::Color> {
 pub fn from_list(values: &[Value], starting_idx: usize) -> nu_table::Table {
     let config = crate::data::config::config(Tag::unknown());
 
-    let header_style = if let Ok(config) = config {
+    let header_style = if let Ok(config) = &config {
         let header_align = config.get("header_align").map_or(Alignment::Left, |a| {
             a.as_string()
                 .map_or(Alignment::Center, |a| match a.to_lowercase().as_str() {
@@ -97,6 +97,19 @@ pub fn from_list(values: &[Value], starting_idx: usize) -> nu_table::Table {
         .collect();
     let entries = values_to_entries(values, &mut headers, starting_idx);
 
+    if let Ok(config) = config {
+        if let Some(style) = config.get("table_mode") {
+            if let Ok(table_mode) = style.as_string() {
+                if table_mode == "light" {
+                    return nu_table::Table {
+                        headers,
+                        data: entries,
+                        theme: Theme::light(),
+                    };
+                }
+            }
+        }
+    }
     nu_table::Table {
         headers,
         data: entries,
diff --git a/crates/nu-table/src/table.rs b/crates/nu-table/src/table.rs
index 7ad91e908..5bd8877bd 100644
--- a/crates/nu-table/src/table.rs
+++ b/crates/nu-table/src/table.rs
@@ -81,6 +81,8 @@ pub struct Theme {
 
     pub print_left_border: bool,
     pub print_right_border: bool,
+    pub print_top_border: bool,
+    pub print_bottom_border: bool,
 }
 
 impl Theme {
@@ -108,6 +110,8 @@ impl Theme {
 
             print_left_border: true,
             print_right_border: true,
+            print_top_border: true,
+            print_bottom_border: true,
         }
     }
     #[allow(unused)]
@@ -136,6 +140,8 @@ impl Theme {
 
             print_left_border: true,
             print_right_border: true,
+            print_top_border: true,
+            print_bottom_border: true,
         }
     }
     #[allow(unused)]
@@ -160,10 +166,12 @@ impl Theme {
             right_vertical: ' ',
 
             separate_header: true,
-            separate_rows: true,
+            separate_rows: false,
 
             print_left_border: true,
             print_right_border: true,
+            print_top_border: false,
+            print_bottom_border: true,
         }
     }
     #[allow(unused)]
@@ -191,6 +199,8 @@ impl Theme {
 
             print_left_border: false,
             print_right_border: false,
+            print_top_border: true,
+            print_bottom_border: true,
         }
     }
 }
@@ -227,84 +237,77 @@ pub struct WrappedTable {
 }
 
 impl WrappedTable {
-    //TODO: optimize this
     fn print_separator(&self, separator_position: SeparatorPosition) {
         let column_count = self.column_widths.len();
+        let mut output = String::new();
 
         match separator_position {
             SeparatorPosition::Top => {
                 for column in self.column_widths.iter().enumerate() {
                     if column.0 == 0 && self.theme.print_left_border {
-                        print!("{}", self.theme.top_left);
+                        output.push(self.theme.top_left);
                     }
-                    print!(
-                        "{}",
-                        std::iter::repeat(self.theme.top_horizontal)
-                            .take(*column.1)
-                            .collect::<String>()
-                    );
 
-                    print!("{}{}", self.theme.top_horizontal, self.theme.top_horizontal);
+                    for _ in 0..*column.1 {
+                        output.push(self.theme.top_horizontal);
+                    }
+
+                    output.push(self.theme.top_horizontal);
+                    output.push(self.theme.top_horizontal);
                     if column.0 == column_count - 1 {
                         if self.theme.print_right_border {
-                            print!("{}", self.theme.top_right);
+                            output.push(self.theme.top_right);
                         }
                     } else {
-                        print!("{}", self.theme.top_center);
+                        output.push(self.theme.top_center);
                     }
                 }
             }
             SeparatorPosition::Middle => {
                 for column in self.column_widths.iter().enumerate() {
                     if column.0 == 0 && self.theme.print_left_border {
-                        print!("{}", self.theme.middle_left);
+                        output.push(self.theme.middle_left);
                     }
-                    print!(
-                        "{}",
-                        std::iter::repeat(self.theme.middle_horizontal)
-                            .take(*column.1)
-                            .collect::<String>()
-                    );
 
-                    print!(
-                        "{}{}",
-                        self.theme.middle_horizontal, self.theme.middle_horizontal
-                    );
+                    for _ in 0..*column.1 {
+                        output.push(self.theme.middle_horizontal);
+                    }
+
+                    output.push(self.theme.middle_horizontal);
+                    output.push(self.theme.middle_horizontal);
+
                     if column.0 == column_count - 1 {
                         if self.theme.print_right_border {
-                            print!("{}", self.theme.middle_right);
+                            output.push(self.theme.middle_right);
                         }
                     } else {
-                        print!("{}", self.theme.center);
+                        output.push(self.theme.center);
                     }
                 }
             }
             SeparatorPosition::Bottom => {
                 for column in self.column_widths.iter().enumerate() {
                     if column.0 == 0 && self.theme.print_left_border {
-                        print!("{}", self.theme.bottom_left);
+                        output.push(self.theme.bottom_left);
                     }
-                    print!(
-                        "{}",
-                        std::iter::repeat(self.theme.bottom_horizontal)
-                            .take(*column.1)
-                            .collect::<String>()
-                    );
+                    for _ in 0..*column.1 {
+                        output.push(self.theme.bottom_horizontal);
+                    }
+                    output.push(self.theme.bottom_horizontal);
+                    output.push(self.theme.bottom_horizontal);
 
-                    print!(
-                        "{}{}",
-                        self.theme.bottom_horizontal, self.theme.bottom_horizontal
-                    );
                     if column.0 == column_count - 1 {
                         if self.theme.print_right_border {
-                            print!("{}", self.theme.bottom_right);
+                            output.push(self.theme.bottom_right);
                         }
                     } else {
-                        print!("{}", self.theme.bottom_center);
+                        output.push(self.theme.bottom_center);
                     }
                 }
             }
         }
+
+        println!("{}", output);
     }
 
     fn print_cell_contents(&self, cells: &[WrappedCell]) {
@@ -399,10 +402,13 @@ impl WrappedTable {
             return;
         }
 
-        self.print_separator(SeparatorPosition::Top);
-        println!();
+        if self.theme.print_top_border {
+            self.print_separator(SeparatorPosition::Top);
+        }
 
-        self.print_cell_contents(&self.headers);
+        if !self.headers.is_empty() {
+            self.print_cell_contents(&self.headers);
+        }
 
         let mut first_row = true;
 
@@ -410,21 +416,21 @@ impl WrappedTable {
             if !first_row {
                 if self.theme.separate_rows {
                     self.print_separator(SeparatorPosition::Middle);
-                    println!();
                 }
             } else {
                 first_row = false;
 
-                if self.theme.separate_header {
+                if self.theme.separate_header && !self.headers.is_empty() {
                     self.print_separator(SeparatorPosition::Middle);
-                    println!();
                 }
             }
 
             self.print_cell_contents(row);
         }
-        self.print_separator(SeparatorPosition::Bottom);
-        println!();
+
+        if self.theme.print_bottom_border {
+            self.print_separator(SeparatorPosition::Bottom);
+        }
     }
 }
 
@@ -499,6 +505,17 @@ pub fn draw_table(table: &Table, termwidth: usize) {
     // maybe_truncate_columns(&mut headers, &mut entries, termwidth);
     let headers_len = table.headers.len();
 
+    // fix the length of the table if there are no headers:
+    let headers_len = if headers_len == 0 {
+        if !table.data.is_empty() && !table.data[0].is_empty() {
+            table.data[0].len()
+        } else {
+            return;
+        }
+    } else {
+        headers_len
+    };
+
     // Measure how big our columns need to be (accounting for separators also)
     let max_naive_column_width = (termwidth - 3 * (headers_len - 1)) / headers_len;
 
@@ -518,18 +535,23 @@ pub fn draw_table(table: &Table, termwidth: usize) {
     // This should give us the final max column width
     let max_column_width = column_space.max_width(termwidth);
 
-    let wrapped_table = wrap_cells(
-        processed_table,
-        // max_per_column,
-        // max_naive_column_width,
-        max_column_width,
-    );
+    let wrapped_table = wrap_cells(processed_table, max_column_width);
 
     wrapped_table.new_print_table();
 }
 
 fn wrap_cells(processed_table: ProcessedTable, max_column_width: usize) -> WrappedTable {
-    let mut column_widths = vec![0; processed_table.headers.len()];
+    let mut column_widths = vec![
+        0;
+        std::cmp::max(
+            processed_table.headers.len(),
+            if !processed_table.data.is_empty() {
+                processed_table.data[0].len()
+            } else {
+                0
+            }
+        )
+    ];
     let mut output_headers = vec![];
     for header in processed_table.headers.into_iter().enumerate() {
         let wrapped = wrap(
diff --git a/crates/nu_plugin_textview/src/textview.rs b/crates/nu_plugin_textview/src/textview.rs
index c050ff189..a1a5dedd0 100644
--- a/crates/nu_plugin_textview/src/textview.rs
+++ b/crates/nu_plugin_textview/src/textview.rs
@@ -134,11 +134,7 @@ pub fn view_text_value(value: &Value) {
                     _ => (),
                 }
             }
-        } else {
-            println!("Couldn't find bat section in config");
         }
-    } else {
-        println!("Error reading config!");
     }
 
     let value_anchor = value.anchor();