forked from extern/nushell
nu-table: Add table option --abbreviated
(#10399)
- Added `--abbreviated`/`-a` option - Adedd `abbreviate_if_longer_than` config opt for it. ```nu ls | table -a 3 ``` ``` ╭───┬────────────────────┬──────┬───────────┬──────────────╮ │ # │ name │ type │ size │ modified │ ├───┼────────────────────┼──────┼───────────┼──────────────┤ │ 0 │ CODE_OF_CONDUCT.md │ file │ 3.4 KiB │ 4 days ago │ │ 1 │ CONTRIBUTING.md │ file │ 18.3 KiB │ 2 weeks ago │ │ 2 │ Cargo.lock │ file │ 144.3 KiB │ 15 hours ago │ │ 3 │ ... │ ... │ ... │ ... │ │ 4 │ tests │ dir │ 4.0 KiB │ 4 months ago │ │ 5 │ toolkit.nu │ file │ 14.6 KiB │ 5 days ago │ │ 6 │ wix │ dir │ 4.0 KiB │ 2 months ago │ ╰───┴────────────────────┴──────┴───────────┴──────────────╯ ``` ```nu $env | table -a 3 ``` ``` ╭──────────────────┬──────────────────────────────────────────────────────────────────────────╮ │ BROWSER │ firefox │ │ CARGO │ /home/maxim/.rustup/toolchains/1.70.0-x86_64-unknown-linux-gnu/bin/cargo │ │ CARGO_HOME │ /home/maxim/.cargo │ │ ... │ ... │ │ XDG_SESSION_TYPE │ x11 │ │ XDG_VTNR │ 7 │ │ _ │ /home/maxim/.cargo/bin/cargo │ ╰──────────────────┴──────────────────────────────────────────────────────────────────────────╯ ``` close #10393 PS: Maybe as a separate issue (good candidate for `GOOD FIRST ISSUE`) add a config option to change a default `...` truncation sign to a custom? (which would be applicable not only for `--abbreviated` but all kind of tables) --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
parent
4ae53d93fb
commit
7cfd4d2cfa
@ -94,6 +94,12 @@ impl Command for Table {
|
|||||||
"expand the table structure in collapse mode.\nBe aware collapse mode currently doesn't support width control",
|
"expand the table structure in collapse mode.\nBe aware collapse mode currently doesn't support width control",
|
||||||
Some('c'),
|
Some('c'),
|
||||||
)
|
)
|
||||||
|
.named(
|
||||||
|
"abbreviated",
|
||||||
|
SyntaxShape::Int,
|
||||||
|
"abbreviate the data in the table by truncating the middle part and only showing amount provided on top and bottom",
|
||||||
|
Some('a'),
|
||||||
|
)
|
||||||
.category(Category::Viewers)
|
.category(Category::Viewers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,33 +110,13 @@ impl Command for Table {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let start_num: Option<i64> = call.get_flag(engine_state, stack, "start-number")?;
|
let list_themes: bool = call.has_flag("list");
|
||||||
let row_offset = start_num.unwrap_or_default() as usize;
|
let cfg = parse_table_config(call, engine_state, stack)?;
|
||||||
let list: bool = call.has_flag("list");
|
let input = CmdInput::new(engine_state, stack, call, input);
|
||||||
|
|
||||||
let width_param: Option<i64> = call.get_flag(engine_state, stack, "width")?;
|
|
||||||
|
|
||||||
let expand: bool = call.has_flag("expand");
|
|
||||||
let expand_limit: Option<usize> = call.get_flag(engine_state, stack, "expand-deep")?;
|
|
||||||
let collapse: bool = call.has_flag("collapse");
|
|
||||||
let flatten: bool = call.has_flag("flatten");
|
|
||||||
let flatten_separator: Option<String> =
|
|
||||||
call.get_flag(engine_state, stack, "flatten-separator")?;
|
|
||||||
|
|
||||||
let table_view = match (expand, collapse) {
|
|
||||||
(false, false) => TableView::General,
|
|
||||||
(_, true) => TableView::Collapsed,
|
|
||||||
(true, _) => TableView::Expanded {
|
|
||||||
limit: expand_limit,
|
|
||||||
flatten,
|
|
||||||
flatten_separator,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 {
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -140,15 +126,7 @@ impl Command for Table {
|
|||||||
let _ = nu_utils::enable_vt_processing();
|
let _ = nu_utils::enable_vt_processing();
|
||||||
}
|
}
|
||||||
|
|
||||||
handle_table_command(
|
handle_table_command(input, cfg)
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
call,
|
|
||||||
input,
|
|
||||||
row_offset,
|
|
||||||
table_view,
|
|
||||||
width_param,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -214,86 +192,138 @@ impl Command for Table {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_table_command(
|
#[derive(Debug, Clone)]
|
||||||
engine_state: &EngineState,
|
struct TableConfig {
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
input: PipelineData,
|
|
||||||
row_offset: usize,
|
row_offset: usize,
|
||||||
table_view: TableView,
|
table_view: TableView,
|
||||||
term_width: Option<i64>,
|
term_width: usize,
|
||||||
) -> Result<PipelineData, ShellError> {
|
abbreviation: Option<usize>,
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
}
|
||||||
let config = get_config(engine_state, stack);
|
|
||||||
|
|
||||||
let span = input.span().unwrap_or(call.head);
|
impl TableConfig {
|
||||||
match input {
|
fn new(
|
||||||
PipelineData::ExternalStream { .. } => Ok(input),
|
row_offset: usize,
|
||||||
PipelineData::Value(Value::Binary { val, .. }, ..) => Ok(PipelineData::ExternalStream {
|
table_view: TableView,
|
||||||
stdout: Some(RawStream::new(
|
term_width: usize,
|
||||||
Box::new(if call.redirect_stdout {
|
abbreviation: Option<usize>,
|
||||||
vec![Ok(val)].into_iter()
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
row_offset,
|
||||||
|
table_view,
|
||||||
|
term_width,
|
||||||
|
abbreviation,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_table_config(
|
||||||
|
call: &Call,
|
||||||
|
state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
) -> 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 expand: bool = call.has_flag("expand");
|
||||||
|
let expand_limit: Option<usize> = call.get_flag(state, stack, "expand-deep")?;
|
||||||
|
let collapse: bool = call.has_flag("collapse");
|
||||||
|
let flatten: bool = call.has_flag("flatten");
|
||||||
|
let flatten_separator: Option<String> = call.get_flag(state, stack, "flatten-separator")?;
|
||||||
|
let abbrivation: Option<usize> = call
|
||||||
|
.get_flag(state, stack, "abbreviated")?
|
||||||
|
.or_else(|| get_config(state, stack).table_abbreviation_threshold);
|
||||||
|
let table_view = match (expand, collapse) {
|
||||||
|
(false, false) => TableView::General,
|
||||||
|
(_, true) => TableView::Collapsed,
|
||||||
|
(true, _) => TableView::Expanded {
|
||||||
|
limit: expand_limit,
|
||||||
|
flatten,
|
||||||
|
flatten_separator,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let term_width = get_width_param(width_param);
|
||||||
|
let cfg = TableConfig::new(row_offset, table_view, term_width, abbrivation);
|
||||||
|
|
||||||
|
Ok(cfg)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CmdInput<'a> {
|
||||||
|
engine_state: &'a EngineState,
|
||||||
|
stack: &'a mut Stack,
|
||||||
|
call: &'a Call,
|
||||||
|
data: PipelineData,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CmdInput<'a> {
|
||||||
|
fn new(
|
||||||
|
engine_state: &'a EngineState,
|
||||||
|
stack: &'a mut Stack,
|
||||||
|
call: &'a Call,
|
||||||
|
data: PipelineData,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
call,
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_table_command(
|
||||||
|
mut input: CmdInput<'_>,
|
||||||
|
cfg: TableConfig,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let span = input.data.span().unwrap_or(input.call.head);
|
||||||
|
match input.data {
|
||||||
|
PipelineData::ExternalStream { .. } => Ok(input.data),
|
||||||
|
PipelineData::Value(Value::Binary { val, .. }, ..) => {
|
||||||
|
let stream_list = if input.call.redirect_stdout {
|
||||||
|
vec![Ok(val)]
|
||||||
} else {
|
} else {
|
||||||
vec![Ok(format!("{}\n", nu_pretty_hex::pretty_hex(&val))
|
let hex = format!("{}\n", nu_pretty_hex::pretty_hex(&val))
|
||||||
.as_bytes()
|
.as_bytes()
|
||||||
.to_vec())]
|
.to_vec();
|
||||||
.into_iter()
|
vec![Ok(hex)]
|
||||||
}),
|
};
|
||||||
|
|
||||||
|
let ctrlc = input.engine_state.ctrlc.clone();
|
||||||
|
let stream = RawStream::new(
|
||||||
|
Box::new(stream_list.into_iter()),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
call.head,
|
input.call.head,
|
||||||
None,
|
None,
|
||||||
)),
|
);
|
||||||
|
|
||||||
|
Ok(PipelineData::ExternalStream {
|
||||||
|
stdout: Some(stream),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: call.head,
|
span: input.call.head,
|
||||||
metadata: None,
|
metadata: None,
|
||||||
trim_end_newline: false,
|
trim_end_newline: false,
|
||||||
}),
|
})
|
||||||
|
}
|
||||||
// None of these two receive a StyleComputer because handle_row_stream() can produce it by itself using engine_state and stack.
|
// None of these two receive a StyleComputer because handle_row_stream() can produce it by itself using engine_state and stack.
|
||||||
PipelineData::Value(Value::List { vals, .. }, metadata) => handle_row_stream(
|
PipelineData::Value(Value::List { vals, .. }, metadata) => {
|
||||||
engine_state,
|
let ctrlc = input.engine_state.ctrlc.clone();
|
||||||
stack,
|
let stream = ListStream::from_stream(vals.into_iter(), ctrlc);
|
||||||
ListStream::from_stream(vals.into_iter(), ctrlc.clone()),
|
input.data = PipelineData::Empty;
|
||||||
call,
|
|
||||||
row_offset,
|
|
||||||
ctrlc,
|
|
||||||
metadata,
|
|
||||||
),
|
|
||||||
PipelineData::ListStream(stream, metadata) => handle_row_stream(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
stream,
|
|
||||||
call,
|
|
||||||
row_offset,
|
|
||||||
ctrlc,
|
|
||||||
metadata,
|
|
||||||
),
|
|
||||||
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
|
||||||
let term_width = get_width_param(term_width);
|
|
||||||
|
|
||||||
handle_record(
|
handle_row_stream(input, cfg, stream, metadata)
|
||||||
val,
|
}
|
||||||
span,
|
PipelineData::ListStream(stream, metadata) => {
|
||||||
engine_state,
|
input.data = PipelineData::Empty;
|
||||||
stack,
|
handle_row_stream(input, cfg, stream, metadata)
|
||||||
call,
|
}
|
||||||
table_view,
|
PipelineData::Value(Value::Record { val, .. }, ..) => {
|
||||||
term_width,
|
input.data = PipelineData::Empty;
|
||||||
ctrlc,
|
handle_record(input, cfg, val)
|
||||||
&config,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
PipelineData::Value(Value::LazyRecord { val, .. }, ..) => {
|
PipelineData::Value(Value::LazyRecord { val, .. }, ..) => {
|
||||||
let collected = val.collect()?.into_pipeline_data();
|
input.data = val.collect()?.into_pipeline_data();
|
||||||
handle_table_command(
|
handle_table_command(input, cfg)
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
call,
|
|
||||||
collected,
|
|
||||||
row_offset,
|
|
||||||
table_view,
|
|
||||||
term_width,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
||||||
// Propagate this error outward, so that it goes to stderr
|
// Propagate this error outward, so that it goes to stderr
|
||||||
@ -302,72 +332,54 @@ fn handle_table_command(
|
|||||||
}
|
}
|
||||||
PipelineData::Value(Value::CustomValue { val, .. }, ..) => {
|
PipelineData::Value(Value::CustomValue { val, .. }, ..) => {
|
||||||
let base_pipeline = val.to_base_value(span)?.into_pipeline_data();
|
let base_pipeline = val.to_base_value(span)?.into_pipeline_data();
|
||||||
Table.run(engine_state, stack, call, base_pipeline)
|
Table.run(input.engine_state, input.stack, input.call, base_pipeline)
|
||||||
|
}
|
||||||
|
PipelineData::Value(Value::Range { val, .. }, metadata) => {
|
||||||
|
let ctrlc = input.engine_state.ctrlc.clone();
|
||||||
|
let stream = ListStream::from_stream(val.into_range_iter(ctrlc.clone())?, ctrlc);
|
||||||
|
input.data = PipelineData::Empty;
|
||||||
|
handle_row_stream(input, cfg, stream, metadata)
|
||||||
}
|
}
|
||||||
PipelineData::Value(Value::Range { val, .. }, metadata) => handle_row_stream(
|
|
||||||
engine_state,
|
|
||||||
stack,
|
|
||||||
ListStream::from_stream(val.into_range_iter(ctrlc.clone())?, ctrlc.clone()),
|
|
||||||
call,
|
|
||||||
row_offset,
|
|
||||||
ctrlc,
|
|
||||||
metadata,
|
|
||||||
),
|
|
||||||
x => Ok(x),
|
x => Ok(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn supported_table_modes() -> Vec<Value> {
|
|
||||||
vec![
|
|
||||||
Value::test_string("basic"),
|
|
||||||
Value::test_string("compact"),
|
|
||||||
Value::test_string("compact_double"),
|
|
||||||
Value::test_string("default"),
|
|
||||||
Value::test_string("heavy"),
|
|
||||||
Value::test_string("light"),
|
|
||||||
Value::test_string("none"),
|
|
||||||
Value::test_string("reinforced"),
|
|
||||||
Value::test_string("rounded"),
|
|
||||||
Value::test_string("thin"),
|
|
||||||
Value::test_string("with_love"),
|
|
||||||
Value::test_string("psql"),
|
|
||||||
Value::test_string("markdown"),
|
|
||||||
Value::test_string("dots"),
|
|
||||||
Value::test_string("restructured"),
|
|
||||||
Value::test_string("ascii_rounded"),
|
|
||||||
Value::test_string("basic_compact"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_record(
|
fn handle_record(
|
||||||
record: Record,
|
input: CmdInput,
|
||||||
span: Span,
|
cfg: TableConfig,
|
||||||
engine_state: &EngineState,
|
mut record: Record,
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
table_view: TableView,
|
|
||||||
term_width: usize,
|
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
|
||||||
config: &Config,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
// Create a StyleComputer to compute styles for each value in the table.
|
let config = get_config(input.engine_state, input.stack);
|
||||||
let style_computer = &StyleComputer::from_config(engine_state, stack);
|
let span = input.data.span().unwrap_or(input.call.head);
|
||||||
|
let styles = &StyleComputer::from_config(input.engine_state, input.stack);
|
||||||
|
let ctrlc = input.engine_state.ctrlc.clone();
|
||||||
let ctrlc1 = ctrlc.clone();
|
let ctrlc1 = ctrlc.clone();
|
||||||
|
|
||||||
let result = if record.is_empty() {
|
if record.is_empty() {
|
||||||
create_empty_placeholder("record", term_width, engine_state, stack)
|
let value =
|
||||||
} else {
|
create_empty_placeholder("record", cfg.term_width, input.engine_state, input.stack);
|
||||||
let indent = (config.table_indent.left, config.table_indent.right);
|
let value = Value::string(value, span);
|
||||||
let opts = TableOpts::new(config, style_computer, ctrlc, span, 0, term_width, indent);
|
return Ok(value.into_pipeline_data());
|
||||||
let result = build_table_kv(record, table_view, opts, span)?;
|
|
||||||
match result {
|
|
||||||
Some(output) => maybe_strip_color(output, config),
|
|
||||||
None => report_unsuccessful_output(ctrlc1, term_width),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let val = Value::string(result, call.head);
|
if let Some(limit) = cfg.abbreviation {
|
||||||
|
if record.cols.len() > limit * 2 + 1 {
|
||||||
|
record.cols = abbreviate_list(&record.cols, limit, String::from("..."));
|
||||||
|
record.vals =
|
||||||
|
abbreviate_list(&record.vals, limit, Value::string("...", Span::unknown()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let indent = (config.table_indent.left, config.table_indent.right);
|
||||||
|
let opts = TableOpts::new(&config, styles, ctrlc, span, 0, cfg.term_width, indent);
|
||||||
|
let result = build_table_kv(record, cfg.table_view, opts, span)?;
|
||||||
|
|
||||||
|
let result = match result {
|
||||||
|
Some(output) => maybe_strip_color(output, &config),
|
||||||
|
None => report_unsuccessful_output(ctrlc1, cfg.term_width),
|
||||||
|
};
|
||||||
|
|
||||||
|
let val = Value::string(result, span);
|
||||||
|
|
||||||
Ok(val.into_pipeline_data())
|
Ok(val.into_pipeline_data())
|
||||||
}
|
}
|
||||||
@ -429,27 +441,31 @@ fn build_table_batch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handle_row_stream(
|
fn handle_row_stream(
|
||||||
engine_state: &EngineState,
|
input: CmdInput<'_>,
|
||||||
stack: &mut Stack,
|
cfg: TableConfig,
|
||||||
stream: ListStream,
|
stream: ListStream,
|
||||||
call: &Call,
|
|
||||||
row_offset: usize,
|
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
|
||||||
metadata: Option<Box<PipelineMetadata>>,
|
metadata: Option<Box<PipelineMetadata>>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let ctrlc = input.engine_state.ctrlc.clone();
|
||||||
|
|
||||||
let stream = match metadata.as_deref() {
|
let stream = match metadata.as_deref() {
|
||||||
// First, `ls` sources:
|
// First, `ls` sources:
|
||||||
Some(PipelineMetadata {
|
Some(PipelineMetadata {
|
||||||
data_source: DataSource::Ls,
|
data_source: DataSource::Ls,
|
||||||
}) => {
|
}) => {
|
||||||
let config = get_config(engine_state, stack);
|
let config = get_config(input.engine_state, input.stack);
|
||||||
let ctrlc = ctrlc.clone();
|
let ctrlc = ctrlc.clone();
|
||||||
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") {
|
let ls_colors_env_str = match input.stack.get_env_var(input.engine_state, "LS_COLORS") {
|
||||||
Some(v) => Some(env_to_string("LS_COLORS", &v, engine_state, stack)?),
|
Some(v) => Some(env_to_string(
|
||||||
|
"LS_COLORS",
|
||||||
|
&v,
|
||||||
|
input.engine_state,
|
||||||
|
input.stack,
|
||||||
|
)?),
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
let ls_colors = get_ls_colors(ls_colors_env_str);
|
let ls_colors = get_ls_colors(ls_colors_env_str);
|
||||||
|
let span = input.call.head;
|
||||||
|
|
||||||
ListStream::from_stream(
|
ListStream::from_stream(
|
||||||
stream.map(move |mut x| match &mut x {
|
stream.map(move |mut x| match &mut x {
|
||||||
@ -459,7 +475,7 @@ fn handle_row_stream(
|
|||||||
while idx < record.len() {
|
while idx < record.len() {
|
||||||
// Only the name column gets special colors, for now
|
// Only the name column gets special colors, for now
|
||||||
if record.cols[idx] == "name" {
|
if record.cols[idx] == "name" {
|
||||||
let span = record.vals.get(idx).map(|v| v.span()).unwrap_or(head);
|
let span = record.vals.get(idx).map(|v| v.span()).unwrap_or(span);
|
||||||
if let Some(Value::String { val, .. }) = record.vals.get(idx) {
|
if let Some(Value::String { val, .. }) = record.vals.get(idx) {
|
||||||
let val = render_path_name(val, &config, &ls_colors, span);
|
let val = render_path_name(val, &config, &ls_colors, span);
|
||||||
if let Some(val) = val {
|
if let Some(val) = val {
|
||||||
@ -483,6 +499,7 @@ fn handle_row_stream(
|
|||||||
data_source: DataSource::HtmlThemes,
|
data_source: DataSource::HtmlThemes,
|
||||||
}) => {
|
}) => {
|
||||||
let ctrlc = ctrlc.clone();
|
let ctrlc = ctrlc.clone();
|
||||||
|
let span = input.call.head;
|
||||||
|
|
||||||
ListStream::from_stream(
|
ListStream::from_stream(
|
||||||
stream.map(move |mut x| match &mut x {
|
stream.map(move |mut x| match &mut x {
|
||||||
@ -494,7 +511,7 @@ fn handle_row_stream(
|
|||||||
// Simple routine to grab the hex code, convert to a style,
|
// Simple routine to grab the hex code, convert to a style,
|
||||||
// then place it in a new Value::String.
|
// then place it in a new Value::String.
|
||||||
|
|
||||||
let span = record.vals.get(idx).map(|v| v.span()).unwrap_or(head);
|
let span = record.vals.get(idx).map(|v| v.span()).unwrap_or(span);
|
||||||
if let Some(Value::String { val, .. }) = record.vals.get(idx) {
|
if let Some(Value::String { val, .. }) = record.vals.get(idx) {
|
||||||
let s = match color_from_hex(val) {
|
let s = match color_from_hex(val) {
|
||||||
Ok(c) => match c {
|
Ok(c) => match c {
|
||||||
@ -523,48 +540,23 @@ fn handle_row_stream(
|
|||||||
_ => stream,
|
_ => stream,
|
||||||
};
|
};
|
||||||
|
|
||||||
let head = call.head;
|
let paginator = PagingTableCreator::new(
|
||||||
let width_param: Option<i64> = call.get_flag(engine_state, stack, "width")?;
|
input.call.head,
|
||||||
|
|
||||||
let collapse: bool = call.has_flag("collapse");
|
|
||||||
|
|
||||||
let expand: bool = call.has_flag("expand");
|
|
||||||
let limit: Option<usize> = call.get_flag(engine_state, stack, "expand-deep")?;
|
|
||||||
let flatten: bool = call.has_flag("flatten");
|
|
||||||
let flatten_separator: Option<String> =
|
|
||||||
call.get_flag(engine_state, stack, "flatten-separator")?;
|
|
||||||
|
|
||||||
let table_view = match (expand, collapse) {
|
|
||||||
(_, true) => TableView::Collapsed,
|
|
||||||
(true, _) => TableView::Expanded {
|
|
||||||
flatten,
|
|
||||||
flatten_separator,
|
|
||||||
limit,
|
|
||||||
},
|
|
||||||
_ => TableView::General,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(PipelineData::ExternalStream {
|
|
||||||
stdout: Some(RawStream::new(
|
|
||||||
Box::new(PagingTableCreator::new(
|
|
||||||
head,
|
|
||||||
stream,
|
stream,
|
||||||
// These are passed in as a way to have PagingTable create StyleComputers
|
// These are passed in as a way to have PagingTable create StyleComputers
|
||||||
// for the values it outputs. Because engine_state is passed in, config doesn't need to.
|
// for the values it outputs. Because engine_state is passed in, config doesn't need to.
|
||||||
engine_state.clone(),
|
input.engine_state.clone(),
|
||||||
stack.clone(),
|
input.stack.clone(),
|
||||||
ctrlc.clone(),
|
ctrlc.clone(),
|
||||||
row_offset,
|
cfg,
|
||||||
width_param,
|
);
|
||||||
table_view,
|
let stream = RawStream::new(Box::new(paginator), ctrlc, input.call.head, None);
|
||||||
)),
|
|
||||||
ctrlc,
|
Ok(PipelineData::ExternalStream {
|
||||||
head,
|
stdout: Some(stream),
|
||||||
None,
|
|
||||||
)),
|
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
span: head,
|
span: input.call.head,
|
||||||
metadata: None,
|
metadata: None,
|
||||||
trim_end_newline: false,
|
trim_end_newline: false,
|
||||||
})
|
})
|
||||||
@ -600,24 +592,19 @@ struct PagingTableCreator {
|
|||||||
engine_state: EngineState,
|
engine_state: EngineState,
|
||||||
stack: Stack,
|
stack: Stack,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
row_offset: usize,
|
|
||||||
width_param: Option<i64>,
|
|
||||||
view: TableView,
|
|
||||||
elements_displayed: usize,
|
elements_displayed: usize,
|
||||||
reached_end: bool,
|
reached_end: bool,
|
||||||
|
cfg: TableConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PagingTableCreator {
|
impl PagingTableCreator {
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn new(
|
fn new(
|
||||||
head: Span,
|
head: Span,
|
||||||
stream: ListStream,
|
stream: ListStream,
|
||||||
engine_state: EngineState,
|
engine_state: EngineState,
|
||||||
stack: Stack,
|
stack: Stack,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
row_offset: usize,
|
cfg: TableConfig,
|
||||||
width_param: Option<i64>,
|
|
||||||
view: TableView,
|
|
||||||
) -> Self {
|
) -> Self {
|
||||||
PagingTableCreator {
|
PagingTableCreator {
|
||||||
head,
|
head,
|
||||||
@ -625,9 +612,7 @@ impl PagingTableCreator {
|
|||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
ctrlc,
|
ctrlc,
|
||||||
row_offset,
|
cfg,
|
||||||
width_param,
|
|
||||||
view,
|
|
||||||
elements_displayed: 0,
|
elements_displayed: 0,
|
||||||
reached_end: false,
|
reached_end: false,
|
||||||
}
|
}
|
||||||
@ -686,11 +671,23 @@ impl PagingTableCreator {
|
|||||||
style_comp,
|
style_comp,
|
||||||
self.ctrlc.clone(),
|
self.ctrlc.clone(),
|
||||||
self.head,
|
self.head,
|
||||||
self.row_offset,
|
self.cfg.row_offset,
|
||||||
get_width_param(self.width_param),
|
self.cfg.term_width,
|
||||||
(cfg.table_indent.left, cfg.table_indent.right),
|
(cfg.table_indent.left, cfg.table_indent.right),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn build_table(&mut self, batch: Vec<Value>) -> Result<Option<String>, ShellError> {
|
||||||
|
match &self.cfg.table_view {
|
||||||
|
TableView::General => self.build_general(batch),
|
||||||
|
TableView::Collapsed => self.build_collapsed(batch),
|
||||||
|
TableView::Expanded {
|
||||||
|
limit,
|
||||||
|
flatten,
|
||||||
|
flatten_separator,
|
||||||
|
} => self.build_extended(batch, *limit, *flatten, flatten_separator.clone()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Iterator for PagingTableCreator {
|
impl Iterator for PagingTableCreator {
|
||||||
@ -736,49 +733,52 @@ impl Iterator for PagingTableCreator {
|
|||||||
// Increase elements_displayed by one so on next iteration next branch of this
|
// Increase elements_displayed by one so on next iteration next branch of this
|
||||||
// if else triggers and terminates stream
|
// if else triggers and terminates stream
|
||||||
self.elements_displayed = 1;
|
self.elements_displayed = 1;
|
||||||
let term_width = get_width_param(self.width_param);
|
let result = create_empty_placeholder(
|
||||||
let result =
|
"list",
|
||||||
create_empty_placeholder("list", term_width, &self.engine_state, &self.stack);
|
self.cfg.term_width,
|
||||||
|
&self.engine_state,
|
||||||
|
&self.stack,
|
||||||
|
);
|
||||||
Some(Ok(result.into_bytes()))
|
Some(Ok(result.into_bytes()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let table = match &self.view {
|
if let Some(limit) = self.cfg.abbreviation {
|
||||||
TableView::General => self.build_general(batch),
|
// todo: could be optimized cause we already consumed the list there's no point in goint back to pagination;
|
||||||
TableView::Collapsed => self.build_collapsed(batch),
|
|
||||||
TableView::Expanded {
|
if batch.len() > limit * 2 + 1 {
|
||||||
|
batch = abbreviate_list(
|
||||||
|
&batch,
|
||||||
limit,
|
limit,
|
||||||
flatten,
|
Value::string(String::from("..."), Span::unknown()),
|
||||||
flatten_separator,
|
);
|
||||||
} => self.build_extended(batch, *limit, *flatten, flatten_separator.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.row_offset += idx;
|
let is_record_list = batch[..limit]
|
||||||
|
.iter()
|
||||||
|
.all(|value| matches!(value, Value::Record { .. }))
|
||||||
|
&& batch[limit + 1..]
|
||||||
|
.iter()
|
||||||
|
.all(|value| matches!(value, Value::Record { .. }));
|
||||||
|
|
||||||
match table {
|
if limit > 0 && is_record_list {
|
||||||
Ok(Some(table)) => {
|
// in case it's a record list we set a default text to each column instead of a single value.
|
||||||
let table = maybe_strip_color(table, &get_config(&self.engine_state, &self.stack));
|
|
||||||
|
|
||||||
let mut bytes = table.as_bytes().to_vec();
|
let cols = batch[0].as_record().expect("ok").cols.clone();
|
||||||
bytes.push(b'\n'); // nu-table tables don't come with a newline on the end
|
let vals =
|
||||||
|
vec![Value::string(String::from("..."), Span::unknown()); cols.len()];
|
||||||
Some(Ok(bytes))
|
batch[limit] = Value::record(Record { cols, vals }, Span::unknown());
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
|
||||||
let msg = if nu_utils::ctrl_c::was_pressed(&self.ctrlc) {
|
|
||||||
"".into()
|
|
||||||
} else {
|
|
||||||
// assume this failed because the table was too wide
|
|
||||||
// TODO: more robust error classification
|
|
||||||
let term_width = get_width_param(self.width_param);
|
|
||||||
format!("Couldn't fit table into {term_width} columns!")
|
|
||||||
};
|
|
||||||
Some(Ok(msg.as_bytes().to_vec()))
|
|
||||||
}
|
}
|
||||||
Err(err) => Some(Err(err)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let table = self.build_table(batch);
|
||||||
|
|
||||||
|
self.cfg.row_offset += idx;
|
||||||
|
|
||||||
|
let config = get_config(&self.engine_state, &self.stack);
|
||||||
|
convert_table_to_output(table, &config, &self.ctrlc, self.cfg.term_width)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -822,7 +822,7 @@ fn render_path_name(
|
|||||||
Some(Value::string(val, span))
|
Some(Value::string(val, span))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
enum TableView {
|
enum TableView {
|
||||||
General,
|
General,
|
||||||
Collapsed,
|
Collapsed,
|
||||||
@ -869,3 +869,70 @@ fn create_empty_placeholder(
|
|||||||
.draw(config, termwidth)
|
.draw(config, termwidth)
|
||||||
.expect("Could not create empty table placeholder")
|
.expect("Could not create empty table placeholder")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_table_to_output(
|
||||||
|
table: Result<Option<String>, ShellError>,
|
||||||
|
config: &Config,
|
||||||
|
ctrlc: &Option<Arc<AtomicBool>>,
|
||||||
|
term_width: usize,
|
||||||
|
) -> Option<Result<Vec<u8>, ShellError>> {
|
||||||
|
match table {
|
||||||
|
Ok(Some(table)) => {
|
||||||
|
let table = maybe_strip_color(table, config);
|
||||||
|
|
||||||
|
let mut bytes = table.as_bytes().to_vec();
|
||||||
|
bytes.push(b'\n'); // nu-table tables don't come with a newline on the end
|
||||||
|
|
||||||
|
Some(Ok(bytes))
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
let msg = if nu_utils::ctrl_c::was_pressed(ctrlc) {
|
||||||
|
String::from("")
|
||||||
|
} else {
|
||||||
|
// assume this failed because the table was too wide
|
||||||
|
// TODO: more robust error classification
|
||||||
|
format!("Couldn't fit table into {} columns!", term_width)
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(Ok(msg.as_bytes().to_vec()))
|
||||||
|
}
|
||||||
|
Err(err) => Some(Err(err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abbreviate_list<T>(list: &[T], limit: usize, text: T) -> Vec<T>
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
let head = &list[..limit];
|
||||||
|
let tail = &list[list.len() - limit..];
|
||||||
|
|
||||||
|
let mut out = Vec::with_capacity(limit * 2 + 1);
|
||||||
|
out.extend(head.iter().cloned());
|
||||||
|
out.push(text);
|
||||||
|
out.extend(tail.iter().cloned());
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_table_modes() -> Vec<Value> {
|
||||||
|
vec![
|
||||||
|
Value::test_string("basic"),
|
||||||
|
Value::test_string("compact"),
|
||||||
|
Value::test_string("compact_double"),
|
||||||
|
Value::test_string("default"),
|
||||||
|
Value::test_string("heavy"),
|
||||||
|
Value::test_string("light"),
|
||||||
|
Value::test_string("none"),
|
||||||
|
Value::test_string("reinforced"),
|
||||||
|
Value::test_string("rounded"),
|
||||||
|
Value::test_string("thin"),
|
||||||
|
Value::test_string("with_love"),
|
||||||
|
Value::test_string("psql"),
|
||||||
|
Value::test_string("markdown"),
|
||||||
|
Value::test_string("dots"),
|
||||||
|
Value::test_string("restructured"),
|
||||||
|
Value::test_string("ascii_rounded"),
|
||||||
|
Value::test_string("basic_compact"),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
@ -2686,3 +2686,114 @@ fn table_leading_trailing_space_bg_expand() {
|
|||||||
"╭───┬───────┬───────┬───────────────────────╮│ # │ a │ b │ c │├───┼───────┼───────┼───────────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ hello │ ╭───┬───────────────╮ ││ │ │ world │ │ 0 │ 1 │ ││ │ │ │ │ 1 │ 2 │ ││ │ │ │ │ 2 │ ╭───┬───────╮ │ ││ │ │ │ │ │ │ 0 │ 1 │ │ ││ │ │ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ │ │ ╰───┴───────╯ │ ││ │ │ │ ╰───┴───────────────╯ │╰───┴───────┴───────┴───────────────────────╯"
|
"╭───┬───────┬───────┬───────────────────────╮│ # │ a │ b │ c │├───┼───────┼───────┼───────────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ hello │ ╭───┬───────────────╮ ││ │ │ world │ │ 0 │ 1 │ ││ │ │ │ │ 1 │ 2 │ ││ │ │ │ │ 2 │ ╭───┬───────╮ │ ││ │ │ │ │ │ │ 0 │ 1 │ │ ││ │ │ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ │ │ ╰───┴───────╯ │ ││ │ │ │ ╰───┴───────────────╯ │╰───┴───────┴───────┴───────────────────────╯"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_abbreviation() {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 100"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬───┬───┬────────────────╮│ # │ a │ b │ c │├───┼───┼───┼────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ 5 │ [list 3 items] ││ 2 │ 1 │ 2 │ 3 ││ 3 │ 1 │ 2 │ 3 ││ 4 │ 1 │ 2 │ 3 ││ 5 │ 1 │ 2 │ 3 ││ 6 │ 1 │ 2 │ 3 │╰───┴───┴───┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 2"#
|
||||||
|
);
|
||||||
|
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 │╰───┴─────┴─────┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 1"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬─────┬─────┬─────╮│ # │ a │ b │ c │├───┼─────┼─────┼─────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ ... │ ... │ ... ││ 2 │ 1 │ 2 │ 3 │╰───┴─────┴─────┴─────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"[[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 0"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬─────╮│ 0 │ ... │╰───┴─────╯");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_abbreviation_kv() {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 100"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬───────────────────╮│ a │ 1 ││ b │ {record 3 fields} ││ c │ [list 4 items] ││ e │ 1 ││ q │ 2 ││ t │ 4 ││ r │ 1 ││ x │ 9 │╰───┴───────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 2"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭─────┬───────────────────╮│ a │ 1 ││ b │ {record 3 fields} ││ ... │ ... ││ r │ 1 ││ x │ 9 │╰─────┴───────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 1"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
actual.out,
|
||||||
|
"╭─────┬─────╮│ a │ 1 ││ ... │ ... ││ x │ 9 │╰─────┴─────╯"
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 0"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭─────┬─────╮│ ... │ ... │╰─────┴─────╯");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_abbreviation_kv_expand() {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 100 -e"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬───────────────────╮│ a │ 1 ││ │ ╭───┬───────────╮ ││ b │ │ a │ 1 │ ││ │ │ │ ╭───┬───╮ │ ││ │ │ b │ │ 0 │ 1 │ │ ││ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ ╰───┴───╯ │ ││ │ │ │ ╭───┬───╮ │ ││ │ │ c │ │ 0 │ 1 │ │ ││ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ ╰───┴───╯ │ ││ │ ╰───┴───────────╯ ││ │ ╭───┬───────────╮ ││ c │ │ 0 │ 1 │ ││ │ │ 1 │ 2 │ ││ │ │ 2 │ ╭───┬───╮ │ ││ │ │ │ │ 0 │ 1 │ │ ││ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ ╰───┴───╯ │ ││ │ │ 3 │ 3 │ ││ │ ╰───┴───────────╯ ││ e │ 1 ││ q │ 2 ││ t │ 4 ││ r │ 1 ││ x │ 9 │╰───┴───────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 2 -e"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭─────┬───────────────────╮│ a │ 1 ││ │ ╭───┬───────────╮ ││ b │ │ a │ 1 │ ││ │ │ │ ╭───┬───╮ │ ││ │ │ b │ │ 0 │ 1 │ │ ││ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ ╰───┴───╯ │ ││ │ │ │ ╭───┬───╮ │ ││ │ │ c │ │ 0 │ 1 │ │ ││ │ │ │ │ 1 │ 2 │ │ ││ │ │ │ │ 2 │ 3 │ │ ││ │ │ │ ╰───┴───╯ │ ││ │ ╰───┴───────────╯ ││ ... │ ... ││ r │ 1 ││ x │ 9 │╰─────┴───────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 1 -e"#
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
actual.out,
|
||||||
|
"╭─────┬─────╮│ a │ 1 ││ ... │ ... ││ x │ 9 │╰─────┴─────╯"
|
||||||
|
);
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"{ a: 1 b: { a: 1 b: [1 2 3] c: [1 2 3] } c: [1 2 [1 2 3] 3] e: 1 q: 2 t: 4 r: 1 x: 9 } | table -a 0 -e"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭─────┬─────╮│ ... │ ... │╰─────┴─────╯");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_abbreviation_by_config() {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 100; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬───┬───┬────────────────╮│ # │ a │ b │ c │├───┼───┼───┼────────────────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ 4 │ 5 │ [list 3 items] ││ 2 │ 1 │ 2 │ 3 ││ 3 │ 1 │ 2 │ 3 ││ 4 │ 1 │ 2 │ 3 ││ 5 │ 1 │ 2 │ 3 ││ 6 │ 1 │ 2 │ 3 │╰───┴───┴───┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 2; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table"#
|
||||||
|
);
|
||||||
|
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 │╰───┴─────┴─────┴────────────────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 1; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬─────┬─────┬─────╮│ # │ a │ b │ c │├───┼─────┼─────┼─────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ ... │ ... │ ... ││ 2 │ 1 │ 2 │ 3 │╰───┴─────┴─────┴─────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 0; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬─────╮│ 0 │ ... │╰───┴─────╯");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn table_abbreviation_by_config_override() {
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 2; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 1"#
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "╭───┬─────┬─────┬─────╮│ # │ a │ b │ c │├───┼─────┼─────┼─────┤│ 0 │ 1 │ 2 │ 3 ││ 1 │ ... │ ... │ ... ││ 2 │ 1 │ 2 │ 3 │╰───┴─────┴─────┴─────╯");
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
r#"$env.config.table.abbreviated_row_count = 1; [[a b, c]; [1 2 3] [4 5 [1 2 3]] [1 2 3] [1 2 3] [1 2 3] [1 2 3] [1 2 3]] | table -a 2"#
|
||||||
|
);
|
||||||
|
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 │╰───┴─────┴─────┴────────────────╯");
|
||||||
|
}
|
||||||
|
@ -76,6 +76,7 @@ pub struct Config {
|
|||||||
pub table_move_header: bool,
|
pub table_move_header: bool,
|
||||||
pub table_show_empty: bool,
|
pub table_show_empty: bool,
|
||||||
pub table_indent: TableIndent,
|
pub table_indent: TableIndent,
|
||||||
|
pub table_abbreviation_threshold: Option<usize>,
|
||||||
pub use_ls_colors: bool,
|
pub use_ls_colors: bool,
|
||||||
pub color_config: HashMap<String, Value>,
|
pub color_config: HashMap<String, Value>,
|
||||||
pub use_grid_icons: bool,
|
pub use_grid_icons: bool,
|
||||||
@ -134,6 +135,7 @@ impl Default for Config {
|
|||||||
trim_strategy: TRIM_STRATEGY_DEFAULT,
|
trim_strategy: TRIM_STRATEGY_DEFAULT,
|
||||||
table_move_header: false,
|
table_move_header: false,
|
||||||
table_indent: TableIndent { left: 1, right: 1 },
|
table_indent: TableIndent { left: 1, right: 1 },
|
||||||
|
table_abbreviation_threshold: None,
|
||||||
|
|
||||||
datetime_normal_format: None,
|
datetime_normal_format: None,
|
||||||
datetime_table_format: None,
|
datetime_table_format: None,
|
||||||
@ -1023,6 +1025,17 @@ impl Value {
|
|||||||
"show_empty" => {
|
"show_empty" => {
|
||||||
try_bool!(cols, vals, index, span, table_show_empty)
|
try_bool!(cols, vals, index, span, table_show_empty)
|
||||||
}
|
}
|
||||||
|
"abbreviated_row_count" => {
|
||||||
|
if let Ok(b) = value.as_int() {
|
||||||
|
if b < 0 {
|
||||||
|
invalid!(Some(span), "should be an int unsigned");
|
||||||
|
}
|
||||||
|
|
||||||
|
config.table_abbreviation_threshold = Some(b as usize);
|
||||||
|
} else {
|
||||||
|
invalid!(Some(span), "should be an int");
|
||||||
|
}
|
||||||
|
}
|
||||||
x => {
|
x => {
|
||||||
invalid_key!(
|
invalid_key!(
|
||||||
cols,
|
cols,
|
||||||
|
@ -165,6 +165,7 @@ $env.config = {
|
|||||||
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
|
truncating_suffix: "..." # A suffix used by the 'truncating' methodology
|
||||||
}
|
}
|
||||||
header_on_separator: false # show header text on separator/border line
|
header_on_separator: false # show header text on separator/border line
|
||||||
|
# abbreviated_row_count: 10 # limit data rows from top and bottom after reaching a set point
|
||||||
}
|
}
|
||||||
|
|
||||||
error_style: "fancy" # "fancy" or "plain" for screen reader-friendly error messages
|
error_style: "fancy" # "fancy" or "plain" for screen reader-friendly error messages
|
||||||
|
Loading…
Reference in New Issue
Block a user