diff options
-rw-r--r-- | crates/ide/src/completion.rs | 33 | ||||
-rw-r--r-- | crates/ide/src/completion/completion_context.rs | 22 | ||||
-rw-r--r-- | crates/ide/src/completion/patterns.rs | 19 |
3 files changed, 70 insertions, 4 deletions
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index b0e35b2bd..570091ba3 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs | |||
@@ -112,6 +112,11 @@ pub(crate) fn completions( | |||
112 | ) -> Option<Completions> { | 112 | ) -> Option<Completions> { |
113 | let ctx = CompletionContext::new(db, position, config)?; | 113 | let ctx = CompletionContext::new(db, position, config)?; |
114 | 114 | ||
115 | if ctx.no_completion_required() { | ||
116 | // No work required here. | ||
117 | return None; | ||
118 | } | ||
119 | |||
115 | let mut acc = Completions::default(); | 120 | let mut acc = Completions::default(); |
116 | complete_attribute::complete_attribute(&mut acc, &ctx); | 121 | complete_attribute::complete_attribute(&mut acc, &ctx); |
117 | complete_fn_param::complete_fn_param(&mut acc, &ctx); | 122 | complete_fn_param::complete_fn_param(&mut acc, &ctx); |
@@ -157,6 +162,23 @@ mod tests { | |||
157 | panic!("completion detail not found: {}", expected.detail) | 162 | panic!("completion detail not found: {}", expected.detail) |
158 | } | 163 | } |
159 | 164 | ||
165 | fn check_no_completion(ra_fixture: &str) { | ||
166 | let (analysis, position) = fixture::position(ra_fixture); | ||
167 | let config = CompletionConfig::default(); | ||
168 | analysis.completions(&config, position).unwrap(); | ||
169 | |||
170 | let completions: Option<Vec<String>> = analysis | ||
171 | .completions(&config, position) | ||
172 | .unwrap() | ||
173 | .and_then(|completions| if completions.is_empty() { None } else { Some(completions) }) | ||
174 | .map(|completions| { | ||
175 | completions.into_iter().map(|completion| format!("{:?}", completion)).collect() | ||
176 | }); | ||
177 | |||
178 | // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic. | ||
179 | assert_eq!(completions, None, "Completions were generated, but weren't expected"); | ||
180 | } | ||
181 | |||
160 | #[test] | 182 | #[test] |
161 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { | 183 | fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() { |
162 | check_detail_and_documentation( | 184 | check_detail_and_documentation( |
@@ -208,4 +230,15 @@ mod tests { | |||
208 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, | 230 | DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" }, |
209 | ); | 231 | ); |
210 | } | 232 | } |
233 | |||
234 | #[test] | ||
235 | fn test_no_completions_required() { | ||
236 | check_no_completion( | ||
237 | r#" | ||
238 | fn foo() { | ||
239 | for i i<|> | ||
240 | } | ||
241 | "#, | ||
242 | ) | ||
243 | } | ||
211 | } | 244 | } |
diff --git a/crates/ide/src/completion/completion_context.rs b/crates/ide/src/completion/completion_context.rs index 8dea8a4bf..c9473cca6 100644 --- a/crates/ide/src/completion/completion_context.rs +++ b/crates/ide/src/completion/completion_context.rs | |||
@@ -16,10 +16,10 @@ use crate::{ | |||
16 | call_info::ActiveParameter, | 16 | call_info::ActiveParameter, |
17 | completion::{ | 17 | completion::{ |
18 | patterns::{ | 18 | patterns::{ |
19 | has_bind_pat_parent, has_block_expr_parent, has_field_list_parent, | 19 | fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent, |
20 | has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent, | 20 | has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent, |
21 | has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, | 21 | has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, |
22 | is_in_loop_body, is_match_arm, unsafe_is_prev, | 22 | has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev, |
23 | }, | 23 | }, |
24 | CompletionConfig, | 24 | CompletionConfig, |
25 | }, | 25 | }, |
@@ -91,6 +91,8 @@ pub(crate) struct CompletionContext<'a> { | |||
91 | pub(super) impl_as_prev_sibling: bool, | 91 | pub(super) impl_as_prev_sibling: bool, |
92 | pub(super) is_match_arm: bool, | 92 | pub(super) is_match_arm: bool, |
93 | pub(super) has_item_list_or_source_file_parent: bool, | 93 | pub(super) has_item_list_or_source_file_parent: bool, |
94 | pub(super) for_is_prev2: bool, | ||
95 | pub(super) fn_is_prev: bool, | ||
94 | pub(super) locals: Vec<(String, Local)>, | 96 | pub(super) locals: Vec<(String, Local)>, |
95 | } | 97 | } |
96 | 98 | ||
@@ -174,6 +176,8 @@ impl<'a> CompletionContext<'a> { | |||
174 | if_is_prev: false, | 176 | if_is_prev: false, |
175 | is_match_arm: false, | 177 | is_match_arm: false, |
176 | has_item_list_or_source_file_parent: false, | 178 | has_item_list_or_source_file_parent: false, |
179 | for_is_prev2: false, | ||
180 | fn_is_prev: false, | ||
177 | locals, | 181 | locals, |
178 | }; | 182 | }; |
179 | 183 | ||
@@ -221,6 +225,14 @@ impl<'a> CompletionContext<'a> { | |||
221 | Some(ctx) | 225 | Some(ctx) |
222 | } | 226 | } |
223 | 227 | ||
228 | /// Checks whether completions in that particular case don't make much sense. | ||
229 | /// Examples: | ||
230 | /// - `fn <|>` -- we expect function name, it's unlikely that "hint" will be helpful. | ||
231 | /// - `for _ i<|>` -- obviously, it'll be "in" keyword. | ||
232 | pub(crate) fn no_completion_required(&self) -> bool { | ||
233 | self.fn_is_prev || self.for_is_prev2 | ||
234 | } | ||
235 | |||
224 | /// The range of the identifier that is being completed. | 236 | /// The range of the identifier that is being completed. |
225 | pub(crate) fn source_range(&self) -> TextRange { | 237 | pub(crate) fn source_range(&self) -> TextRange { |
226 | // check kind of macro-expanded token, but use range of original token | 238 | // check kind of macro-expanded token, but use range of original token |
@@ -253,6 +265,8 @@ impl<'a> CompletionContext<'a> { | |||
253 | self.mod_declaration_under_caret = | 265 | self.mod_declaration_under_caret = |
254 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) | 266 | find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset) |
255 | .filter(|module| module.item_list().is_none()); | 267 | .filter(|module| module.item_list().is_none()); |
268 | self.for_is_prev2 = for_is_prev2(syntax_element.clone()); | ||
269 | self.fn_is_prev = fn_is_prev(syntax_element.clone()); | ||
256 | } | 270 | } |
257 | 271 | ||
258 | fn fill( | 272 | fn fill( |
diff --git a/crates/ide/src/completion/patterns.rs b/crates/ide/src/completion/patterns.rs index b17ddf133..76fcad631 100644 --- a/crates/ide/src/completion/patterns.rs +++ b/crates/ide/src/completion/patterns.rs | |||
@@ -116,6 +116,25 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { | |||
116 | .is_some() | 116 | .is_some() |
117 | } | 117 | } |
118 | 118 | ||
119 | pub(crate) fn fn_is_prev(element: SyntaxElement) -> bool { | ||
120 | element | ||
121 | .into_token() | ||
122 | .and_then(|it| previous_non_trivia_token(it)) | ||
123 | .filter(|it| it.kind() == FN_KW) | ||
124 | .is_some() | ||
125 | } | ||
126 | |||
127 | /// Check if the token previous to the previous one is `for`. | ||
128 | /// For example, `for _ i<|>` => true. | ||
129 | pub(crate) fn for_is_prev2(element: SyntaxElement) -> bool { | ||
130 | element | ||
131 | .into_token() | ||
132 | .and_then(|it| previous_non_trivia_token(it)) | ||
133 | .and_then(|it| previous_non_trivia_token(it)) | ||
134 | .filter(|it| it.kind() == FOR_KW) | ||
135 | .is_some() | ||
136 | } | ||
137 | |||
119 | #[test] | 138 | #[test] |
120 | fn test_if_is_prev() { | 139 | fn test_if_is_prev() { |
121 | check_pattern_is_applicable(r"if l<|>", if_is_prev); | 140 | check_pattern_is_applicable(r"if l<|>", if_is_prev); |