Skip to content

Commit 70157f0

Browse files
committed
Remove redundant completions
1 parent 59483c2 commit 70157f0

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

crates/ide/src/completion.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ pub(crate) fn completions(
112112
) -> Option<Completions> {
113113
let ctx = CompletionContext::new(db, position, config)?;
114114

115+
if ctx.no_completion_required() {
116+
// No work required here.
117+
return None;
118+
}
119+
115120
let mut acc = Completions::default();
116121
complete_attribute::complete_attribute(&mut acc, &ctx);
117122
complete_fn_param::complete_fn_param(&mut acc, &ctx);
@@ -157,6 +162,23 @@ mod tests {
157162
panic!("completion detail not found: {}", expected.detail)
158163
}
159164

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+
160182
#[test]
161183
fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
162184
check_detail_and_documentation(
@@ -208,4 +230,15 @@ mod tests {
208230
DetailAndDocumentation { detail: "fn foo(&self)", documentation: " Do the foo" },
209231
);
210232
}
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+
}
211244
}

crates/ide/src/completion/completion_context.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,10 @@ use crate::{
1616
call_info::ActiveParameter,
1717
completion::{
1818
patterns::{
19-
has_bind_pat_parent, has_block_expr_parent, has_field_list_parent,
20-
has_impl_as_prev_sibling, has_impl_parent, has_item_list_or_source_file_parent,
21-
has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev,
22-
is_in_loop_body, is_match_arm, unsafe_is_prev,
19+
fn_is_prev, for_is_prev2, has_bind_pat_parent, has_block_expr_parent,
20+
has_field_list_parent, has_impl_as_prev_sibling, has_impl_parent,
21+
has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
22+
has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
2323
},
2424
CompletionConfig,
2525
},
@@ -91,6 +91,8 @@ pub(crate) struct CompletionContext<'a> {
9191
pub(super) impl_as_prev_sibling: bool,
9292
pub(super) is_match_arm: bool,
9393
pub(super) has_item_list_or_source_file_parent: bool,
94+
pub(super) for_is_prev2: bool,
95+
pub(super) fn_is_prev: bool,
9496
pub(super) locals: Vec<(String, Local)>,
9597
}
9698

@@ -174,6 +176,8 @@ impl<'a> CompletionContext<'a> {
174176
if_is_prev: false,
175177
is_match_arm: false,
176178
has_item_list_or_source_file_parent: false,
179+
for_is_prev2: false,
180+
fn_is_prev: false,
177181
locals,
178182
};
179183

@@ -221,6 +225,14 @@ impl<'a> CompletionContext<'a> {
221225
Some(ctx)
222226
}
223227

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+
224236
/// The range of the identifier that is being completed.
225237
pub(crate) fn source_range(&self) -> TextRange {
226238
// check kind of macro-expanded token, but use range of original token
@@ -253,6 +265,8 @@ impl<'a> CompletionContext<'a> {
253265
self.mod_declaration_under_caret =
254266
find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
255267
.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());
256270
}
257271

258272
fn fill(

crates/ide/src/completion/patterns.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,25 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
116116
.is_some()
117117
}
118118

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+
119138
#[test]
120139
fn test_if_is_prev() {
121140
check_pattern_is_applicable(r"if l<|>", if_is_prev);

0 commit comments

Comments
 (0)