omg it compiles

This commit is contained in:
Devyn Cairns 2024-06-18 21:39:42 -07:00
parent acee591003
commit b0d24ea434
24 changed files with 111 additions and 140 deletions

View File

@ -39,6 +39,8 @@ impl Command for TimeIt {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// FIXME: get working with IR. I don't think this should actually be so AST dependent
let call = call.assert_ast_call()?;
let command_to_run = call.positional_nth(0); let command_to_run = call.positional_nth(0);
// Get the start time after all other computation has been done. // Get the start time after all other computation has been done.

View File

@ -30,6 +30,7 @@ impl Command for ExportEnv {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let block_id = call let block_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -35,6 +35,7 @@ impl Command for SourceEnv {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?; let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
// Note: this hidden positional is the block_id that corresponded to the 0th position // Note: this hidden positional is the block_id that corresponded to the 0th position

View File

@ -103,7 +103,7 @@ impl Command for Du {
let current_dir = current_dir(engine_state, stack)?; let current_dir = current_dir(engine_state, stack)?;
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let paths = if call.rest_iter(0).count() == 0 { let paths = if !call.has_positional_args(stack, 0) {
None None
} else { } else {
Some(paths) Some(paths)

View File

@ -110,7 +110,7 @@ impl Command for Ls {
}; };
let pattern_arg = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let pattern_arg = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let input_pattern_arg = if call.rest_iter(0).count() == 0 { let input_pattern_arg = if !call.has_positional_args(stack, 0) {
None None
} else { } else {
Some(pattern_arg) Some(pattern_arg)

View File

@ -57,7 +57,7 @@ impl Command for Open {
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?; let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);
if paths.is_empty() && call.rest_iter(0).next().is_none() { if paths.is_empty() && !call.has_positional_args(stack, 0) {
// try to use path from pipeline input if there were no positional or spread args // try to use path from pipeline input if there were no positional or spread args
let (filename, span) = match input { let (filename, span) = match input {
PipelineData::Value(val, ..) => { PipelineData::Value(val, ..) => {

View File

@ -4,10 +4,8 @@ use nu_engine::get_eval_block;
use nu_engine::{command_prelude::*, current_dir}; use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with; use nu_path::expand_path_with;
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression}, ast, byte_stream::copy_with_interrupt, process::ChildPipe, ByteStreamSource, DataSource,
byte_stream::copy_with_interrupt, OutDest, PipelineMetadata,
process::ChildPipe,
ByteStreamSource, DataSource, OutDest, PipelineMetadata,
}; };
use std::{ use std::{
fs::File, fs::File,
@ -70,24 +68,6 @@ impl Command for Save {
let append = call.has_flag(engine_state, stack, "append")?; let append = call.has_flag(engine_state, stack, "append")?;
let force = call.has_flag(engine_state, stack, "force")?; let force = call.has_flag(engine_state, stack, "force")?;
let progress = call.has_flag(engine_state, stack, "progress")?; let progress = call.has_flag(engine_state, stack, "progress")?;
let out_append = if let Some(Expression {
expr: Expr::Bool(out_append),
..
}) = call.get_parser_info("out-append")
{
*out_append
} else {
false
};
let err_append = if let Some(Expression {
expr: Expr::Bool(err_append),
..
}) = call.get_parser_info("err-append")
{
*err_append
} else {
false
};
let span = call.head; let span = call.head;
#[allow(deprecated)] #[allow(deprecated)]
@ -110,14 +90,7 @@ impl Command for Save {
PipelineData::ByteStream(stream, metadata) => { PipelineData::ByteStream(stream, metadata) => {
check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?; check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?;
let (file, stderr_file) = get_files( let (file, stderr_file) = get_files(&path, stderr_path.as_ref(), append, force)?;
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
let size = stream.known_size(); let size = stream.known_size();
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
@ -222,14 +195,7 @@ impl Command for Save {
stderr_path.as_ref(), stderr_path.as_ref(),
)?; )?;
let (mut file, _) = get_files( let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
for val in ls { for val in ls {
file.write_all(&value_to_bytes(val)?) file.write_all(&value_to_bytes(val)?)
.map_err(|err| ShellError::IOError { .map_err(|err| ShellError::IOError {
@ -259,14 +225,7 @@ impl Command for Save {
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?; input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
// Only open file after successful conversion // Only open file after successful conversion
let (mut file, _) = get_files( let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
&path,
stderr_path.as_ref(),
append,
out_append,
err_append,
force,
)?;
file.write_all(&bytes).map_err(|err| ShellError::IOError { file.write_all(&bytes).map_err(|err| ShellError::IOError {
msg: err.to_string(), msg: err.to_string(),
@ -398,7 +357,8 @@ fn convert_to_extension(
let eval_block = get_eval_block(engine_state); let eval_block = get_eval_block(engine_state);
eval_block(engine_state, stack, block, input) eval_block(engine_state, stack, block, input)
} else { } else {
decl.run(engine_state, stack, &Call::new(span), input) let call = ast::Call::new(span);
decl.run(engine_state, stack, &(&call).into(), input)
} }
} else { } else {
Ok(input) Ok(input)
@ -474,19 +434,17 @@ fn get_files(
path: &Spanned<PathBuf>, path: &Spanned<PathBuf>,
stderr_path: Option<&Spanned<PathBuf>>, stderr_path: Option<&Spanned<PathBuf>>,
append: bool, append: bool,
out_append: bool,
err_append: bool,
force: bool, force: bool,
) -> Result<(File, Option<File>), ShellError> { ) -> Result<(File, Option<File>), ShellError> {
// First check both paths // First check both paths
let (path, path_span) = prepare_path(path, append || out_append, force)?; let (path, path_span) = prepare_path(path, append, force)?;
let stderr_path_and_span = stderr_path let stderr_path_and_span = stderr_path
.as_ref() .as_ref()
.map(|stderr_path| prepare_path(stderr_path, append || err_append, force)) .map(|stderr_path| prepare_path(stderr_path, append, force))
.transpose()?; .transpose()?;
// Only if both files can be used open and possibly truncate them // Only if both files can be used open and possibly truncate them
let file = open_file(path, path_span, append || out_append)?; let file = open_file(path, path_span, append)?;
let stderr_file = stderr_path_and_span let stderr_file = stderr_path_and_span
.map(|(stderr_path, stderr_path_span)| { .map(|(stderr_path, stderr_path_span)| {
@ -499,7 +457,7 @@ fn get_files(
inner: vec![], inner: vec![],
}) })
} else { } else {
open_file(stderr_path, stderr_path_span, append || err_append) open_file(stderr_path, stderr_path_span, append)
} }
}) })
.transpose()?; .transpose()?;

View File

@ -1,6 +1,6 @@
use dialoguer::Input; use dialoguer::Input;
use nu_engine::{command_prelude::*, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_expression};
use nu_protocol::{ast::Expr, FromValue, NuGlob}; use nu_protocol::{FromValue, NuGlob};
use std::{ use std::{
error::Error, error::Error,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -92,42 +92,19 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
/// Get rest arguments from given `call`, starts with `starting_pos`. /// Get rest arguments from given `call`, starts with `starting_pos`.
/// ///
/// It's similar to `call.rest`, except that it always returns NuGlob. And if input argument has /// It's similar to `call.rest`, except that it always returns NuGlob.
/// Type::Glob, the NuGlob is unquoted, which means it's required to expand.
pub fn get_rest_for_glob_pattern( pub fn get_rest_for_glob_pattern(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
starting_pos: usize, starting_pos: usize,
) -> Result<Vec<Spanned<NuGlob>>, ShellError> { ) -> Result<Vec<Spanned<NuGlob>>, ShellError> {
let mut output = vec![];
let eval_expression = get_eval_expression(engine_state); let eval_expression = get_eval_expression(engine_state);
for result in call.rest_iter_flattened(starting_pos, |expr| { call.rest_iter_flattened(engine_state, stack, eval_expression, starting_pos)?
let result = eval_expression(engine_state, stack, expr); .into_iter()
match result { // This used to be much more complex, but I think `FromValue` should be able to handle the
Err(e) => Err(e), // nuance here.
Ok(result) => { .map(FromValue::from_value)
let span = result.span(); .collect()
// convert from string to quoted string if expr is a variable
// or string interpolation
match result {
Value::String { val, .. }
if matches!(
&expr.expr,
Expr::FullCellPath(_) | Expr::StringInterpolation(_)
) =>
{
// should not expand if given input type is not glob.
Ok(Value::glob(val, expr.ty != Type::Glob, span))
}
other => Ok(other),
}
}
}
})? {
output.push(FromValue::from_value(result)?);
}
Ok(output)
} }

View File

@ -149,27 +149,27 @@ pub fn transpose(
if !args.rest.is_empty() && args.header_row { if !args.rest.is_empty() && args.header_row {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "Can not provide header names and use `--header-row`".into(), msg: "Can not provide header names and use `--header-row`".into(),
span: call.get_named_arg("header-row").expect("has flag").span, span: call.get_flag_span(stack, "header-row").expect("has flag"),
}); });
} }
if !args.header_row && args.keep_all { if !args.header_row && args.keep_all {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "Can only be used with `--header-row`(`-r`)".into(), msg: "Can only be used with `--header-row`(`-r`)".into(),
span: call.get_named_arg("keep-all").expect("has flag").span, span: call.get_flag_span(stack, "keep-all").expect("has flag"),
}); });
} }
if !args.header_row && args.keep_last { if !args.header_row && args.keep_last {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "Can only be used with `--header-row`(`-r`)".into(), msg: "Can only be used with `--header-row`(`-r`)".into(),
span: call.get_named_arg("keep-last").expect("has flag").span, span: call.get_flag_span(stack, "keep-last").expect("has flag"),
}); });
} }
if args.keep_all && args.keep_last { if args.keep_all && args.keep_last {
return Err(ShellError::IncompatibleParameters { return Err(ShellError::IncompatibleParameters {
left_message: "can't use `--keep-last` at the same time".into(), left_message: "can't use `--keep-last` at the same time".into(),
left_span: call.get_named_arg("keep-last").expect("has flag").span, left_span: call.get_flag_span(stack, "keep-last").expect("has flag"),
right_message: "because of `--keep-all`".into(), right_message: "because of `--keep-all`".into(),
right_span: call.get_named_arg("keep-all").expect("has flag").span, right_span: call.get_flag_span(stack, "keep-all").expect("has flag"),
}); });
} }

View File

@ -1,7 +1,6 @@
use nu_engine::{CallExt, ClosureEval}; use nu_engine::{CallExt, ClosureEval};
use nu_protocol::{ use nu_protocol::{
ast::Call, engine::{Call, Closure, EngineState, Stack},
engine::{Closure, EngineState, Stack},
IntoPipelineData, PipelineData, ShellError, Span, Value, IntoPipelineData, PipelineData, ShellError, Span, Value,
}; };

View File

@ -676,7 +676,7 @@ Operating system commands:
} }
}; };
let output = heavy_lifting(code, escape, osc, call)?; let output = heavy_lifting(code, escape, osc, stack, call)?;
Ok(Value::string(output, call.head).into_pipeline_data()) Ok(Value::string(output, call.head).into_pipeline_data())
} }
@ -710,26 +710,30 @@ Operating system commands:
} }
}; };
let output = heavy_lifting(code, escape, osc, call)?; let output = heavy_lifting(code, escape, osc, &Stack::new(), call)?;
Ok(Value::string(output, call.head).into_pipeline_data()) Ok(Value::string(output, call.head).into_pipeline_data())
} }
} }
fn heavy_lifting(code: Value, escape: bool, osc: bool, call: &Call) -> Result<String, ShellError> { fn heavy_lifting(
code: Value,
escape: bool,
osc: bool,
stack: &Stack,
call: &Call,
) -> Result<String, ShellError> {
let param_is_string = matches!(code, Value::String { .. }); let param_is_string = matches!(code, Value::String { .. });
if escape && osc { if escape && osc {
return Err(ShellError::IncompatibleParameters { return Err(ShellError::IncompatibleParameters {
left_message: "escape".into(), left_message: "escape".into(),
left_span: call left_span: call
.get_named_arg("escape") .get_flag_span(stack, "escape")
.expect("Unexpected missing argument") .expect("Unexpected missing argument"),
.span,
right_message: "osc".into(), right_message: "osc".into(),
right_span: call right_span: call
.get_named_arg("osc") .get_flag_span(stack, "osc")
.expect("Unexpected missing argument") .expect("Unexpected missing argument"),
.span,
}); });
} }
let code_string = if param_is_string { let code_string = if param_is_string {
@ -741,10 +745,7 @@ fn heavy_lifting(code: Value, escape: bool, osc: bool, call: &Call) -> Result<St
if (escape || osc) && (param_is_valid_string) { if (escape || osc) && (param_is_valid_string) {
let code_vec: Vec<char> = code_string.chars().collect(); let code_vec: Vec<char> = code_string.chars().collect();
if code_vec[0] == '\\' { if code_vec[0] == '\\' {
let span = match call.get_flag_expr("escape") { let span = call.get_flag_span(stack, "escape").unwrap_or(call.head);
Some(expr) => expr.span,
None => call.head,
};
return Err(ShellError::TypeMismatch { return Err(ShellError::TypeMismatch {
err_message: "no need for escape characters".into(), err_message: "no need for escape characters".into(),

View File

@ -58,7 +58,7 @@ impl Command for IsTerminal {
_ => { _ => {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "Only one stream may be checked".into(), msg: "Only one stream may be checked".into(),
span: Span::merge_many(call.arguments.iter().map(|arg| arg.span())), span: call.arguments_span(),
}); });
} }
}; };

View File

@ -84,27 +84,26 @@ impl Command for Kill {
{ {
return Err(ShellError::IncompatibleParameters { return Err(ShellError::IncompatibleParameters {
left_message: "force".to_string(), left_message: "force".to_string(),
left_span: call left_span: call.get_flag_span(stack, "force").ok_or_else(|| {
.get_named_arg("force") ShellError::GenericError {
.ok_or_else(|| ShellError::GenericError {
error: "Flag error".into(), error: "Flag error".into(),
msg: "flag force not found".into(), msg: "flag force not found".into(),
span: Some(call.head), span: Some(call.head),
help: None, help: None,
inner: vec![], inner: vec![],
})? }
.span, })?,
right_message: "signal".to_string(), right_message: "signal".to_string(),
right_span: Span::merge( right_span: Span::merge(
call.get_named_arg("signal") call.get_flag_span(stack, "signal").ok_or_else(|| {
.ok_or_else(|| ShellError::GenericError { ShellError::GenericError {
error: "Flag error".into(), error: "Flag error".into(),
msg: "flag signal not found".into(), msg: "flag signal not found".into(),
span: Some(call.head), span: Some(call.head),
help: None, help: None,
inner: vec![], inner: vec![],
})? }
.span, })?,
signal_span, signal_span,
), ),
}); });

View File

@ -17,8 +17,7 @@ pub use str_::*;
use nu_engine::CallExt; use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::Call, engine::{Call, EngineState, Stack, StateWorkingSet},
engine::{EngineState, Stack, StateWorkingSet},
ShellError, ShellError,
}; };

View File

@ -62,7 +62,7 @@ On Windows based systems, Nushell will wait for the command to finish and then e
command.envs(envs); command.envs(envs);
// Configure args. // Configure args.
let args = crate::eval_arguments_from_call(engine_state, stack, call)?; let args = crate::eval_arguments_from_call(engine_state, stack, call.assert_ast_call()?)?;
command.args(args.into_iter().map(|s| s.item)); command.args(args.into_iter().map(|s| s.item));
// Execute the child process, replacing/terminating the current process // Execute the child process, replacing/terminating the current process

View File

@ -87,7 +87,7 @@ impl Command for NuCheck {
&path_str.item, &path_str.item,
engine_state, engine_state,
stack, stack,
get_dirs_var_from_call(call), get_dirs_var_from_call(call.assert_ast_call()?), // FIXME
) { ) {
Ok(path) => { Ok(path) => {
if let Some(path) = path { if let Some(path) = path {

View File

@ -1,7 +1,7 @@
use nu_cmd_base::hook::eval_hook; use nu_cmd_base::hook::eval_hook;
use nu_engine::{command_prelude::*, env_to_strings, get_eval_expression}; use nu_engine::{command_prelude::*, env_to_strings, get_eval_expression};
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression}, ast::{self, Expr, Expression},
did_you_mean, did_you_mean,
process::ChildProcess, process::ChildProcess,
ByteStream, NuGlob, OutDest, ByteStream, NuGlob, OutDest,
@ -45,6 +45,9 @@ impl Command for External {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// FIXME: this currently only works with AST calls, but I think #13089 totally fixes that
// so if I can merge that, this should just work fine
let call = call.assert_ast_call()?;
let cwd = engine_state.cwd(Some(stack))?; let cwd = engine_state.cwd(Some(stack))?;
// Evaluate the command name in the same way the arguments are evaluated. Since this isn't // Evaluate the command name in the same way the arguments are evaluated. Since this isn't
@ -236,7 +239,7 @@ fn remove_quotes(s: &str) -> Cow<'_, str> {
pub fn eval_arguments_from_call( pub fn eval_arguments_from_call(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &ast::Call,
) -> Result<Vec<Spanned<String>>, ShellError> { ) -> Result<Vec<Spanned<String>>, ShellError> {
let ctrlc = &engine_state.ctrlc; let ctrlc = &engine_state.ctrlc;
let cwd = engine_state.cwd(Some(stack))?; let cwd = engine_state.cwd(Some(stack))?;

View File

@ -1,10 +1,5 @@
use nu_protocol::record; use nu_engine::command_prelude::*;
use nu_protocol::Value; use nu_protocol::{record, Value};
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Type,
};
#[derive(Clone)] #[derive(Clone)]
pub struct UName; pub struct UName;

View File

@ -339,7 +339,7 @@ fn get_theme_flag(
struct CmdInput<'a> { struct CmdInput<'a> {
engine_state: &'a EngineState, engine_state: &'a EngineState,
stack: &'a mut Stack, stack: &'a mut Stack,
call: &'a Call, call: &'a Call<'a>,
data: PipelineData, data: PipelineData,
} }
@ -347,7 +347,7 @@ impl<'a> CmdInput<'a> {
fn new( fn new(
engine_state: &'a EngineState, engine_state: &'a EngineState,
stack: &'a mut Stack, stack: &'a mut Stack,
call: &'a Call, call: &'a Call<'a>,
data: PipelineData, data: PipelineData,
) -> Self { ) -> Self {
Self { Self {

View File

@ -4,7 +4,7 @@ use nu_protocol::{
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{self, EngineState, Stack, StateWorkingSet}, engine::{self, EngineState, Stack, StateWorkingSet},
eval_const::eval_constant, eval_const::eval_constant,
ir, FromValue, ShellError, Value, ir, FromValue, ShellError, Span, Value,
}; };
pub trait CallExt { pub trait CallExt {
@ -23,6 +23,9 @@ pub trait CallExt {
name: &str, name: &str,
) -> Result<Option<T>, ShellError>; ) -> Result<Option<T>, ShellError>;
/// Efficiently get the span of a flag argument
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span>;
fn rest<T: FromValue>( fn rest<T: FromValue>(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -107,6 +110,10 @@ impl CallExt for ast::Call {
} }
} }
fn get_flag_span(&self, _stack: &Stack, name: &str) -> Option<Span> {
self.get_named_arg(name).map(|arg| arg.span)
}
fn rest<T: FromValue>( fn rest<T: FromValue>(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -224,6 +231,11 @@ impl CallExt for ir::Call {
} }
} }
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
self.named_iter(stack)
.find_map(|(i_name, _)| (i_name.item == name).then_some(i_name.span))
}
fn rest<T: FromValue>( fn rest<T: FromValue>(
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,
@ -317,6 +329,10 @@ impl CallExt for engine::Call<'_> {
proxy!(self.get_flag(engine_state, stack, name)) proxy!(self.get_flag(engine_state, stack, name))
} }
fn get_flag_span(&self, stack: &Stack, name: &str) -> Option<Span> {
proxy!(self.get_flag_span(stack, name))
}
fn rest<T: FromValue>( fn rest<T: FromValue>(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,

View File

@ -244,6 +244,7 @@ pub fn path_str(
} }
pub const DIR_VAR_PARSER_INFO: &str = "dirs_var"; pub const DIR_VAR_PARSER_INFO: &str = "dirs_var";
// FIXME: this should be possible on IR calls
pub fn get_dirs_var_from_call(call: &Call) -> Option<VarId> { pub fn get_dirs_var_from_call(call: &Call) -> Option<VarId> {
call.get_parser_info(DIR_VAR_PARSER_INFO).and_then(|x| { call.get_parser_info(DIR_VAR_PARSER_INFO).and_then(|x| {
if let Expr::Var(id) = x.expr { if let Expr::Var(id) = x.expr {

View File

@ -1,7 +1,7 @@
use nu_parser::*; use nu_parser::*;
use nu_protocol::{ use nu_protocol::{
ast::{Argument, Call, Expr, ExternalArgument, PathMember, Range}, ast::{Argument, Expr, ExternalArgument, PathMember, Range},
engine::{Command, EngineState, Stack, StateWorkingSet}, engine::{Call, Command, EngineState, Stack, StateWorkingSet},
ParseError, PipelineData, ShellError, Signature, Span, SyntaxShape, ParseError, PipelineData, ShellError, Signature, Span, SyntaxShape,
}; };
use rstest::rstest; use rstest::rstest;
@ -1507,10 +1507,7 @@ mod range {
#[cfg(test)] #[cfg(test)]
mod input_types { mod input_types {
use super::*; use super::*;
use nu_protocol::{ use nu_protocol::{ast::Argument, engine::Call, Category, PipelineData, ShellError, Type};
ast::{Argument, Call},
Category, PipelineData, ShellError, Type,
};
#[derive(Clone)] #[derive(Clone)]
pub struct LsTest; pub struct LsTest;

View File

@ -21,6 +21,18 @@ pub enum CallImpl<'a> {
} }
impl Call<'_> { impl Call<'_> {
/// Returns a new AST call with the given span. This is often used by commands that need an
/// empty call to pass to a command. It's not easily possible to add anything to this.
pub fn new(span: Span) -> Self {
// this is using the boxed variant, which isn't so efficient... but this is only temporary
// anyway.
Call {
head: span,
decl_id: 0,
inner: CallImpl::AstBox(Box::new(ast::Call::new(span))),
}
}
/// Convert the `Call` from any lifetime into `'static`, by cloning the data within onto the /// Convert the `Call` from any lifetime into `'static`, by cloning the data within onto the
/// heap. /// heap.
pub fn to_owned(&self) -> Call<'static> { pub fn to_owned(&self) -> Call<'static> {
@ -85,6 +97,16 @@ impl Call<'_> {
.rest_const(working_set, starting_pos) .rest_const(working_set, starting_pos)
} }
/// Returns a span covering the call's arguments.
pub fn arguments_span(&self) -> Span {
match &self.inner {
CallImpl::AstRef(call) => call.arguments_span(),
CallImpl::AstBox(call) => call.arguments_span(),
CallImpl::IrRef(call) => call.arguments_span(),
CallImpl::IrBox(call) => call.arguments_span(),
}
}
/// Returns a span covering the whole call. /// Returns a span covering the whole call.
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match &self.inner { match &self.inner {

View File

@ -175,7 +175,7 @@ impl CallBuilder {
self.inner.args_base = stack.argument_stack.get_base(); self.inner.args_base = stack.argument_stack.get_base();
} }
self.inner.args_len += 1; self.inner.args_len += 1;
self.inner.span.end = self.inner.span.end.max(argument.span().end); self.inner.span = self.inner.span.append(argument.span());
stack.argument_stack.push(argument); stack.argument_stack.push(argument);
self self
} }