diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-01-29 14:35:28 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-01-29 14:35:28 +0000 |
commit | b67f695956e17314b16f262cce9884f6d99c50dc (patch) | |
tree | 5b0a4d636639a6df2d079975590b14b0ae9afdec /crates/ra_assists/src/assists | |
parent | e3c81b67ffb97f5ac34af54d7c42033f7830bf2f (diff) | |
parent | 6d219c9a10a99e480c39c2382053105028d33247 (diff) |
Merge #2942
2942: Properly select a target for auto importing r=matklad a=SomeoneToIgnore
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/2932
The corresponding test has the caret placed in the beggining of the document despite the import inserted, but I don't thing I should fix it here:
* in real life, there's some text written before the import and for those cases the caret behaves normally
* it's a separate functionality that needs to be refactored anyway later (the `auto_import_text_edit`)
Co-authored-by: Kirill Bulatov <[email protected]>
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r-- | crates/ra_assists/src/assists/auto_import.rs | 46 |
1 files changed, 17 insertions, 29 deletions
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs index 9163cc662..69126a1c9 100644 --- a/crates/ra_assists/src/assists/auto_import.rs +++ b/crates/ra_assists/src/assists/auto_import.rs | |||
@@ -1,8 +1,8 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 2 | use ra_syntax::{ |
3 | ast::{self, AstNode}, | 3 | ast::{self, AstNode}, |
4 | SmolStr, SyntaxElement, | 4 | SmolStr, |
5 | SyntaxKind::{NAME_REF, USE_ITEM}, | 5 | SyntaxKind::USE_ITEM, |
6 | SyntaxNode, | 6 | SyntaxNode, |
7 | }; | 7 | }; |
8 | 8 | ||
@@ -32,25 +32,28 @@ pub(crate) fn auto_import<F: ImportsLocator>( | |||
32 | ctx: AssistCtx<impl HirDatabase>, | 32 | ctx: AssistCtx<impl HirDatabase>, |
33 | imports_locator: &mut F, | 33 | imports_locator: &mut F, |
34 | ) -> Option<Assist> { | 34 | ) -> Option<Assist> { |
35 | let path: ast::Path = ctx.find_node_at_offset()?; | 35 | let path_to_import: ast::Path = ctx.find_node_at_offset()?; |
36 | let module = path.syntax().ancestors().find_map(ast::Module::cast); | 36 | let path_to_import_syntax = path_to_import.syntax(); |
37 | if path_to_import_syntax.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() { | ||
38 | return None; | ||
39 | } | ||
40 | |||
41 | let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast); | ||
37 | let position = match module.and_then(|it| it.item_list()) { | 42 | let position = match module.and_then(|it| it.item_list()) { |
38 | Some(item_list) => item_list.syntax().clone(), | 43 | Some(item_list) => item_list.syntax().clone(), |
39 | None => { | 44 | None => { |
40 | let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?; | 45 | let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?; |
41 | current_file.syntax().clone() | 46 | current_file.syntax().clone() |
42 | } | 47 | } |
43 | }; | 48 | }; |
44 | let source_analyzer = ctx.source_analyzer(&position, None); | 49 | let source_analyzer = ctx.source_analyzer(&position, None); |
45 | let module_with_name_to_import = source_analyzer.module()?; | 50 | let module_with_name_to_import = source_analyzer.module()?; |
46 | let path_to_import = ctx.covering_element().ancestors().find_map(ast::Path::cast)?; | ||
47 | if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() { | 51 | if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() { |
48 | return None; | 52 | return None; |
49 | } | 53 | } |
50 | 54 | ||
51 | let name_to_import = &find_applicable_name_ref(ctx.covering_element())?.syntax().to_string(); | ||
52 | let proposed_imports = imports_locator | 55 | let proposed_imports = imports_locator |
53 | .find_imports(&name_to_import.to_string()) | 56 | .find_imports(&path_to_import_syntax.to_string()) |
54 | .into_iter() | 57 | .into_iter() |
55 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) | 58 | .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) |
56 | .filter(|use_path| !use_path.segments.is_empty()) | 59 | .filter(|use_path| !use_path.segments.is_empty()) |
@@ -64,26 +67,11 @@ pub(crate) fn auto_import<F: ImportsLocator>( | |||
64 | ctx.add_assist_group(AssistId("auto_import"), "auto import", || { | 67 | ctx.add_assist_group(AssistId("auto_import"), "auto import", || { |
65 | proposed_imports | 68 | proposed_imports |
66 | .into_iter() | 69 | .into_iter() |
67 | .map(|import| import_to_action(import, &position, &path_to_import.syntax())) | 70 | .map(|import| import_to_action(import, &position, &path_to_import_syntax)) |
68 | .collect() | 71 | .collect() |
69 | }) | 72 | }) |
70 | } | 73 | } |
71 | 74 | ||
72 | fn find_applicable_name_ref(element: SyntaxElement) -> Option<ast::NameRef> { | ||
73 | if element.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() { | ||
74 | None | ||
75 | } else if element.kind() == NAME_REF { | ||
76 | Some(element.as_node().cloned().and_then(ast::NameRef::cast)?) | ||
77 | } else { | ||
78 | let parent = element.parent()?; | ||
79 | if parent.kind() == NAME_REF { | ||
80 | Some(ast::NameRef::cast(parent)?) | ||
81 | } else { | ||
82 | None | ||
83 | } | ||
84 | } | ||
85 | } | ||
86 | |||
87 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | 75 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { |
88 | let mut action_builder = ActionBuilder::default(); | 76 | let mut action_builder = ActionBuilder::default(); |
89 | action_builder.label(format!("Import `{}`", &import)); | 77 | action_builder.label(format!("Import `{}`", &import)); |
@@ -110,16 +98,16 @@ mod tests { | |||
110 | auto_import, | 98 | auto_import, |
111 | TestImportsLocator::new, | 99 | TestImportsLocator::new, |
112 | r" | 100 | r" |
113 | PubStruct<|> | 101 | <|>PubStruct |
114 | 102 | ||
115 | pub mod PubMod { | 103 | pub mod PubMod { |
116 | pub struct PubStruct; | 104 | pub struct PubStruct; |
117 | } | 105 | } |
118 | ", | 106 | ", |
119 | r" | 107 | r" |
120 | use PubMod::PubStruct; | 108 | <|>use PubMod::PubStruct; |
121 | 109 | ||
122 | PubStruct<|> | 110 | PubStruct |
123 | 111 | ||
124 | pub mod PubMod { | 112 | pub mod PubMod { |
125 | pub struct PubStruct; | 113 | pub struct PubStruct; |
@@ -134,7 +122,7 @@ mod tests { | |||
134 | auto_import, | 122 | auto_import, |
135 | TestImportsLocator::new, | 123 | TestImportsLocator::new, |
136 | r" | 124 | r" |
137 | PubStruct<|> | 125 | PubSt<|>ruct |
138 | 126 | ||
139 | pub mod PubMod1 { | 127 | pub mod PubMod1 { |
140 | pub struct PubStruct; | 128 | pub struct PubStruct; |
@@ -149,7 +137,7 @@ mod tests { | |||
149 | r" | 137 | r" |
150 | use PubMod1::PubStruct; | 138 | use PubMod1::PubStruct; |
151 | 139 | ||
152 | PubStruct<|> | 140 | PubSt<|>ruct |
153 | 141 | ||
154 | pub mod PubMod1 { | 142 | pub mod PubMod1 { |
155 | pub struct PubStruct; | 143 | pub struct PubStruct; |