Allow aliasing parser keywords (#8250)

This commit is contained in:
Jakub Žádník 2023-03-10 23:20:31 +02:00 committed by GitHub
parent baddc86d9d
commit af1ab39851
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 313 additions and 400 deletions

View file

@ -74,8 +74,19 @@ fn alias_fails_with_invalid_name() {
let actual = nu!( let actual = nu!(
cwd: ".", pipeline( cwd: ".", pipeline(
r#" r#"
alias ^foo = "bar" alias ^foo = echo "bar"
"# "#
)); ));
assert!(actual.err.contains(err_msg)); 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"));
}

View file

@ -95,6 +95,13 @@ pub enum ParseError {
)] )]
UnexpectedKeyword(String, #[label("unexpected {0}")] Span), 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")] #[error("Unknown operator")]
#[diagnostic(code(nu::parser::unknown_operator), help("{1}"))] #[diagnostic(code(nu::parser::unknown_operator), help("{1}"))]
UnknownOperator( UnknownOperator(
@ -419,6 +426,7 @@ impl ParseError {
ParseError::UnsupportedOperation(_, _, _, s, _) => *s, ParseError::UnsupportedOperation(_, _, _, s, _) => *s,
ParseError::ExpectedKeyword(_, s) => *s, ParseError::ExpectedKeyword(_, s) => *s,
ParseError::UnexpectedKeyword(_, s) => *s, ParseError::UnexpectedKeyword(_, s) => *s,
ParseError::CantAliasKeyword(_, s) => *s,
ParseError::BuiltinCommandInPipeline(_, s) => *s, ParseError::BuiltinCommandInPipeline(_, s) => *s,
ParseError::AssignInPipeline(_, _, _, s) => *s, ParseError::AssignInPipeline(_, _, _, s) => *s,
ParseError::LetBuiltinVar(_, s) => *s, ParseError::LetBuiltinVar(_, s) => *s,

View file

@ -28,6 +28,92 @@ use crate::{
unescape_unquote_string, ParseError, Token, TokenContents, 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<ParseError>) {
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( pub fn parse_def_predecl(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], spans: &[Span],
@ -657,7 +743,11 @@ pub fn parse_alias(
if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) { if let Some(decl_id) = working_set.find_decl(b"alias", &Type::Any) {
let (command_spans, rest_spans) = spans.split_at(split_id); 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, working_set,
span(command_spans), span(command_spans),
rest_spans, rest_spans,
@ -665,16 +755,17 @@ pub fn parse_alias(
expand_aliases_denylist, expand_aliases_denylist,
); );
if call.has_flag("help") { let has_help_flag = alias_call.has_flag("help");
return (
Pipeline::from_vec(vec![Expression { let alias_pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(alias_call),
span: span(spans), span: span(spans),
ty: output, ty: output,
custom_completion: None, custom_completion: None,
}]), }]);
None,
); if has_help_flag {
return (alias_pipeline, None);
} }
if spans.len() >= split_id + 3 { if spans.len() >= split_id + 3 {
@ -692,12 +783,7 @@ pub fn parse_alias(
if let Some(mod_name) = module_name { if let Some(mod_name) = module_name {
if alias_name == mod_name { if alias_name == mod_name {
return ( return (
Pipeline::from_vec(vec![Expression { alias_pipeline,
expr: Expr::Call(call),
span: span(spans),
ty: output,
custom_completion: None,
}]),
Some(ParseError::NamedAsModule( Some(ParseError::NamedAsModule(
"alias".to_string(), "alias".to_string(),
String::from_utf8_lossy(&alias_name).to_string(), String::from_utf8_lossy(&alias_name).to_string(),
@ -708,12 +794,7 @@ pub fn parse_alias(
if &alias_name == b"main" { if &alias_name == b"main" {
return ( return (
Pipeline::from_vec(vec![Expression { alias_pipeline,
expr: Expr::Call(call),
span: span(spans),
ty: output,
custom_completion: None,
}]),
Some(ParseError::ExportMainAliasNotAllowed(spans[split_id])), Some(ParseError::ExportMainAliasNotAllowed(spans[split_id])),
); );
} }
@ -741,21 +822,36 @@ pub fn parse_alias(
let (command, wrapped_call) = match expr { let (command, wrapped_call) = match expr {
Expression { 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::<Vec<String>>()
.join(", "),
rhs_call.head,
)),
);
}
(Some(cmd.clone_box()), expr)
}
Expression { Expression {
expr: Expr::ExternalCall(..), expr: Expr::ExternalCall(..),
.. ..
} => (None, expr), } => (None, expr),
_ => { _ => {
return ( return (
Pipeline::from_vec(vec![Expression { alias_pipeline,
expr: Expr::Call(call),
span: span(spans),
ty: output,
custom_completion: None,
}]),
Some(ParseError::InternalError( Some(ParseError::InternalError(
"Parsed call not a call".into(), "Parsed call not a call".into(),
expr.span, expr.span,
@ -798,15 +894,7 @@ pub fn parse_alias(
None None
}; };
return ( return (alias_pipeline, err);
Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call),
span: span(spans),
ty: Type::Any,
custom_completion: None,
}]),
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<ParseError>) {
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( pub fn parse_overlay_new(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], call: Box<Call>,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) { ) -> (Pipeline, Option<ParseError>) {
if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay new" { let call_span = call.span();
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 (overlay_name, _) = if let Some(expr) = call.positional_nth(0) { let (overlay_name, _) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { match eval_constant(working_set, expr) {
Ok(val) => match value_as_string(val, expr.span) { Ok(val) => match value_as_string(val, expr.span) {
Ok(s) => (s, expr.span), Ok(s) => (s, expr.span),
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
}, },
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
} }
} else { } else {
return ( return (
garbage_pipeline(spans), garbage_pipeline(&[call_span]),
Some(ParseError::UnknownState( Some(ParseError::UnknownState(
"internal error: Missing required positional after call parsing".into(), "internal error: Missing required positional after call parsing".into(),
call_span, call_span,
@ -2805,7 +2700,7 @@ pub fn parse_overlay_new(
let pipeline = Pipeline::from_vec(vec![Expression { let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: span(spans), span: call_span,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}]); }]);
@ -2829,78 +2724,26 @@ pub fn parse_overlay_new(
pub fn parse_overlay_use( pub fn parse_overlay_use(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], call: Box<Call>,
expand_aliases_denylist: &[usize], expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) { ) -> (Pipeline, Option<ParseError>) {
if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay use" { let call_span = call.span();
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 (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { match eval_constant(working_set, expr) {
Ok(val) => match value_as_string(val, expr.span) { Ok(val) => match value_as_string(val, expr.span) {
Ok(s) => (s, expr.span), Ok(s) => (s, expr.span),
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
}, },
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
} }
} else { } else {
return ( return (
garbage_pipeline(spans), garbage_pipeline(&[call_span]),
Some(ParseError::UnknownState( Some(ParseError::UnknownState(
"internal error: Missing required positional after call parsing".into(), "internal error: Missing required positional after call parsing".into(),
call_span, call_span,
@ -2916,13 +2759,13 @@ pub fn parse_overlay_use(
item: s, item: s,
span: new_name_expression.span, 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 { } else {
return ( return (
garbage_pipeline(spans), garbage_pipeline(&[call_span]),
Some(ParseError::ExpectedKeyword( Some(ParseError::ExpectedKeyword(
"as keyword".to_string(), "as keyword".to_string(),
kw_expression.span, kw_expression.span,
@ -2938,7 +2781,7 @@ pub fn parse_overlay_use(
let pipeline = Pipeline::from_vec(vec![Expression { let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call.clone()), expr: Expr::Call(call.clone()),
span: span(spans), span: call_span,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}]); }]);
@ -3037,7 +2880,7 @@ pub fn parse_overlay_use(
} else { } else {
return ( return (
pipeline, pipeline,
Some(ParseError::ModuleOrOverlayNotFound(spans[1])), Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)),
); );
}; };
@ -3081,7 +2924,7 @@ pub fn parse_overlay_use(
} else { } else {
return ( return (
pipeline, pipeline,
Some(ParseError::ModuleOrOverlayNotFound(spans[1])), Some(ParseError::ModuleOrOverlayNotFound(overlay_name_span)),
); );
} }
} else { } else {
@ -3091,7 +2934,10 @@ pub fn parse_overlay_use(
); );
} }
} else { } 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 { let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: span(spans), span: call_span,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}]); }]);
@ -3142,78 +2988,26 @@ pub fn parse_overlay_use(
pub fn parse_overlay_hide( pub fn parse_overlay_hide(
working_set: &mut StateWorkingSet, working_set: &mut StateWorkingSet,
spans: &[Span], call: Box<Call>,
expand_aliases_denylist: &[usize],
) -> (Pipeline, Option<ParseError>) { ) -> (Pipeline, Option<ParseError>) {
if spans.len() > 1 && working_set.get_span_contents(span(&spans[0..2])) != b"overlay hide" { let call_span = call.span();
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 (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) { let (overlay_name, overlay_name_span) = if let Some(expr) = call.positional_nth(0) {
match eval_constant(working_set, expr) { match eval_constant(working_set, expr) {
Ok(val) => match value_as_string(val, expr.span) { Ok(val) => match value_as_string(val, expr.span) {
Ok(s) => (s, expr.span), Ok(s) => (s, expr.span),
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
}, },
Err(err) => { Err(err) => {
return (garbage_pipeline(spans), Some(err)); return (garbage_pipeline(&[call_span]), Some(err));
} }
} }
} else { } else {
( (
String::from_utf8_lossy(working_set.last_overlay_name()).to_string(), 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 { let pipeline = Pipeline::from_vec(vec![Expression {
expr: Expr::Call(call), expr: Expr::Call(call),
span: span(spans), span: call_span,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
}]); }]);

View file

@ -19,9 +19,10 @@ use nu_protocol::{
}; };
use crate::parse_keywords::{ use crate::parse_keywords::{
parse_alias, parse_def, parse_def_predecl, parse_export_in_block, parse_extern, parse_for, is_unaliasable_parser_keyword, parse_alias, parse_def, parse_def_predecl,
parse_hide, parse_let_or_const, parse_module, parse_old_alias, parse_overlay, parse_source, parse_export_in_block, parse_extern, parse_for, parse_hide, parse_keyword, parse_let_or_const,
parse_use, parse_where, parse_where_expr, 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; use itertools::Itertools;
@ -5309,6 +5310,49 @@ pub fn parse_builtin_commands(
expand_aliases_denylist: &[usize], expand_aliases_denylist: &[usize],
is_subexpression: bool, is_subexpression: bool,
) -> (Pipeline, Option<ParseError>) { ) -> (Pipeline, Option<ParseError>) {
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]); let name = working_set.get_span_contents(lite_command.parts[0]);
match name { match name {
@ -5330,7 +5374,12 @@ pub fn parse_builtin_commands(
parse_use(working_set, &lite_command.parts, expand_aliases_denylist); parse_use(working_set, &lite_command.parts, expand_aliases_denylist);
(pipeline, err) (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" => { b"source" | b"source-env" => {
parse_source(working_set, &lite_command.parts, expand_aliases_denylist) parse_source(working_set, &lite_command.parts, expand_aliases_denylist)
} }
@ -5346,6 +5395,7 @@ pub fn parse_builtin_commands(
expand_aliases_denylist, expand_aliases_denylist,
is_subexpression, is_subexpression,
); );
(Pipeline::from_vec(vec![expr]), err) (Pipeline::from_vec(vec![expr]), err)
} }
} }

View file

@ -326,7 +326,7 @@ fn overlay_use_do_not_eval_twice() {
} }
#[test] #[test]
fn remove_overlay() { fn hide_overlay() {
let inp = &[ let inp = &[
r#"module spam { export def foo [] { "foo" } }"#, r#"module spam { export def foo [] { "foo" } }"#,
r#"overlay use spam"#, r#"overlay use spam"#,
@ -345,7 +345,7 @@ fn remove_overlay() {
} }
#[test] #[test]
fn remove_last_overlay() { fn hide_last_overlay() {
let inp = &[ let inp = &[
r#"module spam { export def foo [] { "foo" } }"#, r#"module spam { export def foo [] { "foo" } }"#,
r#"overlay use spam"#, r#"overlay use spam"#,
@ -364,7 +364,7 @@ fn remove_last_overlay() {
} }
#[test] #[test]
fn remove_overlay_scoped() { fn hide_overlay_scoped() {
let inp = &[ let inp = &[
r#"module spam { export def foo [] { "foo" } }"#, r#"module spam { export def foo [] { "foo" } }"#,
r#"overlay use spam"#, r#"overlay use spam"#,
@ -380,7 +380,7 @@ fn remove_overlay_scoped() {
} }
#[test] #[test]
fn remove_overlay_env() { fn hide_overlay_env() {
let inp = &[ let inp = &[
r#"module spam { export-env { let-env FOO = "foo" } }"#, r#"module spam { export-env { let-env FOO = "foo" } }"#,
r#"overlay use spam"#, r#"overlay use spam"#,
@ -396,7 +396,7 @@ fn remove_overlay_env() {
} }
#[test] #[test]
fn remove_overlay_scoped_env() { fn hide_overlay_scoped_env() {
let inp = &[ let inp = &[
r#"module spam { export-env { let-env FOO = "foo" } }"#, r#"module spam { export-env { let-env FOO = "foo" } }"#,
r#"overlay use spam"#, r#"overlay use spam"#,
@ -453,7 +453,7 @@ fn list_overlay_scoped() {
} }
#[test] #[test]
fn remove_overlay_discard_decl() { fn hide_overlay_discard_decl() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"def bagr [] { "bagr" }"#, r#"def bagr [] { "bagr" }"#,
@ -472,7 +472,7 @@ fn remove_overlay_discard_decl() {
} }
#[test] #[test]
fn remove_overlay_discard_alias() { fn hide_overlay_discard_alias() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"alias bagr = echo "bagr""#, r#"alias bagr = echo "bagr""#,
@ -491,7 +491,7 @@ fn remove_overlay_discard_alias() {
} }
#[test] #[test]
fn remove_overlay_discard_env() { fn hide_overlay_discard_env() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"let-env BAGR = 'bagr'"#, r#"let-env BAGR = 'bagr'"#,
@ -507,7 +507,7 @@ fn remove_overlay_discard_env() {
} }
#[test] #[test]
fn remove_overlay_keep_decl() { fn hide_overlay_keep_decl() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"def bagr [] { "bagr" }"#, r#"def bagr [] { "bagr" }"#,
@ -523,7 +523,7 @@ fn remove_overlay_keep_decl() {
} }
#[test] #[test]
fn remove_overlay_keep_alias() { fn hide_overlay_keep_alias() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"alias bagr = echo 'bagr'"#, r#"alias bagr = echo 'bagr'"#,
@ -539,7 +539,7 @@ fn remove_overlay_keep_alias() {
} }
#[test] #[test]
fn remove_overlay_dont_keep_env() { fn hide_overlay_dont_keep_env() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"let-env BAGR = 'bagr'"#, r#"let-env BAGR = 'bagr'"#,
@ -555,7 +555,7 @@ fn remove_overlay_dont_keep_env() {
} }
#[test] #[test]
fn remove_overlay_dont_keep_overwritten_decl() { fn hide_overlay_dont_keep_overwritten_decl() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"def foo [] { 'bar' }"#, r#"def foo [] { 'bar' }"#,
@ -574,7 +574,7 @@ fn remove_overlay_dont_keep_overwritten_decl() {
} }
#[test] #[test]
fn remove_overlay_dont_keep_overwritten_alias() { fn hide_overlay_dont_keep_overwritten_alias() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"alias bar = echo `baz`"#, r#"alias bar = echo `baz`"#,
@ -593,7 +593,7 @@ fn remove_overlay_dont_keep_overwritten_alias() {
} }
#[test] #[test]
fn remove_overlay_dont_keep_overwritten_env() { fn hide_overlay_dont_keep_overwritten_env() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"let-env BAZ = 'bagr'"#, r#"let-env BAZ = 'bagr'"#,
@ -609,7 +609,7 @@ fn remove_overlay_dont_keep_overwritten_env() {
} }
#[test] #[test]
fn remove_overlay_keep_decl_in_latest_overlay() { fn hide_overlay_keep_decl_in_latest_overlay() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"def bagr [] { 'bagr' }"#, r#"def bagr [] { 'bagr' }"#,
@ -627,7 +627,7 @@ fn remove_overlay_keep_decl_in_latest_overlay() {
} }
#[test] #[test]
fn remove_overlay_keep_alias_in_latest_overlay() { fn hide_overlay_keep_alias_in_latest_overlay() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"alias bagr = echo 'bagr'"#, r#"alias bagr = echo 'bagr'"#,
@ -645,7 +645,7 @@ fn remove_overlay_keep_alias_in_latest_overlay() {
} }
#[test] #[test]
fn remove_overlay_dont_keep_env_in_latest_overlay() { fn hide_overlay_dont_keep_env_in_latest_overlay() {
let inp = &[ let inp = &[
r#"overlay use samples/spam.nu"#, r#"overlay use samples/spam.nu"#,
r#"let-env BAGR = 'bagr'"#, r#"let-env BAGR = 'bagr'"#,
@ -811,7 +811,7 @@ fn overlay_can_add_renamed_overlay() {
} }
#[test] #[test]
fn overlay_remove_renamed_overlay() { fn overlay_hide_renamed_overlay() {
let inp = &[ let inp = &[
r#"module spam { export def foo [] { "foo" } }"#, r#"module spam { export def foo [] { "foo" } }"#,
r#"overlay use spam as eggs"#, r#"overlay use spam as eggs"#,
@ -827,7 +827,7 @@ fn overlay_remove_renamed_overlay() {
} }
#[test] #[test]
fn overlay_remove_and_add_renamed_overlay() { fn overlay_hide_and_add_renamed_overlay() {
let inp = &[ let inp = &[
r#"module spam { export def foo [] { "foo" } }"#, r#"module spam { export def foo [] { "foo" } }"#,
r#"overlay use spam as eggs"#, r#"overlay use spam as eggs"#,
@ -1261,3 +1261,53 @@ fn overlay_use_main_not_exported() {
assert!(actual.err.contains("external_command")); 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");
}