Repository: rust-analyzer/rust-analyzer
Pull request #217 (2018-11-07 18:39:13)

Complete paths in use trees


Patch:
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs
index f708f07a0b..6c5fd0be6c 100644
--- a/crates/ra_analysis/src/completion/reference_completion.rs
+++ b/crates/ra_analysis/src/completion/reference_completion.rs
@@ -105,6 +105,9 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
     let parent = name_ref.syntax().parent()?;
     if let Some(segment) = ast::PathSegment::cast(parent) {
         let path = segment.parent_path();
+        if let Some(crate_path) = crate_path(path) {
+            return Some(NameRefKind::CratePath(crate_path));
+        }
         if path.qualifier().is_none() {
             let enclosing_fn = name_ref
                 .syntax()
@@ -113,9 +116,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
                 .find_map(ast::FnDef::cast);
             return Some(NameRefKind::LocalRef { enclosing_fn });
         }
-        if let Some(crate_path) = crate_path(path) {
-            return Some(NameRefKind::CratePath(crate_path));
-        }
     }
     None
 }
@@ -129,10 +129,21 @@ fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
             ast::PathSegmentKind::CrateKw => break,
             ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
         }
-        path = path.qualifier()?;
+        path = qualifier(path)?;
     }
     res.reverse();
-    Some(res)
+    return Some(res);
+
+    fn qualifier(path: ast::Path) -> Option<ast::Path> {
+        if let Some(q) = path.qualifier() {
+            return Some(q);
+        }
+        // TODO: this bottom up traversal is not too precise.
+        // Should we handle do a top-down analysiss, recording results?
+        let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
+        let use_tree = use_tree_list.parent_use_tree();
+        use_tree.path()
+    }
 }
 
 fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs
index d93f926729..6b0d62610c 100644
--- a/crates/ra_syntax/src/ast/mod.rs
+++ b/crates/ra_syntax/src/ast/mod.rs
@@ -296,6 +296,15 @@ impl<'a> PathSegment<'a> {
     }
 }
 
+impl<'a> UseTreeList<'a> {
+    pub fn parent_use_tree(self) -> UseTree<'a> {
+        self.syntax()
+            .parent()
+            .and_then(UseTree::cast)
+            .expect("UseTreeLists are always nested in UseTrees")
+    }
+}
+
 fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> {
     children(parent).next()
 }


Test patch:
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs
index c605d34f0b..719c166b5c 100644
--- a/crates/ra_analysis/tests/tests.rs
+++ b/crates/ra_analysis/tests/tests.rs
@@ -452,3 +452,44 @@ fn test_complete_crate_path() {
         &completions,
     );
 }
+
+#[test]
+fn test_complete_crate_path_with_braces() {
+    let (analysis, position) = analysis_and_position(
+        "
+        //- /lib.rs
+        mod foo;
+        struct Spam;
+        //- /foo.rs
+        use crate::{Sp<|>};
+    ",
+    );
+    let completions = analysis.completions(position).unwrap().unwrap();
+    assert_eq_dbg(
+        r#"[CompletionItem { label: "foo", lookup: None, snippet: None },
+            CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
+        &completions,
+    );
+}
+
+#[test]
+fn test_complete_crate_path_in_nested_tree() {
+    let (analysis, position) = analysis_and_position(
+        "
+        //- /lib.rs
+        mod foo;
+        pub mod bar {
+            pub mod baz {
+                pub struct Spam;
+            }
+        }
+        //- /foo.rs
+        use crate::{bar::{baz::Sp<|>}};
+    ",
+    );
+    let completions = analysis.completions(position).unwrap().unwrap();
+    assert_eq_dbg(
+        r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#,
+        &completions,
+    );
+}