diff --git a/crates/nu-command/src/default_context.rs b/crates/nu-command/src/default_context.rs index 7ea6e6cdde..ca06a4c04d 100644 --- a/crates/nu-command/src/default_context.rs +++ b/crates/nu-command/src/default_context.rs @@ -79,6 +79,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState { Skip, SkipUntil, SkipWhile, + Slice, Sort, SortBy, SplitList, diff --git a/crates/nu-command/src/filters/mod.rs b/crates/nu-command/src/filters/mod.rs index eaa9452b31..ed1e9d8614 100644 --- a/crates/nu-command/src/filters/mod.rs +++ b/crates/nu-command/src/filters/mod.rs @@ -40,6 +40,7 @@ mod select; #[cfg(feature = "rand")] mod shuffle; mod skip; +mod slice; mod sort; mod sort_by; mod take; @@ -99,6 +100,7 @@ pub use select::Select; #[cfg(feature = "rand")] pub use shuffle::Shuffle; pub use skip::*; +pub use slice::Slice; pub use sort::Sort; pub use sort_by::SortBy; pub use take::*; diff --git a/crates/nu-command/src/filters/range.rs b/crates/nu-command/src/filters/range.rs index 35d6fddd3f..74ca8e1186 100644 --- a/crates/nu-command/src/filters/range.rs +++ b/crates/nu-command/src/filters/range.rs @@ -1,6 +1,5 @@ use nu_engine::command_prelude::*; -use nu_protocol::Range as NumRange; -use std::ops::Bound; +use nu_protocol::{report_parse_warning, ParseWarning}; #[derive(Clone)] pub struct Range; @@ -17,7 +16,7 @@ impl Command for Range { Type::List(Box::new(Type::Any)), )]) .required("rows", SyntaxShape::Range, "Range of rows to return.") - .category(Category::Filters) + .category(Category::Deprecated) } fn description(&self) -> &str { @@ -65,69 +64,16 @@ impl Command for Range { input: PipelineData, ) -> Result { let head = call.head; - let metadata = input.metadata(); - let rows: Spanned = call.req(engine_state, stack, 0)?; - - match rows.item { - NumRange::IntRange(range) => { - let start = range.start(); - let end = match range.end() { - Bound::Included(end) => end, - Bound::Excluded(end) => end - 1, - Bound::Unbounded => { - if range.step() < 0 { - i64::MIN - } else { - i64::MAX - } - } - }; - - // only collect the input if we have any negative indices - if start < 0 || end < 0 { - let v: Vec<_> = input.into_iter().collect(); - let vlen: i64 = v.len() as i64; - - let from = if start < 0 { - (vlen + start) as usize - } else { - start as usize - }; - - let to = if end < 0 { - (vlen + end) as usize - } else if end > v.len() as i64 { - v.len() - } else { - end as usize - }; - - if from > to { - Ok(PipelineData::Value(Value::nothing(head), None)) - } else { - let iter = v.into_iter().skip(from).take(to - from + 1); - Ok(iter.into_pipeline_data(head, engine_state.signals().clone())) - } - } else { - let from = start as usize; - let to = end as usize; - - if from > to { - Ok(PipelineData::Value(Value::nothing(head), None)) - } else { - let iter = input.into_iter().skip(from).take(to - from + 1); - Ok(iter.into_pipeline_data(head, engine_state.signals().clone())) - } - } - .map(|x| x.set_metadata(metadata)) - } - NumRange::FloatRange(_) => Err(ShellError::UnsupportedInput { - msg: "float range".into(), - input: "value originates from here".into(), - msg_span: call.head, - input_span: rows.span, - }), - } + report_parse_warning( + &StateWorkingSet::new(engine_state), + &ParseWarning::DeprecatedWarning { + old_command: "range".into(), + new_suggestion: "use `slice`".into(), + span: head, + url: "`help slice`".into(), + }, + ); + super::Slice::run(&super::Slice, engine_state, stack, call, input) } } diff --git a/crates/nu-command/src/filters/slice.rs b/crates/nu-command/src/filters/slice.rs new file mode 100644 index 0000000000..e3f49a4bb3 --- /dev/null +++ b/crates/nu-command/src/filters/slice.rs @@ -0,0 +1,144 @@ +use nu_engine::command_prelude::*; +use nu_protocol::Range; +use std::ops::Bound; + +#[derive(Clone)] +pub struct Slice; + +impl Command for Slice { + fn name(&self) -> &str { + "slice" + } + + fn signature(&self) -> Signature { + Signature::build("slice") + .input_output_types(vec![( + Type::List(Box::new(Type::Any)), + Type::List(Box::new(Type::Any)), + )]) + .required("rows", SyntaxShape::Range, "Range of rows to return.") + .category(Category::Filters) + } + + fn description(&self) -> &str { + "Return only the selected rows." + } + + fn search_terms(&self) -> Vec<&str> { + vec!["filter", "head", "tail", "range"] + } + + fn examples(&self) -> Vec { + vec![ + Example { + example: "[0,1,2,3,4,5] | slice 4..5", + description: "Get the last 2 items", + result: Some(Value::list( + vec![Value::test_int(4), Value::test_int(5)], + Span::test_data(), + )), + }, + Example { + example: "[0,1,2,3,4,5] | slice (-2)..", + description: "Get the last 2 items", + result: Some(Value::list( + vec![Value::test_int(4), Value::test_int(5)], + Span::test_data(), + )), + }, + Example { + example: "[0,1,2,3,4,5] | slice (-3)..-2", + description: "Get the next to last 2 items", + result: Some(Value::list( + vec![Value::test_int(3), Value::test_int(4)], + Span::test_data(), + )), + }, + ] + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + input: PipelineData, + ) -> Result { + let head = call.head; + let metadata = input.metadata(); + let rows: Spanned = call.req(engine_state, stack, 0)?; + + match rows.item { + Range::IntRange(range) => { + let start = range.start(); + let end = match range.end() { + Bound::Included(end) => end, + Bound::Excluded(end) => end - 1, + Bound::Unbounded => { + if range.step() < 0 { + i64::MIN + } else { + i64::MAX + } + } + }; + + // only collect the input if we have any negative indices + if start < 0 || end < 0 { + let v: Vec<_> = input.into_iter().collect(); + let vlen: i64 = v.len() as i64; + + let from = if start < 0 { + (vlen + start) as usize + } else { + start as usize + }; + + let to = if end < 0 { + (vlen + end) as usize + } else if end > v.len() as i64 { + v.len() + } else { + end as usize + }; + + if from > to { + Ok(PipelineData::Value(Value::nothing(head), None)) + } else { + let iter = v.into_iter().skip(from).take(to - from + 1); + Ok(iter.into_pipeline_data(head, engine_state.signals().clone())) + } + } else { + let from = start as usize; + let to = end as usize; + + if from > to { + Ok(PipelineData::Value(Value::nothing(head), None)) + } else { + let iter = input.into_iter().skip(from).take(to - from + 1); + Ok(iter.into_pipeline_data(head, engine_state.signals().clone())) + } + } + .map(|x| x.set_metadata(metadata)) + } + Range::FloatRange(_) => Err(ShellError::UnsupportedInput { + msg: "float range".into(), + input: "value originates from here".into(), + msg_span: call.head, + input_span: rows.span, + }), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(Slice {}) + } +} diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs index c772e78544..73d5d4577a 100644 --- a/crates/nu-command/tests/commands/mod.rs +++ b/crates/nu-command/tests/commands/mod.rs @@ -82,7 +82,6 @@ mod print; #[cfg(feature = "sqlite")] mod query; mod random; -mod range; mod redirection; mod reduce; mod reject; @@ -100,6 +99,7 @@ mod seq; mod seq_char; mod seq_date; mod skip; +mod slice; mod sort; mod sort_by; mod source_env; diff --git a/crates/nu-command/tests/commands/range.rs b/crates/nu-command/tests/commands/slice.rs similarity index 83% rename from crates/nu-command/tests/commands/range.rs rename to crates/nu-command/tests/commands/slice.rs index 5135de6f20..d7639ad8dc 100644 --- a/crates/nu-command/tests/commands/range.rs +++ b/crates/nu-command/tests/commands/slice.rs @@ -4,7 +4,7 @@ use nu_test_support::{nu, pipeline}; #[test] fn selects_a_row() { - Playground::setup("range_test_1", |dirs, sandbox| { + Playground::setup("slice_test_1", |dirs, sandbox| { sandbox.with_files(&[EmptyFile("notes.txt"), EmptyFile("tests.txt")]); let actual = nu!( @@ -12,7 +12,7 @@ fn selects_a_row() { " ls | sort-by name - | range 0..0 + | slice 0..0 | get name.0 " )); @@ -23,7 +23,7 @@ fn selects_a_row() { #[test] fn selects_some_rows() { - Playground::setup("range_test_2", |dirs, sandbox| { + Playground::setup("slice_test_2", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("notes.txt"), EmptyFile("tests.txt"), @@ -35,7 +35,7 @@ fn selects_some_rows() { " ls | get name - | range 1..2 + | slice 1..2 | length " )); @@ -46,7 +46,7 @@ fn selects_some_rows() { #[test] fn negative_indices() { - Playground::setup("range_test_negative_indices", |dirs, sandbox| { + Playground::setup("slice_test_negative_indices", |dirs, sandbox| { sandbox.with_files(&[ EmptyFile("notes.txt"), EmptyFile("tests.txt"), @@ -58,7 +58,7 @@ fn negative_indices() { " ls | get name - | range (-1..) + | slice (-1..) | length " )); diff --git a/tests/repl/test_engine.rs b/tests/repl/test_engine.rs index 5c2fb8e533..d89a722bec 100644 --- a/tests/repl/test_engine.rs +++ b/tests/repl/test_engine.rs @@ -441,7 +441,7 @@ fn better_operator_spans() -> TestResult { #[test] fn range_right_exclusive() -> TestResult { - run_test(r#"[1, 4, 5, 8, 9] | range 1..<3 | math sum"#, "9") + run_test(r#"[1, 4, 5, 8, 9] | slice 1..<3 | math sum"#, "9") } /// Issue #7872