forked from extern/nushell
Track call arguments in a single list (#5125)
* Initial implementation of ordered call args * Run cargo fmt * Fix some clippy lints * Add positional len and nth * Cargo fmt * Remove more old nth calls * Good ole rustfmt * Add named len Co-authored-by: Hristo Filaretov <h.filaretov@protonmail.com>
This commit is contained in:
@ -145,10 +145,10 @@ pub fn flatten_expression(
|
||||
let mut output = vec![(call.head, FlatShape::InternalCall)];
|
||||
|
||||
let mut args = vec![];
|
||||
for positional in &call.positional {
|
||||
for positional in call.positional_iter() {
|
||||
args.extend(flatten_expression(working_set, positional));
|
||||
}
|
||||
for named in &call.named {
|
||||
for named in call.named_iter() {
|
||||
args.push((named.0.span, FlatShape::Flag));
|
||||
if let Some(expr) = &named.1 {
|
||||
args.extend(flatten_expression(working_set, expr));
|
||||
|
@ -1,7 +1,10 @@
|
||||
use nu_protocol::ast::Expr;
|
||||
use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature};
|
||||
use nu_protocol::{PipelineData, Spanned};
|
||||
use nu_protocol::{
|
||||
ast::{Argument, Call, Expr, Expression},
|
||||
engine::Command,
|
||||
ShellError, Signature,
|
||||
};
|
||||
use nu_protocol::{PipelineData, Spanned, Type};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KnownExternal {
|
||||
@ -34,69 +37,56 @@ impl Command for KnownExternal {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// FIXME: This is a bit of a hack, and it'd be nice for the parser/AST to be able to handle the original
|
||||
// order of the parameters. Until then, we need to recover the original order.
|
||||
|
||||
// FIXME: This is going to be a bit expensive, but we need to do it to ensure any new block/subexpression
|
||||
// we find when parsing the external call is handled properly.
|
||||
let mut engine_state = engine_state.clone();
|
||||
|
||||
let call_span = call.span();
|
||||
let contents = engine_state.get_span_contents(&call_span);
|
||||
let head_span = call.head;
|
||||
let decl_id = engine_state
|
||||
.find_decl("run-external".as_bytes())
|
||||
.ok_or(ShellError::ExternalNotSupported(head_span))?;
|
||||
|
||||
let redirect_stdout = call.redirect_stdout;
|
||||
let redirect_stderr = call.redirect_stderr;
|
||||
let command = engine_state.get_decl(decl_id);
|
||||
|
||||
let (lexed, _) = crate::lex(contents, call_span.start, &[], &[], true);
|
||||
let mut extern_call = Call::new(head_span);
|
||||
|
||||
let spans: Vec<_> = lexed.into_iter().map(|x| x.span).collect();
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let (external_call, _) = crate::parse_external_call(&mut working_set, &spans, &[]);
|
||||
let delta = working_set.render();
|
||||
engine_state.merge_delta(delta, None, ".")?;
|
||||
let working_state = StateWorkingSet::new(engine_state);
|
||||
let extern_name = working_state.get_span_contents(call.head);
|
||||
let extern_name = String::from_utf8(extern_name.to_vec())
|
||||
.expect("this was already parsed as a command name");
|
||||
let arg_extern_name = Expression {
|
||||
expr: Expr::String(extern_name),
|
||||
span: call.head,
|
||||
ty: Type::String,
|
||||
custom_completion: None,
|
||||
};
|
||||
|
||||
match external_call.expr {
|
||||
Expr::ExternalCall(head, args) => {
|
||||
let decl_id = engine_state
|
||||
.find_decl("run-external".as_bytes())
|
||||
.ok_or(ShellError::ExternalNotSupported(head.span))?;
|
||||
extern_call.add_positional(arg_extern_name);
|
||||
|
||||
let command = engine_state.get_decl(decl_id);
|
||||
|
||||
let mut call = Call::new(head.span);
|
||||
|
||||
call.positional.push(*head);
|
||||
|
||||
for arg in args {
|
||||
call.positional.push(arg.clone())
|
||||
}
|
||||
|
||||
if redirect_stdout {
|
||||
call.named.push((
|
||||
Spanned {
|
||||
item: "redirect-stdout".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
if redirect_stderr {
|
||||
call.named.push((
|
||||
Spanned {
|
||||
item: "redirect-stderr".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
command.run(&engine_state, stack, &call, input)
|
||||
}
|
||||
x => {
|
||||
println!("{:?}", x);
|
||||
panic!("internal error: known external not actually external")
|
||||
for arg in &call.arguments {
|
||||
match arg {
|
||||
Argument::Positional(positional) => extern_call.add_positional(positional.clone()),
|
||||
Argument::Named(named) => extern_call.add_named(named.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
if call.redirect_stdout {
|
||||
extern_call.add_named((
|
||||
Spanned {
|
||||
item: "redirect-stdout".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
if call.redirect_stderr {
|
||||
extern_call.add_named((
|
||||
Spanned {
|
||||
item: "redirect-stderr".into(),
|
||||
span: call_span,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
command.run(engine_state, stack, &extern_call, input)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use nu_path::canonicalize_with;
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Block, Call, Expr, Expression, ImportPattern, ImportPatternHead, ImportPatternMember,
|
||||
Pipeline,
|
||||
Argument, Block, Call, Expr, Expression, ImportPattern, ImportPatternHead,
|
||||
ImportPatternMember, Pipeline,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, Exportable, Overlay, PositionalArg, Span, SyntaxShape, Type, CONFIG_VARIABLE_ID,
|
||||
@ -142,7 +142,7 @@ pub fn parse_for(
|
||||
let sig = decl.signature();
|
||||
|
||||
// Let's get our block and make sure it has the right signature
|
||||
if let Some(arg) = call.positional.get(2) {
|
||||
if let Some(arg) = call.positional_nth(2) {
|
||||
match arg {
|
||||
Expression {
|
||||
expr: Expr::Block(block_id),
|
||||
@ -178,8 +178,8 @@ pub fn parse_for(
|
||||
};
|
||||
|
||||
// All positional arguments must be in the call positional vector by this point
|
||||
let var_decl = call.positional.get(0).expect("for call already checked");
|
||||
let block = call.positional.get(2).expect("for call already checked");
|
||||
let var_decl = call.positional_nth(0).expect("for call already checked");
|
||||
let block = call.positional_nth(2).expect("for call already checked");
|
||||
|
||||
let error = None;
|
||||
if let (Some(var_id), Some(block_id)) = (&var_decl.as_var(), block.as_block()) {
|
||||
@ -311,7 +311,7 @@ pub fn parse_def(
|
||||
let sig = decl.signature();
|
||||
|
||||
// Let's get our block and make sure it has the right signature
|
||||
if let Some(arg) = call.positional.get(2) {
|
||||
if let Some(arg) = call.positional_nth(2) {
|
||||
match arg {
|
||||
Expression {
|
||||
expr: Expr::Block(block_id),
|
||||
@ -347,9 +347,9 @@ pub fn parse_def(
|
||||
};
|
||||
|
||||
// All positional arguments must be in the call positional vector by this point
|
||||
let name_expr = call.positional.get(0).expect("def call already checked");
|
||||
let sig = call.positional.get(1).expect("def call already checked");
|
||||
let block = call.positional.get(2).expect("def call already checked");
|
||||
let name_expr = call.positional_nth(0).expect("def call already checked");
|
||||
let sig = call.positional_nth(1).expect("def call already checked");
|
||||
let block = call.positional_nth(2).expect("def call already checked");
|
||||
|
||||
let mut error = None;
|
||||
|
||||
@ -457,8 +457,8 @@ pub fn parse_extern(
|
||||
(call, call_span)
|
||||
}
|
||||
};
|
||||
let name_expr = call.positional.get(0);
|
||||
let sig = call.positional.get(1);
|
||||
let name_expr = call.positional_nth(0);
|
||||
let sig = call.positional_nth(1);
|
||||
|
||||
if let (Some(name_expr), Some(sig)) = (name_expr, sig) {
|
||||
if let (Some(name), Some(mut signature)) = (&name_expr.as_string(), sig.as_signature()) {
|
||||
@ -626,8 +626,7 @@ pub fn parse_export(
|
||||
let mut call = Box::new(Call {
|
||||
head: spans[0],
|
||||
decl_id: export_decl_id,
|
||||
positional: vec![],
|
||||
named: vec![],
|
||||
arguments: vec![],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
@ -895,7 +894,7 @@ pub fn parse_export(
|
||||
if let Some(name_span) = spans.get(2) {
|
||||
let (name_expr, err) = parse_string(working_set, *name_span);
|
||||
error = error.or(err);
|
||||
call.positional.push(name_expr);
|
||||
call.add_positional(name_expr);
|
||||
|
||||
if let Some(block_span) = spans.get(3) {
|
||||
let (block_expr, err) = parse_block_expression(
|
||||
@ -922,7 +921,7 @@ pub fn parse_export(
|
||||
None
|
||||
};
|
||||
|
||||
call.positional.push(block_expr);
|
||||
call.add_positional(block_expr);
|
||||
|
||||
exportable
|
||||
} else {
|
||||
@ -1184,8 +1183,10 @@ pub fn parse_module(
|
||||
let call = Box::new(Call {
|
||||
head: spans[0],
|
||||
decl_id: module_decl_id,
|
||||
positional: vec![module_name_expr, block_expr],
|
||||
named: vec![],
|
||||
arguments: vec![
|
||||
Argument::Positional(module_name_expr),
|
||||
Argument::Positional(block_expr),
|
||||
],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
@ -1264,7 +1265,7 @@ pub fn parse_use(
|
||||
}
|
||||
};
|
||||
|
||||
let import_pattern = if let Some(expr) = call.nth(0) {
|
||||
let import_pattern = if let Some(expr) = call.positional_nth(0) {
|
||||
if let Some(pattern) = expr.as_import_pattern() {
|
||||
pattern
|
||||
} else {
|
||||
@ -1422,8 +1423,7 @@ pub fn parse_use(
|
||||
let call = Box::new(Call {
|
||||
head: spans[0],
|
||||
decl_id: use_decl_id,
|
||||
positional: vec![import_pattern_expr],
|
||||
named: vec![],
|
||||
arguments: vec![Argument::Positional(import_pattern_expr)],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
@ -1493,7 +1493,7 @@ pub fn parse_hide(
|
||||
}
|
||||
};
|
||||
|
||||
let import_pattern = if let Some(expr) = call.nth(0) {
|
||||
let import_pattern = if let Some(expr) = call.positional_nth(0) {
|
||||
if let Some(pattern) = expr.as_import_pattern() {
|
||||
pattern
|
||||
} else {
|
||||
@ -1630,8 +1630,7 @@ pub fn parse_hide(
|
||||
let call = Box::new(Call {
|
||||
head: spans[0],
|
||||
decl_id: hide_decl_id,
|
||||
positional: vec![import_pattern_expr],
|
||||
named: vec![],
|
||||
arguments: vec![Argument::Positional(import_pattern_expr)],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
@ -1715,8 +1714,10 @@ pub fn parse_let(
|
||||
let call = Box::new(Call {
|
||||
decl_id,
|
||||
head: spans[0],
|
||||
positional: vec![lvalue, rvalue],
|
||||
named: vec![],
|
||||
arguments: vec![
|
||||
Argument::Positional(lvalue),
|
||||
Argument::Positional(rvalue),
|
||||
],
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
});
|
||||
@ -1834,7 +1835,7 @@ pub fn parse_source(
|
||||
|
||||
// Adding this expression to the positional creates a syntax highlighting error
|
||||
// after writing `source example.nu`
|
||||
call_with_block.positional.push(Expression {
|
||||
call_with_block.add_positional(Expression {
|
||||
expr: Expr::Int(block_id as i64),
|
||||
span: spans[1],
|
||||
ty: Type::Any,
|
||||
@ -1947,8 +1948,7 @@ pub fn parse_register(
|
||||
// The ? operator is not used because the error has to be kept to be printed in the shell
|
||||
// For that reason the values are kept in a result that will be passed at the end of this call
|
||||
let arguments = call
|
||||
.positional
|
||||
.get(0)
|
||||
.positional_nth(0)
|
||||
.map(|expr| {
|
||||
let name_expr = working_set.get_span_contents(expr.span);
|
||||
|
||||
@ -1992,7 +1992,7 @@ pub fn parse_register(
|
||||
|
||||
// Signature is an optional value from the call and will be used to decide if
|
||||
// the plugin is called to get the signatures or to use the given signature
|
||||
let signature = call.positional.get(1).map(|expr| {
|
||||
let signature = call.positional_nth(1).map(|expr| {
|
||||
let signature = working_set.get_span_contents(expr.span);
|
||||
serde_json::from_slice::<Signature>(signature).map_err(|_| {
|
||||
ParseError::LabeledError(
|
||||
|
@ -8,8 +8,9 @@ use crate::{
|
||||
|
||||
use nu_protocol::{
|
||||
ast::{
|
||||
Block, Call, CellPath, Expr, Expression, FullCellPath, ImportPattern, ImportPatternHead,
|
||||
ImportPatternMember, Operator, PathMember, Pipeline, RangeInclusion, RangeOperator,
|
||||
Argument, Block, Call, CellPath, Expr, Expression, FullCellPath, ImportPattern,
|
||||
ImportPatternHead, ImportPatternMember, Operator, PathMember, Pipeline, RangeInclusion,
|
||||
RangeOperator,
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
span, BlockId, Flag, PositionalArg, Signature, Span, Spanned, SyntaxShape, Type, Unit, VarId,
|
||||
@ -127,16 +128,16 @@ pub fn trim_quotes(bytes: &[u8]) -> &[u8] {
|
||||
|
||||
pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseError> {
|
||||
// Allow the call to pass if they pass in the help flag
|
||||
if call.named.iter().any(|(n, _)| n.item == "help") {
|
||||
if call.named_iter().any(|(n, _)| n.item == "help") {
|
||||
return None;
|
||||
}
|
||||
|
||||
if call.positional.len() < sig.required_positional.len() {
|
||||
if call.positional_len() < sig.required_positional.len() {
|
||||
// Comparing the types of all signature positional arguments against the parsed
|
||||
// expressions found in the call. If one type is not found then it could be assumed
|
||||
// that that positional argument is missing from the parsed call
|
||||
for argument in &sig.required_positional {
|
||||
let found = call.positional.iter().fold(false, |ac, expr| {
|
||||
let found = call.positional_iter().fold(false, |ac, expr| {
|
||||
if argument.shape.to_type() == expr.ty || argument.shape == SyntaxShape::Any {
|
||||
true
|
||||
} else {
|
||||
@ -144,7 +145,7 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseEr
|
||||
}
|
||||
});
|
||||
if !found {
|
||||
if let Some(last) = call.positional.last() {
|
||||
if let Some(last) = call.positional_iter().last() {
|
||||
return Some(ParseError::MissingPositional(
|
||||
argument.name.clone(),
|
||||
Span {
|
||||
@ -166,8 +167,8 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseEr
|
||||
}
|
||||
}
|
||||
|
||||
let missing = &sig.required_positional[call.positional.len()];
|
||||
if let Some(last) = call.positional.last() {
|
||||
let missing = &sig.required_positional[call.positional_len()];
|
||||
if let Some(last) = call.positional_iter().last() {
|
||||
Some(ParseError::MissingPositional(
|
||||
missing.name.clone(),
|
||||
Span {
|
||||
@ -188,7 +189,7 @@ pub fn check_call(command: Span, sig: &Signature, call: &Call) -> Option<ParseEr
|
||||
}
|
||||
} else {
|
||||
for req_flag in sig.named.iter().filter(|x| x.required) {
|
||||
if call.named.iter().all(|(n, _)| n.item != req_flag.long) {
|
||||
if call.named_iter().all(|(n, _)| n.item != req_flag.long) {
|
||||
return Some(ParseError::MissingRequiredFlag(
|
||||
req_flag.long.clone(),
|
||||
command,
|
||||
@ -743,7 +744,7 @@ pub fn parse_internal_call(
|
||||
if let Some(long_name) = long_name {
|
||||
// We found a long flag, like --bar
|
||||
error = error.or(err);
|
||||
call.named.push((long_name, arg));
|
||||
call.add_named((long_name, arg));
|
||||
spans_idx += 1;
|
||||
continue;
|
||||
}
|
||||
@ -766,7 +767,7 @@ pub fn parse_internal_call(
|
||||
parse_value(working_set, *arg, &arg_shape, expand_aliases_denylist);
|
||||
error = error.or(err);
|
||||
|
||||
call.named.push((
|
||||
call.add_named((
|
||||
Spanned {
|
||||
item: flag.long.clone(),
|
||||
span: spans[spans_idx],
|
||||
@ -783,7 +784,7 @@ pub fn parse_internal_call(
|
||||
})
|
||||
}
|
||||
} else {
|
||||
call.named.push((
|
||||
call.add_named((
|
||||
Spanned {
|
||||
item: flag.long.clone(),
|
||||
span: spans[spans_idx],
|
||||
@ -844,10 +845,10 @@ pub fn parse_internal_call(
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
call.positional.push(arg);
|
||||
call.add_positional(arg);
|
||||
positional_idx += 1;
|
||||
} else {
|
||||
call.positional.push(Expression::garbage(arg_span));
|
||||
call.add_positional(Expression::garbage(arg_span));
|
||||
error = error.or_else(|| {
|
||||
Some(ParseError::ExtraPositional(
|
||||
signature.call_signature(),
|
||||
@ -4384,26 +4385,25 @@ pub fn parse_expression(
|
||||
env_vars.push(sh.1);
|
||||
}
|
||||
|
||||
let positional = vec![
|
||||
Expression {
|
||||
let arguments = vec![
|
||||
Argument::Positional(Expression {
|
||||
expr: Expr::List(env_vars),
|
||||
span: span(&spans[..pos]),
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
},
|
||||
Expression {
|
||||
}),
|
||||
Argument::Positional(Expression {
|
||||
expr: Expr::Block(block_id),
|
||||
span: span(&spans[pos..]),
|
||||
ty,
|
||||
custom_completion: None,
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
let expr = Expr::Call(Box::new(Call {
|
||||
head: Span { start: 0, end: 0 },
|
||||
decl_id,
|
||||
named: vec![],
|
||||
positional,
|
||||
arguments,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
}));
|
||||
@ -4657,7 +4657,7 @@ pub fn parse_block(
|
||||
if let Some(Expression {
|
||||
expr: Expr::Keyword(_, _, expr),
|
||||
..
|
||||
}) = call.positional.get_mut(1)
|
||||
}) = call.positional_iter_mut().nth(1)
|
||||
{
|
||||
if expr.has_in_variable(working_set) {
|
||||
*expr = Box::new(wrap_expr_with_collect(
|
||||
@ -4810,14 +4810,14 @@ pub fn discover_captures_in_expr(
|
||||
}
|
||||
}
|
||||
|
||||
for named in &call.named {
|
||||
for named in call.named_iter() {
|
||||
if let Some(arg) = &named.1 {
|
||||
let result = discover_captures_in_expr(working_set, arg, seen, seen_blocks);
|
||||
output.extend(&result);
|
||||
}
|
||||
}
|
||||
|
||||
for positional in &call.positional {
|
||||
for positional in call.positional_iter() {
|
||||
let result = discover_captures_in_expr(working_set, positional, seen, seen_blocks);
|
||||
output.extend(&result);
|
||||
}
|
||||
@ -4985,12 +4985,12 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
|
||||
|
||||
let block_id = working_set.add_block(block);
|
||||
|
||||
output.push(Expression {
|
||||
output.push(Argument::Positional(Expression {
|
||||
expr: Expr::Block(block_id),
|
||||
span,
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
});
|
||||
}));
|
||||
|
||||
// The containing, synthetic call to `collect`.
|
||||
// We don't want to have a real span as it will confuse flattening
|
||||
@ -4998,8 +4998,7 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression)
|
||||
Expression {
|
||||
expr: Expr::Call(Box::new(Call {
|
||||
head: Span::new(0, 0),
|
||||
named: vec![],
|
||||
positional: output,
|
||||
arguments: output,
|
||||
decl_id,
|
||||
redirect_stdout: true,
|
||||
redirect_stderr: false,
|
||||
|
Reference in New Issue
Block a user