mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Add extern def which allows raw arguments (#8956)
# Description Extends the `extern` syntax to allow commands that accept raw arguments. This is mainly added to allow wrapper type scripts for external commands. This is an example on how this can be used: ```nushell extern foo [...rest] { print ($rest | str join ',' ) } foo --bar baz -- -q -u -x # => --bar,baz,--,-q,-u,-x ``` (It's only possible to accept a single ...varargs argument in the signature) # User-Facing Changes No breaking changes, just extra possibilities. # Tests + Formatting Added a test for this new behaviour and ran the toolkit pr checker # After Submitting This is advanced functionality but it should be documented, I will open a new PR on the book for that Co-authored-by: Jelle Besseling <jelle@bigbridge.nl>
This commit is contained in:
parent
6f9b9914cf
commit
44493dac51
3 changed files with 71 additions and 41 deletions
|
@ -19,6 +19,7 @@ impl Command for Extern {
|
|||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||
.required("def_name", SyntaxShape::String, "definition name")
|
||||
.required("params", SyntaxShape::Signature, "parameters")
|
||||
.optional("body", SyntaxShape::Block, "wrapper function block")
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
|
|
|
@ -141,3 +141,11 @@ fn def_with_paren_params() {
|
|||
|
||||
assert_eq!(actual.out, "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extern_with_block() {
|
||||
let actual =
|
||||
nu!("extern foo [...rest] { print ($rest | str join ',' ) }; foo --bar baz -- -q -u -x");
|
||||
|
||||
assert_eq!(actual.out, "--bar,baz,--,-q,-u,-x");
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ pub fn parse_def_predecl(working_set: &mut StateWorkingSet, spans: &[Span]) {
|
|||
working_set.error(ParseError::DuplicateCommandDef(spans[1]));
|
||||
}
|
||||
}
|
||||
} else if name == b"extern" && spans.len() == 3 {
|
||||
} else if name == b"extern" && spans.len() >= 3 {
|
||||
let name_expr = parse_string(working_set, spans[1]);
|
||||
let name = name_expr.as_string();
|
||||
|
||||
|
@ -430,40 +430,7 @@ pub fn parse_def(
|
|||
*declaration = signature.clone().into_block_command(block_id);
|
||||
|
||||
let mut block = working_set.get_block_mut(block_id);
|
||||
let calls_itself = block.pipelines.iter().any(|pipeline| {
|
||||
pipeline
|
||||
.elements
|
||||
.iter()
|
||||
.any(|pipe_element| match pipe_element {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call_expr),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if call_expr.decl_id == decl_id {
|
||||
return true;
|
||||
}
|
||||
call_expr.arguments.iter().any(|arg| match arg {
|
||||
Argument::Positional(Expression { expr, .. }) => match expr {
|
||||
Expr::Keyword(.., expr) => {
|
||||
let expr = expr.as_ref();
|
||||
let Expression { expr, .. } = expr;
|
||||
match expr {
|
||||
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
});
|
||||
let calls_itself = block_calls_itself(block, decl_id);
|
||||
block.recursive = Some(calls_itself);
|
||||
block.signature = signature;
|
||||
block.redirect_env = def_call == b"def-env";
|
||||
|
@ -543,6 +510,7 @@ pub fn parse_extern(
|
|||
};
|
||||
let name_expr = call.positional_nth(0);
|
||||
let sig = call.positional_nth(1);
|
||||
let body = call.positional_nth(2);
|
||||
|
||||
if let (Some(name_expr), Some(sig)) = (name_expr, sig) {
|
||||
if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
|
||||
|
@ -581,13 +549,29 @@ pub fn parse_extern(
|
|||
signature.extra_usage = extra_usage.clone();
|
||||
signature.allows_unknown_args = true;
|
||||
|
||||
let decl = KnownExternal {
|
||||
name: external_name,
|
||||
usage: [usage, extra_usage].join("\n"),
|
||||
signature,
|
||||
};
|
||||
if let Some(block_id) = body.and_then(|x| x.as_block()) {
|
||||
if signature.rest_positional.is_none() {
|
||||
working_set.error(ParseError::InternalError(
|
||||
"Extern block must have a rest positional argument".into(),
|
||||
name_expr.span,
|
||||
));
|
||||
} else {
|
||||
*declaration = signature.clone().into_block_command(block_id);
|
||||
|
||||
*declaration = Box::new(decl);
|
||||
let block = working_set.get_block_mut(block_id);
|
||||
let calls_itself = block_calls_itself(block, decl_id);
|
||||
block.recursive = Some(calls_itself);
|
||||
block.signature = signature;
|
||||
}
|
||||
} else {
|
||||
let decl = KnownExternal {
|
||||
name: external_name,
|
||||
usage: [usage, extra_usage].join("\n"),
|
||||
signature,
|
||||
};
|
||||
|
||||
*declaration = Box::new(decl);
|
||||
}
|
||||
} else {
|
||||
working_set.error(ParseError::InternalError(
|
||||
"Predeclaration failed to add declaration".into(),
|
||||
|
@ -614,6 +598,43 @@ pub fn parse_extern(
|
|||
}])
|
||||
}
|
||||
|
||||
fn block_calls_itself(block: &Block, decl_id: usize) -> bool {
|
||||
block.pipelines.iter().any(|pipeline| {
|
||||
pipeline
|
||||
.elements
|
||||
.iter()
|
||||
.any(|pipe_element| match pipe_element {
|
||||
PipelineElement::Expression(
|
||||
_,
|
||||
Expression {
|
||||
expr: Expr::Call(call_expr),
|
||||
..
|
||||
},
|
||||
) => {
|
||||
if call_expr.decl_id == decl_id {
|
||||
return true;
|
||||
}
|
||||
call_expr.arguments.iter().any(|arg| match arg {
|
||||
Argument::Positional(Expression { expr, .. }) => match expr {
|
||||
Expr::Keyword(.., expr) => {
|
||||
let expr = expr.as_ref();
|
||||
let Expression { expr, .. } = expr;
|
||||
match expr {
|
||||
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
Expr::Call(call_expr2) => call_expr2.decl_id == decl_id,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_alias(
|
||||
working_set: &mut StateWorkingSet,
|
||||
lite_command: &LiteCommand,
|
||||
|
|
Loading…
Reference in a new issue