Add minor theme support (#2449)

* WIP - compiling but not working

* semi-working

* making progress

* working except for table lines

* fmt + clippy

* cleaned up some comments

* working line colors

* fmt, clippy, updated sample config.toml

* removed extra comments
This commit is contained in:
Darren Schroeder
2020-09-01 00:09:55 -05:00
committed by GitHub
parent 860c2a606d
commit adbbcafd30
12 changed files with 904 additions and 384 deletions

View File

@ -1,4 +1,5 @@
use nu_table::{draw_table, StyledString, Table, TextStyle, Theme};
use std::collections::HashMap;
fn main() {
let args: Vec<_> = std::env::args().collect();
@ -23,5 +24,7 @@ fn main() {
Theme::compact(),
);
draw_table(&t, width);
// FIXME: Config isn't available from here so just put these here to compile
let color_hm: HashMap<String, ansi_term::Style> = HashMap::new();
draw_table(&t, width, &color_hm);
}

View File

@ -1,4 +1,6 @@
use crate::wrap::{column_width, split_sublines, wrap, Alignment, Subline, WrappedCell};
use ansi_term::{Color, Style};
use std::collections::HashMap;
enum SeparatorPosition {
Top,
@ -23,38 +25,240 @@ impl StyledString {
pub fn new(contents: String, style: TextStyle) -> StyledString {
StyledString { contents, style }
}
pub fn set_style(&mut self, style: TextStyle) {
self.style = style;
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub struct TextStyle {
pub is_bold: bool,
pub alignment: Alignment,
pub color: Option<ansi_term::Colour>,
pub color_style: Option<Style>,
}
impl TextStyle {
pub fn basic() -> TextStyle {
pub fn new() -> TextStyle {
TextStyle {
is_bold: false,
alignment: Alignment::Left,
color: None,
color_style: Some(Style::default()),
}
}
pub fn bold(&self, bool_value: Option<bool>) -> TextStyle {
let bv = match bool_value {
Some(v) => v,
None => false,
};
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_bold: bv,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_bold(&self) -> bool {
self.color_style.unwrap_or_default().is_bold
}
pub fn dimmed(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_dimmed: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_dimmed(&self) -> bool {
self.color_style.unwrap_or_default().is_dimmed
}
pub fn italic(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_italic: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_italic(&self) -> bool {
self.color_style.unwrap_or_default().is_italic
}
pub fn underline(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_underline: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_underline(&self) -> bool {
self.color_style.unwrap_or_default().is_underline
}
pub fn blink(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_blink: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_blink(&self) -> bool {
self.color_style.unwrap_or_default().is_blink
}
pub fn reverse(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_reverse: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_reverse(&self) -> bool {
self.color_style.unwrap_or_default().is_reverse
}
pub fn hidden(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_hidden: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_hidden(&self) -> bool {
self.color_style.unwrap_or_default().is_hidden
}
pub fn strikethrough(&self) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
is_strikethrough: true,
..self.color_style.unwrap_or_default()
}),
}
}
pub fn is_strikethrough(&self) -> bool {
self.color_style.unwrap_or_default().is_strikethrough
}
pub fn fg(&self, foregound: Color) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
foreground: Some(foregound),
..self.color_style.unwrap_or_default()
}),
}
}
pub fn on(&self, background: Color) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
background: Some(background),
..self.color_style.unwrap_or_default()
}),
}
}
pub fn bg(&self, background: Color) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
background: Some(background),
..self.color_style.unwrap_or_default()
}),
}
}
pub fn alignment(&self, align: Alignment) -> TextStyle {
TextStyle {
alignment: align,
color_style: self.color_style,
}
}
pub fn style(&self, style: Style) -> TextStyle {
TextStyle {
alignment: self.alignment,
color_style: Some(Style {
foreground: style.foreground,
background: style.background,
is_bold: style.is_bold,
is_dimmed: style.is_dimmed,
is_italic: style.is_italic,
is_underline: style.is_underline,
is_blink: style.is_blink,
is_reverse: style.is_reverse,
is_hidden: style.is_hidden,
is_strikethrough: style.is_strikethrough,
}),
}
}
pub fn basic() -> TextStyle {
TextStyle::new()
.alignment(Alignment::Left)
.style(Style::default())
}
pub fn basic_right() -> TextStyle {
TextStyle {
is_bold: false,
alignment: Alignment::Right,
color: None,
}
TextStyle::new()
.alignment(Alignment::Right)
.style(Style::default())
}
pub fn default_header() -> TextStyle {
TextStyle {
is_bold: true,
alignment: Alignment::Center,
color: Some(ansi_term::Colour::Green),
}
TextStyle::new()
.alignment(Alignment::Center)
.fg(Color::Green)
.bold(Some(true))
}
pub fn with_attributes(bo: bool, al: Alignment, co: Color) -> TextStyle {
TextStyle::new().alignment(al).fg(co).bold(Some(bo))
}
pub fn with_style(al: Alignment, style: Style) -> TextStyle {
TextStyle::new().alignment(al).style(Style {
foreground: style.foreground,
background: style.background,
is_bold: style.is_bold,
is_dimmed: style.is_dimmed,
is_italic: style.is_italic,
is_underline: style.is_underline,
is_blink: style.is_blink,
is_reverse: style.is_reverse,
is_hidden: style.is_hidden,
is_strikethrough: style.is_strikethrough,
})
}
}
impl Default for TextStyle {
fn default() -> Self {
Self::new()
}
}
@ -300,71 +504,148 @@ pub struct WrappedTable {
}
impl WrappedTable {
fn print_separator(&self, separator_position: SeparatorPosition) {
fn print_separator(
&self,
separator_position: SeparatorPosition,
color_hm: &HashMap<String, Style>,
) {
let column_count = self.column_widths.len();
let mut output = String::new();
let sep_color = color_hm
.get("separator_color")
.unwrap_or(&Style::default())
.to_owned();
match separator_position {
SeparatorPosition::Top => {
for column in self.column_widths.iter().enumerate() {
if column.0 == 0 && self.theme.print_left_border {
output.push(self.theme.top_left);
output.push_str(
&sep_color
.paint(&self.theme.top_left.to_string())
.to_string(),
);
}
for _ in 0..*column.1 {
output.push(self.theme.top_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.top_horizontal.to_string())
.to_string(),
);
}
output.push(self.theme.top_horizontal);
output.push(self.theme.top_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.top_horizontal.to_string())
.to_string(),
);
output.push_str(
&sep_color
.paint(&self.theme.top_horizontal.to_string())
.to_string(),
);
if column.0 == column_count - 1 {
if self.theme.print_right_border {
output.push(self.theme.top_right);
output.push_str(
&sep_color
.paint(&self.theme.top_right.to_string())
.to_string(),
);
}
} else {
output.push(self.theme.top_center);
output.push_str(
&sep_color
.paint(&self.theme.top_center.to_string())
.to_string(),
);
}
}
}
SeparatorPosition::Middle => {
for column in self.column_widths.iter().enumerate() {
if column.0 == 0 && self.theme.print_left_border {
output.push(self.theme.middle_left);
output.push_str(
&sep_color
.paint(&self.theme.middle_left.to_string())
.to_string(),
);
}
for _ in 0..*column.1 {
output.push(self.theme.middle_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.middle_horizontal.to_string())
.to_string(),
);
}
output.push(self.theme.middle_horizontal);
output.push(self.theme.middle_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.middle_horizontal.to_string())
.to_string(),
);
output.push_str(
&sep_color
.paint(&self.theme.middle_horizontal.to_string())
.to_string(),
);
if column.0 == column_count - 1 {
if self.theme.print_right_border {
output.push(self.theme.middle_right);
output.push_str(
&sep_color
.paint(&self.theme.middle_right.to_string())
.to_string(),
);
}
} else {
output.push(self.theme.center);
output
.push_str(&sep_color.paint(&self.theme.center.to_string()).to_string());
}
}
}
SeparatorPosition::Bottom => {
for column in self.column_widths.iter().enumerate() {
if column.0 == 0 && self.theme.print_left_border {
output.push(self.theme.bottom_left);
output.push_str(
&sep_color
.paint(&self.theme.bottom_left.to_string())
.to_string(),
);
}
for _ in 0..*column.1 {
output.push(self.theme.bottom_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.bottom_horizontal.to_string())
.to_string(),
);
}
output.push(self.theme.bottom_horizontal);
output.push(self.theme.bottom_horizontal);
output.push_str(
&sep_color
.paint(&self.theme.bottom_horizontal.to_string())
.to_string(),
);
output.push_str(
&sep_color
.paint(&self.theme.bottom_horizontal.to_string())
.to_string(),
);
if column.0 == column_count - 1 {
if self.theme.print_right_border {
output.push(self.theme.bottom_right);
output.push_str(
&sep_color
.paint(&self.theme.bottom_right.to_string())
.to_string(),
);
}
} else {
output.push(self.theme.bottom_center);
output.push_str(
&sep_color
.paint(&self.theme.bottom_center.to_string())
.to_string(),
);
}
}
}
@ -373,15 +654,24 @@ impl WrappedTable {
println!("{}", output);
}
fn print_cell_contents(&self, cells: &[WrappedCell]) {
fn print_cell_contents(&self, cells: &[WrappedCell], color_hm: &HashMap<String, Style>) {
let sep_color = color_hm
.get("separator_color")
.unwrap_or(&Style::default())
.to_owned();
for current_line in 0.. {
let mut lines_printed = 0;
let mut output = if self.theme.print_left_border {
self.theme.left_vertical.to_string()
} else {
String::new()
};
let mut output = String::new();
if self.theme.print_left_border {
output.push_str(
&sep_color
.paint(&self.theme.left_vertical.to_string())
.to_string(),
);
}
for column in cells.iter().enumerate() {
if let Some(line) = (column.1).lines.get(current_line) {
let remainder = self.column_widths[column.0] - line.width;
@ -389,13 +679,7 @@ impl WrappedTable {
match column.1.style.alignment {
Alignment::Left => {
if let Some(color) = column.1.style.color {
let color = if column.1.style.is_bold {
color.bold()
} else {
color.normal()
};
if let Some(color) = column.1.style.color_style {
output.push_str(&color.paint(&line.line).to_string());
} else {
output.push_str(&line.line);
@ -408,13 +692,7 @@ impl WrappedTable {
for _ in 0..remainder / 2 {
output.push(' ');
}
if let Some(color) = column.1.style.color {
let color = if column.1.style.is_bold {
color.bold()
} else {
color.normal()
};
if let Some(color) = column.1.style.color_style {
output.push_str(&color.paint(&line.line).to_string());
} else {
output.push_str(&line.line);
@ -427,13 +705,7 @@ impl WrappedTable {
for _ in 0..remainder {
output.push(' ');
}
if let Some(color) = column.1.style.color {
let color = if column.1.style.is_bold {
color.bold()
} else {
color.normal()
};
if let Some(color) = column.1.style.color_style {
output.push_str(&color.paint(&line.line).to_string());
} else {
output.push_str(&line.line);
@ -448,9 +720,17 @@ impl WrappedTable {
}
}
if column.0 < cells.len() - 1 {
output.push(self.theme.center_vertical);
output.push_str(
&sep_color
.paint(&self.theme.center_vertical.to_string())
.to_string(),
);
} else if self.theme.print_right_border {
output.push(self.theme.right_vertical);
output.push_str(
&sep_color
.paint(&self.theme.right_vertical.to_string())
.to_string(),
);
}
}
if lines_printed == 0 {
@ -460,20 +740,21 @@ impl WrappedTable {
}
}
}
fn new_print_table(&self) {
fn new_print_table(&self, color_hm: &HashMap<String, Style>) {
if self.data.is_empty() {
return;
}
if self.theme.print_top_border {
self.print_separator(SeparatorPosition::Top);
self.print_separator(SeparatorPosition::Top, &color_hm);
}
let skip_headers = (self.headers.len() == 2 && self.headers[1].max_width == 0)
|| (self.headers.len() == 1 && self.headers[0].max_width == 0);
if !self.headers.is_empty() && !skip_headers {
self.print_cell_contents(&self.headers);
self.print_cell_contents(&self.headers, &color_hm);
}
let mut first_row = true;
@ -481,21 +762,21 @@ impl WrappedTable {
for row in &self.data {
if !first_row {
if self.theme.separate_rows {
self.print_separator(SeparatorPosition::Middle);
self.print_separator(SeparatorPosition::Middle, &color_hm);
}
} else {
first_row = false;
if self.theme.separate_header && !self.headers.is_empty() && !skip_headers {
self.print_separator(SeparatorPosition::Middle);
self.print_separator(SeparatorPosition::Middle, &color_hm);
}
}
self.print_cell_contents(row);
self.print_cell_contents(row, &color_hm);
}
if self.theme.print_bottom_border {
self.print_separator(SeparatorPosition::Bottom);
self.print_separator(SeparatorPosition::Bottom, &color_hm);
}
}
}
@ -507,7 +788,7 @@ fn process_table(table: &Table) -> ProcessedTable {
for column in row {
out_row.push(ProcessedCell {
contents: split_sublines(&column.contents),
style: column.style.clone(),
style: column.style,
});
}
processed_data.push(out_row);
@ -517,7 +798,7 @@ fn process_table(table: &Table) -> ProcessedTable {
for header in &table.headers {
processed_headers.push(ProcessedCell {
contents: split_sublines(&header.contents),
style: header.style.clone(),
style: header.style,
});
}
@ -554,7 +835,7 @@ fn get_max_column_widths(processed_table: &ProcessedTable) -> Vec<usize> {
output
}
pub fn draw_table(table: &Table, termwidth: usize) {
pub fn draw_table(table: &Table, termwidth: usize, color_hm: &HashMap<String, Style>) {
// Remove the edges, if used
let termwidth = if table.theme.print_left_border && table.theme.print_right_border {
termwidth - 2
@ -603,7 +884,7 @@ pub fn draw_table(table: &Table, termwidth: usize) {
let wrapped_table = wrap_cells(processed_table, max_column_width);
wrapped_table.new_print_table();
wrapped_table.new_print_table(&color_hm);
}
fn wrap_cells(processed_table: ProcessedTable, max_column_width: usize) -> WrappedTable {

View File

@ -2,7 +2,7 @@ use crate::table::TextStyle;
use std::{fmt::Display, iter::Iterator};
use unicode_width::UnicodeWidthStr;
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum Alignment {
Left,
Center,
@ -135,7 +135,6 @@ pub fn wrap<'a>(
let mut first = true;
let mut max_width = 0;
loop {
// println!("{:?}", current_line);
match input.next() {
Some(item) => {
if !first {