diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 033a21e738..820427f3e2 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -600,12 +600,92 @@ pub fn parse_hide( let (name_expr, err) = parse_string(working_set, spans[1]); error = error.or(err); - let name_bytes: Vec = working_set.get_span_contents(spans[1]).into(); + let (import_pattern, err) = parse_import_pattern(working_set, spans[1]); + error = error.or(err); - // TODO: Do the import pattern stuff for bulk-hiding + let exported_names: Vec> = + if let Some(block_id) = working_set.find_module(&import_pattern.head) { + working_set + .get_block(block_id) + .exports + .iter() + .map(|(name, _)| name.clone()) + .collect() + } else { + if import_pattern.members.is_empty() { + // The pattern head can be e.g. a function name, not just a module + vec![import_pattern.head.clone()] + } else { + return ( + garbage_statement(spans), + Some(ParseError::ModuleNotFound(spans[1])), + ); + } + }; - if working_set.hide_decl(&name_bytes).is_none() { - error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1]))); + // This kind of inverts the import pattern matching found in parse_use() + let names_to_hide = if import_pattern.members.is_empty() { + exported_names + } else { + match &import_pattern.members[0] { + ImportPatternMember::Glob { .. } => exported_names + .into_iter() + .map(|name| { + let mut new_name = import_pattern.head.to_vec(); + new_name.push(b'.'); + new_name.extend(&name); + new_name + }) + .collect(), + ImportPatternMember::Name { name, span } => { + let new_exports: Vec> = exported_names + .into_iter() + .filter(|n| n == name) + .map(|n| { + let mut new_name = import_pattern.head.to_vec(); + new_name.push(b'.'); + new_name.extend(&n); + new_name + }) + .collect(); + + if new_exports.is_empty() { + error = error.or(Some(ParseError::ExportNotFound(*span))) + } + + new_exports + } + ImportPatternMember::List { names } => { + let mut output = vec![]; + + for (name, span) in names { + let mut new_exports: Vec> = exported_names + .iter() + .filter_map(|n| if n == name { Some(n.clone()) } else { None }) + .map(|n| { + let mut new_name = import_pattern.head.to_vec(); + new_name.push(b'.'); + new_name.extend(n); + new_name + }) + .collect(); + + if new_exports.is_empty() { + error = error.or(Some(ParseError::ExportNotFound(*span))) + } else { + output.append(&mut new_exports) + } + } + + output + } + } + }; + + for name in names_to_hide { + if working_set.hide_decl(&name).is_none() { + error = error.or_else(|| Some(ParseError::UnknownCommand(spans[1]))); + } } // Create the Hide command call diff --git a/src/tests.rs b/src/tests.rs index 7416aecb6f..db8fde4bd8 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -441,6 +441,46 @@ fn hide_twice_not_allowed() -> TestResult { ) } +#[test] +fn hides_import_1() -> TestResult { + fail_test( + r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.foo; foo"#, + "not found" + ) +} + +#[test] +fn hides_import_2() -> TestResult { + fail_test( + r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.*; foo"#, + "not found" + ) +} + +#[test] +fn hides_import_3() -> TestResult { + fail_test( + r#"module spam { export def foo [] { "foo" } }; use spam; hide spam.[foo]; foo"#, + "not found" + ) +} + +#[test] +fn hides_import_4() -> TestResult { + fail_test( + r#"module spam { export def foo [] { "foo" } }; use spam.foo; hide foo; foo"#, + "not found" + ) +} + +#[test] +fn hides_import_5() -> TestResult { + fail_test( + r#"module spam { export def foo [] { "foo" } }; use spam.*; hide foo; foo"#, + "not found" + ) +} + #[test] fn def_twice_should_fail() -> TestResult { fail_test(