diff options
author | Aleksey Kladov <[email protected]> | 2018-11-07 18:38:41 +0000 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2018-11-07 18:38:41 +0000 |
commit | 6d253b58da955cee73b0715b91d728df5009937d (patch) | |
tree | 8951f181a563fbb08199ab99f383f2d01a756f05 /crates | |
parent | 06fbc6e3edca1e53f1034bf779f2677d87076c1c (diff) |
Complete paths in use trees
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_analysis/src/completion/reference_completion.rs | 21 | ||||
-rw-r--r-- | crates/ra_analysis/tests/tests.rs | 41 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/mod.rs | 9 |
3 files changed, 66 insertions, 5 deletions
diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index f708f07a0..6c5fd0be6 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> { | |||
105 | let parent = name_ref.syntax().parent()?; | 105 | let parent = name_ref.syntax().parent()?; |
106 | if let Some(segment) = ast::PathSegment::cast(parent) { | 106 | if let Some(segment) = ast::PathSegment::cast(parent) { |
107 | let path = segment.parent_path(); | 107 | let path = segment.parent_path(); |
108 | if let Some(crate_path) = crate_path(path) { | ||
109 | return Some(NameRefKind::CratePath(crate_path)); | ||
110 | } | ||
108 | if path.qualifier().is_none() { | 111 | if path.qualifier().is_none() { |
109 | let enclosing_fn = name_ref | 112 | let enclosing_fn = name_ref |
110 | .syntax() | 113 | .syntax() |
@@ -113,9 +116,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> { | |||
113 | .find_map(ast::FnDef::cast); | 116 | .find_map(ast::FnDef::cast); |
114 | return Some(NameRefKind::LocalRef { enclosing_fn }); | 117 | return Some(NameRefKind::LocalRef { enclosing_fn }); |
115 | } | 118 | } |
116 | if let Some(crate_path) = crate_path(path) { | ||
117 | return Some(NameRefKind::CratePath(crate_path)); | ||
118 | } | ||
119 | } | 119 | } |
120 | None | 120 | None |
121 | } | 121 | } |
@@ -129,10 +129,21 @@ fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> { | |||
129 | ast::PathSegmentKind::CrateKw => break, | 129 | ast::PathSegmentKind::CrateKw => break, |
130 | ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None, | 130 | ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None, |
131 | } | 131 | } |
132 | path = path.qualifier()?; | 132 | path = qualifier(path)?; |
133 | } | 133 | } |
134 | res.reverse(); | 134 | res.reverse(); |
135 | Some(res) | 135 | return Some(res); |
136 | |||
137 | fn qualifier(path: ast::Path) -> Option<ast::Path> { | ||
138 | if let Some(q) = path.qualifier() { | ||
139 | return Some(q); | ||
140 | } | ||
141 | // TODO: this bottom up traversal is not too precise. | ||
142 | // Should we handle do a top-down analysiss, recording results? | ||
143 | let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?; | ||
144 | let use_tree = use_tree_list.parent_use_tree(); | ||
145 | use_tree.path() | ||
146 | } | ||
136 | } | 147 | } |
137 | 148 | ||
138 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { | 149 | fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { |
diff --git a/crates/ra_analysis/tests/tests.rs b/crates/ra_analysis/tests/tests.rs index c605d34f0..719c166b5 100644 --- a/crates/ra_analysis/tests/tests.rs +++ b/crates/ra_analysis/tests/tests.rs | |||
@@ -452,3 +452,44 @@ fn test_complete_crate_path() { | |||
452 | &completions, | 452 | &completions, |
453 | ); | 453 | ); |
454 | } | 454 | } |
455 | |||
456 | #[test] | ||
457 | fn test_complete_crate_path_with_braces() { | ||
458 | let (analysis, position) = analysis_and_position( | ||
459 | " | ||
460 | //- /lib.rs | ||
461 | mod foo; | ||
462 | struct Spam; | ||
463 | //- /foo.rs | ||
464 | use crate::{Sp<|>}; | ||
465 | ", | ||
466 | ); | ||
467 | let completions = analysis.completions(position).unwrap().unwrap(); | ||
468 | assert_eq_dbg( | ||
469 | r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, | ||
470 | CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | ||
471 | &completions, | ||
472 | ); | ||
473 | } | ||
474 | |||
475 | #[test] | ||
476 | fn test_complete_crate_path_in_nested_tree() { | ||
477 | let (analysis, position) = analysis_and_position( | ||
478 | " | ||
479 | //- /lib.rs | ||
480 | mod foo; | ||
481 | pub mod bar { | ||
482 | pub mod baz { | ||
483 | pub struct Spam; | ||
484 | } | ||
485 | } | ||
486 | //- /foo.rs | ||
487 | use crate::{bar::{baz::Sp<|>}}; | ||
488 | ", | ||
489 | ); | ||
490 | let completions = analysis.completions(position).unwrap().unwrap(); | ||
491 | assert_eq_dbg( | ||
492 | r#"[CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, | ||
493 | &completions, | ||
494 | ); | ||
495 | } | ||
diff --git a/crates/ra_syntax/src/ast/mod.rs b/crates/ra_syntax/src/ast/mod.rs index d93f92672..6b0d62610 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> { | |||
296 | } | 296 | } |
297 | } | 297 | } |
298 | 298 | ||
299 | impl<'a> UseTreeList<'a> { | ||
300 | pub fn parent_use_tree(self) -> UseTree<'a> { | ||
301 | self.syntax() | ||
302 | .parent() | ||
303 | .and_then(UseTree::cast) | ||
304 | .expect("UseTreeLists are always nested in UseTrees") | ||
305 | } | ||
306 | } | ||
307 | |||
299 | fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { | 308 | fn child_opt<'a, P: AstNode<'a>, C: AstNode<'a>>(parent: P) -> Option<C> { |
300 | children(parent).next() | 309 | children(parent).next() |
301 | } | 310 | } |