Skip to content

Commit 3a16950

Browse files
committed
Cleanup ImmediateLocation determination
1 parent f41c983 commit 3a16950

File tree

5 files changed

+132
-115
lines changed

5 files changed

+132
-115
lines changed

crates/ide_completion/src/completions/keyword.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
104104
if expects_item || has_block_expr_parent {
105105
add_keyword(ctx, acc, "mod", "mod $0");
106106
}
107-
if ctx.has_ident_or_ref_pat_parent() {
107+
if ctx.expects_ident_pat_or_ref_expr() {
108108
add_keyword(ctx, acc, "mut", "mut ");
109109
}
110110
if expects_item || expects_assoc_item || has_block_expr_parent {

crates/ide_completion/src/completions/qualified_path.rs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
2020
None => return,
2121
};
2222
let context_module = ctx.scope.module();
23-
2423
if ctx.expects_item() || ctx.expects_assoc_item() {
2524
if let PathResolution::Def(hir::ModuleDef::Module(module)) = resolution {
2625
let module_scope = module.scope(ctx.db, context_module);
@@ -606,14 +605,33 @@ fn main() { T::$0; }
606605
macro_rules! foo { () => {} }
607606
608607
fn main() { let _ = crate::$0 }
609-
"#,
608+
"#,
610609
expect![[r##"
611610
fn main() fn()
612611
ma foo!(…) #[macro_export] macro_rules! foo
613612
"##]],
614613
);
615614
}
616615

616+
#[test]
617+
fn completes_qualified_macros_in_impl() {
618+
check(
619+
r#"
620+
#[macro_export]
621+
macro_rules! foo { () => {} }
622+
623+
struct MyStruct {}
624+
625+
impl MyStruct {
626+
crate::$0
627+
}
628+
"#,
629+
expect![[r##"
630+
ma foo! #[macro_export] macro_rules! foo
631+
"##]],
632+
);
633+
}
634+
617635
#[test]
618636
fn test_super_super_completion() {
619637
check(

crates/ide_completion/src/context.rs

Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ use text_edit::Indel;
1717

1818
use crate::{
1919
patterns::{
20-
for_is_prev2, has_bind_pat_parent, has_block_expr_parent, has_field_list_parent,
21-
has_impl_parent, has_item_list_or_source_file_parent, has_prev_sibling, has_ref_parent,
22-
has_trait_parent, inside_impl_trait_block, is_in_loop_body, is_match_arm, previous_token,
20+
determine_location, for_is_prev2, has_prev_sibling, inside_impl_trait_block,
21+
is_in_loop_body, is_match_arm, previous_token, ImmediateLocation,
2322
},
2423
CompletionConfig,
2524
};
@@ -30,18 +29,6 @@ pub(crate) enum PatternRefutability {
3029
Irrefutable,
3130
}
3231

33-
/// Direct parent container of the cursor position
34-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35-
pub(crate) enum ImmediateLocation {
36-
Impl,
37-
Trait,
38-
RecordFieldList,
39-
RefPatOrExpr,
40-
IdentPat,
41-
BlockExpr,
42-
ItemList,
43-
}
44-
4532
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
4633
pub(crate) enum PrevSibling {
4734
Trait,
@@ -301,15 +288,15 @@ impl<'a> CompletionContext<'a> {
301288
matches!(self.completion_location, Some(ImmediateLocation::BlockExpr))
302289
}
303290

304-
pub(crate) fn has_ident_or_ref_pat_parent(&self) -> bool {
291+
pub(crate) fn expects_ident_pat_or_ref_expr(&self) -> bool {
305292
matches!(
306293
self.completion_location,
307-
Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefPatOrExpr)
294+
Some(ImmediateLocation::IdentPat) | Some(ImmediateLocation::RefExpr)
308295
)
309296
}
310297

311298
pub(crate) fn expect_record_field(&self) -> bool {
312-
matches!(self.completion_location, Some(ImmediateLocation::RecordFieldList))
299+
matches!(self.completion_location, Some(ImmediateLocation::RecordField))
313300
}
314301

315302
pub(crate) fn has_impl_or_trait_prev_sibling(&self) -> bool {
@@ -324,9 +311,8 @@ impl<'a> CompletionContext<'a> {
324311
}
325312

326313
fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
327-
dbg!(file_with_fake_ident);
328314
let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
329-
let syntax_element = NodeOrToken::Token(fake_ident_token);
315+
let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
330316
self.previous_token = previous_token(syntax_element.clone());
331317
self.in_loop_body = is_in_loop_body(syntax_element.clone());
332318
self.is_match_arm = is_match_arm(syntax_element.clone());
@@ -336,22 +322,6 @@ impl<'a> CompletionContext<'a> {
336322
self.prev_sibling = Some(PrevSibling::Trait)
337323
}
338324

339-
if has_block_expr_parent(syntax_element.clone()) {
340-
self.completion_location = Some(ImmediateLocation::BlockExpr);
341-
} else if has_bind_pat_parent(syntax_element.clone()) {
342-
self.completion_location = Some(ImmediateLocation::IdentPat);
343-
} else if has_ref_parent(syntax_element.clone()) {
344-
self.completion_location = Some(ImmediateLocation::RefPatOrExpr);
345-
} else if has_impl_parent(syntax_element.clone()) {
346-
self.completion_location = Some(ImmediateLocation::Impl);
347-
} else if has_field_list_parent(syntax_element.clone()) {
348-
self.completion_location = Some(ImmediateLocation::RecordFieldList);
349-
} else if has_trait_parent(syntax_element.clone()) {
350-
self.completion_location = Some(ImmediateLocation::Trait);
351-
} else if has_item_list_or_source_file_parent(syntax_element.clone()) {
352-
self.completion_location = Some(ImmediateLocation::ItemList);
353-
}
354-
355325
self.mod_declaration_under_caret =
356326
find_node_at_offset::<ast::Module>(&file_with_fake_ident, offset)
357327
.filter(|module| module.item_list().is_none());
@@ -364,6 +334,8 @@ impl<'a> CompletionContext<'a> {
364334
let fn_is_prev = self.previous_token_is(T![fn]);
365335
let for_is_prev2 = for_is_prev2(syntax_element.clone());
366336
self.no_completion_required = (fn_is_prev && !inside_impl_trait_block) || for_is_prev2;
337+
338+
self.completion_location = determine_location(fake_ident_token);
367339
}
368340

369341
fn fill_impl_def(&mut self) {

crates/ide_completion/src/patterns.rs

Lines changed: 102 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -11,102 +11,133 @@ use syntax::{
1111
#[cfg(test)]
1212
use crate::test_utils::{check_pattern_is_applicable, check_pattern_is_not_applicable};
1313

14-
pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15-
not_same_range_ancestor(element)
16-
.filter(|it| it.kind() == ASSOC_ITEM_LIST)
17-
.and_then(|it| it.parent())
18-
.filter(|it| it.kind() == TRAIT)
19-
.is_some()
20-
}
21-
#[test]
22-
fn test_has_trait_parent() {
23-
check_pattern_is_applicable(r"trait A { f$0 }", has_trait_parent);
14+
/// Direct parent container of the cursor position
15+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16+
pub(crate) enum ImmediateLocation {
17+
Impl,
18+
Trait,
19+
RecordField,
20+
RefExpr,
21+
IdentPat,
22+
BlockExpr,
23+
ItemList,
24+
}
25+
26+
pub(crate) fn determine_location(tok: SyntaxToken) -> Option<ImmediateLocation> {
27+
// First "expand" the element we are completing to its maximum so that we can check in what
28+
// context it immediately lies. This for example means if the token is a NameRef at the end of
29+
// a path, we want to look at where the path is in the tree.
30+
let node = match tok.parent().and_then(ast::NameLike::cast)? {
31+
ast::NameLike::NameRef(name_ref) => {
32+
if let Some(segment) = name_ref.syntax().parent().and_then(ast::PathSegment::cast) {
33+
let p = segment.parent_path();
34+
if p.parent_path().is_none() {
35+
p.syntax()
36+
.ancestors()
37+
.take_while(|it| it.text_range() == p.syntax().text_range())
38+
.last()?
39+
} else {
40+
return None;
41+
}
42+
} else {
43+
return None;
44+
}
45+
}
46+
it @ ast::NameLike::Name(_) | it @ ast::NameLike::Lifetime(_) => it.syntax().clone(),
47+
};
48+
let parent = match node.parent() {
49+
Some(parent) => parent,
50+
// SourceFile
51+
None => {
52+
return match node.kind() {
53+
MACRO_ITEMS | SOURCE_FILE => Some(ImmediateLocation::ItemList),
54+
_ => None,
55+
}
56+
}
57+
};
58+
let res = match_ast! {
59+
match parent {
60+
ast::IdentPat(_it) => ImmediateLocation::IdentPat,
61+
ast::BlockExpr(_it) => ImmediateLocation::BlockExpr,
62+
ast::SourceFile(_it) => ImmediateLocation::ItemList,
63+
ast::ItemList(_it) => ImmediateLocation::ItemList,
64+
ast::RefExpr(_it) => ImmediateLocation::RefExpr,
65+
ast::RefPat(_it) => ImmediateLocation::RefExpr,
66+
ast::RecordField(_it) => ImmediateLocation::RecordField,
67+
ast::AssocItemList(it) => match it.syntax().parent().map(|it| it.kind()) {
68+
Some(IMPL) => ImmediateLocation::Impl,
69+
Some(TRAIT) => ImmediateLocation::Trait,
70+
_ => return None,
71+
},
72+
_ => return None,
73+
}
74+
};
75+
Some(res)
2476
}
2577

26-
pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27-
not_same_range_ancestor(element)
28-
.filter(|it| it.kind() == ASSOC_ITEM_LIST)
29-
.and_then(|it| it.parent())
30-
.filter(|it| it.kind() == IMPL)
31-
.is_some()
32-
}
33-
#[test]
34-
fn test_has_impl_parent() {
35-
check_pattern_is_applicable(r"impl A { f$0 }", has_impl_parent);
78+
#[cfg(test)]
79+
fn check_location(code: &str, loc: ImmediateLocation) {
80+
check_pattern_is_applicable(code, |e| {
81+
assert_eq!(determine_location(e.into_token().expect("Expected a token")), Some(loc));
82+
true
83+
});
3684
}
3785

38-
pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
39-
// Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
40-
// where we only check the first parent with different text range.
41-
element
42-
.ancestors()
43-
.find(|it| it.kind() == IMPL)
44-
.map(|it| ast::Impl::cast(it).unwrap())
45-
.map(|it| it.trait_().is_some())
46-
.unwrap_or(false)
47-
}
4886
#[test]
49-
fn test_inside_impl_trait_block() {
50-
check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block);
51-
check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
52-
check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
53-
check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
87+
fn test_has_trait_parent() {
88+
check_location(r"trait A { f$0 }", ImmediateLocation::Trait);
5489
}
5590

56-
pub(crate) fn has_field_list_parent(element: SyntaxElement) -> bool {
57-
not_same_range_ancestor(element).filter(|it| it.kind() == RECORD_FIELD_LIST).is_some()
91+
#[test]
92+
fn test_has_impl_parent() {
93+
check_location(r"impl A { f$0 }", ImmediateLocation::Impl);
5894
}
5995
#[test]
6096
fn test_has_field_list_parent() {
61-
check_pattern_is_applicable(r"struct Foo { f$0 }", has_field_list_parent);
62-
check_pattern_is_applicable(r"struct Foo { f$0 pub f: i32}", has_field_list_parent);
97+
check_location(r"struct Foo { f$0 }", ImmediateLocation::RecordField);
98+
check_location(r"struct Foo { f$0 pub f: i32}", ImmediateLocation::RecordField);
6399
}
64100

65-
pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
66-
not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
67-
}
68101
#[test]
69102
fn test_has_block_expr_parent() {
70-
check_pattern_is_applicable(r"fn my_fn() { let a = 2; f$0 }", has_block_expr_parent);
103+
check_location(r"fn my_fn() { let a = 2; f$0 }", ImmediateLocation::BlockExpr);
71104
}
72105

73-
pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
74-
element.ancestors().any(|it| it.kind() == IDENT_PAT)
106+
#[test]
107+
fn test_has_ident_pat_parent() {
108+
check_location(r"fn my_fn(m$0) {}", ImmediateLocation::IdentPat);
109+
check_location(r"fn my_fn() { let m$0 }", ImmediateLocation::IdentPat);
110+
check_location(r"fn my_fn(&m$0) {}", ImmediateLocation::IdentPat);
111+
check_location(r"fn my_fn() { let &m$0 }", ImmediateLocation::IdentPat);
75112
}
76113

77114
#[test]
78-
fn test_has_bind_pat_parent() {
79-
check_pattern_is_applicable(r"fn my_fn(m$0) {}", has_bind_pat_parent);
80-
check_pattern_is_applicable(r"fn my_fn() { let m$0 }", has_bind_pat_parent);
115+
fn test_has_ref_expr_parent() {
116+
check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
81117
}
82118

83-
pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
84-
not_same_range_ancestor(element)
85-
.filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
86-
.is_some()
87-
}
88119
#[test]
89-
fn test_has_ref_parent() {
90-
check_pattern_is_applicable(r"fn my_fn(&m$0) {}", has_ref_parent);
91-
check_pattern_is_applicable(r"fn my() { let &m$0 }", has_ref_parent);
120+
fn test_has_item_list_or_source_file_parent() {
121+
check_location(r"i$0", ImmediateLocation::ItemList);
122+
check_location(r"mod foo { f$0 }", ImmediateLocation::ItemList);
92123
}
93124

94-
pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
95-
let it = element
125+
pub(crate) fn inside_impl_trait_block(element: SyntaxElement) -> bool {
126+
// Here we search `impl` keyword up through the all ancestors, unlike in `has_impl_parent`,
127+
// where we only check the first parent with different text range.
128+
element
96129
.ancestors()
97-
.take_while(|it| it.text_range() == element.text_range())
98-
.last()
99-
.map(|it| (it.kind(), it.parent()));
100-
match it {
101-
Some((_, Some(it))) => it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST,
102-
Some((MACRO_ITEMS, None) | (SOURCE_FILE, None)) => true,
103-
_ => false,
104-
}
130+
.find(|it| it.kind() == IMPL)
131+
.map(|it| ast::Impl::cast(it).unwrap())
132+
.map(|it| it.trait_().is_some())
133+
.unwrap_or(false)
105134
}
106135
#[test]
107-
fn test_has_item_list_or_source_file_parent() {
108-
check_pattern_is_applicable(r"i$0", has_item_list_or_source_file_parent);
109-
check_pattern_is_applicable(r"mod foo { f$0 }", has_item_list_or_source_file_parent);
136+
fn test_inside_impl_trait_block() {
137+
check_pattern_is_applicable(r"impl Foo for Bar { f$0 }", inside_impl_trait_block);
138+
check_pattern_is_applicable(r"impl Foo for Bar { fn f$0 }", inside_impl_trait_block);
139+
check_pattern_is_not_applicable(r"impl A { f$0 }", inside_impl_trait_block);
140+
check_pattern_is_not_applicable(r"impl A { fn f$0 }", inside_impl_trait_block);
110141
}
111142

112143
pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
@@ -166,12 +197,8 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
166197
.is_some()
167198
}
168199

169-
fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
170-
element
171-
.ancestors()
172-
.take_while(|it| it.text_range() == element.text_range())
173-
.last()
174-
.and_then(|it| it.parent())
200+
pub(crate) fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
201+
element.ancestors().skip_while(|it| it.text_range() == element.text_range()).next()
175202
}
176203

177204
fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {

crates/ide_completion/src/test_utils.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ pub(crate) fn check_edit_with_config(
132132
assert_eq_text!(&ra_fixture_after, &actual)
133133
}
134134

135-
pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
135+
pub(crate) fn check_pattern_is_applicable(code: &str, check: impl FnOnce(SyntaxElement) -> bool) {
136136
let (db, pos) = position(code);
137137

138138
let sema = Semantics::new(&db);

0 commit comments

Comments
 (0)