mirror of
https://github.com/nushell/nushell
synced 2024-12-25 20:43:09 +00:00
Allow aliasing parser keywords (#8250)
This commit is contained in:
parent
baddc86d9d
commit
af1ab39851
5 changed files with 313 additions and 400 deletions
|
@ -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"));
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<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(
|
||||
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::<Vec<String>>()
|
||||
.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<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(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
expand_aliases_denylist: &[usize],
|
||||
call: Box<Call>,
|
||||
) -> (Pipeline, Option<ParseError>) {
|
||||
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<Call>,
|
||||
expand_aliases_denylist: &[usize],
|
||||
) -> (Pipeline, Option<ParseError>) {
|
||||
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<Call>,
|
||||
) -> (Pipeline, Option<ParseError>) {
|
||||
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,
|
||||
}]);
|
||||
|
|
|
@ -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<u8>, Option<ParseError>
|
|||
}
|
||||
// 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<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]);
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue