From bd6c550470e11e3346cd6d99eeea9bd1a3b8b7f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jakub=20=C5=BD=C3=A1dn=C3=ADk?= <kubouch@gmail.com>
Date: Wed, 27 Oct 2021 00:06:08 +0300
Subject: [PATCH] Change import pattern delimiter to space

Subcommands and module imports will have the same syntax now.
---
 crates/nu-command/src/core_commands/use_.rs |  2 +-
 crates/nu-parser/src/parse_keywords.rs      | 16 ++---
 crates/nu-parser/src/parser.rs              | 80 +++++++++------------
 src/tests.rs                                | 36 ++++------
 4 files changed, 53 insertions(+), 81 deletions(-)

diff --git a/crates/nu-command/src/core_commands/use_.rs b/crates/nu-command/src/core_commands/use_.rs
index 27635687d5..3b899c96ef 100644
--- a/crates/nu-command/src/core_commands/use_.rs
+++ b/crates/nu-command/src/core_commands/use_.rs
@@ -15,7 +15,7 @@ impl Command for Use {
     }
 
     fn signature(&self) -> nu_protocol::Signature {
-        Signature::build("use").required("pattern", SyntaxShape::String, "import pattern")
+        Signature::build("use").rest("pattern", SyntaxShape::String, "import pattern parts")
     }
 
     fn run(
diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs
index beafb70544..c5de2f8918 100644
--- a/crates/nu-parser/src/parse_keywords.rs
+++ b/crates/nu-parser/src/parse_keywords.rs
@@ -495,7 +495,7 @@ pub fn parse_use(
         let (module_name_expr, err) = parse_string(working_set, spans[1]);
         error = error.or(err);
 
-        let (import_pattern, err) = parse_import_pattern(working_set, spans[1]);
+        let (import_pattern, err) = parse_import_pattern(working_set, &spans[1..]);
         error = error.or(err);
 
         let (import_pattern, exports) =
@@ -548,8 +548,7 @@ pub fn parse_use(
                 .into_iter()
                 .map(|(name, id)| {
                     let mut new_name = import_pattern.head.to_vec();
-                    new_name.push(b':');
-                    new_name.push(b':');
+                    new_name.push(b' ');
                     new_name.extend(&name);
                     (new_name, id)
                 })
@@ -634,7 +633,7 @@ pub fn parse_hide(
         let (name_expr, err) = parse_string(working_set, spans[1]);
         error = error.or(err);
 
-        let (import_pattern, err) = parse_import_pattern(working_set, spans[1]);
+        let (import_pattern, err) = parse_import_pattern(working_set, &spans[1..]);
         error = error.or(err);
 
         let exported_names: Vec<Vec<u8>> =
@@ -664,8 +663,7 @@ pub fn parse_hide(
                     .into_iter()
                     .map(|name| {
                         let mut new_name = import_pattern.head.to_vec();
-                        new_name.push(b':');
-                        new_name.push(b':');
+                        new_name.push(b' ');
                         new_name.extend(&name);
                         new_name
                     })
@@ -676,8 +674,7 @@ pub fn parse_hide(
                         .filter(|n| n == name)
                         .map(|n| {
                             let mut new_name = import_pattern.head.to_vec();
-                            new_name.push(b':');
-                            new_name.push(b':');
+                            new_name.push(b' ');
                             new_name.extend(&n);
                             new_name
                         })
@@ -698,8 +695,7 @@ pub fn parse_hide(
                             .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.push(b':');
+                                new_name.push(b' ');
                                 new_name.extend(n);
                                 new_name
                             })
diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs
index 798a89ec6b..8087ffdc1a 100644
--- a/crates/nu-parser/src/parser.rs
+++ b/crates/nu-parser/src/parser.rs
@@ -1693,70 +1693,54 @@ pub fn parse_type(_working_set: &StateWorkingSet, bytes: &[u8]) -> Type {
 
 pub fn parse_import_pattern(
     working_set: &mut StateWorkingSet,
-    span: Span,
+    spans: &[Span],
 ) -> (ImportPattern, Option<ParseError>) {
-    let source = working_set.get_span_contents(span);
     let mut error = None;
 
-    let (tokens, err) = lex(source, span.start, &[], &[b':']);
-    error = error.or(err);
+    // return (
+    //     ImportPattern {
+    //         head: vec![],
+    //         members: vec![],
+    //     },
+    //     Some(ParseError::MissingImportPattern(span)),
+    // );
 
-    if tokens.is_empty() {
-        return (
-            ImportPattern {
-                head: vec![],
-                members: vec![],
-            },
-            Some(ParseError::MissingImportPattern(span)),
-        );
-    }
+    // return (
+    //     ImportPattern {
+    //         head: vec![],
+    //         members: vec![],
+    //     },
+    //     Some(ParseError::WrongImportPattern(span)),
+    // );
 
-    // We can have either "head" or "head::tail"
-    if (tokens.len() != 1) && (tokens.len() != 4) {
-        return (
-            ImportPattern {
-                head: vec![],
-                members: vec![],
-            },
-            Some(ParseError::WrongImportPattern(span)),
-        );
-    }
-
-    // Check if the second : of the :: is really a :
-    let has_second_colon = if let Some(t) = tokens.get(2) {
-        let potential_colon = working_set.get_span_contents(t.span);
-        potential_colon == b":"
+    let head = if let Some(head_span) = spans.get(0) {
+        let (head_expr, err) = parse_string(working_set, *head_span);
+        error = error.or(err);
+        working_set.get_span_contents(head_expr.span).to_vec()
     } else {
-        false
+        return (
+            ImportPattern {
+                head: vec![],
+                members: vec![],
+            },
+            Some(ParseError::WrongImportPattern(span(spans))),
+        );
     };
 
-    if (tokens.len() == 4) && !has_second_colon {
-        // Applies only to the "head::tail" structure; "head" only doesn't have :
-        return (
-            ImportPattern {
-                head: vec![],
-                members: vec![],
-            },
-            Some(ParseError::WrongImportPattern(span)),
-        );
-    }
-
-    let head = working_set.get_span_contents(tokens[0].span).to_vec();
-
-    if let Some(tail) = tokens.get(3) {
+    if let Some(tail_span) = spans.get(1) {
         // FIXME: expand this to handle deeper imports once we support module imports
-        let tail_span = tail.span;
-        let tail = working_set.get_span_contents(tail.span);
+        let tail = working_set.get_span_contents(*tail_span);
         if tail == b"*" {
             (
                 ImportPattern {
                     head,
-                    members: vec![ImportPatternMember::Glob { span: tail_span }],
+                    members: vec![ImportPatternMember::Glob { span: *tail_span }],
                 },
                 error,
             )
         } else if tail.starts_with(b"[") {
-            let (result, err) = parse_list_expression(working_set, tail_span, &SyntaxShape::String);
+            let (result, err) =
+                parse_list_expression(working_set, *tail_span, &SyntaxShape::String);
             error = error.or(err);
 
             let mut output = vec![];
@@ -1793,7 +1777,7 @@ pub fn parse_import_pattern(
                     head,
                     members: vec![ImportPatternMember::Name {
                         name: tail.to_vec(),
-                        span: tail_span,
+                        span: *tail_span,
                     }],
                 },
                 error,
diff --git a/src/tests.rs b/src/tests.rs
index 0d51af0547..a3146456c8 100644
--- a/src/tests.rs
+++ b/src/tests.rs
@@ -389,7 +389,7 @@ fn better_block_types() -> TestResult {
 #[test]
 fn module_imports_1() -> TestResult {
     run_test(
-        r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo; foo::a"#,
+        r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo; foo a"#,
         "1",
     )
 }
@@ -397,7 +397,7 @@ fn module_imports_1() -> TestResult {
 #[test]
 fn module_imports_2() -> TestResult {
     run_test(
-        r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo::a; a"#,
+        r#"module foo { export def a [] { 1 }; def b [] { 2 } }; use foo a; a"#,
         "1",
     )
 }
@@ -405,7 +405,7 @@ fn module_imports_2() -> TestResult {
 #[test]
 fn module_imports_3() -> TestResult {
     run_test(
-        r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo::*; b"#,
+        r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo *; b"#,
         "2",
     )
 }
@@ -413,7 +413,7 @@ fn module_imports_3() -> TestResult {
 #[test]
 fn module_imports_4() -> TestResult {
     fail_test(
-        r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo::c"#,
+        r#"module foo { export def a [] { 1 }; export def b [] { 2 } }; use foo c"#,
         "not find import",
     )
 }
@@ -421,7 +421,7 @@ fn module_imports_4() -> TestResult {
 #[test]
 fn module_imports_5() -> TestResult {
     run_test(
-        r#"module foo { export def a [] { 1 }; def b [] { 2 }; export def c [] { 3 } }; use foo::[a, c]; c"#,
+        r#"module foo { export def a [] { 1 }; def b [] { 2 }; export def c [] { 3 } }; use foo [a, c]; c"#,
         "3",
     )
 }
@@ -429,7 +429,7 @@ fn module_imports_5() -> TestResult {
 #[test]
 fn module_import_uses_internal_command() -> TestResult {
     run_test(
-        r#"module foo { def b [] { 2 }; export def a [] { b }  }; use foo; foo::a"#,
+        r#"module foo { def b [] { 2 }; export def a [] { b }  }; use foo; foo a"#,
         "2",
     )
 }
@@ -442,14 +442,6 @@ fn module_import_does_not_parse_with_incorrect_delimiter() -> TestResult {
     )
 }
 
-#[test]
-fn module_import_does_not_parse_with_missing_tail() -> TestResult {
-    fail_test(
-        r#"module foo { export def a [] { 1 }  }; use foo::"#,
-        not_found_msg(),
-    )
-}
-
 // TODO: Test the use/hide tests also as separate lines in REPL (i.e., with  merging the delta in between)
 #[test]
 fn hides_def() -> TestResult {
@@ -507,7 +499,7 @@ 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"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam; hide spam foo; foo"#,
         not_found_msg(),
     )
 }
@@ -515,7 +507,7 @@ fn hides_import_1() -> TestResult {
 #[test]
 fn hides_import_2() -> TestResult {
     fail_test(
-        r#"module spam { export def foo [] { "foo" } }; use spam; hide spam::*; foo"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam; hide spam *; foo"#,
         not_found_msg(),
     )
 }
@@ -523,7 +515,7 @@ fn hides_import_2() -> TestResult {
 #[test]
 fn hides_import_3() -> TestResult {
     fail_test(
-        r#"module spam { export def foo [] { "foo" } }; use spam; hide spam::[foo]; foo"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam; hide spam [foo]; foo"#,
         not_found_msg(),
     )
 }
@@ -531,7 +523,7 @@ fn hides_import_3() -> TestResult {
 #[test]
 fn hides_import_4() -> TestResult {
     fail_test(
-        r#"module spam { export def foo [] { "foo" } }; use spam::foo; hide foo; foo"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam foo; hide foo; foo"#,
         not_found_msg(),
     )
 }
@@ -539,7 +531,7 @@ fn hides_import_4() -> TestResult {
 #[test]
 fn hides_import_5() -> TestResult {
     fail_test(
-        r#"module spam { export def foo [] { "foo" } }; use spam::*; hide foo; foo"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam *; hide foo; foo"#,
         not_found_msg(),
     )
 }
@@ -555,7 +547,7 @@ fn def_twice_should_fail() -> TestResult {
 #[test]
 fn use_import_after_hide() -> TestResult {
     run_test(
-        r#"module spam { export def foo [] { "foo" } }; use spam::foo; hide foo; use spam::foo; foo"#,
+        r#"module spam { export def foo [] { "foo" } }; use spam foo; hide foo; use spam foo; foo"#,
         "foo",
     )
 }
@@ -563,7 +555,7 @@ fn use_import_after_hide() -> TestResult {
 #[test]
 fn hide_shadowed_decl() -> TestResult {
     run_test(
-        r#"module spam { export def foo [] { "bar" } }; def foo [] { "foo" }; do { use spam::foo; hide foo; foo }"#,
+        r#"module spam { export def foo [] { "bar" } }; def foo [] { "foo" }; do { use spam foo; hide foo; foo }"#,
         "foo",
     )
 }
@@ -571,7 +563,7 @@ fn hide_shadowed_decl() -> TestResult {
 #[test]
 fn hides_all_decls_within_scope() -> TestResult {
     fail_test(
-        r#"module spam { export def foo [] { "bar" } }; def foo [] { "foo" }; use spam::foo; hide foo; foo"#,
+        r#"module spam { export def foo [] { "bar" } }; def foo [] { "foo" }; use spam foo; hide foo; foo"#,
         not_found_msg(),
     )
 }