aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/completion.rs33
-rw-r--r--crates/ide/src/completion/completion_context.rs22
-rw-r--r--crates/ide/src/completion/patterns.rs19
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
119pub(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.
129pub(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]
120fn test_if_is_prev() { 139fn 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);