From 45eba8b922a0ae6068315b45f84d35594e81f0e0 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Thu, 2 Dec 2021 18:59:10 +1300 Subject: [PATCH] Introduce metadata into the pipeline (#397) --- crates/nu-command/src/core_commands/echo.rs | 21 +-- crates/nu-command/src/filesystem/ls.rs | 70 +++------ crates/nu-command/src/filters/each.rs | 17 ++- crates/nu-command/src/filters/length.rs | 2 +- crates/nu-command/src/filters/lines.rs | 6 +- crates/nu-command/src/filters/par_each.rs | 19 ++- crates/nu-command/src/filters/range.rs | 10 +- crates/nu-command/src/filters/select.rs | 15 +- crates/nu-command/src/filters/wrap.rs | 6 +- crates/nu-command/src/formats/from/eml.rs | 11 +- crates/nu-command/src/formats/from/ods.rs | 2 +- crates/nu-command/src/formats/from/url.rs | 13 +- crates/nu-command/src/formats/from/xlsx.rs | 2 +- crates/nu-command/src/math/eval.rs | 4 +- crates/nu-command/src/math/utils.rs | 10 +- crates/nu-command/src/random/bool.rs | 11 +- crates/nu-command/src/random/chars.rs | 11 +- .../nu-command/src/strings/format/command.rs | 11 +- crates/nu-command/src/strings/parse.rs | 7 +- crates/nu-command/src/system/run_external.rs | 2 +- crates/nu-command/src/viewers/griddle.rs | 6 +- crates/nu-command/src/viewers/table.rs | 91 +++++++++++- crates/nu-plugin/src/evaluated_call.rs | 2 +- crates/nu-plugin/src/plugin.rs | 8 +- crates/nu-protocol/src/engine/engine_state.rs | 4 +- crates/nu-protocol/src/pipeline_data.rs | 139 ++++++++++++------ crates/nu_plugin_example/src/main.rs | 4 +- src/main.rs | 24 ++- 28 files changed, 329 insertions(+), 199 deletions(-) diff --git a/crates/nu-command/src/core_commands/echo.rs b/crates/nu-command/src/core_commands/echo.rs index 06a1ff1ed4..0f7c05e343 100644 --- a/crates/nu-command/src/core_commands/echo.rs +++ b/crates/nu-command/src/core_commands/echo.rs @@ -34,19 +34,22 @@ impl Command for Echo { let n = to_be_echoed.len(); match n.cmp(&1usize) { // More than one value is converted in a stream of values - std::cmp::Ordering::Greater => PipelineData::Stream(ValueStream::from_stream( - to_be_echoed.into_iter(), - engine_state.ctrlc.clone(), - )), + std::cmp::Ordering::Greater => PipelineData::Stream( + ValueStream::from_stream(to_be_echoed.into_iter(), engine_state.ctrlc.clone()), + None, + ), // But a single value can be forwarded as it is - std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone()), + std::cmp::Ordering::Equal => PipelineData::Value(to_be_echoed[0].clone(), None), // When there are no elements, we echo the empty string - std::cmp::Ordering::Less => PipelineData::Value(Value::String { - val: "".to_string(), - span: call.head, - }), + std::cmp::Ordering::Less => PipelineData::Value( + Value::String { + val: "".to_string(), + span: call.head, + }, + None, + ), } }) } diff --git a/crates/nu-command/src/filesystem/ls.rs b/crates/nu-command/src/filesystem/ls.rs index 21a8cd8e25..4dc8416e0a 100644 --- a/crates/nu-command/src/filesystem/ls.rs +++ b/crates/nu-command/src/filesystem/ls.rs @@ -1,10 +1,10 @@ use chrono::{DateTime, Utc}; -use lscolors::{LsColors, Style}; use nu_engine::eval_expression; use nu_protocol::ast::Call; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, IntoInterruptiblePipelineData, PipelineData, Signature, SyntaxShape, Value, + Category, DataSource, IntoInterruptiblePipelineData, PipelineData, PipelineMetadata, Signature, + SyntaxShape, Value, }; #[derive(Clone)] @@ -37,7 +37,6 @@ impl Command for Ls { call: &Call, _input: PipelineData, ) -> Result { - let config = stack.get_config()?; let pattern = if let Some(expr) = call.positional.get(0) { let result = eval_expression(engine_state, stack, expr)?; let mut result = result.as_string()?; @@ -57,10 +56,6 @@ impl Command for Ls { let call_span = call.head; let glob = glob::glob(&pattern).unwrap(); - let ls_colors = match stack.get_env_var("LS_COLORS") { - Some(s) => LsColors::from_string(&s), - None => LsColors::default(), - }; Ok(glob .into_iter() @@ -72,22 +67,11 @@ impl Command for Ls { let is_dir = metadata.is_dir(); let filesize = metadata.len(); let mut cols = vec!["name".into(), "type".into(), "size".into()]; - let style = - ls_colors.style_for_path_with_metadata(path.clone(), Some(&metadata)); - let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default(); - let use_ls_colors = config.use_ls_colors; let mut vals = vec![ - if use_ls_colors { - Value::String { - val: ansi_style.apply(path.to_string_lossy()).to_string(), - span: call_span, - } - } else { - Value::String { - val: path.to_string_lossy().to_string(), - span: call_span, - } + Value::String { + val: path.to_string_lossy().to_string(), + span: call_span, }, if is_symlink { Value::string("symlink", call_span) @@ -120,34 +104,26 @@ impl Command for Ls { span: call_span, } } - Err(_) => { - let style = ls_colors.style_for_path(path.clone()); - let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default(); - let use_ls_colors = config.use_ls_colors; - - Value::Record { - cols: vec!["name".into(), "type".into(), "size".into()], - vals: vec![ - if use_ls_colors { - Value::String { - val: ansi_style.apply(path.to_string_lossy()).to_string(), - span: call_span, - } - } else { - Value::String { - val: path.to_string_lossy().to_string(), - span: call_span, - } - }, - Value::Nothing { span: call_span }, - Value::Nothing { span: call_span }, - ], - span: call_span, - } - } + Err(_) => Value::Record { + cols: vec!["name".into(), "type".into(), "size".into()], + vals: vec![ + Value::String { + val: path.to_string_lossy().to_string(), + span: call_span, + }, + Value::Nothing { span: call_span }, + Value::Nothing { span: call_span }, + ], + span: call_span, + }, }, _ => Value::Nothing { span: call_span }, }) - .into_pipeline_data(engine_state.ctrlc.clone())) + .into_pipeline_data_with_metadata( + PipelineMetadata { + data_source: DataSource::Ls, + }, + engine_state.ctrlc.clone(), + )) } } diff --git a/crates/nu-command/src/filters/each.rs b/crates/nu-command/src/filters/each.rs index 533f81c785..b85ebfdedc 100644 --- a/crates/nu-command/src/filters/each.rs +++ b/crates/nu-command/src/filters/each.rs @@ -74,8 +74,8 @@ impl Command for Each { let span = call.head; match input { - PipelineData::Value(Value::Range { .. }) - | PipelineData::Value(Value::List { .. }) + PipelineData::Value(Value::Range { .. }, ..) + | PipelineData::Value(Value::List { .. }, ..) | PipelineData::Stream { .. } => Ok(input .into_iter() .enumerate() @@ -109,7 +109,7 @@ impl Command for Each { } }) .into_pipeline_data(ctrlc)), - PipelineData::Value(Value::Record { cols, vals, .. }) => { + PipelineData::Value(Value::Record { cols, vals, .. }, ..) => { let mut output_cols = vec![]; let mut output_vals = vec![]; @@ -138,9 +138,12 @@ impl Command for Each { } match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? { - PipelineData::Value(Value::Record { - mut cols, mut vals, .. - }) => { + PipelineData::Value( + Value::Record { + mut cols, mut vals, .. + }, + .., + ) => { // TODO check that the lengths match when traversing record output_cols.append(&mut cols); output_vals.append(&mut vals); @@ -159,7 +162,7 @@ impl Command for Each { } .into_pipeline_data()) } - PipelineData::Value(x) => { + PipelineData::Value(x, ..) => { let block = engine_state.get_block(block_id); if let Some(var) = block.signature.get_positional(0) { diff --git a/crates/nu-command/src/filters/length.rs b/crates/nu-command/src/filters/length.rs index f8bb8229c6..bddf977b93 100644 --- a/crates/nu-command/src/filters/length.rs +++ b/crates/nu-command/src/filters/length.rs @@ -26,7 +26,7 @@ impl Command for Length { input: PipelineData, ) -> Result { match input { - PipelineData::Value(Value::Nothing { .. }) => Ok(Value::Int { + PipelineData::Value(Value::Nothing { .. }, ..) => Ok(Value::Int { val: 0, span: call.head, } diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index 5e282c3f71..d0f1f599ea 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -34,7 +34,7 @@ impl Command for Lines { // Collect is needed because the string may not live long enough for // the Rc structure to continue using it. If split could take ownership // of the split values, then this wouldn't be needed - PipelineData::Value(Value::String { val, span }) => { + PipelineData::Value(Value::String { val, span }, ..) => { let lines = val .split(SPLIT_CHAR) .map(|s| s.to_string()) @@ -50,7 +50,7 @@ impl Command for Lines { Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) } - PipelineData::Stream(stream) => { + PipelineData::Stream(stream, ..) => { let iter = stream .into_iter() .filter_map(|value| { @@ -78,7 +78,7 @@ impl Command for Lines { Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) } - PipelineData::Value(val) => Err(ShellError::UnsupportedInput( + PipelineData::Value(val, ..) => Err(ShellError::UnsupportedInput( format!("Not supported input: {}", val.as_string()?), call.head, )), diff --git a/crates/nu-command/src/filters/par_each.rs b/crates/nu-command/src/filters/par_each.rs index 6e34c3e839..45c62ecea9 100644 --- a/crates/nu-command/src/filters/par_each.rs +++ b/crates/nu-command/src/filters/par_each.rs @@ -57,7 +57,7 @@ impl Command for ParEach { let span = call.head; match input { - PipelineData::Value(Value::Range { val, .. }) => Ok(val + PipelineData::Value(Value::Range { val, .. }, ..) => Ok(val .into_range_iter()? .enumerate() .par_bridge() @@ -98,7 +98,7 @@ impl Command for ParEach { .into_iter() .flatten() .into_pipeline_data(ctrlc)), - PipelineData::Value(Value::List { vals: val, .. }) => Ok(val + PipelineData::Value(Value::List { vals: val, .. }, ..) => Ok(val .into_iter() .enumerate() .par_bridge() @@ -139,7 +139,7 @@ impl Command for ParEach { .into_iter() .flatten() .into_pipeline_data(ctrlc)), - PipelineData::Stream(stream) => Ok(stream + PipelineData::Stream(stream, ..) => Ok(stream .enumerate() .par_bridge() .map(move |(idx, x)| { @@ -179,7 +179,7 @@ impl Command for ParEach { .into_iter() .flatten() .into_pipeline_data(ctrlc)), - PipelineData::Value(Value::Record { cols, vals, .. }) => { + PipelineData::Value(Value::Record { cols, vals, .. }, ..) => { let mut output_cols = vec![]; let mut output_vals = vec![]; @@ -208,9 +208,12 @@ impl Command for ParEach { } match eval_block(&engine_state, &mut stack, block, PipelineData::new(span))? { - PipelineData::Value(Value::Record { - mut cols, mut vals, .. - }) => { + PipelineData::Value( + Value::Record { + mut cols, mut vals, .. + }, + .., + ) => { // TODO check that the lengths match when traversing record output_cols.append(&mut cols); output_vals.append(&mut vals); @@ -229,7 +232,7 @@ impl Command for ParEach { } .into_pipeline_data()) } - PipelineData::Value(x) => { + PipelineData::Value(x, ..) => { let block = engine_state.get_block(block_id); if let Some(var) = block.signature.get_positional(0) { diff --git a/crates/nu-command/src/filters/range.rs b/crates/nu-command/src/filters/range.rs index dce0a6c8a7..071c91d046 100644 --- a/crates/nu-command/src/filters/range.rs +++ b/crates/nu-command/src/filters/range.rs @@ -90,7 +90,10 @@ impl Command for Range { }; if from > to { - Ok(PipelineData::Value(Value::Nothing { span: call.head })) + Ok(PipelineData::Value( + Value::Nothing { span: call.head }, + None, + )) } else { let iter = v.into_iter().skip(from).take(to - from + 1); Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) @@ -100,7 +103,10 @@ impl Command for Range { let to = rows_to as usize; if from > to { - Ok(PipelineData::Value(Value::Nothing { span: call.head })) + Ok(PipelineData::Value( + Value::Nothing { span: call.head }, + None, + )) } else { let iter = input.into_iter().skip(from).take(to - from + 1); Ok(iter.into_pipeline_data(engine_state.ctrlc.clone())) diff --git a/crates/nu-command/src/filters/select.rs b/crates/nu-command/src/filters/select.rs index 4c8d4f9d51..27c797aada 100644 --- a/crates/nu-command/src/filters/select.rs +++ b/crates/nu-command/src/filters/select.rs @@ -68,10 +68,13 @@ fn select( } match input { - PipelineData::Value(Value::List { - vals: input_vals, - span, - }) => { + PipelineData::Value( + Value::List { + vals: input_vals, + span, + }, + .., + ) => { let mut output = vec![]; for input_val in input_vals { @@ -92,7 +95,7 @@ fn select( .into_iter() .into_pipeline_data(engine_state.ctrlc.clone())) } - PipelineData::Stream(stream) => Ok(stream + PipelineData::Stream(stream, ..) => Ok(stream .map(move |x| { let mut cols = vec![]; let mut vals = vec![]; @@ -113,7 +116,7 @@ fn select( Value::Record { cols, vals, span } }) .into_pipeline_data(engine_state.ctrlc.clone())), - PipelineData::Value(v) => { + PipelineData::Value(v, ..) => { let mut cols = vec![]; let mut vals = vec![]; diff --git a/crates/nu-command/src/filters/wrap.rs b/crates/nu-command/src/filters/wrap.rs index 0c92fbed29..46f6fe5b3e 100644 --- a/crates/nu-command/src/filters/wrap.rs +++ b/crates/nu-command/src/filters/wrap.rs @@ -35,7 +35,7 @@ impl Command for Wrap { let name: String = call.req(engine_state, stack, 0)?; match input { - PipelineData::Value(Value::List { vals, .. }) => Ok(vals + PipelineData::Value(Value::List { vals, .. }, ..) => Ok(vals .into_iter() .map(move |x| Value::Record { cols: vec![name.clone()], @@ -43,14 +43,14 @@ impl Command for Wrap { span, }) .into_pipeline_data(engine_state.ctrlc.clone())), - PipelineData::Stream(stream) => Ok(stream + PipelineData::Stream(stream, ..) => Ok(stream .map(move |x| Value::Record { cols: vec![name.clone()], vals: vec![x], span, }) .into_pipeline_data(engine_state.ctrlc.clone())), - PipelineData::Value(input) => Ok(Value::Record { + PipelineData::Value(input, ..) => Ok(Value::Record { cols: vec![name], vals: vec![input], span, diff --git a/crates/nu-command/src/formats/from/eml.rs b/crates/nu-command/src/formats/from/eml.rs index 7a4d1a9cb3..b15eb3da98 100644 --- a/crates/nu-command/src/formats/from/eml.rs +++ b/crates/nu-command/src/formats/from/eml.rs @@ -230,10 +230,13 @@ fn from_eml( ); } - Ok(PipelineData::Value(Value::from(Spanned { - item: collected, - span: head, - }))) + Ok(PipelineData::Value( + Value::from(Spanned { + item: collected, + span: head, + }), + None, + )) } #[cfg(test)] diff --git a/crates/nu-command/src/formats/from/ods.rs b/crates/nu-command/src/formats/from/ods.rs index 2997f98728..e178ee67a2 100644 --- a/crates/nu-command/src/formats/from/ods.rs +++ b/crates/nu-command/src/formats/from/ods.rs @@ -194,7 +194,7 @@ fn from_ods( span: head, }; - Ok(PipelineData::Value(record)) + Ok(PipelineData::Value(record, None)) } #[cfg(test)] diff --git a/crates/nu-command/src/formats/from/url.rs b/crates/nu-command/src/formats/from/url.rs index 5d3f4731c5..2afa6808f9 100644 --- a/crates/nu-command/src/formats/from/url.rs +++ b/crates/nu-command/src/formats/from/url.rs @@ -67,11 +67,14 @@ fn from_url(input: PipelineData, head: Span, config: &Config) -> Result Err(ShellError::UnsupportedInput( "String not compatible with url-encoding".to_string(), diff --git a/crates/nu-command/src/formats/from/xlsx.rs b/crates/nu-command/src/formats/from/xlsx.rs index 032ad363cb..0746c3e864 100644 --- a/crates/nu-command/src/formats/from/xlsx.rs +++ b/crates/nu-command/src/formats/from/xlsx.rs @@ -194,7 +194,7 @@ fn from_xlsx( span: head, }; - Ok(PipelineData::Value(record)) + Ok(PipelineData::Value(record, None)) } #[cfg(test)] diff --git a/crates/nu-command/src/math/eval.rs b/crates/nu-command/src/math/eval.rs index 87265b021d..1cfd6ec425 100644 --- a/crates/nu-command/src/math/eval.rs +++ b/crates/nu-command/src/math/eval.rs @@ -57,14 +57,14 @@ pub fn eval( ) -> Result { if let Some(expr) = spanned_expr { match parse(&expr.item, &expr.span) { - Ok(value) => Ok(PipelineData::Value(value)), + Ok(value) => Ok(PipelineData::Value(value, None)), Err(err) => Err(ShellError::UnsupportedInput( format!("Math evaluation error: {}", err), expr.span, )), } } else { - if let PipelineData::Value(Value::Nothing { .. }) = input { + if let PipelineData::Value(Value::Nothing { .. }, ..) = input { return Ok(input); } input.map( diff --git a/crates/nu-command/src/math/utils.rs b/crates/nu-command/src/math/utils.rs index 13228b7857..68418c0828 100644 --- a/crates/nu-command/src/math/utils.rs +++ b/crates/nu-command/src/math/utils.rs @@ -62,12 +62,12 @@ pub fn calculate( mf: impl Fn(&[Value], &Span) -> Result, ) -> Result { match values { - PipelineData::Stream(s) => helper_for_tables(&s.collect::>(), name, mf), - PipelineData::Value(Value::List { ref vals, .. }) => match &vals[..] { + PipelineData::Stream(s, ..) => helper_for_tables(&s.collect::>(), name, mf), + PipelineData::Value(Value::List { ref vals, .. }, ..) => match &vals[..] { [Value::Record { .. }, _end @ ..] => helper_for_tables(vals, name, mf), _ => mf(vals, &name), }, - PipelineData::Value(Value::Record { vals, cols, span }) => { + PipelineData::Value(Value::Record { vals, cols, span }, ..) => { let new_vals: Result, ShellError> = vals.into_iter().map(|val| mf(&[val], &name)).collect(); match new_vals { @@ -79,7 +79,7 @@ pub fn calculate( Err(err) => Err(err), } } - PipelineData::Value(Value::Range { val, .. }) => { + PipelineData::Value(Value::Range { val, .. }, ..) => { let new_vals: Result, ShellError> = val .into_range_iter()? .map(|val| mf(&[val], &name)) @@ -87,6 +87,6 @@ pub fn calculate( mf(&new_vals?, &name) } - PipelineData::Value(val) => mf(&[val], &name), + PipelineData::Value(val, ..) => mf(&[val], &name), } } diff --git a/crates/nu-command/src/random/bool.rs b/crates/nu-command/src/random/bool.rs index bd462d5c76..439b9469e0 100644 --- a/crates/nu-command/src/random/bool.rs +++ b/crates/nu-command/src/random/bool.rs @@ -78,10 +78,13 @@ fn bool( let mut rng = thread_rng(); let bool_result: bool = rng.gen_bool(probability); - Ok(PipelineData::Value(Value::Bool { - val: bool_result, - span, - })) + Ok(PipelineData::Value( + Value::Bool { + val: bool_result, + span, + }, + None, + )) } #[cfg(test)] diff --git a/crates/nu-command/src/random/chars.rs b/crates/nu-command/src/random/chars.rs index 71021bc63f..5c3aba0314 100644 --- a/crates/nu-command/src/random/chars.rs +++ b/crates/nu-command/src/random/chars.rs @@ -70,10 +70,13 @@ fn chars( .map(char::from) .collect::(); - Ok(PipelineData::Value(Value::String { - val: random_string, - span, - })) + Ok(PipelineData::Value( + Value::String { + val: random_string, + span, + }, + None, + )) } #[cfg(test)] diff --git a/crates/nu-command/src/strings/format/command.rs b/crates/nu-command/src/strings/format/command.rs index aac42acba9..548a51fd19 100644 --- a/crates/nu-command/src/strings/format/command.rs +++ b/crates/nu-command/src/strings/format/command.rs @@ -125,7 +125,10 @@ fn format( // We can only handle a Record or a List of Record's match data_as_value { Value::Record { .. } => match format_record(format_operations, &data_as_value) { - Ok(value) => Ok(PipelineData::Value(Value::string(value, Span::unknown()))), + Ok(value) => Ok(PipelineData::Value( + Value::string(value, Span::unknown()), + None, + )), Err(value) => Err(value), }, @@ -151,10 +154,10 @@ fn format( } } - Ok(PipelineData::Stream(ValueStream::from_stream( - list.into_iter(), + Ok(PipelineData::Stream( + ValueStream::from_stream(list.into_iter(), None), None, - ))) + )) } _ => Err(ShellError::UnsupportedInput( "Input data is not supported by this command.".to_string(), diff --git a/crates/nu-command/src/strings/parse.rs b/crates/nu-command/src/strings/parse.rs index bebf0fdc42..d0954df930 100644 --- a/crates/nu-command/src/strings/parse.rs +++ b/crates/nu-command/src/strings/parse.rs @@ -74,6 +74,7 @@ fn operate( let head = call.head; let pattern: Spanned = call.req(engine_state, stack, 0)?; let regex: bool = call.has_flag("regex"); + let ctrlc = engine_state.ctrlc.clone(); let pattern_item = pattern.item; let pattern_span = pattern.span; @@ -125,10 +126,10 @@ fn operate( } } - Ok(PipelineData::Stream(ValueStream::from_stream( - parsed.into_iter(), + Ok(PipelineData::Stream( + ValueStream::from_stream(parsed.into_iter(), ctrlc), None, - ))) + )) } fn build_regex(input: &str, span: Span) -> Result { diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 3689588822..3d81f31f08 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -94,7 +94,7 @@ impl ExternalCommand { // If there is an input from the pipeline. The stdin from the process // is piped so it can be used to send the input information - if !matches!(input, PipelineData::Value(Value::Nothing { .. })) { + if !matches!(input, PipelineData::Value(Value::Nothing { .. }, ..)) { process.stdin(Stdio::piped()); } diff --git a/crates/nu-command/src/viewers/griddle.rs b/crates/nu-command/src/viewers/griddle.rs index a04457f310..585a6ca247 100644 --- a/crates/nu-command/src/viewers/griddle.rs +++ b/crates/nu-command/src/viewers/griddle.rs @@ -64,7 +64,7 @@ prints out the list properly."# let use_grid_icons = config.use_grid_icons; match input { - PipelineData::Value(Value::List { vals, .. }) => { + PipelineData::Value(Value::List { vals, .. }, ..) => { // dbg!("value::list"); let data = convert_to_list(vals, &config); if let Some(items) = data { @@ -81,7 +81,7 @@ prints out the list properly."# Ok(PipelineData::new(call.head)) } } - PipelineData::Stream(stream) => { + PipelineData::Stream(stream, ..) => { // dbg!("value::stream"); let data = convert_to_list(stream, &config); if let Some(items) = data { @@ -99,7 +99,7 @@ prints out the list properly."# Ok(PipelineData::new(call.head)) } } - PipelineData::Value(Value::Record { cols, vals, .. }) => { + PipelineData::Value(Value::Record { cols, vals, .. }, ..) => { // dbg!("value::record"); let mut items = vec![]; diff --git a/crates/nu-command/src/viewers/table.rs b/crates/nu-command/src/viewers/table.rs index d883230aa2..75b3786319 100644 --- a/crates/nu-command/src/viewers/table.rs +++ b/crates/nu-command/src/viewers/table.rs @@ -1,9 +1,11 @@ use super::color_config::style_primitive; use crate::viewers::color_config::get_color_config; +use lscolors::{LsColors, Style}; use nu_protocol::ast::{Call, PathMember}; use nu_protocol::engine::{Command, EngineState, Stack}; use nu_protocol::{ - Category, Config, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value, + Category, Config, DataSource, IntoPipelineData, PipelineData, PipelineMetadata, ShellError, + Signature, Span, Value, ValueStream, }; use nu_table::{StyledString, TextStyle, Theme}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -45,7 +47,7 @@ impl Command for Table { }; match input { - PipelineData::Value(Value::List { vals, .. }) => { + PipelineData::Value(Value::List { vals, .. }, ..) => { let table = convert_to_table(vals, ctrlc, &config)?; if let Some(table) = table { @@ -60,7 +62,84 @@ impl Command for Table { Ok(PipelineData::new(call.head)) } } - PipelineData::Stream(stream) => { + PipelineData::Stream(stream, metadata) => { + let stream = match metadata { + Some(PipelineMetadata { + data_source: DataSource::Ls, + }) => { + let config = config.clone(); + let ctrlc = ctrlc.clone(); + + let ls_colors = match stack.get_env_var("LS_COLORS") { + Some(s) => LsColors::from_string(&s), + None => LsColors::default(), + }; + + ValueStream::from_stream( + stream.map(move |mut x| match &mut x { + Value::Record { cols, vals, .. } => { + let mut idx = 0; + + while idx < cols.len() { + if cols[idx] == "name" { + if let Some(Value::String { val: path, span }) = + vals.get(idx) + { + match std::fs::symlink_metadata(&path) { + Ok(metadata) => { + let style = ls_colors + .style_for_path_with_metadata( + path.clone(), + Some(&metadata), + ); + let ansi_style = style + .map(Style::to_crossterm_style) + .unwrap_or_default(); + let use_ls_colors = config.use_ls_colors; + + if use_ls_colors { + vals[idx] = Value::String { + val: ansi_style + .apply(path) + .to_string(), + span: *span, + }; + } + } + Err(_) => { + let style = + ls_colors.style_for_path(path.clone()); + let ansi_style = style + .map(Style::to_crossterm_style) + .unwrap_or_default(); + let use_ls_colors = config.use_ls_colors; + + if use_ls_colors { + vals[idx] = Value::String { + val: ansi_style + .apply(path) + .to_string(), + span: *span, + }; + } + } + } + } + } + + idx += 1; + } + + x + } + _ => x, + }), + ctrlc, + ) + } + _ => stream, + }; + let table = convert_to_table(stream, ctrlc, &config)?; if let Some(table) = table { @@ -75,7 +154,7 @@ impl Command for Table { Ok(PipelineData::new(call.head)) } } - PipelineData::Value(Value::Record { cols, vals, .. }) => { + PipelineData::Value(Value::Record { cols, vals, .. }, ..) => { let mut output = vec![]; for (c, v) in cols.into_iter().zip(vals.into_iter()) { @@ -105,8 +184,8 @@ impl Command for Table { } .into_pipeline_data()) } - PipelineData::Value(Value::Error { error }) => Err(error), - PipelineData::Value(Value::CustomValue { val, span }) => { + PipelineData::Value(Value::Error { error }, ..) => Err(error), + PipelineData::Value(Value::CustomValue { val, span }, ..) => { let base_pipeline = val.to_base_value(span)?.into_pipeline_data(); self.run(engine_state, stack, call, base_pipeline) } diff --git a/crates/nu-plugin/src/evaluated_call.rs b/crates/nu-plugin/src/evaluated_call.rs index 31a4f6333f..70ad1478e1 100644 --- a/crates/nu-plugin/src/evaluated_call.rs +++ b/crates/nu-plugin/src/evaluated_call.rs @@ -147,7 +147,7 @@ mod test { assert!(call.has_flag("flag")); let required: f64 = call.req(0).unwrap(); - assert_eq!(required, 1.0); + assert!((required - 1.0).abs() < f64::EPSILON); let optional: Option = call.opt(1).unwrap(); assert_eq!(optional, Some("something".to_string())); diff --git a/crates/nu-plugin/src/plugin.rs b/crates/nu-plugin/src/plugin.rs index 9ff85de91a..ef284ae939 100644 --- a/crates/nu-plugin/src/plugin.rs +++ b/crates/nu-plugin/src/plugin.rs @@ -145,8 +145,8 @@ impl Command for PluginDeclaration { })?; let input = match input { - PipelineData::Value(value) => value, - PipelineData::Stream(stream) => { + PipelineData::Value(value, ..) => value, + PipelineData::Stream(stream, ..) => { let values = stream.collect::>(); Value::List { @@ -192,7 +192,9 @@ impl Command for PluginDeclaration { })?; match response { - PluginResponse::Value(value) => Ok(PipelineData::Value(value.as_ref().clone())), + PluginResponse::Value(value) => { + Ok(PipelineData::Value(value.as_ref().clone(), None)) + } PluginResponse::Error(msg) => Err(ShellError::LabeledError( "Error received from plugin".into(), msg, diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index d710bdc96a..1da95153e9 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -363,8 +363,8 @@ impl EngineState { let decl = self.get_decl(decl_id); match input { - PipelineData::Stream(_) => decl, - PipelineData::Value(value) => match value { + PipelineData::Stream(..) => decl, + PipelineData::Value(value, ..) => match value { Value::CustomValue { val, .. } => { // This filter works because the custom definitions were declared // before the default nushell declarations. This means that the custom diff --git a/crates/nu-protocol/src/pipeline_data.rs b/crates/nu-protocol/src/pipeline_data.rs index c47dfae3a9..51d917970b 100644 --- a/crates/nu-protocol/src/pipeline_data.rs +++ b/crates/nu-protocol/src/pipeline_data.rs @@ -33,19 +33,29 @@ use crate::{ast::PathMember, Config, ShellError, Span, Value, ValueStream}; /// Nushell. #[derive(Debug)] pub enum PipelineData { - Value(Value), - Stream(ValueStream), + Value(Value, Option), + Stream(ValueStream, Option), +} + +#[derive(Debug)] +pub struct PipelineMetadata { + pub data_source: DataSource, +} + +#[derive(Debug)] +pub enum DataSource { + Ls, } impl PipelineData { pub fn new(span: Span) -> PipelineData { - PipelineData::Value(Value::Nothing { span }) + PipelineData::Value(Value::Nothing { span }, None) } pub fn into_value(self, span: Span) -> Value { match self { - PipelineData::Value(v) => v, - PipelineData::Stream(s) => Value::List { + PipelineData::Value(v, ..) => v, + PipelineData::Stream(s, ..) => Value::List { vals: s.collect(), span, // FIXME? }, @@ -55,7 +65,7 @@ impl PipelineData { pub fn into_interruptible_iter(self, ctrlc: Option>) -> PipelineIterator { let mut iter = self.into_iter(); - if let PipelineIterator(PipelineData::Stream(s)) = &mut iter { + if let PipelineIterator(PipelineData::Stream(s, ..)) = &mut iter { s.ctrlc = ctrlc; } @@ -64,20 +74,20 @@ impl PipelineData { pub fn collect_string(self, separator: &str, config: &Config) -> String { match self { - PipelineData::Value(v) => v.into_string(separator, config), - PipelineData::Stream(s) => s.into_string(separator, config), + PipelineData::Value(v, ..) => v.into_string(separator, config), + PipelineData::Stream(s, ..) => s.into_string(separator, config), } } pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result { match self { // FIXME: there are probably better ways of doing this - PipelineData::Stream(stream) => Value::List { + PipelineData::Stream(stream, ..) => Value::List { vals: stream.collect(), span: Span::unknown(), } .follow_cell_path(cell_path), - PipelineData::Value(v) => v.follow_cell_path(cell_path), + PipelineData::Value(v, ..) => v.follow_cell_path(cell_path), } } @@ -88,12 +98,12 @@ impl PipelineData { ) -> Result<(), ShellError> { match self { // FIXME: there are probably better ways of doing this - PipelineData::Stream(stream) => Value::List { + PipelineData::Stream(stream, ..) => Value::List { vals: stream.collect(), span: Span::unknown(), } .update_cell_path(cell_path, callback), - PipelineData::Value(v) => v.update_cell_path(cell_path, callback), + PipelineData::Value(v, ..) => v.update_cell_path(cell_path, callback), } } @@ -108,14 +118,14 @@ impl PipelineData { F: FnMut(Value) -> Value + 'static + Send, { match self { - PipelineData::Value(Value::List { vals, .. }) => { + PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc)) } - PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data(ctrlc)), - PipelineData::Value(Value::Range { val, .. }) => { + PipelineData::Stream(stream, ..) => Ok(stream.map(f).into_pipeline_data(ctrlc)), + PipelineData::Value(Value::Range { val, .. }, ..) => { Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc)) } - PipelineData::Value(v) => match f(v) { + PipelineData::Value(v, ..) => match f(v) { Value::Error { error } => Err(error), v => Ok(v.into_pipeline_data()), }, @@ -135,15 +145,17 @@ impl PipelineData { F: FnMut(Value) -> U + 'static + Send, { match self { - PipelineData::Value(Value::List { vals, .. }) => { + PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc)) } - PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)), - PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() { + PipelineData::Stream(stream, ..) => { + Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)) + } + PipelineData::Value(Value::Range { val, .. }, ..) => match val.into_range_iter() { Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)), Err(error) => Err(error), }, - PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)), + PipelineData::Value(v, ..) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)), } } @@ -157,14 +169,14 @@ impl PipelineData { F: FnMut(&Value) -> bool + 'static + Send, { match self { - PipelineData::Value(Value::List { vals, .. }) => { + PipelineData::Value(Value::List { vals, .. }, ..) => { Ok(vals.into_iter().filter(f).into_pipeline_data(ctrlc)) } - PipelineData::Stream(stream) => Ok(stream.filter(f).into_pipeline_data(ctrlc)), - PipelineData::Value(Value::Range { val, .. }) => { + PipelineData::Stream(stream, ..) => Ok(stream.filter(f).into_pipeline_data(ctrlc)), + PipelineData::Value(Value::Range { val, .. }, ..) => { Ok(val.into_range_iter()?.filter(f).into_pipeline_data(ctrlc)) } - PipelineData::Value(v) => { + PipelineData::Value(v, ..) => { if f(&v) { Ok(v.into_pipeline_data()) } else { @@ -190,22 +202,33 @@ impl IntoIterator for PipelineData { fn into_iter(self) -> Self::IntoIter { match self { - PipelineData::Value(Value::List { vals, .. }) => { - PipelineIterator(PipelineData::Stream(ValueStream { - stream: Box::new(vals.into_iter()), - ctrlc: None, - })) + PipelineData::Value(Value::List { vals, .. }, metadata) => { + PipelineIterator(PipelineData::Stream( + ValueStream { + stream: Box::new(vals.into_iter()), + ctrlc: None, + }, + metadata, + )) + } + PipelineData::Value(Value::Range { val, .. }, metadata) => { + match val.into_range_iter() { + Ok(iter) => PipelineIterator(PipelineData::Stream( + ValueStream { + stream: Box::new(iter), + ctrlc: None, + }, + metadata, + )), + Err(error) => PipelineIterator(PipelineData::Stream( + ValueStream { + stream: Box::new(std::iter::once(Value::Error { error })), + ctrlc: None, + }, + metadata, + )), + } } - PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() { - Ok(iter) => PipelineIterator(PipelineData::Stream(ValueStream { - stream: Box::new(iter), - ctrlc: None, - })), - Err(error) => PipelineIterator(PipelineData::Stream(ValueStream { - stream: Box::new(std::iter::once(Value::Error { error })), - ctrlc: None, - })), - }, x => PipelineIterator(x), } } @@ -216,9 +239,9 @@ impl Iterator for PipelineIterator { fn next(&mut self) -> Option { match &mut self.0 { - PipelineData::Value(Value::Nothing { .. }) => None, - PipelineData::Value(v) => Some(std::mem::take(v)), - PipelineData::Stream(stream) => stream.next(), + PipelineData::Value(Value::Nothing { .. }, ..) => None, + PipelineData::Value(v, ..) => Some(std::mem::take(v)), + PipelineData::Stream(stream, ..) => stream.next(), } } } @@ -232,12 +255,17 @@ where V: Into, { fn into_pipeline_data(self) -> PipelineData { - PipelineData::Value(self.into()) + PipelineData::Value(self.into(), None) } } pub trait IntoInterruptiblePipelineData { fn into_pipeline_data(self, ctrlc: Option>) -> PipelineData; + fn into_pipeline_data_with_metadata( + self, + metadata: PipelineMetadata, + ctrlc: Option>, + ) -> PipelineData; } impl IntoInterruptiblePipelineData for I @@ -247,9 +275,26 @@ where ::Item: Into, { fn into_pipeline_data(self, ctrlc: Option>) -> PipelineData { - PipelineData::Stream(ValueStream { - stream: Box::new(self.into_iter().map(Into::into)), - ctrlc, - }) + PipelineData::Stream( + ValueStream { + stream: Box::new(self.into_iter().map(Into::into)), + ctrlc, + }, + None, + ) + } + + fn into_pipeline_data_with_metadata( + self, + metadata: PipelineMetadata, + ctrlc: Option>, + ) -> PipelineData { + PipelineData::Stream( + ValueStream { + stream: Box::new(self.into_iter().map(Into::into)), + ctrlc, + }, + Some(metadata), + ) } } diff --git a/crates/nu_plugin_example/src/main.rs b/crates/nu_plugin_example/src/main.rs index f43c5046fb..2f2c89a260 100644 --- a/crates/nu_plugin_example/src/main.rs +++ b/crates/nu_plugin_example/src/main.rs @@ -126,14 +126,14 @@ fn test2(call: &EvaluatedCall, input: &Value) -> Result { let vals = (0..3) .map(|v| Value::Int { val: v * i, - span: call.head.clone(), + span: call.head, }) .collect::>(); Value::Record { cols: cols.clone(), vals, - span: call.head.clone(), + span: call.head, } }) .collect::>(); diff --git a/src/main.rs b/src/main.rs index 974fb4be86..666f516be8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use nu_parser::parse; use nu_protocol::{ ast::Call, engine::{EngineState, Stack, StateWorkingSet}, - IntoPipelineData, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, + PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, }; use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt}; @@ -313,8 +313,8 @@ fn main() -> Result<()> { } } -fn print_value( - value: Value, +fn print_pipeline_data( + input: PipelineData, engine_state: &EngineState, stack: &mut Stack, ) -> Result<(), ShellError> { @@ -325,15 +325,13 @@ fn print_value( let output = match engine_state.find_decl("table".as_bytes()) { Some(decl_id) => { - let table = engine_state.get_decl(decl_id).run( - engine_state, - stack, - &Call::new(), - value.into_pipeline_data(), - )?; + let table = + engine_state + .get_decl(decl_id) + .run(engine_state, stack, &Call::new(), input)?; table.collect_string("\n", &config) } - None => value.into_string(", ", &config), + None => input.collect_string(", ", &config), }; let stdout = std::io::stdout(); @@ -436,11 +434,7 @@ fn eval_source( PipelineData::new(Span::unknown()), ) { Ok(pipeline_data) => { - if let Err(err) = print_value( - pipeline_data.into_value(Span::unknown()), - engine_state, - stack, - ) { + if let Err(err) = print_pipeline_data(pipeline_data, engine_state, stack) { let working_set = StateWorkingSet::new(engine_state); report_error(&working_set, &err);