diff --git a/crates/nu-cli/src/completions/custom_completions.rs b/crates/nu-cli/src/completions/custom_completions.rs index 9ce1a3954f..85018287be 100644 --- a/crates/nu-cli/src/completions/custom_completions.rs +++ b/crates/nu-cli/src/completions/custom_completions.rs @@ -66,6 +66,7 @@ impl Completer for CustomCompletion { ], redirect_stdout: true, redirect_stderr: true, + parser_info: vec![], }, PipelineData::empty(), ); diff --git a/crates/nu-command/src/core_commands/export_use.rs b/crates/nu-command/src/core_commands/export_use.rs index 758fba60e6..68e53eb207 100644 --- a/crates/nu-command/src/core_commands/export_use.rs +++ b/crates/nu-command/src/core_commands/export_use.rs @@ -17,7 +17,12 @@ impl Command for ExportUse { fn signature(&self) -> nu_protocol::Signature { Signature::build("export use") .input_output_types(vec![(Type::Nothing, Type::Nothing)]) - .required("pattern", SyntaxShape::ImportPattern, "import pattern") + .required("module", SyntaxShape::String, "Module or module file") + .optional( + "members", + SyntaxShape::Any, + "Which members of the module to import", + ) .category(Category::Core) } diff --git a/crates/nu-command/src/core_commands/hide.rs b/crates/nu-command/src/core_commands/hide.rs index 5cbe01c6bb..3918098ef5 100644 --- a/crates/nu-command/src/core_commands/hide.rs +++ b/crates/nu-command/src/core_commands/hide.rs @@ -15,7 +15,12 @@ impl Command for Hide { fn signature(&self) -> nu_protocol::Signature { Signature::build("hide") .input_output_types(vec![(Type::Nothing, Type::Nothing)]) - .required("pattern", SyntaxShape::ImportPattern, "import pattern") + .required("module", SyntaxShape::String, "Module or module file") + .optional( + "members", + SyntaxShape::Any, + "Which members of the module to import", + ) .category(Category::Core) } @@ -44,7 +49,7 @@ This command is a parser keyword. For details, check: let env_var_name = if let Some(Expression { expr: Expr::ImportPattern(pat), .. - }) = call.positional_nth(0) + }) = call.parser_info_nth(0) { Spanned { item: String::from_utf8_lossy(&pat.head.name).to_string(), diff --git a/crates/nu-command/src/core_commands/overlay/use_.rs b/crates/nu-command/src/core_commands/overlay/use_.rs index 8b409331a8..5dbcd9b825 100644 --- a/crates/nu-command/src/core_commands/overlay/use_.rs +++ b/crates/nu-command/src/core_commands/overlay/use_.rs @@ -66,7 +66,7 @@ impl Command for OverlayUse { let mut name_arg: Spanned = call.req(engine_state, caller_stack, 0)?; name_arg.item = trim_quotes_str(&name_arg.item).to_string(); - let maybe_origin_module_id = if let Some(overlay_expr) = call.positional_nth(0) { + let maybe_origin_module_id = if let Some(overlay_expr) = call.parser_info_nth(0) { if let Expr::Overlay(module_id) = overlay_expr.expr { module_id } else { diff --git a/crates/nu-command/src/core_commands/use_.rs b/crates/nu-command/src/core_commands/use_.rs index d52293038f..dd4e162c0c 100644 --- a/crates/nu-command/src/core_commands/use_.rs +++ b/crates/nu-command/src/core_commands/use_.rs @@ -20,7 +20,12 @@ impl Command for Use { fn signature(&self) -> nu_protocol::Signature { Signature::build("use") .input_output_types(vec![(Type::Nothing, Type::Nothing)]) - .required("pattern", SyntaxShape::ImportPattern, "import pattern") + .required("module", SyntaxShape::String, "Module or module file") + .optional( + "members", + SyntaxShape::Any, + "Which members of the module to import", + ) .category(Category::Core) } @@ -43,7 +48,7 @@ impl Command for Use { let import_pattern = if let Some(Expression { expr: Expr::ImportPattern(pat), .. - }) = call.positional_nth(0) + }) = call.parser_info_nth(0) { pat } else { diff --git a/crates/nu-command/src/deprecated/source.rs b/crates/nu-command/src/deprecated/source.rs index 6e138cbbbc..a5fb36d63d 100644 --- a/crates/nu-command/src/deprecated/source.rs +++ b/crates/nu-command/src/deprecated/source.rs @@ -45,7 +45,7 @@ impl Command for Source { ) -> Result { // Note: this hidden positional is the block_id that corresponded to the 0th position // it is put here by the parser - let block_id: i64 = call.req(engine_state, stack, 1)?; + let block_id: i64 = call.req_parser_info(engine_state, stack, 0)?; let block = engine_state.get_block(block_id as usize).clone(); eval_block( diff --git a/crates/nu-command/src/env/source_env.rs b/crates/nu-command/src/env/source_env.rs index 82eda8eeb7..a74fcdfd8c 100644 --- a/crates/nu-command/src/env/source_env.rs +++ b/crates/nu-command/src/env/source_env.rs @@ -42,7 +42,7 @@ impl Command for SourceEnv { // Note: this hidden positional is the block_id that corresponded to the 0th position // it is put here by the parser - let block_id: i64 = call.req(engine_state, caller_stack, 1)?; + let block_id: i64 = call.req_parser_info(engine_state, caller_stack, 0)?; // Set the currently evaluated directory (file-relative PWD) let mut parent = if let Some(path) = diff --git a/crates/nu-engine/src/call_ext.rs b/crates/nu-engine/src/call_ext.rs index 4e604ec9d3..615a15161d 100644 --- a/crates/nu-engine/src/call_ext.rs +++ b/crates/nu-engine/src/call_ext.rs @@ -34,6 +34,13 @@ pub trait CallExt { stack: &mut Stack, pos: usize, ) -> Result; + + fn req_parser_info( + &self, + engine_state: &EngineState, + stack: &mut Stack, + pos: usize, + ) -> Result; } impl CallExt for Call { @@ -99,4 +106,23 @@ impl CallExt for Call { )) } } + + fn req_parser_info( + &self, + engine_state: &EngineState, + stack: &mut Stack, + pos: usize, + ) -> Result { + if let Some(expr) = self.parser_info_nth(pos) { + let result = eval_expression(engine_state, stack, expr)?; + FromValue::from_value(&result) + } else if self.parser_info.is_empty() { + Err(ShellError::AccessEmptyContent(self.head)) + } else { + Err(ShellError::AccessBeyondEnd( + self.parser_info.len() - 1, + self.head, + )) + } + } } diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 8bea5f4143..107e8a2fff 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -814,6 +814,7 @@ pub fn eval_element_with_input( ], redirect_stdout: false, redirect_stderr: false, + parser_info: vec![], }, input, ) diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 4b14ca7bb5..5705c389bb 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -1,4 +1,3 @@ -use crate::eval::{eval_constant, value_as_string}; use log::trace; use nu_path::canonicalize_with; use nu_protocol::{ @@ -17,13 +16,14 @@ static LIB_DIRS_ENV: &str = "NU_LIB_DIRS"; static PLUGIN_DIRS_ENV: &str = "NU_PLUGIN_DIRS"; use crate::{ + eval::{eval_constant, value_as_string}, known_external::KnownExternal, lex, lite_parser::{lite_parse, LiteCommand, LiteElement}, parser::{ - check_call, check_name, garbage, garbage_pipeline, parse, parse_internal_call, - parse_multispan_value, parse_signature, parse_string, parse_value, parse_var_with_opt_type, - trim_quotes, ParsedInternalCall, + check_call, check_name, garbage, garbage_pipeline, parse, parse_import_pattern, + parse_internal_call, parse_multispan_value, parse_signature, parse_string, parse_value, + parse_var_with_opt_type, trim_quotes, ParsedInternalCall, }, unescape_unquote_string, ParseError, }; @@ -578,6 +578,7 @@ pub fn parse_alias( decl_id, redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], })); return ( Pipeline::from_vec(vec![Expression { @@ -840,6 +841,7 @@ pub fn parse_export_in_module( arguments: vec![], redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], }); let exportables = if let Some(kw_span) = spans.get(1) { @@ -1505,6 +1507,7 @@ pub fn parse_module( ], redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], }); ( @@ -1562,7 +1565,7 @@ pub fn parse_use( ); } - let (call, call_span, use_decl_id) = match working_set.find_decl(b"use", &Type::Any) { + let (call, call_span, args_spans) = match working_set.find_decl(b"use", &Type::Any) { Some(decl_id) => { let (command_spans, rest_spans) = spans.split_at(split_id); @@ -1595,7 +1598,7 @@ pub fn parse_use( ); } - (call, call_span, decl_id) + (call, call_span, rest_spans) } None => { return ( @@ -1609,34 +1612,31 @@ pub fn parse_use( } }; - let import_pattern = if let Some(expr) = call.positional_nth(0) { - if let Some(pattern) = expr.as_import_pattern() { - pattern - } else { - return ( - garbage_pipeline(spans), - vec![], - Some(ParseError::UnknownState( - "internal error: Import pattern positional is not import pattern".into(), - expr.span, - )), - ); - } + let mut error = None; + + let (import_pattern_expr, err) = + parse_import_pattern(working_set, args_spans, expand_aliases_denylist); + error = error.or(err); + + let import_pattern = if let Expression { + expr: Expr::ImportPattern(import_pattern), + .. + } = &import_pattern_expr + { + import_pattern.clone() } else { return ( garbage_pipeline(spans), vec![], Some(ParseError::UnknownState( - "internal error: Missing required positional after call parsing".into(), - call_span, + "internal error: Import pattern positional is not import pattern".into(), + import_pattern_expr.span, )), ); }; let cwd = working_set.get_cwd(); - let mut error = None; - // TODO: Add checking for importing too long import patterns, e.g.: // > use spam foo non existent names here do not throw error let (import_pattern, module) = if let Some(module_id) = import_pattern.head.id { @@ -1842,18 +1842,13 @@ pub fn parse_use( // Create a new Use command call to pass the new import pattern let import_pattern_expr = Expression { expr: Expr::ImportPattern(import_pattern), - span: span(&spans[1..]), - ty: Type::List(Box::new(Type::String)), + span: span(args_spans), + ty: Type::Any, custom_completion: None, }; - let call = Box::new(Call { - head: span(spans.split_at(split_id).0), - decl_id: use_decl_id, - arguments: vec![Argument::Positional(import_pattern_expr)], - redirect_stdout: true, - redirect_stderr: false, - }); + let mut call = call; + call.add_parser_info(import_pattern_expr); ( Pipeline::from_vec(vec![Expression { @@ -1882,7 +1877,7 @@ pub fn parse_hide( ); } - let (call, call_span, hide_decl_id) = match working_set.find_decl(b"hide", &Type::Any) { + let (call, args_spans) = match working_set.find_decl(b"hide", &Type::Any) { Some(decl_id) => { let ParsedInternalCall { call, @@ -1912,7 +1907,7 @@ pub fn parse_hide( ); } - (call, call_span, decl_id) + (call, &spans[1..]) } None => { return ( @@ -1925,29 +1920,28 @@ pub fn parse_hide( } }; - let import_pattern = if let Some(expr) = call.positional_nth(0) { - if let Some(pattern) = expr.as_import_pattern() { - pattern - } else { - return ( - garbage_pipeline(spans), - Some(ParseError::UnknownState( - "internal error: Import pattern positional is not import pattern".into(), - call_span, - )), - ); - } + let mut error = None; + + let (import_pattern_expr, err) = + parse_import_pattern(working_set, args_spans, expand_aliases_denylist); + error = error.or(err); + + let import_pattern = if let Expression { + expr: Expr::ImportPattern(import_pattern), + .. + } = &import_pattern_expr + { + import_pattern.clone() } else { return ( garbage_pipeline(spans), Some(ParseError::UnknownState( - "internal error: Missing required positional after call parsing".into(), - call_span, + "internal error: Import pattern positional is not import pattern".into(), + import_pattern_expr.span, )), ); }; - let mut error = None; let bytes = working_set.get_span_contents(spans[0]); if bytes == b"hide" && spans.len() >= 2 { @@ -2054,18 +2048,13 @@ pub fn parse_hide( // Create a new Use command call to pass the new import pattern let import_pattern_expr = Expression { expr: Expr::ImportPattern(import_pattern), - span: span(&spans[1..]), - ty: Type::List(Box::new(Type::String)), + span: span(args_spans), + ty: Type::Any, custom_completion: None, }; - let call = Box::new(Call { - head: spans[0], - decl_id: hide_decl_id, - arguments: vec![Argument::Positional(import_pattern_expr)], - redirect_stdout: true, - redirect_stderr: false, - }); + let mut call = call; + call.add_parser_info(import_pattern_expr); ( Pipeline::from_vec(vec![Expression { @@ -2612,13 +2601,16 @@ pub fn parse_overlay_use( // Change the call argument to include the Overlay expression with the module ID let mut call = call; - if let Some(overlay_expr) = call.positional_nth_mut(0) { - overlay_expr.expr = Expr::Overlay(if is_module_updated { + call.add_parser_info(Expression { + expr: Expr::Overlay(if is_module_updated { Some(origin_module_id) } else { None - }); - } // no need to check for else since it was already checked + }), + span: overlay_name_span, + ty: Type::Any, + custom_completion: None, + }); let pipeline = Pipeline::from_vec(vec![Expression { expr: Expr::Call(call), @@ -2836,6 +2828,7 @@ pub fn parse_let_or_const( ], redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], }); return ( @@ -2957,6 +2950,7 @@ pub fn parse_mut( ], redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], }); return ( @@ -3131,7 +3125,7 @@ pub fn parse_source( // FIXME: Adding this expression to the positional creates a syntax highlighting error // after writing `source example.nu` - call_with_block.add_positional(Expression { + call_with_block.add_parser_info(Expression { expr: Expr::Int(block_id as i64), span: spans[1], ty: Type::Any, diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index 720e2d9e7b..115bb6cbe6 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -695,16 +695,6 @@ pub fn parse_multispan_value( (arg, error) } - SyntaxShape::ImportPattern => { - trace!("parsing: import pattern"); - - let (arg, err) = - parse_import_pattern(working_set, &spans[*spans_idx..], expand_aliases_denylist); - error = error.or(err); - *spans_idx = spans.len() - 1; - - (arg, error) - } SyntaxShape::Keyword(keyword, arg) => { trace!( "parsing: keyword({}) {:?}", @@ -5171,6 +5161,7 @@ pub fn parse_expression( arguments, redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], })); ( @@ -5904,6 +5895,7 @@ fn wrap_expr_with_collect(working_set: &mut StateWorkingSet, expr: &Expression) decl_id, redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], })), span, ty: Type::String, diff --git a/crates/nu-protocol/src/ast/call.rs b/crates/nu-protocol/src/ast/call.rs index 449305620d..2ae2e9bebe 100644 --- a/crates/nu-protocol/src/ast/call.rs +++ b/crates/nu-protocol/src/ast/call.rs @@ -18,6 +18,8 @@ pub struct Call { pub arguments: Vec, pub redirect_stdout: bool, pub redirect_stderr: bool, + /// this field is used by the parser to pass additional command-specific information + pub parser_info: Vec, } impl Call { @@ -28,6 +30,7 @@ impl Call { arguments: vec![], redirect_stdout: true, redirect_stderr: false, + parser_info: vec![], } } @@ -67,6 +70,10 @@ impl Call { self.arguments.push(Argument::Positional(positional)); } + pub fn add_parser_info(&mut self, info: Expression) { + self.parser_info.push(info); + } + pub fn add_unknown(&mut self, unknown: Expression) { self.arguments.push(Argument::Unknown(unknown)); } @@ -99,6 +106,10 @@ impl Call { self.positional_iter().count() } + pub fn parser_info_nth(&self, i: usize) -> Option<&Expression> { + self.parser_info.get(i) + } + pub fn has_flag(&self, flag_name: &str) -> bool { for name in self.named_iter() { if flag_name == name.0.item {