mirror of
https://github.com/nushell/nushell
synced 2024-11-14 00:47:09 +00:00
nu-table/ Add -t/theme
argument && Replace -n/start-number
with -i/index
(#11058)
ref #11054 cc: @fdncred I've not figured out how to be able to have a flag option as `table -i` :( ```nu ~/bin/nushell> [[a b, c]; [1 [2 3 3] 3] [4 5 [1 2 [1 2 3]]]] | table -e --width=80 --theme basic -i false +---+-------+-----------+ | a | b | c | +---+-------+-----------+ | 1 | +---+ | 3 | | | | 2 | | | | | +---+ | | | | | 3 | | | | | +---+ | | | | | 3 | | | | | +---+ | | +---+-------+-----------+ | 4 | 5 | +-------+ | | | | | 1 | | | | | +-------+ | | | | | 2 | | | | | +-------+ | | | | | +---+ | | | | | | | 1 | | | | | | | +---+ | | | | | | | 2 | | | | | | | +---+ | | | | | | | 3 | | | | | | | +---+ | | | | | +-------+ | +---+-------+-----------+ ``` ```nu ~/bin/nushell> [[a b, c]; [1 [2 3 3] 3] [4 5 [1 2 [1 2 3]]]] | table -e --width=80 --theme basic -i 100 +-----+---+-------------+-----------------------+ | # | a | b | c | +-----+---+-------------+-----------------------+ | 100 | 1 | +-----+---+ | 3 | | | | | 100 | 2 | | | | | | +-----+---+ | | | | | | 101 | 3 | | | | | | +-----+---+ | | | | | | 102 | 3 | | | | | | +-----+---+ | | +-----+---+-------------+-----------------------+ | 101 | 4 | 5 | +-----+-------------+ | | | | | | 100 | 1 | | | | | | +-----+-------------+ | | | | | | 101 | 2 | | | | | | +-----+-------------+ | | | | | | 102 | +-----+---+ | | | | | | | | | 100 | 1 | | | | | | | | | +-----+---+ | | | | | | | | | 101 | 2 | | | | | | | | | +-----+---+ | | | | | | | | | 102 | 3 | | | | | | | | | +-----+---+ | | | | | | +-----+-------------+ | +-----+---+-------------+-----------------------+ ```
This commit is contained in:
parent
c110ddff66
commit
e9c298713e
9 changed files with 253 additions and 63 deletions
|
@ -1,14 +1,18 @@
|
||||||
|
// todo: (refactoring) limit get_config() usage to 1 call
|
||||||
|
// overall reduce the redundant calls to StyleComputer etc.
|
||||||
|
// the goal is to configure it once...
|
||||||
|
|
||||||
use lscolors::{LsColors, Style};
|
use lscolors::{LsColors, Style};
|
||||||
use nu_color_config::color_from_hex;
|
use nu_color_config::color_from_hex;
|
||||||
use nu_color_config::{StyleComputer, TextStyle};
|
use nu_color_config::{StyleComputer, TextStyle};
|
||||||
use nu_engine::{env::get_config, env_to_string, CallExt};
|
use nu_engine::{env::get_config, env_to_string, CallExt};
|
||||||
use nu_protocol::record;
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Config, DataSource, Example, IntoPipelineData, ListStream, PipelineData,
|
Category, Config, DataSource, Example, IntoPipelineData, ListStream, PipelineData,
|
||||||
PipelineMetadata, RawStream, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
PipelineMetadata, RawStream, Record, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
use nu_protocol::{record, TableMode};
|
||||||
use nu_table::common::create_nu_table_config;
|
use nu_table::common::create_nu_table_config;
|
||||||
use nu_table::{
|
use nu_table::{
|
||||||
CollapsedTable, ExpandedTable, JustTable, NuTable, NuTableCell, StringResult, TableOpts,
|
CollapsedTable, ExpandedTable, JustTable, NuTable, NuTableCell, StringResult, TableOpts,
|
||||||
|
@ -16,6 +20,7 @@ use nu_table::{
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
use std::io::IsTerminal;
|
use std::io::IsTerminal;
|
||||||
|
use std::str::FromStr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{path::PathBuf, sync::atomic::AtomicBool};
|
use std::{path::PathBuf, sync::atomic::AtomicBool};
|
||||||
|
@ -60,12 +65,17 @@ impl Command for Table {
|
||||||
.input_output_types(vec![(Type::Any, Type::Any)])
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
// TODO: make this more precise: what turns into string and what into raw stream
|
// TODO: make this more precise: what turns into string and what into raw stream
|
||||||
.named(
|
.named(
|
||||||
"start-number",
|
"theme",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::String,
|
||||||
"row number to start viewing from",
|
"set a table mode/theme",
|
||||||
Some('n'),
|
Some('t'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"index",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"set or set off a index mode/theme",
|
||||||
|
Some('i'),
|
||||||
)
|
)
|
||||||
.switch("list", "list available table modes/themes", Some('l'))
|
|
||||||
.named(
|
.named(
|
||||||
"width",
|
"width",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
|
@ -101,6 +111,7 @@ impl Command for Table {
|
||||||
"abbreviate the data in the table by truncating the middle part and only showing amount provided on top and bottom",
|
"abbreviate the data in the table by truncating the middle part and only showing amount provided on top and bottom",
|
||||||
Some('a'),
|
Some('a'),
|
||||||
)
|
)
|
||||||
|
.switch("list", "list available table modes/themes", Some('l'))
|
||||||
.category(Category::Viewers)
|
.category(Category::Viewers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,15 +123,15 @@ impl Command for Table {
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let list_themes: bool = call.has_flag("list");
|
let list_themes: bool = call.has_flag("list");
|
||||||
let cfg = parse_table_config(call, engine_state, stack)?;
|
|
||||||
let input = CmdInput::new(engine_state, stack, call, input);
|
|
||||||
|
|
||||||
// if list argument is present we just need to return a list of supported table themes
|
// if list argument is present we just need to return a list of supported table themes
|
||||||
if list_themes {
|
if list_themes {
|
||||||
let val = Value::list(supported_table_modes(), Span::test_data());
|
let val = Value::list(supported_table_modes(), Span::test_data());
|
||||||
return Ok(val.into_pipeline_data());
|
return Ok(val.into_pipeline_data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let cfg = parse_table_config(call, engine_state, stack)?;
|
||||||
|
let input = CmdInput::new(engine_state, stack, call, input);
|
||||||
|
|
||||||
// reset vt processing, aka ansi because illbehaved externals can break it
|
// reset vt processing, aka ansi because illbehaved externals can break it
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
|
@ -179,30 +190,53 @@ impl Command for Table {
|
||||||
}),
|
}),
|
||||||
])),
|
])),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Change a theme",
|
||||||
|
example: r#"[[a b]; [1 2] [2 [4 4]]] | table --theme basic"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Force a show of an index",
|
||||||
|
example: r#"[[a b]; [1 2] [2 [4 4]]] | table -i"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Set an index to start from a value",
|
||||||
|
example: r#"[[a b]; [1 2] [2 [4 4]]] | table -i 100"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Remove an index",
|
||||||
|
example: r#"[[a b]; [1 2] [2 [4 4]]] | table -i false"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct TableConfig {
|
struct TableConfig {
|
||||||
row_offset: usize,
|
index: Option<usize>,
|
||||||
table_view: TableView,
|
table_view: TableView,
|
||||||
term_width: usize,
|
term_width: usize,
|
||||||
|
theme: TableMode,
|
||||||
abbreviation: Option<usize>,
|
abbreviation: Option<usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableConfig {
|
impl TableConfig {
|
||||||
fn new(
|
fn new(
|
||||||
row_offset: usize,
|
|
||||||
table_view: TableView,
|
table_view: TableView,
|
||||||
term_width: usize,
|
term_width: usize,
|
||||||
|
theme: TableMode,
|
||||||
abbreviation: Option<usize>,
|
abbreviation: Option<usize>,
|
||||||
|
index: Option<usize>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
row_offset,
|
index,
|
||||||
table_view,
|
table_view,
|
||||||
term_width,
|
term_width,
|
||||||
abbreviation,
|
abbreviation,
|
||||||
|
theme,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,8 +246,6 @@ fn parse_table_config(
|
||||||
state: &EngineState,
|
state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
) -> Result<TableConfig, ShellError> {
|
) -> Result<TableConfig, ShellError> {
|
||||||
let start_num: Option<i64> = call.get_flag(state, stack, "start-number")?;
|
|
||||||
let row_offset = start_num.unwrap_or_default() as usize;
|
|
||||||
let width_param: Option<i64> = call.get_flag(state, stack, "width")?;
|
let width_param: Option<i64> = call.get_flag(state, stack, "width")?;
|
||||||
let expand: bool = call.has_flag("expand");
|
let expand: bool = call.has_flag("expand");
|
||||||
let expand_limit: Option<usize> = call.get_flag(state, stack, "expand-deep")?;
|
let expand_limit: Option<usize> = call.get_flag(state, stack, "expand-deep")?;
|
||||||
|
@ -232,13 +264,73 @@ fn parse_table_config(
|
||||||
flatten_separator,
|
flatten_separator,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
let theme =
|
||||||
|
get_theme_flag(call, state, stack)?.unwrap_or_else(|| get_config(state, stack).table_mode);
|
||||||
|
let index = get_index_flag(call, state, stack)?;
|
||||||
|
|
||||||
let term_width = get_width_param(width_param);
|
let term_width = get_width_param(width_param);
|
||||||
let cfg = TableConfig::new(row_offset, table_view, term_width, abbrivation);
|
let cfg = TableConfig::new(table_view, term_width, theme, abbrivation, index);
|
||||||
|
|
||||||
Ok(cfg)
|
Ok(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_index_flag(
|
||||||
|
call: &Call,
|
||||||
|
state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
) -> Result<Option<usize>, ShellError> {
|
||||||
|
let index: Option<Value> = call.get_flag(state, stack, "index")?;
|
||||||
|
let value = match index {
|
||||||
|
Some(value) => value,
|
||||||
|
None => return Ok(Some(0)),
|
||||||
|
};
|
||||||
|
|
||||||
|
match value {
|
||||||
|
Value::Bool { val, .. } => {
|
||||||
|
if val {
|
||||||
|
Ok(Some(0))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Int { val, internal_span } => {
|
||||||
|
if val < 0 {
|
||||||
|
Err(ShellError::UnsupportedInput {
|
||||||
|
msg: String::from("got a negative integer"),
|
||||||
|
input: val.to_string(),
|
||||||
|
msg_span: call.span(),
|
||||||
|
input_span: internal_span,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Ok(Some(val as usize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Value::Nothing { .. } => Ok(Some(0)),
|
||||||
|
_ => Err(ShellError::CantConvert {
|
||||||
|
to_type: String::from("index"),
|
||||||
|
from_type: String::new(),
|
||||||
|
span: call.span(),
|
||||||
|
help: Some(String::from("supported values: [bool, int, nothing]")),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_theme_flag(
|
||||||
|
call: &Call,
|
||||||
|
state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
) -> Result<Option<TableMode>, ShellError> {
|
||||||
|
call.get_flag(state, stack, "theme")?
|
||||||
|
.map(|theme: String| TableMode::from_str(&theme))
|
||||||
|
.transpose()
|
||||||
|
.map_err(|err| ShellError::CantConvert {
|
||||||
|
to_type: String::from("theme"),
|
||||||
|
from_type: String::from("string"),
|
||||||
|
span: call.span(),
|
||||||
|
help: Some(String::from(err)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
struct CmdInput<'a> {
|
struct CmdInput<'a> {
|
||||||
engine_state: &'a EngineState,
|
engine_state: &'a EngineState,
|
||||||
stack: &'a mut Stack,
|
stack: &'a mut Stack,
|
||||||
|
@ -366,7 +458,17 @@ fn handle_record(
|
||||||
}
|
}
|
||||||
|
|
||||||
let indent = (config.table_indent.left, config.table_indent.right);
|
let indent = (config.table_indent.left, config.table_indent.right);
|
||||||
let opts = TableOpts::new(&config, styles, ctrlc, span, 0, cfg.term_width, indent);
|
let opts = TableOpts::new(
|
||||||
|
&config,
|
||||||
|
styles,
|
||||||
|
ctrlc,
|
||||||
|
span,
|
||||||
|
cfg.term_width,
|
||||||
|
indent,
|
||||||
|
cfg.theme,
|
||||||
|
cfg.index.unwrap_or(0),
|
||||||
|
cfg.index.is_none(),
|
||||||
|
);
|
||||||
let result = build_table_kv(record, cfg.table_view, opts, span)?;
|
let result = build_table_kv(record, cfg.table_view, opts, span)?;
|
||||||
|
|
||||||
let result = match result {
|
let result = match result {
|
||||||
|
@ -581,6 +683,7 @@ struct PagingTableCreator {
|
||||||
elements_displayed: usize,
|
elements_displayed: usize,
|
||||||
reached_end: bool,
|
reached_end: bool,
|
||||||
cfg: TableConfig,
|
cfg: TableConfig,
|
||||||
|
row_offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PagingTableCreator {
|
impl PagingTableCreator {
|
||||||
|
@ -601,6 +704,7 @@ impl PagingTableCreator {
|
||||||
cfg,
|
cfg,
|
||||||
elements_displayed: 0,
|
elements_displayed: 0,
|
||||||
reached_end: false,
|
reached_end: false,
|
||||||
|
row_offset: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,9 +761,11 @@ impl PagingTableCreator {
|
||||||
style_comp,
|
style_comp,
|
||||||
self.ctrlc.clone(),
|
self.ctrlc.clone(),
|
||||||
self.head,
|
self.head,
|
||||||
self.cfg.row_offset,
|
|
||||||
self.cfg.term_width,
|
self.cfg.term_width,
|
||||||
(cfg.table_indent.left, cfg.table_indent.right),
|
(cfg.table_indent.left, cfg.table_indent.right),
|
||||||
|
self.cfg.theme,
|
||||||
|
self.cfg.index.unwrap_or(0) + self.row_offset,
|
||||||
|
self.cfg.index.is_none(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -770,7 +876,7 @@ impl Iterator for PagingTableCreator {
|
||||||
|
|
||||||
let table = self.build_table(batch);
|
let table = self.build_table(batch);
|
||||||
|
|
||||||
self.cfg.row_offset += idx;
|
self.row_offset += idx;
|
||||||
|
|
||||||
let config = get_config(&self.engine_state, &self.stack);
|
let config = get_config(&self.engine_state, &self.stack);
|
||||||
convert_table_to_output(table, &config, &self.ctrlc, self.cfg.term_width)
|
convert_table_to_output(table, &config, &self.ctrlc, self.cfg.term_width)
|
||||||
|
@ -858,7 +964,7 @@ fn create_empty_placeholder(
|
||||||
let out = TableOutput::new(table, false, false);
|
let out = TableOutput::new(table, false, false);
|
||||||
|
|
||||||
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
||||||
let config = create_nu_table_config(&config, style_computer, &out, false);
|
let config = create_nu_table_config(&config, style_computer, &out, false, TableMode::default());
|
||||||
|
|
||||||
out.table
|
out.table
|
||||||
.draw(config, termwidth)
|
.draw(config, termwidth)
|
||||||
|
|
|
@ -2812,3 +2812,52 @@ fn table_abbreviation_by_config_override() {
|
||||||
);
|
);
|
||||||
assert_eq!(actual.out, "╭───┬─────┬─────┬────────────────╮│ # │ a │ b │ c │├───┼─────┼─────┼────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ 5 │ [list 3 items] ││ 2 │ ... │ ... │ ... ││ 3 │ 1 │ 2 │ 3 ││ 4 │ 1 │ 2 │ 3 │╰───┴─────┴─────┴────────────────╯");
|
assert_eq!(actual.out, "╭───┬─────┬─────┬────────────────╮│ # │ a │ b │ c │├───┼─────┼─────┼────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ 5 │ [list 3 items] ││ 2 │ ... │ ... │ ... ││ 3 │ 1 │ 2 │ 3 ││ 4 │ 1 │ 2 │ 3 │╰───┴─────┴─────┴────────────────╯");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_theme_arg() {
|
||||||
|
let actual = nu!("[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3]] | table --width=80 --theme light");
|
||||||
|
assert_eq!(actual.out, " # a b c ──────────────────────────── 0 1 2 3 1 4 5 [list 3 items] 2 1 2 3 ");
|
||||||
|
|
||||||
|
let actual = nu!(theme_cmd(
|
||||||
|
"basic",
|
||||||
|
false,
|
||||||
|
"[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3]] | table --width=80 --theme light"
|
||||||
|
));
|
||||||
|
assert_eq!(actual.out, "─#───a───b─────────c──────── 0 1 2 3 1 4 5 [list 3 items] 2 1 2 3 ");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_index_arg() {
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic -i false");
|
||||||
|
assert_eq!(actual.out, "+---+----------------+| a | b |+---+----------------+| 1 | 2 |+---+----------------+| 2 | [list 2 items] |+---+----------------+");
|
||||||
|
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic -i true");
|
||||||
|
assert_eq!(actual.out, "+---+---+----------------+| # | a | b |+---+---+----------------+| 0 | 1 | 2 |+---+---+----------------+| 1 | 2 | [list 2 items] |+---+---+----------------+");
|
||||||
|
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic -i 10");
|
||||||
|
assert_eq!(actual.out, "+----+---+----------------+| # | a | b |+----+---+----------------+| 10 | 1 | 2 |+----+---+----------------+| 11 | 2 | [list 2 items] |+----+---+----------------+");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_expand_index_arg() {
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i false");
|
||||||
|
assert_eq!(actual.out, "+---+-------+| a | b |+---+-------+| 1 | 2 |+---+-------+| 2 | +---+ || | | 4 | || | +---+ || | | 4 | || | +---+ |+---+-------+");
|
||||||
|
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i true");
|
||||||
|
assert_eq!(actual.out, "+---+---+-----------+| # | a | b |+---+---+-----------+| 0 | 1 | 2 |+---+---+-----------+| 1 | 2 | +---+---+ || | | | 0 | 4 | || | | +---+---+ || | | | 1 | 4 | || | | +---+---+ |+---+---+-----------+");
|
||||||
|
|
||||||
|
let actual = nu!("[[a b]; [1 2] [2 [4 4]]] | table --width=80 --theme basic --expand -i 10");
|
||||||
|
assert_eq!(actual.out, "+----+---+------------+| # | a | b |+----+---+------------+| 10 | 1 | 2 |+----+---+------------+| 11 | 2 | +----+---+ || | | | 10 | 4 | || | | +----+---+ || | | | 11 | 4 | || | | +----+---+ |+----+---+------------+");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_list() {
|
||||||
|
let actual = nu!("table --list");
|
||||||
|
assert_eq!(actual.out, "╭────┬────────────────╮│ 0 │ basic ││ 1 │ compact ││ 2 │ compact_double ││ 3 │ default ││ 4 │ heavy ││ 5 │ light ││ 6 │ none ││ 7 │ reinforced ││ 8 │ rounded ││ 9 │ thin ││ 10 │ with_love ││ 11 │ psql ││ 12 │ markdown ││ 13 │ dots ││ 14 │ restructured ││ 15 │ ascii_rounded ││ 16 │ basic_compact │╰────┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!("ls | table --list");
|
||||||
|
assert_eq!(actual.out, "╭────┬────────────────╮│ 0 │ basic ││ 1 │ compact ││ 2 │ compact_double ││ 3 │ default ││ 4 │ heavy ││ 5 │ light ││ 6 │ none ││ 7 │ reinforced ││ 8 │ rounded ││ 9 │ thin ││ 10 │ with_love ││ 11 │ psql ││ 12 │ markdown ││ 13 │ dots ││ 14 │ restructured ││ 15 │ ascii_rounded ││ 16 │ basic_compact │╰────┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!("table --list --theme basic");
|
||||||
|
assert_eq!(actual.out, "╭────┬────────────────╮│ 0 │ basic ││ 1 │ compact ││ 2 │ compact_double ││ 3 │ default ││ 4 │ heavy ││ 5 │ light ││ 6 │ none ││ 7 │ reinforced ││ 8 │ rounded ││ 9 │ thin ││ 10 │ with_love ││ 11 │ psql ││ 12 │ markdown ││ 13 │ dots ││ 14 │ restructured ││ 15 │ ascii_rounded ││ 16 │ basic_compact │╰────┴────────────────╯");
|
||||||
|
}
|
||||||
|
|
|
@ -38,9 +38,11 @@ fn try_build_map(
|
||||||
style_computer,
|
style_computer,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
0,
|
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
(config.table_indent.left, config.table_indent.right),
|
(config.table_indent.left, config.table_indent.right),
|
||||||
|
config.table_mode,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
let result = ExpandedTable::new(None, false, String::new()).build_map(&record, opts);
|
let result = ExpandedTable::new(None, false, String::new()).build_map(&record, opts);
|
||||||
match result {
|
match result {
|
||||||
|
@ -63,10 +65,13 @@ fn try_build_list(
|
||||||
style_computer,
|
style_computer,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
0,
|
|
||||||
usize::MAX,
|
usize::MAX,
|
||||||
(config.table_indent.left, config.table_indent.right),
|
(config.table_indent.left, config.table_indent.right),
|
||||||
|
config.table_mode,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts);
|
let result = ExpandedTable::new(None, false, String::new()).build_list(&vals, opts);
|
||||||
match result {
|
match result {
|
||||||
Ok(Some(out)) => out,
|
Ok(Some(out)) => out,
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{record, Config, ShellError, Span, Value};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, Default)]
|
||||||
pub enum TableMode {
|
pub enum TableMode {
|
||||||
Basic,
|
Basic,
|
||||||
Thin,
|
Thin,
|
||||||
|
|
|
@ -17,9 +17,10 @@ pub fn create_nu_table_config(
|
||||||
comp: &StyleComputer,
|
comp: &StyleComputer,
|
||||||
out: &TableOutput,
|
out: &TableOutput,
|
||||||
expand: bool,
|
expand: bool,
|
||||||
|
mode: TableMode,
|
||||||
) -> NuTableConfig {
|
) -> NuTableConfig {
|
||||||
NuTableConfig {
|
NuTableConfig {
|
||||||
theme: load_theme_from_config(config),
|
theme: load_theme(mode),
|
||||||
with_footer: with_footer(config, out.with_header, out.table.count_rows()),
|
with_footer: with_footer(config, out.with_header, out.table.count_rows()),
|
||||||
with_index: out.with_index,
|
with_index: out.with_index,
|
||||||
with_header: out.with_header,
|
with_header: out.with_header,
|
||||||
|
@ -173,8 +174,8 @@ fn is_cfg_trim_keep_words(config: &Config) -> bool {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_theme_from_config(config: &Config) -> TableTheme {
|
pub fn load_theme(mode: TableMode) -> TableTheme {
|
||||||
match config.table_mode {
|
match mode {
|
||||||
TableMode::Basic => TableTheme::basic(),
|
TableMode::Basic => TableTheme::basic(),
|
||||||
TableMode::Thin => TableTheme::thin(),
|
TableMode::Thin => TableTheme::thin(),
|
||||||
TableMode::Light => TableTheme::light(),
|
TableMode::Light => TableTheme::light(),
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_protocol::{Config, Record, Value};
|
use nu_protocol::{Config, Record, TableMode, Value};
|
||||||
|
|
||||||
use crate::UnstructuredTable;
|
use crate::UnstructuredTable;
|
||||||
|
|
||||||
use crate::common::nu_value_to_string_clean;
|
use crate::common::nu_value_to_string_clean;
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{get_index_style, load_theme_from_config},
|
common::{get_index_style, load_theme},
|
||||||
StringResult, TableOpts,
|
StringResult, TableOpts,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,7 +13,13 @@ pub struct CollapsedTable;
|
||||||
|
|
||||||
impl CollapsedTable {
|
impl CollapsedTable {
|
||||||
pub fn build(value: Value, opts: TableOpts<'_>) -> StringResult {
|
pub fn build(value: Value, opts: TableOpts<'_>) -> StringResult {
|
||||||
collapsed_table(value, opts.config, opts.width, opts.style_computer)
|
collapsed_table(
|
||||||
|
value,
|
||||||
|
opts.config,
|
||||||
|
opts.width,
|
||||||
|
opts.style_computer,
|
||||||
|
opts.mode,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,10 +28,11 @@ fn collapsed_table(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
term_width: usize,
|
term_width: usize,
|
||||||
style_computer: &StyleComputer,
|
style_computer: &StyleComputer,
|
||||||
|
mode: TableMode,
|
||||||
) -> StringResult {
|
) -> StringResult {
|
||||||
colorize_value(&mut value, config, style_computer);
|
colorize_value(&mut value, config, style_computer);
|
||||||
|
|
||||||
let theme = load_theme_from_config(config);
|
let theme = load_theme(mode);
|
||||||
let mut table = UnstructuredTable::new(value, config);
|
let mut table = UnstructuredTable::new(value, config);
|
||||||
let is_empty = table.truncate(&theme, term_width);
|
let is_empty = table.truncate(&theme, term_width);
|
||||||
if is_empty {
|
if is_empty {
|
||||||
|
|
|
@ -3,17 +3,18 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
use nu_color_config::{Alignment, StyleComputer, TextStyle};
|
||||||
use nu_engine::column::get_columns;
|
use nu_engine::column::get_columns;
|
||||||
use nu_protocol::{Config, Record, ShellError, Span, TableIndexMode, Value};
|
use nu_protocol::{Config, Record, ShellError, Span, Value};
|
||||||
use tabled::grid::config::Position;
|
use tabled::grid::config::Position;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
common::{
|
||||||
create_nu_table_config, error_sign, get_header_style, get_index_style,
|
create_nu_table_config, error_sign, get_header_style, get_index_style, load_theme,
|
||||||
load_theme_from_config, nu_value_to_string, nu_value_to_string_clean,
|
nu_value_to_string, nu_value_to_string_clean, nu_value_to_string_colored, wrap_text,
|
||||||
nu_value_to_string_colored, wrap_text, NuText, StringResult, TableResult,
|
NuText, StringResult, TableResult, INDEX_COLUMN_NAME,
|
||||||
INDEX_COLUMN_NAME,
|
|
||||||
},
|
},
|
||||||
string_width, NuTable, NuTableCell, TableOpts, TableOutput,
|
string_width,
|
||||||
|
types::has_index,
|
||||||
|
NuTable, NuTableCell, TableOpts, TableOutput,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -83,11 +84,8 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
||||||
|
|
||||||
let headers = get_columns(input);
|
let headers = get_columns(input);
|
||||||
|
|
||||||
let with_index = match cfg.opts.config.table_index_mode {
|
let with_index = has_index(&cfg.opts, &headers);
|
||||||
TableIndexMode::Always => true,
|
let row_offset = cfg.opts.index_offset;
|
||||||
TableIndexMode::Never => false,
|
|
||||||
TableIndexMode::Auto => headers.iter().any(|header| header == INDEX_COLUMN_NAME),
|
|
||||||
};
|
|
||||||
|
|
||||||
// The header with the INDEX is removed from the table headers since
|
// The header with the INDEX is removed from the table headers since
|
||||||
// it is added to the natural table index
|
// it is added to the natural table index
|
||||||
|
@ -115,7 +113,7 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
||||||
return Err(*error.clone());
|
return Err(*error.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
let index = row + cfg.opts.row_offset;
|
let index = row + row_offset;
|
||||||
let text = item
|
let text = item
|
||||||
.as_record()
|
.as_record()
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -343,7 +341,7 @@ fn expanded_table_list(input: &[Value], cfg: Cfg<'_>) -> TableResult {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
|
fn expanded_table_kv(record: &Record, cfg: Cfg<'_>) -> StringResult {
|
||||||
let theme = load_theme_from_config(cfg.opts.config);
|
let theme = load_theme(cfg.opts.mode);
|
||||||
let key_width = record
|
let key_width = record
|
||||||
.columns()
|
.columns()
|
||||||
.map(|col| string_width(col))
|
.map(|col| string_width(col))
|
||||||
|
@ -556,7 +554,8 @@ fn dive_options<'b>(cfg: &Cfg<'b>, span: Span) -> Cfg<'b> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn maybe_expand_table(out: TableOutput, term_width: usize, opts: &TableOpts<'_>) -> StringResult {
|
fn maybe_expand_table(out: TableOutput, term_width: usize, opts: &TableOpts<'_>) -> StringResult {
|
||||||
let mut table_config = create_nu_table_config(opts.config, opts.style_computer, &out, false);
|
let mut table_config =
|
||||||
|
create_nu_table_config(opts.config, opts.style_computer, &out, false, opts.mode);
|
||||||
let total_width = out.table.total_width(&table_config);
|
let total_width = out.table.total_width(&table_config);
|
||||||
if total_width < term_width {
|
if total_width < term_width {
|
||||||
const EXPAND_THRESHOLD: f32 = 0.80;
|
const EXPAND_THRESHOLD: f32 = 0.80;
|
||||||
|
@ -577,7 +576,13 @@ fn set_data_styles(table: &mut NuTable, styles: HashMap<Position, TextStyle>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_table_cfg(cfg: &Cfg<'_>, out: &TableOutput) -> crate::NuTableConfig {
|
fn create_table_cfg(cfg: &Cfg<'_>, out: &TableOutput) -> crate::NuTableConfig {
|
||||||
create_nu_table_config(cfg.opts.config, cfg.opts.style_computer, out, false)
|
create_nu_table_config(
|
||||||
|
cfg.opts.config,
|
||||||
|
cfg.opts.style_computer,
|
||||||
|
out,
|
||||||
|
false,
|
||||||
|
cfg.opts.mode,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn value_to_string(value: &Value, cfg: &Cfg<'_>) -> String {
|
fn value_to_string(value: &Value, cfg: &Cfg<'_>) -> String {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use nu_color_config::TextStyle;
|
use nu_color_config::TextStyle;
|
||||||
use nu_engine::column::get_columns;
|
use nu_engine::column::get_columns;
|
||||||
use nu_protocol::{Config, Record, ShellError, TableIndexMode, Value};
|
use nu_protocol::{Config, Record, ShellError, Value};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
clean_charset, colorize_space,
|
clean_charset, colorize_space,
|
||||||
|
@ -11,6 +11,8 @@ use crate::{
|
||||||
NuTable, NuTableCell, StringResult, TableOpts, TableOutput, TableResult,
|
NuTable, NuTableCell, StringResult, TableOpts, TableOutput, TableResult,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::has_index;
|
||||||
|
|
||||||
pub struct JustTable;
|
pub struct JustTable;
|
||||||
|
|
||||||
impl JustTable {
|
impl JustTable {
|
||||||
|
@ -24,7 +26,7 @@ impl JustTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, ShellError> {
|
fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>, ShellError> {
|
||||||
match table(input, opts.row_offset, opts.clone())? {
|
match table(input, &opts)? {
|
||||||
Some(mut out) => {
|
Some(mut out) => {
|
||||||
let left = opts.config.table_indent.left;
|
let left = opts.config.table_indent.left;
|
||||||
let right = opts.config.table_indent.right;
|
let right = opts.config.table_indent.right;
|
||||||
|
@ -33,7 +35,7 @@ fn create_table(input: &[Value], opts: TableOpts<'_>) -> Result<Option<String>,
|
||||||
colorize_space(out.table.get_records_mut(), opts.style_computer);
|
colorize_space(out.table.get_records_mut(), opts.style_computer);
|
||||||
|
|
||||||
let table_config =
|
let table_config =
|
||||||
create_nu_table_config(opts.config, opts.style_computer, &out, false);
|
create_nu_table_config(opts.config, opts.style_computer, &out, false, opts.mode);
|
||||||
Ok(out.table.draw(table_config, opts.width))
|
Ok(out.table.draw(table_config, opts.width))
|
||||||
}
|
}
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
|
@ -65,23 +67,21 @@ fn kv_table(record: &Record, opts: TableOpts<'_>) -> StringResult {
|
||||||
let right = opts.config.table_indent.right;
|
let right = opts.config.table_indent.right;
|
||||||
out.table.set_indent(left, right);
|
out.table.set_indent(left, right);
|
||||||
|
|
||||||
let table_config = create_nu_table_config(opts.config, opts.style_computer, &out, false);
|
let table_config =
|
||||||
|
create_nu_table_config(opts.config, opts.style_computer, &out, false, opts.mode);
|
||||||
let table = out.table.draw(table_config, opts.width);
|
let table = out.table.draw(table_config, opts.width);
|
||||||
|
|
||||||
Ok(table)
|
Ok(table)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table(input: &[Value], row_offset: usize, opts: TableOpts<'_>) -> TableResult {
|
fn table(input: &[Value], opts: &TableOpts<'_>) -> TableResult {
|
||||||
if input.is_empty() {
|
if input.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut headers = get_columns(input);
|
let mut headers = get_columns(input);
|
||||||
let with_index = match opts.config.table_index_mode {
|
let with_index = has_index(opts, &headers);
|
||||||
TableIndexMode::Always => true,
|
let row_offset = opts.index_offset;
|
||||||
TableIndexMode::Never => false,
|
|
||||||
TableIndexMode::Auto => headers.iter().any(|header| header == INDEX_COLUMN_NAME),
|
|
||||||
};
|
|
||||||
|
|
||||||
let with_header = !headers.is_empty();
|
let with_header = !headers.is_empty();
|
||||||
if !with_header {
|
if !with_header {
|
||||||
|
@ -112,7 +112,7 @@ fn to_table_with_header(
|
||||||
headers: Vec<String>,
|
headers: Vec<String>,
|
||||||
with_index: bool,
|
with_index: bool,
|
||||||
row_offset: usize,
|
row_offset: usize,
|
||||||
opts: TableOpts<'_>,
|
opts: &TableOpts<'_>,
|
||||||
) -> Result<Option<NuTable>, ShellError> {
|
) -> Result<Option<NuTable>, ShellError> {
|
||||||
let count_rows = input.len() + 1;
|
let count_rows = input.len() + 1;
|
||||||
let count_columns = headers.len();
|
let count_columns = headers.len();
|
||||||
|
@ -140,7 +140,7 @@ fn to_table_with_header(
|
||||||
|
|
||||||
let skip_index = usize::from(with_index);
|
let skip_index = usize::from(with_index);
|
||||||
for (col, header) in headers.iter().enumerate().skip(skip_index) {
|
for (col, header) in headers.iter().enumerate().skip(skip_index) {
|
||||||
let (text, style) = get_string_value_with_header(item, header, &opts);
|
let (text, style) = get_string_value_with_header(item, header, opts);
|
||||||
|
|
||||||
table.insert((row + 1, col), text);
|
table.insert((row + 1, col), text);
|
||||||
table.insert_style((row + 1, col), style);
|
table.insert_style((row + 1, col), style);
|
||||||
|
@ -154,7 +154,7 @@ fn to_table_with_no_header(
|
||||||
input: &[Value],
|
input: &[Value],
|
||||||
with_index: bool,
|
with_index: bool,
|
||||||
row_offset: usize,
|
row_offset: usize,
|
||||||
opts: TableOpts<'_>,
|
opts: &TableOpts<'_>,
|
||||||
) -> Result<Option<NuTable>, ShellError> {
|
) -> Result<Option<NuTable>, ShellError> {
|
||||||
let mut table = NuTable::new(input.len(), with_index as usize + 1);
|
let mut table = NuTable::new(input.len(), with_index as usize + 1);
|
||||||
table.set_index_style(get_index_style(opts.style_computer));
|
table.set_index_style(get_index_style(opts.style_computer));
|
||||||
|
@ -173,7 +173,7 @@ fn to_table_with_no_header(
|
||||||
table.insert((row, 0), text);
|
table.insert((row, 0), text);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (text, style) = get_string_value(item, &opts);
|
let (text, style) = get_string_value(item, opts);
|
||||||
|
|
||||||
let pos = (row, with_index as usize);
|
let pos = (row, with_index as usize);
|
||||||
table.insert(pos, text);
|
table.insert(pos, text);
|
||||||
|
|
|
@ -8,9 +8,9 @@ pub use collapse::CollapsedTable;
|
||||||
pub use expanded::ExpandedTable;
|
pub use expanded::ExpandedTable;
|
||||||
pub use general::JustTable;
|
pub use general::JustTable;
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_protocol::{Config, Span};
|
use nu_protocol::{Config, Span, TableIndexMode, TableMode};
|
||||||
|
|
||||||
use crate::NuTable;
|
use crate::{common::INDEX_COLUMN_NAME, NuTable};
|
||||||
|
|
||||||
pub struct TableOutput {
|
pub struct TableOutput {
|
||||||
pub table: NuTable,
|
pub table: NuTable,
|
||||||
|
@ -34,29 +34,46 @@ pub struct TableOpts<'a> {
|
||||||
config: &'a Config,
|
config: &'a Config,
|
||||||
style_computer: &'a StyleComputer<'a>,
|
style_computer: &'a StyleComputer<'a>,
|
||||||
span: Span,
|
span: Span,
|
||||||
row_offset: usize,
|
|
||||||
width: usize,
|
width: usize,
|
||||||
indent: (usize, usize),
|
indent: (usize, usize),
|
||||||
|
mode: TableMode,
|
||||||
|
index_offset: usize,
|
||||||
|
index_remove: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TableOpts<'a> {
|
impl<'a> TableOpts<'a> {
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
config: &'a Config,
|
config: &'a Config,
|
||||||
style_computer: &'a StyleComputer<'a>,
|
style_computer: &'a StyleComputer<'a>,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
span: Span,
|
span: Span,
|
||||||
row_offset: usize,
|
|
||||||
width: usize,
|
width: usize,
|
||||||
indent: (usize, usize),
|
indent: (usize, usize),
|
||||||
|
mode: TableMode,
|
||||||
|
index_offset: usize,
|
||||||
|
index_remove: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ctrlc,
|
ctrlc,
|
||||||
config,
|
config,
|
||||||
style_computer,
|
style_computer,
|
||||||
span,
|
span,
|
||||||
row_offset,
|
|
||||||
indent,
|
indent,
|
||||||
width,
|
width,
|
||||||
|
mode,
|
||||||
|
index_offset,
|
||||||
|
index_remove,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_index(opts: &TableOpts<'_>, headers: &[String]) -> bool {
|
||||||
|
let with_index = match opts.config.table_index_mode {
|
||||||
|
TableIndexMode::Always => true,
|
||||||
|
TableIndexMode::Never => false,
|
||||||
|
TableIndexMode::Auto => headers.iter().any(|header| header == INDEX_COLUMN_NAME),
|
||||||
|
};
|
||||||
|
|
||||||
|
with_index && !opts.index_remove
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue