diff --git a/crates/nu-command/src/commands/range.rs b/crates/nu-command/src/commands/range.rs index 016ffbfc..2f6d0542 100644 --- a/crates/nu-command/src/commands/range.rs +++ b/crates/nu-command/src/commands/range.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use nu_engine::WholeStreamCommand; use nu_errors::ShellError; -use nu_protocol::{Signature, SyntaxShape, Value}; +use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value}; struct RangeArgs { range: nu_protocol::Range, @@ -26,6 +26,29 @@ impl WholeStreamCommand for Range { "Return only the selected rows." } + fn examples(&self) -> Vec { + vec![ + Example { + description: "Return rows 1 through 3", + example: "echo [1 2 3 4 5] | range 1..3", + result: Some(vec![ + UntaggedValue::int(2).into(), + UntaggedValue::int(3).into(), + UntaggedValue::int(4).into(), + ]), + }, + Example { + description: "Return the third row from the end, through to the end", + example: "echo [1 2 3 4 5] | range (-3..)", + result: Some(vec![ + UntaggedValue::int(3).into(), + UntaggedValue::int(4).into(), + UntaggedValue::int(5).into(), + ]), + }, + ] + } + fn run(&self, args: CommandArgs) -> Result { range(args) } @@ -37,13 +60,40 @@ fn range(args: CommandArgs) -> Result { range: args.req(0)?, }; - let from = cmd_args.range.min_usize()?; - let to = cmd_args.range.max_usize()?; + let from_raw = cmd_args.range.min_i64()?; + let to_raw = cmd_args.range.max_i64()?; + // only collect the input if we have any negative indices + if from_raw < 0 || to_raw < 0 { + let input = args.input.into_vec(); + let input_size = input.len() as i64; - if from > to { - Ok(OutputStream::one(Value::nothing())) + let from = if from_raw < 0 { + (input_size + from_raw) as usize + } else { + from_raw as usize + }; + + let to = if to_raw < 0 { + (input_size + to_raw) as usize + } else if to_raw > input.len() as i64 { + input.len() + } else { + to_raw as usize + }; + + if from > to { + Ok(OutputStream::one(Value::nothing())) + } else { + Ok(OutputStream::from(input[from..to].to_vec())) + } } else { - Ok(args.input.skip(from).take(to - from + 1).to_output_stream()) + let from = from_raw as usize; + let to = to_raw as usize; + if from > to { + Ok(OutputStream::one(Value::nothing())) + } else { + Ok(args.input.skip(from).take(to - from + 1).to_output_stream()) + } } } diff --git a/crates/nu-command/tests/commands/range.rs b/crates/nu-command/tests/commands/range.rs index a8509ca8..7aa3e4e6 100644 --- a/crates/nu-command/tests/commands/range.rs +++ b/crates/nu-command/tests/commands/range.rs @@ -43,3 +43,26 @@ fn selects_some_rows() { assert_eq!(actual.out, "2"); }); } + +#[test] +fn negative_indices() { + Playground::setup("range_test_negative_indices", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("notes.txt"), + EmptyFile("tests.txt"), + EmptyFile("persons.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | range (-1..) + | length + "# + )); + + assert_eq!(actual.out, "1"); + }); +}