diff --git a/crates/nu-command/tests/commands/alias.rs b/crates/nu-command/tests/commands/alias.rs index 984740ee41..91fb269939 100644 --- a/crates/nu-command/tests/commands/alias.rs +++ b/crates/nu-command/tests/commands/alias.rs @@ -74,8 +74,19 @@ fn alias_fails_with_invalid_name() { let actual = nu!( cwd: ".", pipeline( r#" - alias ^foo = "bar" + alias ^foo = echo "bar" "# )); assert!(actual.err.contains(err_msg)); } + +#[test] +fn cant_alias_keyword() { + let actual = nu!( + cwd: ".", pipeline( + r#" + alias ou = let + "# + )); + assert!(actual.err.contains("cant_alias_keyword")); +} diff --git a/crates/nu-parser/src/errors.rs b/crates/nu-parser/src/errors.rs index 140b7ea2e6..ec9f394507 100644 --- a/crates/nu-parser/src/errors.rs +++ b/crates/nu-parser/src/errors.rs @@ -95,6 +95,13 @@ pub enum ParseError { )] UnexpectedKeyword(String, #[label("unexpected {0}")] Span), + #[error("Can't create alias to parser keyword.")] + #[diagnostic( + code(nu::parser::cant_alias_keyword), + help("Only the following keywords can be aliased: {0}.") + )] + CantAliasKeyword(String, #[label("not supported in alias")] Span), + #[error("Unknown operator")] #[diagnostic(code(nu::parser::unknown_operator), help("{1}"))] UnknownOperator( @@ -419,6 +426,7 @@ impl ParseError { ParseError::UnsupportedOperation(_, _, _, s, _) => *s, ParseError::ExpectedKeyword(_, s) => *s, ParseError::UnexpectedKeyword(_, s) => *s, + ParseError::CantAliasKeyword(_, s) => *s, ParseError::BuiltinCommandInPipeline(_, s) => *s, ParseError::AssignInPipeline(_, _, _, s) => *s, ParseError::LetBuiltinVar(_, s) => *s, diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 0eb301d9a4..fd29da114a 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -28,6 +28,92 @@ use crate::{ unescape_unquote_string, ParseError, Token, TokenContents, }; +/// These parser keywords can be aliased +pub const ALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[b"overlay hide", b"overlay new", b"overlay use"]; + +/// These parser keywords cannot be aliased (either not possible, or support not yet added) +pub const UNALIASABLE_PARSER_KEYWORDS: &[&[u8]] = &[ + b"export", + b"def", + b"export def", + b"for", + b"extern", + b"export extern", + b"alias", + b"export alias", + b"export-env", + b"module", + b"use", + b"export use", + b"hide", + // b"overlay", + // b"overlay hide", + // b"overlay new", + // b"overlay use", + b"let", + b"const", + b"mut", + b"source", + b"where", + b"register", +]; + +/// Check whether spans start with a parser keyword that can be aliased +pub fn is_unaliasable_parser_keyword(working_set: &StateWorkingSet, spans: &[Span]) -> bool { + // try two words + if let (Some(span1), Some(span2)) = (spans.get(0), spans.get(1)) { + let cmd_name = working_set.get_span_contents(span(&[*span1, *span2])); + return UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name); + } + + // try one word + if let Some(span1) = spans.get(0) { + let cmd_name = working_set.get_span_contents(*span1); + UNALIASABLE_PARSER_KEYWORDS.contains(&cmd_name) + } else { + false + } +} + +/// This is a new more compact method of calling parse_xxx() functions without repeating the +/// parse_call() in each function. Remaining keywords can be moved here. +pub fn parse_keyword( + working_set: &mut StateWorkingSet, + lite_command: &LiteCommand, + expand_aliases_denylist: &[usize], + is_subexpression: bool, +) -> (Pipeline, Option) { + let (call_expr, err) = parse_call( + working_set, + &lite_command.parts, + lite_command.parts[0], + expand_aliases_denylist, + is_subexpression, + ); + + if err.is_some() { + return (Pipeline::from_vec(vec![call_expr]), err); + } + + if let Expression { + expr: Expr::Call(call), + .. + } = call_expr.clone() + { + // Apply parse keyword side effects + let cmd = working_set.get_decl(call.decl_id); + + match cmd.name() { + "overlay hide" => parse_overlay_hide(working_set, call), + "overlay new" => parse_overlay_new(working_set, call), + "overlay use" => parse_overlay_use(working_set, call, expand_aliases_denylist), + _ => (Pipeline::from_vec(vec![call_expr]), err), + } + } else { + (Pipeline::from_vec(vec![call_expr]), err) + } +} + pub fn parse_def_predecl( working_set: &mut StateWorkingSet, spans: &[Span], @@ -657,7 +743,11 @@ pub fn parse_alias( if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) { let (command_spans, rest_spans) = spans.split_at(split_id); - let ParsedInternalCall { call, output, .. } = parse_internal_call( + let ParsedInternalCall { + call: alias_call, + output, + .. + } = parse_internal_call( working_set, span(command_spans), rest_spans, @@ -665,16 +755,17 @@ pub fn parse_alias( expand_aliases_denylist, ); - if call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), - None, - ); + let has_help_flag = alias_call.has_flag("help"); + + let alias_pipeline = Pipeline::from_vec(vec![Expression { + expr: Expr::Call(alias_call), + span: span(spans), + ty: output, + custom_completion: None, + }]); + + if has_help_flag { + return (alias_pipeline, None); } if spans.len() >= split_id + 3 { @@ -692,12 +783,7 @@ pub fn parse_alias( if let Some(mod_name) = module_name { if alias_name == mod_name { return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), + alias_pipeline, Some(ParseError::NamedAsModule( "alias".to_string(), String::from_utf8_lossy(&alias_name).to_string(), @@ -708,12 +794,7 @@ pub fn parse_alias( if &alias_name == b"main" { return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), + alias_pipeline, Some(ParseError::ExportMainAliasNotAllowed(spans[split_id])), ); } @@ -741,21 +822,36 @@ pub fn parse_alias( let (command, wrapped_call) = match expr { Expression { - expr: Expr::Call(ref call), + expr: Expr::Call(ref rhs_call), .. - } => (Some(working_set.get_decl(call.decl_id).clone_box()), expr), + } => { + let cmd = working_set.get_decl(rhs_call.decl_id); + + if cmd.is_parser_keyword() + && !ALIASABLE_PARSER_KEYWORDS.contains(&cmd.name().as_bytes()) + { + return ( + alias_pipeline, + Some(ParseError::CantAliasKeyword( + ALIASABLE_PARSER_KEYWORDS + .iter() + .map(|bytes| String::from_utf8_lossy(bytes).to_string()) + .collect::>() + .join(", "), + rhs_call.head, + )), + ); + } + + (Some(cmd.clone_box()), expr) + } Expression { expr: Expr::ExternalCall(..), .. } => (None, expr), _ => { return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: output, - custom_completion: None, - }]), + alias_pipeline, Some(ParseError::InternalError( "Parsed call not a call".into(), expr.span, @@ -798,15 +894,7 @@ pub fn parse_alias( None }; - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - err, - ); + return (alias_pipeline, err); } ( @@ -2582,220 +2670,27 @@ pub fn parse_hide( } } -pub fn parse_overlay( - working_set: &mut StateWorkingSet, - spans: &[Span], - expand_aliases_denylist: &[usize], -) -> (Pipeline, Option) { - if working_set.get_span_contents(spans[0]) != b"overlay" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'overlay' command".into(), - span(spans), - )), - ); - } - - if spans.len() > 1 { - let subcommand = working_set.get_span_contents(spans[1]); - - match subcommand { - b"use" => { - return parse_overlay_use(working_set, spans, expand_aliases_denylist); - } - b"list" => { - // TODO: Abstract this code blob, it's repeated all over the place: - let call = match working_set.find_decl(b"overlay list", &Type::Any) { - Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( - working_set, - span(&spans[..2]), - if spans.len() > 2 { &spans[2..] } else { &[] }, - decl_id, - expand_aliases_denylist, - ); - let decl = working_set.get_decl(decl_id); - - let call_span = span(spans); - - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); - } - - call - } - None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'overlay' declaration not found".into(), - span(spans), - )), - ) - } - }; - - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - None, - ); - } - b"new" => { - return parse_overlay_new(working_set, spans, expand_aliases_denylist); - } - b"hide" => { - return parse_overlay_hide(working_set, spans, expand_aliases_denylist); - } - _ => { /* continue parsing overlay */ } - } - } - - let call = match working_set.find_decl(b"overlay", &Type::Any) { - Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( - working_set, - spans[0], - &spans[1..], - decl_id, - expand_aliases_denylist, - ); - let decl = working_set.get_decl(decl_id); - - let call_span = span(spans); - - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); - } - - call - } - None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'overlay' declaration not found".into(), - span(spans), - )), - ) - } - }; - - ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: span(spans), - ty: Type::Any, - custom_completion: None, - }]), - None, - ) -} - pub fn parse_overlay_new( working_set: &mut StateWorkingSet, - spans: &[Span], - expand_aliases_denylist: &[usize], + call: Box, ) -> (Pipeline, Option) { - if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay new" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'overlay new' command".into(), - span(spans), - )), - ); - } - - let (call, call_span) = match working_set.find_decl(b"overlay new", &Type::Any) { - Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( - working_set, - span(&spans[0..2]), - &spans[2..], - decl_id, - expand_aliases_denylist, - ); - let decl = working_set.get_decl(decl_id); - - let call_span = span(spans); - - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); - } - - (call, call_span) - } - None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'overlay new' declaration not found".into(), - span(spans), - )), - ) - } - }; + let call_span = call.span(); let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } }, Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } } } else { return ( - garbage_pipeline(spans), + garbage_pipeline(&[call_span]), Some(ParseError::UnknownState( "internal error: Missing required positional after call parsing".into(), call_span, @@ -2805,7 +2700,7 @@ pub fn parse_overlay_new( let pipeline = Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), - span: span(spans), + span: call_span, ty: Type::Any, custom_completion: None, }]); @@ -2829,78 +2724,26 @@ pub fn parse_overlay_new( pub fn parse_overlay_use( working_set: &mut StateWorkingSet, - spans: &[Span], + call: Box, expand_aliases_denylist: &[usize], ) -> (Pipeline, Option) { - if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay use" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'overlay use' command".into(), - span(spans), - )), - ); - } - - // TODO: Allow full import pattern as argument (requires custom naming of module/overlay) - let (call, call_span) = match working_set.find_decl(b"overlay use", &Type::Any) { - Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( - working_set, - span(&spans[0..2]), - &spans[2..], - decl_id, - expand_aliases_denylist, - ); - let decl = working_set.get_decl(decl_id); - - let call_span = span(spans); - - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); - } - - (call, call_span) - } - None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'overlay use' declaration not found".into(), - span(spans), - )), - ) - } - }; + let call_span = call.span(); let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } }, Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } } } else { return ( - garbage_pipeline(spans), + garbage_pipeline(&[call_span]), Some(ParseError::UnknownState( "internal error: Missing required positional after call parsing".into(), call_span, @@ -2916,13 +2759,13 @@ pub fn parse_overlay_use( item: s, span: new_name_expression.span, }), - Err(err) => return (garbage_pipeline(spans), Some(err)), + Err(err) => return (garbage_pipeline(&[call_span]), Some(err)), }, - Err(err) => return (garbage_pipeline(spans), Some(err)), + Err(err) => return (garbage_pipeline(&[call_span]), Some(err)), } } else { return ( - garbage_pipeline(spans), + garbage_pipeline(&[call_span]), Some(ParseError::ExpectedKeyword( "as keyword".to_string(), kw_expression.span, @@ -2938,7 +2781,7 @@ pub fn parse_overlay_use( let pipeline = Pipeline::from_vec(vec![Expression { expr: Expr::Call(call.clone()), - span: span(spans), + span: call_span, ty: Type::Any, custom_completion: None, }]); @@ -3037,7 +2880,7 @@ pub fn parse_overlay_use( } else { return ( pipeline, - Some(ParseError::ModuleOrOverlayNotFound(spans[1])), + Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)), ); }; @@ -3081,7 +2924,7 @@ pub fn parse_overlay_use( } else { return ( pipeline, - Some(ParseError::ModuleOrOverlayNotFound(spans[1])), + Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)), ); } } else { @@ -3091,7 +2934,10 @@ pub fn parse_overlay_use( ); } } else { - return (garbage_pipeline(spans), Some(ParseError::NonUtf8(spans[1]))); + return ( + garbage_pipeline(&[call_span]), + Some(ParseError::NonUtf8(overlay_name_span)), + ); } } }; @@ -3132,7 +2978,7 @@ pub fn parse_overlay_use( let pipeline = Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), - span: span(spans), + span: call_span, ty: Type::Any, custom_completion: None, }]); @@ -3142,78 +2988,26 @@ pub fn parse_overlay_use( pub fn parse_overlay_hide( working_set: &mut StateWorkingSet, - spans: &[Span], - expand_aliases_denylist: &[usize], + call: Box, ) -> (Pipeline, Option) { - if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay hide" { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Wrong call name for 'overlay hide' command".into(), - span(spans), - )), - ); - } - - let call = match working_set.find_decl(b"overlay hide", &Type::Any) { - Some(decl_id) => { - let ParsedInternalCall { - call, - error: mut err, - output, - } = parse_internal_call( - working_set, - span(&spans[0..2]), - &spans[2..], - decl_id, - expand_aliases_denylist, - ); - let decl = working_set.get_decl(decl_id); - - let call_span = span(spans); - - err = check_call(call_span, &decl.signature(), &call).or(err); - if err.is_some() || call.has_flag("help") { - return ( - Pipeline::from_vec(vec![Expression { - expr: Expr::Call(call), - span: call_span, - ty: output, - custom_completion: None, - }]), - err, - ); - } - - call - } - None => { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: 'overlay hide' declaration not found".into(), - span(spans), - )), - ) - } - }; + let call_span = call.span(); let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { match eval_constant(working_set, expr) { Ok(val) => match value_as_string(val, expr.span) { Ok(s) => (s, expr.span), Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } }, Err(err) => { - return (garbage_pipeline(spans), Some(err)); + return (garbage_pipeline(&[call_span]), Some(err)); } } } else { ( String::from_utf8_lossy(working_set.last_overlay_name()).to_string(), - call.head, + call_span, ) }; @@ -3221,7 +3015,7 @@ pub fn parse_overlay_hide( let pipeline = Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), - span: span(spans), + span: call_span, ty: Type::Any, custom_completion: None, }]); diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 27ae3d5373..1da0fdc940 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -19,9 +19,10 @@ use nu_protocol::{ }; use crate::parse_keywords::{ - parse_alias, parse_def, parse_def_predecl, parse_export_in_block, parse_extern, parse_for, - parse_hide, parse_let_or_const, parse_module, parse_old_alias, parse_overlay, parse_source, - parse_use, parse_where, parse_where_expr, + is_unaliasable_parser_keyword, parse_alias, parse_def, parse_def_predecl, + parse_export_in_block, parse_extern, parse_for, parse_hide, parse_keyword, parse_let_or_const, + parse_module, parse_old_alias, parse_overlay_hide, parse_overlay_new, parse_overlay_use, + parse_source, parse_use, parse_where, parse_where_expr, }; use itertools::Itertools; @@ -2681,9 +2682,9 @@ pub fn unescape_string(bytes: &[u8], span: Span) -> (Vec, Option } // fall through -- escape not accepted above, must be error. err = Some(ParseError::InvalidLiteral( - "invalid unicode escape '\\u{X...}', must be 1-6 hex digits, max value 10FFFF".into(), - "string".into(), - Span::new(span.start + idx, span.end), + "invalid unicode escape '\\u{X...}', must be 1-6 hex digits, max value 10FFFF".into(), + "string".into(), + Span::new(span.start + idx, span.end), )); break 'us_loop; } @@ -4665,17 +4666,17 @@ pub fn parse_value( } else { /* Parser very sensitive to order of shapes tried. Recording the original order for postierity let shapes = [ - SyntaxShape::Binary, - SyntaxShape::Int, - SyntaxShape::Number, - SyntaxShape::Range, - SyntaxShape::DateTime, - SyntaxShape::Filesize, - SyntaxShape::Duration, - SyntaxShape::Record, - SyntaxShape::Closure(None), - SyntaxShape::Block, - SyntaxShape::String, + SyntaxShape::Binary, + SyntaxShape::Int, + SyntaxShape::Number, + SyntaxShape::Range, + SyntaxShape::DateTime, + SyntaxShape::Filesize, + SyntaxShape::Duration, + SyntaxShape::Record, + SyntaxShape::Closure(None), + SyntaxShape::Block, + SyntaxShape::String, ]; */ let shapes = [ @@ -5309,6 +5310,49 @@ pub fn parse_builtin_commands( expand_aliases_denylist: &[usize], is_subexpression: bool, ) -> (Pipeline, Option) { + if !is_math_expression_like(working_set, lite_command.parts[0], expand_aliases_denylist) + && !is_unaliasable_parser_keyword(working_set, &lite_command.parts) + { + let name = working_set.get_span_contents(lite_command.parts[0]); + if let Some(decl_id) = working_set.find_decl(name, &Type::Any) { + let cmd = working_set.get_decl(decl_id); + if cmd.is_alias() { + // Parse keywords that can be aliased. Note that we check for "unaliasable" keywords + // because alias can have any name, therefore, we can't check for "aliasable" keywords. + let (call_expr, err) = parse_call( + working_set, + &lite_command.parts, + lite_command.parts[0], + expand_aliases_denylist, + is_subexpression, + ); + + if err.is_none() { + if let Expression { + expr: Expr::Call(call), + .. + } = call_expr + { + // Apply parse keyword side effects + let cmd = working_set.get_decl(call.decl_id); + match cmd.name() { + "overlay hide" => return parse_overlay_hide(working_set, call), + "overlay new" => return parse_overlay_new(working_set, call), + "overlay use" => { + return parse_overlay_use( + working_set, + call, + expand_aliases_denylist, + ) + } + _ => { /* this alias is not a parser keyword */ } + } + } + } + } + } + } + let name = working_set.get_span_contents(lite_command.parts[0]); match name { @@ -5330,7 +5374,12 @@ pub fn parse_builtin_commands( parse_use(working_set, &lite_command.parts, expand_aliases_denylist); (pipeline, err) } - b"overlay" => parse_overlay(working_set, &lite_command.parts, expand_aliases_denylist), + b"overlay" => parse_keyword( + working_set, + lite_command, + expand_aliases_denylist, + is_subexpression, + ), b"source" | b"source-env" => { parse_source(working_set, &lite_command.parts, expand_aliases_denylist) } @@ -5346,6 +5395,7 @@ pub fn parse_builtin_commands( expand_aliases_denylist, is_subexpression, ); + (Pipeline::from_vec(vec![expr]), err) } } diff --git a/tests/overlays/mod.rs b/tests/overlays/mod.rs index 2ee188c6f3..94b86832e0 100644 --- a/tests/overlays/mod.rs +++ b/tests/overlays/mod.rs @@ -326,7 +326,7 @@ fn overlay_use_do_not_eval_twice() { } #[test] -fn remove_overlay() { +fn hide_overlay() { let inp = &[ r#"module spam { export def foo [] { "foo" } }"#, r#"overlay use spam"#, @@ -345,7 +345,7 @@ fn remove_overlay() { } #[test] -fn remove_last_overlay() { +fn hide_last_overlay() { let inp = &[ r#"module spam { export def foo [] { "foo" } }"#, r#"overlay use spam"#, @@ -364,7 +364,7 @@ fn remove_last_overlay() { } #[test] -fn remove_overlay_scoped() { +fn hide_overlay_scoped() { let inp = &[ r#"module spam { export def foo [] { "foo" } }"#, r#"overlay use spam"#, @@ -380,7 +380,7 @@ fn remove_overlay_scoped() { } #[test] -fn remove_overlay_env() { +fn hide_overlay_env() { let inp = &[ r#"module spam { export-env { let-env FOO = "foo" } }"#, r#"overlay use spam"#, @@ -396,7 +396,7 @@ fn remove_overlay_env() { } #[test] -fn remove_overlay_scoped_env() { +fn hide_overlay_scoped_env() { let inp = &[ r#"module spam { export-env { let-env FOO = "foo" } }"#, r#"overlay use spam"#, @@ -453,7 +453,7 @@ fn list_overlay_scoped() { } #[test] -fn remove_overlay_discard_decl() { +fn hide_overlay_discard_decl() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"def bagr [] { "bagr" }"#, @@ -472,7 +472,7 @@ fn remove_overlay_discard_decl() { } #[test] -fn remove_overlay_discard_alias() { +fn hide_overlay_discard_alias() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"alias bagr = echo "bagr""#, @@ -491,7 +491,7 @@ fn remove_overlay_discard_alias() { } #[test] -fn remove_overlay_discard_env() { +fn hide_overlay_discard_env() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"let-env BAGR = 'bagr'"#, @@ -507,7 +507,7 @@ fn remove_overlay_discard_env() { } #[test] -fn remove_overlay_keep_decl() { +fn hide_overlay_keep_decl() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"def bagr [] { "bagr" }"#, @@ -523,7 +523,7 @@ fn remove_overlay_keep_decl() { } #[test] -fn remove_overlay_keep_alias() { +fn hide_overlay_keep_alias() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"alias bagr = echo 'bagr'"#, @@ -539,7 +539,7 @@ fn remove_overlay_keep_alias() { } #[test] -fn remove_overlay_dont_keep_env() { +fn hide_overlay_dont_keep_env() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"let-env BAGR = 'bagr'"#, @@ -555,7 +555,7 @@ fn remove_overlay_dont_keep_env() { } #[test] -fn remove_overlay_dont_keep_overwritten_decl() { +fn hide_overlay_dont_keep_overwritten_decl() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"def foo [] { 'bar' }"#, @@ -574,7 +574,7 @@ fn remove_overlay_dont_keep_overwritten_decl() { } #[test] -fn remove_overlay_dont_keep_overwritten_alias() { +fn hide_overlay_dont_keep_overwritten_alias() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"alias bar = echo `baz`"#, @@ -593,7 +593,7 @@ fn remove_overlay_dont_keep_overwritten_alias() { } #[test] -fn remove_overlay_dont_keep_overwritten_env() { +fn hide_overlay_dont_keep_overwritten_env() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"let-env BAZ = 'bagr'"#, @@ -609,7 +609,7 @@ fn remove_overlay_dont_keep_overwritten_env() { } #[test] -fn remove_overlay_keep_decl_in_latest_overlay() { +fn hide_overlay_keep_decl_in_latest_overlay() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"def bagr [] { 'bagr' }"#, @@ -627,7 +627,7 @@ fn remove_overlay_keep_decl_in_latest_overlay() { } #[test] -fn remove_overlay_keep_alias_in_latest_overlay() { +fn hide_overlay_keep_alias_in_latest_overlay() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"alias bagr = echo 'bagr'"#, @@ -645,7 +645,7 @@ fn remove_overlay_keep_alias_in_latest_overlay() { } #[test] -fn remove_overlay_dont_keep_env_in_latest_overlay() { +fn hide_overlay_dont_keep_env_in_latest_overlay() { let inp = &[ r#"overlay use samples/spam.nu"#, r#"let-env BAGR = 'bagr'"#, @@ -811,7 +811,7 @@ fn overlay_can_add_renamed_overlay() { } #[test] -fn overlay_remove_renamed_overlay() { +fn overlay_hide_renamed_overlay() { let inp = &[ r#"module spam { export def foo [] { "foo" } }"#, r#"overlay use spam as eggs"#, @@ -827,7 +827,7 @@ fn overlay_remove_renamed_overlay() { } #[test] -fn overlay_remove_and_add_renamed_overlay() { +fn overlay_hide_and_add_renamed_overlay() { let inp = &[ r#"module spam { export def foo [] { "foo" } }"#, r#"overlay use spam as eggs"#, @@ -1261,3 +1261,53 @@ fn overlay_use_main_not_exported() { assert!(actual.err.contains("external_command")); } + +#[test] +fn alias_overlay_hide() { + let inp = &[ + r#"overlay new spam"#, + r#"def foo [] { 'foo' }"#, + r#"overlay new eggs"#, + r#"alias oh = overlay hide"#, + r#"oh spam"#, + r#"foo"#, + ]; + + let actual = nu!(cwd: "tests/overlays", pipeline(&inp.join("; "))); + let actual_repl = nu!(cwd: "tests/overlays", nu_repl_code(inp)); + + assert!(actual.err.contains("external_command")); + assert!(actual_repl.err.contains("external_command")); +} + +#[test] +fn alias_overlay_use() { + let inp = &[ + r#"module spam { export def foo [] { 'foo' } }"#, + r#"alias ou = overlay use"#, + r#"ou spam"#, + r#"foo"#, + ]; + + let actual = nu!(cwd: "tests/overlays", pipeline(&inp.join("; "))); + let actual_repl = nu!(cwd: "tests/overlays", nu_repl_code(inp)); + + assert_eq!(actual.out, "foo"); + assert_eq!(actual_repl.out, "foo"); +} + +#[test] +fn alias_overlay_new() { + let inp = &[ + r#"alias on = overlay new"#, + r#"on spam"#, + r#"on eggs"#, + r#"overlay list | last"#, + ]; + + let actual = nu!(cwd: "tests/overlays", pipeline(&inp.join("; "))); + let actual_repl = nu!(cwd: "tests/overlays", nu_repl_code(inp)); + + assert_eq!(actual.out, "eggs"); + assert_eq!(actual_repl.out, "eggs"); +}