Add rest support to blocks (#2962)

This commit is contained in:
Jonathan Turner 2021-01-23 10:28:32 +13:00 committed by GitHub
parent a3be6affa4
commit a4b8d4a098
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 83 additions and 9 deletions

View file

@ -8,7 +8,7 @@ use nu_errors::ShellError;
use nu_parser::ParserScope;
use nu_protocol::hir::Block;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_source::{b, DebugDocBuilder, PrettyDebugWithSource, Tag};
use nu_source::{b, DebugDocBuilder, PrettyDebugWithSource, Span, Tag};
use nu_stream::{OutputStream, ToOutputStream};
use std::sync::Arc;
@ -67,16 +67,54 @@ impl WholeStreamCommand for Block {
let input = args.input;
ctx.scope.enter_scope();
if let Some(args) = evaluated.args.positional {
// FIXME: do not do this
for arg in args.into_iter().zip(self.params.positional.iter()) {
let name = arg.1 .0.name();
if name.starts_with('$') {
ctx.scope.add_var(name, arg.0);
} else {
ctx.scope.add_var(format!("${}", name), arg.0);
let mut args_iter = args.into_iter().peekable();
let mut params_iter = self.params.positional.iter();
loop {
match (args_iter.peek(), params_iter.next()) {
(Some(_), Some(param)) => {
let name = param.0.name();
// we just checked the peek above, so this should be infallible
if let Some(arg) = args_iter.next() {
if name.starts_with('$') {
ctx.scope.add_var(name.to_string(), arg);
} else {
ctx.scope.add_var(format!("${}", name), arg);
}
}
}
(Some(arg), None) => {
if block.params.rest_positional.is_none() {
ctx.scope.exit_scope();
return Err(ShellError::labeled_error(
"Unexpected argument to command",
"unexpected argument",
arg.tag.span,
));
} else {
break;
}
}
_ => break,
}
}
if block.params.rest_positional.is_some() {
let elements: Vec<_> = args_iter.collect();
let start = if let Some(first) = elements.first() {
first.tag.span.start()
} else {
0
};
let end = if let Some(last) = elements.last() {
last.tag.span.end()
} else {
0
};
ctx.scope.add_var(
"$rest",
UntaggedValue::Table(elements).into_value(Span::new(start, end)),
);
}
}
if let Some(args) = evaluated.args.named {
for named in &block.params.named {

View file

@ -428,6 +428,42 @@ fn run_broken_inner_custom_command() {
assert!(actual.err.contains("not found"));
}
#[test]
fn run_custom_command_with_rest() {
let actual = nu!(
cwd: ".",
r#"
def rest-me [...rest: string] { echo $rest.1 $rest.0}; rest-me "hello" "world" | to json
"#
);
assert_eq!(actual.out, r#"["world","hello"]"#);
}
#[test]
fn run_custom_command_with_rest_and_arg() {
let actual = nu!(
cwd: ".",
r#"
def rest-me-with-arg [name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-arg "hello" "world" "yay" | to json
"#
);
assert_eq!(actual.out, r#"["yay","world","hello"]"#);
}
#[test]
fn run_custom_command_with_rest_and_flag() {
let actual = nu!(
cwd: ".",
r#"
def rest-me-with-flag [--name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-flag "hello" "world" --name "yay" | to json
"#
);
assert_eq!(actual.out, r#"["world","hello","yay"]"#);
}
#[test]
fn set_variable() {
let actual = nu!(