mirror of
https://github.com/nushell/nushell
synced 2024-12-27 05:23:11 +00:00
Change alias shape inference to proposal of RFC#4 (#2685)
* Change alias shape inference to proposal of RFC#4 * Remove commented code * Fix typo * Change comment to be more informative * Make match statement to lookup in table * Remove resolved question https://github.com/nushell/nushell/pull/2685#discussion_r509832054 * Pick ...or_insert_dependency functions into pieces Previously there was get_shape_of_expr_or_insert dependency, now there is get_shape_of_expr and get_shape_of_expr_or_insert_dependency 2 new functions have been added: get_result_shape_of_math_expr and get_result_shape_of_math_expr_or_insert_dependency * Remove flattening of deep binary expressions Previously deep binary expressions have been flattened through the insertion of fake vars. This logic was quite complicated. Now if a variable depends on the result shape of a binary expression and the result shape can't be computed, the variable simply depends on the whole binary. * Change Expression::Variable(Variable::It(...)) to Expression::Variable(...) * Simplify get_result_shapes_in_math_expr * Simplify infer_shapes_in_binary_expr * Clarify comment * Clarify comment * Fix clippy lint * Move check for real var into checked_insert * Remove comment * Rename var
This commit is contained in:
parent
46d1938f5c
commit
c6fe58467b
13 changed files with 1502 additions and 308 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2864,6 +2864,7 @@ dependencies = [
|
|||
"ctrlc",
|
||||
"dunce",
|
||||
"futures 0.3.5",
|
||||
"itertools",
|
||||
"log 0.4.11",
|
||||
"nu-cli",
|
||||
"nu-data",
|
||||
|
@ -2934,6 +2935,7 @@ dependencies = [
|
|||
"ichwh",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"lazy_static 1.4.0",
|
||||
"log 0.4.11",
|
||||
"meval",
|
||||
"nu-data",
|
||||
|
|
|
@ -51,6 +51,7 @@ ctrlc = {version = "3.1.6", optional = true}
|
|||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||
log = "0.4.11"
|
||||
pretty_env_logger = "0.4.0"
|
||||
itertools = "0.9.0"
|
||||
|
||||
[dev-dependencies]
|
||||
dunce = "1.0.1"
|
||||
|
|
|
@ -91,6 +91,7 @@ uom = {version = "0.28.0", features = ["f64", "try-from"]}
|
|||
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
||||
which = {version = "4.0.2", optional = true}
|
||||
zip = {version = "0.5.7", optional = true}
|
||||
lazy_static = "1.*"
|
||||
|
||||
Inflector = "0.11"
|
||||
clipboard = {version = "0.5.0", optional = true}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
use crate::command_registry::CommandRegistry;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
|
||||
use crate::types::deduction::{VarDeclaration, VarSyntaxShapeDeductor};
|
||||
use deduction_to_signature::DeductionToSignature;
|
||||
use log::trace;
|
||||
use nu_data::config;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::SignatureRegistry;
|
||||
use nu_protocol::hir::{ClassifiedCommand, Expression, NamedValue, SpannedExpression};
|
||||
use nu_protocol::{
|
||||
hir::Block, CommandAction, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
|
||||
UntaggedValue, Value,
|
||||
hir::Block, CommandAction, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::Tagged;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Alias;
|
||||
|
||||
|
@ -86,7 +86,6 @@ pub async fn alias(
|
|||
},
|
||||
_ctx,
|
||||
) = args.process(®istry).await?;
|
||||
let mut processed_args: Vec<String> = vec![];
|
||||
|
||||
if let Some(true) = save {
|
||||
let mut result = nu_data::config::read(name.clone().tag, &None)?;
|
||||
|
@ -110,7 +109,7 @@ pub async fn alias(
|
|||
let alias: Value = raw_input.trim().to_string().into();
|
||||
let alias_start = raw_input.find('[').unwrap_or(0); // used to check if the same alias already exists
|
||||
|
||||
// add to startup if alias doesn't exist and replce if it does
|
||||
// add to startup if alias doesn't exist and replace if it does
|
||||
match result.get_mut("startup") {
|
||||
Some(startup) => {
|
||||
if let UntaggedValue::Table(ref mut commands) = startup.value {
|
||||
|
@ -132,209 +131,41 @@ pub async fn alias(
|
|||
config::write(&result, &None)?;
|
||||
}
|
||||
|
||||
for item in list.iter() {
|
||||
if let Ok(string) = item.as_string() {
|
||||
processed_args.push(format!("${}", string));
|
||||
let mut processed_args: Vec<VarDeclaration> = vec![];
|
||||
for (_, item) in list.iter().enumerate() {
|
||||
match item.as_string() {
|
||||
Ok(var_name) => {
|
||||
let dollar_var_name = format!("${}", var_name);
|
||||
processed_args.push(VarDeclaration {
|
||||
name: dollar_var_name,
|
||||
// type_decl: None,
|
||||
span: item.tag.span,
|
||||
});
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a string",
|
||||
"expected a string",
|
||||
item.tag(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
trace!("Found vars: {:?}", processed_args);
|
||||
|
||||
let inferred_shapes = {
|
||||
if let Some(true) = infer {
|
||||
VarSyntaxShapeDeductor::infer_vars(&processed_args, &block, ®istry)?
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a string",
|
||||
"expected a string",
|
||||
item.tag(),
|
||||
));
|
||||
processed_args.into_iter().map(|arg| (arg, None)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(true) = infer {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::AddAlias(
|
||||
name.to_string(),
|
||||
to_arg_shapes(processed_args, &block, ®istry)?,
|
||||
block,
|
||||
),
|
||||
)))
|
||||
} else {
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::AddAlias(
|
||||
name.to_string(),
|
||||
processed_args
|
||||
.into_iter()
|
||||
.map(|arg| (arg, SyntaxShape::Any))
|
||||
.collect(),
|
||||
block,
|
||||
),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_arg_shapes(
|
||||
args: Vec<String>,
|
||||
block: &Block,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<Vec<(String, SyntaxShape)>, ShellError> {
|
||||
match find_block_shapes(block, registry) {
|
||||
Ok(found) => Ok(args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
(
|
||||
arg.clone(),
|
||||
match found.get(arg) {
|
||||
None | Some((_, None)) => SyntaxShape::Any,
|
||||
Some((_, Some(shape))) => *shape,
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect()),
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
type ShapeMap = HashMap<String, (Span, Option<SyntaxShape>)>;
|
||||
|
||||
fn check_insert(
|
||||
existing: &mut ShapeMap,
|
||||
to_add: (String, (Span, Option<SyntaxShape>)),
|
||||
) -> Result<(), ShellError> {
|
||||
match (to_add.1).1 {
|
||||
None => match existing.get(&to_add.0) {
|
||||
None => {
|
||||
existing.insert(to_add.0, to_add.1);
|
||||
Ok(())
|
||||
}
|
||||
Some(_) => Ok(()),
|
||||
},
|
||||
Some(new) => match existing.insert(to_add.0.clone(), ((to_add.1).0, Some(new))) {
|
||||
None => Ok(()),
|
||||
Some(exist) => match exist.1 {
|
||||
None => Ok(()),
|
||||
Some(shape) => match shape {
|
||||
SyntaxShape::Any => Ok(()),
|
||||
shape if shape == new => Ok(()),
|
||||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Type conflict in alias variable use",
|
||||
format!("{:?}", new),
|
||||
(to_add.1).0,
|
||||
format!("{:?}", shape),
|
||||
exist.0,
|
||||
)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn check_merge(existing: &mut ShapeMap, new: &ShapeMap) -> Result<(), ShellError> {
|
||||
for (k, v) in new.iter() {
|
||||
check_insert(existing, (k.clone(), *v))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_expr_shapes(
|
||||
spanned_expr: &SpannedExpression,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<ShapeMap, ShellError> {
|
||||
match &spanned_expr.expr {
|
||||
// TODO range will need similar if/when invocations can be parsed within range expression
|
||||
Expression::Binary(bin) => find_expr_shapes(&bin.left, registry).and_then(|mut left| {
|
||||
find_expr_shapes(&bin.right, registry)
|
||||
.and_then(|right| check_merge(&mut left, &right).map(|()| left))
|
||||
}),
|
||||
Expression::Block(b) => find_block_shapes(&b, registry),
|
||||
Expression::Path(path) => match &path.head.expr {
|
||||
Expression::Invocation(b) => find_block_shapes(&b, registry),
|
||||
Expression::Variable(var, _) => {
|
||||
let mut result = HashMap::new();
|
||||
result.insert(var.to_string(), (spanned_expr.span, None));
|
||||
Ok(result)
|
||||
}
|
||||
_ => Ok(HashMap::new()),
|
||||
},
|
||||
_ => Ok(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn find_block_shapes(block: &Block, registry: &CommandRegistry) -> Result<ShapeMap, ShellError> {
|
||||
let apply_shape = |found: ShapeMap, sig_shape: SyntaxShape| -> ShapeMap {
|
||||
found
|
||||
.iter()
|
||||
.map(|(v, sh)| match sh.1 {
|
||||
None => (v.clone(), (sh.0, Some(sig_shape))),
|
||||
Some(shape) => (v.clone(), (sh.0, Some(shape))),
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
let signature = DeductionToSignature::get(&name.item, &inferred_shapes);
|
||||
trace!("Inferred signature: {:?}", signature);
|
||||
|
||||
let mut arg_shapes = HashMap::new();
|
||||
for pipeline in &block.block {
|
||||
for classified in &pipeline.list {
|
||||
match classified {
|
||||
ClassifiedCommand::Expr(spanned_expr) => {
|
||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
||||
check_merge(&mut arg_shapes, &found)?
|
||||
}
|
||||
ClassifiedCommand::Internal(internal) => {
|
||||
if let Some(signature) = registry.get(&internal.name) {
|
||||
if let Some(positional) = &internal.args.positional {
|
||||
for (i, spanned_expr) in positional.iter().enumerate() {
|
||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
||||
if i >= signature.positional.len() {
|
||||
if let Some((sig_shape, _)) = &signature.rest_positional {
|
||||
check_merge(
|
||||
&mut arg_shapes,
|
||||
&apply_shape(found, *sig_shape),
|
||||
)?;
|
||||
} else {
|
||||
unreachable!("should have error'd in parsing");
|
||||
}
|
||||
} else {
|
||||
let (pos_type, _) = &signature.positional[i];
|
||||
match pos_type {
|
||||
// TODO pass on mandatory/optional?
|
||||
PositionalType::Mandatory(_, sig_shape)
|
||||
| PositionalType::Optional(_, sig_shape) => {
|
||||
check_merge(
|
||||
&mut arg_shapes,
|
||||
&apply_shape(found, *sig_shape),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(named) = &internal.args.named {
|
||||
for (name, val) in named.iter() {
|
||||
if let NamedValue::Value(_, spanned_expr) = val {
|
||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
||||
match signature.named.get(name) {
|
||||
None => {
|
||||
unreachable!("should have error'd in parsing");
|
||||
}
|
||||
Some((named_type, _)) => {
|
||||
if let NamedType::Mandatory(_, sig_shape)
|
||||
| NamedType::Optional(_, sig_shape) = named_type
|
||||
{
|
||||
check_merge(
|
||||
&mut arg_shapes,
|
||||
&apply_shape(found, *sig_shape),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!("registry has lost name it provided");
|
||||
}
|
||||
}
|
||||
ClassifiedCommand::Dynamic(_) | ClassifiedCommand::Error(_) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(arg_shapes)
|
||||
Ok(OutputStream::one(ReturnSuccess::action(
|
||||
CommandAction::AddAlias(Box::new(signature), block),
|
||||
)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -349,3 +180,42 @@ mod tests {
|
|||
Ok(test_examples(Alias {})?)
|
||||
}
|
||||
}
|
||||
|
||||
mod deduction_to_signature {
|
||||
//For now this logic is relativly simple.
|
||||
//For each var, one mandatory positional is added.
|
||||
//As soon as more support for optional positional arguments is arrived,
|
||||
//this logic might be a little bit more tricky.
|
||||
use crate::types::deduction::{Deduction, VarDeclaration};
|
||||
use nu_protocol::{PositionalType, Signature, SyntaxShape};
|
||||
|
||||
pub struct DeductionToSignature {}
|
||||
impl DeductionToSignature {
|
||||
pub fn get(
|
||||
cmd_name: &str,
|
||||
deductions: &[(VarDeclaration, Option<Deduction>)],
|
||||
) -> Signature {
|
||||
let mut signature = Signature::build(cmd_name);
|
||||
for (decl, deduction) in deductions {
|
||||
match deduction {
|
||||
None => signature.positional.push((
|
||||
PositionalType::optional(&decl.name, SyntaxShape::Any),
|
||||
decl.name.clone(),
|
||||
)),
|
||||
Some(deduction) => match deduction {
|
||||
Deduction::VarShapeDeduction(normal_var_deduction) => {
|
||||
signature.positional.push((
|
||||
PositionalType::optional(
|
||||
&decl.name,
|
||||
normal_var_deduction[0].deduction,
|
||||
),
|
||||
decl.name.clone(),
|
||||
))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
signature
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,9 +185,9 @@ pub(crate) async fn run_internal_command(
|
|||
));
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
CommandAction::AddAlias(name, args, block) => {
|
||||
CommandAction::AddAlias(sig, block) => {
|
||||
context.add_commands(vec![whole_stream_command(
|
||||
AliasCommand::new(name, args, block),
|
||||
AliasCommand::new(*sig, block),
|
||||
)]);
|
||||
InputStream::from_stream(futures::stream::iter(vec![]))
|
||||
}
|
||||
|
|
|
@ -4,29 +4,22 @@ use crate::prelude::*;
|
|||
|
||||
use derive_new::new;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::Block, Scope, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_protocol::{hir::Block, PositionalType, Scope, Signature, UntaggedValue};
|
||||
|
||||
#[derive(new, Clone)]
|
||||
pub struct AliasCommand {
|
||||
name: String,
|
||||
args: Vec<(String, SyntaxShape)>,
|
||||
sig: Signature,
|
||||
block: Block,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for AliasCommand {
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
&self.sig.name
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
let mut alias = Signature::build(&self.name);
|
||||
|
||||
for (arg, shape) in &self.args {
|
||||
alias = alias.optional(arg, *shape, "");
|
||||
}
|
||||
|
||||
alias
|
||||
self.sig.clone()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -43,7 +36,7 @@ impl WholeStreamCommand for AliasCommand {
|
|||
let mut block = self.block.clone();
|
||||
block.set_redirect(call_info.args.external_redirection);
|
||||
|
||||
let alias_command = self.clone();
|
||||
// let alias_command = self.clone();
|
||||
let mut context = EvaluationContext::from_args(&args, ®istry);
|
||||
let input = args.input;
|
||||
|
||||
|
@ -51,21 +44,27 @@ impl WholeStreamCommand for AliasCommand {
|
|||
let evaluated = call_info.evaluate(®istry).await?;
|
||||
|
||||
let mut vars = IndexMap::new();
|
||||
|
||||
let mut num_positionals = 0;
|
||||
if let Some(positional) = &evaluated.args.positional {
|
||||
num_positionals = positional.len();
|
||||
for (pos, arg) in positional.iter().enumerate() {
|
||||
vars.insert(alias_command.args[pos].0.to_string(), arg.clone());
|
||||
for (idx, arg) in positional.iter().enumerate() {
|
||||
let pos_type = &self.sig.positional[idx].0;
|
||||
match pos_type {
|
||||
PositionalType::Mandatory(name, _) | PositionalType::Optional(name, _) => {
|
||||
vars.insert(name.clone(), arg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if alias_command.args.len() > num_positionals {
|
||||
for idx in 0..(alias_command.args.len() - num_positionals) {
|
||||
vars.insert(
|
||||
alias_command.args[idx + num_positionals].0.to_string(),
|
||||
UntaggedValue::nothing().into_untagged_value(),
|
||||
);
|
||||
//Fill out every missing argument with empty value
|
||||
if self.sig.positional.len() > num_positionals {
|
||||
for idx in num_positionals..self.sig.positional.len() {
|
||||
let pos_type = &self.sig.positional[idx].0;
|
||||
match pos_type {
|
||||
PositionalType::Mandatory(name, _) | PositionalType::Optional(name, _) => {
|
||||
vars.insert(name.clone(), UntaggedValue::nothing().into_untagged_value());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ mod path;
|
|||
mod plugin;
|
||||
mod shell;
|
||||
mod stream;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
1
crates/nu-cli/src/types.rs
Normal file
1
crates/nu-cli/src/types.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub(crate) mod deduction;
|
1103
crates/nu-cli/src/types/deduction.rs
Normal file
1103
crates/nu-cli/src/types/deduction.rs
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,118 +1,334 @@
|
|||
use nu_test_support::nu;
|
||||
use nu_test_support::playground::Playground;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nu_test_support::nu;
|
||||
use nu_test_support::playground::Playground;
|
||||
|
||||
#[test]
|
||||
fn alias_args_work() {
|
||||
Playground::setup("append_test_1", |dirs, _| {
|
||||
#[test]
|
||||
fn alias_without_args() {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias double_echo [a b] {echo $a $b}
|
||||
alias -i e [] {^echo hi nushell | to json}
|
||||
e
|
||||
"#
|
||||
);
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!(actual.out, "\"hi nushell\\n\"");
|
||||
#[cfg(windows)]
|
||||
assert_eq!(actual.out, "\"hi nushell\\r\\n\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_args_work() {
|
||||
Playground::setup("append_test_2", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
alias -i double_echo [b] {echo $b | to json}
|
||||
double_echo 1kb
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "1024");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_args_double_echo() {
|
||||
Playground::setup("append_test_1", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
alias -i double_echo [a b] {echo $a $b}
|
||||
double_echo 1 2 | to json
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "[1,2]");
|
||||
})
|
||||
}
|
||||
assert_eq!(actual.out, "[1,2]");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_missing_args_work() {
|
||||
Playground::setup("append_test_1", |dirs, _| {
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn alias_parses_path_tilde() {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
alias double_echo [a b] {^echo $a $b}
|
||||
double_echo bob
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "bob");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn alias_parses_path_tilde() {
|
||||
let actual = nu!(
|
||||
cwd: "tests/fixtures/formats",
|
||||
r#"
|
||||
alias -i new-cd [dir] { cd $dir }
|
||||
new-cd ~
|
||||
pwd
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
assert!(actual.out.contains("home"));
|
||||
#[cfg(target_os = "macos")]
|
||||
assert!(actual.out.contains("Users"));
|
||||
}
|
||||
//If this fails for you, check for any special unicode characters in your ~ path
|
||||
assert!(actual.out.chars().filter(|c| c.clone() == '/').count() == 2);
|
||||
#[cfg(target_os = "linux")]
|
||||
assert!(actual.out.contains("home"));
|
||||
#[cfg(target_os = "macos")]
|
||||
assert!(actual.out.contains("Users"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn error_alias_wrong_shape_shallow() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
#[test]
|
||||
fn alias_missing_args_work() {
|
||||
Playground::setup("append_test_1", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.root(),
|
||||
r#"
|
||||
alias double_echo [a b] {^echo $a $b}
|
||||
double_echo bob
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "bob");
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_in_str_var_right() {
|
||||
// Error from binary of main:
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> alias -i lw [rust_newbie] {echo 1 2 3 | where "hello_world" in $rust_newbie | to json }
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> lw [ big ]
|
||||
// error: Type Error
|
||||
// ┌─ shell:1:11
|
||||
// │
|
||||
// 1 │ lw [ big ]
|
||||
// │ Expected row or table, found integer
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lw [newbie] {echo 1 2 3 | where "hello_world" in $newbie | to json}
|
||||
lw [hello_world_test_repo]
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "[1,2,3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_with_in_str_var_right_mismatch() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lw [rust_newbie] { echo 1 2 3 | where "hello_world" in $rust_newbie | to json }
|
||||
lw [ big_brain_programmer ]
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_with_in_err() {
|
||||
//in operator only applicable for strings atm
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lw [p] {echo 1 2 3 | where $p in [1 3 2] | to json}
|
||||
lw /root/sys
|
||||
"#
|
||||
);
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_contains() {
|
||||
// Output of command in main
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> echo 1 2 3 | where 4 in [1 hi 3] | to json
|
||||
// [1,3]
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> echo 1 2 3 | where 4 in [1 hi 3] | to json
|
||||
// [1,3]
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lw [p] {echo 1 2 3 | where $p in [1 hi 3] | to json}
|
||||
lw 1
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "[1,2,3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_contains_and_var_is_right_side() {
|
||||
//Output of command in main
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> echo 1 2 3 | where 1 in [1 2 hi] | to json
|
||||
// [1,2]
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lw [p] {echo 1 2 3 | where 1 in $p | to json}
|
||||
lw [1 2 hi]
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "[1,2,3]");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_alias_wrong_shape_shallow() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i round-to [num digits] { echo $num | str from -d $digits }
|
||||
round-to 3.45 a
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn error_alias_wrong_shape_deep_invocation() {
|
||||
let actual = nu!(
|
||||
#[test]
|
||||
fn error_alias_wrong_shape_deep_invocation() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i round-to [nums digits] { echo $nums | each {= $(str from -d $digits)}}
|
||||
round-to 3.45 a
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn error_alias_wrong_shape_deep_binary() {
|
||||
let actual = nu!(
|
||||
#[test]
|
||||
fn error_alias_wrong_shape_deep_binary() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i round-plus-one [nums digits] { echo $nums | each {= $(str from -d $digits | str to-decimal) + 1}}
|
||||
round-plus-one 3.45 a
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn error_alias_wrong_shape_deeper_binary() {
|
||||
let actual = nu!(
|
||||
#[test]
|
||||
fn error_alias_wrong_shape_deeper_binary() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i round-one-more [num digits] { echo $num | str from -d $(= $digits + 1) }
|
||||
round-one-more 3.45 a
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
assert!(actual.err.contains("Type"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_alias_syntax_shape_clash() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i clash [a] { echo 1.1 2 3 | each { str from -d $a } | range $a } }
|
||||
#[test]
|
||||
fn error_alias_syntax_shape_clash() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i clash [a] { echo 1.1 2 3 | each { str from -d $a } | range $a }
|
||||
"#
|
||||
);
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("alias"));
|
||||
assert!(actual.err.contains("Contrary types for variable $a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_math_var() {
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i echo_math [math] { echo {= 1 + $math}}
|
||||
echo_math 1 + 1 | to json
|
||||
"#
|
||||
);
|
||||
|
||||
assert_eq!(actual.out, "3");
|
||||
}
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_math_var2() {
|
||||
// Doesn't work also not on main
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> alias -i l [nums digits math] {echo $nums | each {= $(str from -d $digits | str to-decimal) + $math}}}
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> l 3.45 2 1
|
||||
// error: Coercion error
|
||||
// ┌─ shell:1:11
|
||||
// │
|
||||
// 1 │ l 3.45 2 1
|
||||
// │ nothing
|
||||
// │
|
||||
// │ decimal
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i round-plus-one [nums digits math] { echo $nums | each {= $(str from -d $digits | str to-decimal) + $math}}
|
||||
round-plus-one 3.45 2 1 + 1 | to json
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "5.45");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_with_true_and_false() {
|
||||
//https://github.com/nushell/nushell/issues/2416
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i is_empty [a] {if $(echo $a | empty?) == $true { echo $true } { echo $false }}
|
||||
is_empty ""
|
||||
"#
|
||||
);
|
||||
assert!(actual.out.contains("true"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn alias_sent_env() {
|
||||
//https://github.com/nushell/nushell/issues/1835
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i set-env [name value] { echo $nu.env | insert $name $value | get SHELL | to json }
|
||||
set-env SHELL /bin/nu
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "\"/bin/nu\"");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn alias_with_math_arg() {
|
||||
// Doesn't also work on main
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> alias -i lswh [math] {echo 1 2 3 | where $math | to json }
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> lswh $it > 2
|
||||
// error: Type Error
|
||||
// ┌─ shell:1:13
|
||||
// │
|
||||
// 1 │ lswh $it > 2
|
||||
// │ Expected boolean, found block
|
||||
|
||||
// /home/leo/repos/nushell/nushell(TypeDeduction)> lswh {$it > 2}
|
||||
// error: Type Error
|
||||
// ┌─ shell:1:15
|
||||
// │
|
||||
// 1 │ lswh {$it > 2}
|
||||
// │ Expected boolean, found block
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
alias -i lswh [math] { echo 1 2 3 | where $math | to json }
|
||||
lswh $it > 2
|
||||
"#
|
||||
);
|
||||
assert_eq!(actual.out, "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn alias_ls() {
|
||||
//https://github.com/nushell/nushell/issues/1632
|
||||
let actual = nu!(
|
||||
cwd: ".",
|
||||
r#"
|
||||
touch /tmp/nushell_alias_test
|
||||
alias -i l [x] { ls $x }
|
||||
l /tmp | to json
|
||||
"#
|
||||
);
|
||||
assert!(actual.out.contains("nushell_alias_test"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use crate::hir::Block;
|
||||
use crate::value::Value;
|
||||
use crate::SyntaxShape;
|
||||
use crate::{value::Value, Signature};
|
||||
use nu_errors::ShellError;
|
||||
use nu_source::{b, DebugDocBuilder, PrettyDebug};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -23,7 +22,8 @@ pub enum CommandAction {
|
|||
/// Enter the help shell, which allows exploring the help system
|
||||
EnterHelpShell(Value),
|
||||
/// Add an alias command
|
||||
AddAlias(String, Vec<(String, SyntaxShape)>, Block),
|
||||
/// Note: We are passing the Signature in a Box to decrease the memory size of AddAlias
|
||||
AddAlias(Box<Signature>, Block),
|
||||
/// Add plugins from path given
|
||||
AddPlugins(String),
|
||||
/// Go to the previous shell in the shell ring buffer
|
||||
|
|
|
@ -26,7 +26,7 @@ impl NamedType {
|
|||
}
|
||||
|
||||
/// The type of positional arguments
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
pub enum PositionalType {
|
||||
/// A mandatory positional argument with the expected shape of the value
|
||||
Mandatory(String, SyntaxShape),
|
||||
|
|
|
@ -2,7 +2,7 @@ use nu_source::{b, DebugDocBuilder, PrettyDebug};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The syntactic shapes that values must match to be passed into a command. You can think of this as the type-checking that occurs when you call a function.
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
||||
pub enum SyntaxShape {
|
||||
/// Any syntactic form is allowed
|
||||
Any,
|
||||
|
|
Loading…
Reference in a new issue