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:
Leonhard Kipp 2020-10-28 18:49:38 +01:00 committed by GitHub
parent 46d1938f5c
commit c6fe58467b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 1502 additions and 308 deletions

2
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -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}

View file

@ -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(&registry).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, &registry)?
} 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, &registry)?,
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
}
}
}

View file

@ -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![]))
}

View file

@ -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, &registry);
let input = args.input;
@ -51,21 +44,27 @@ impl WholeStreamCommand for AliasCommand {
let evaluated = call_info.evaluate(&registry).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());
}
}
}
}

View file

@ -33,6 +33,7 @@ mod path;
mod plugin;
mod shell;
mod stream;
pub mod types;
pub mod utils;
#[cfg(test)]

View file

@ -0,0 +1 @@
pub(crate) mod deduction;

File diff suppressed because it is too large Load diff

View file

@ -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"));
}
}

View file

@ -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

View file

@ -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),

View file

@ -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,