Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 6550a24

Browse files
committed
More precise where keyword completions
1 parent c522669 commit 6550a24

File tree

7 files changed

+96
-88
lines changed

7 files changed

+96
-88
lines changed

crates/ide-completion/src/completions/item_list.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
3636
let in_block = matches!(kind, None);
3737

3838
'block: loop {
39-
if path_qualifier.is_some() {
39+
if ctx.is_non_trivial_path() {
4040
break 'block;
4141
}
4242
if !in_trait_impl {

crates/ide-completion/src/completions/keyword.rs

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,40 @@
22
//! - `self`, `super` and `crate`, as these are considered part of path completions.
33
//! - `await`, as this is a postfix completion we handle this in the postfix completions.
44
5+
use syntax::ast::Item;
6+
57
use crate::{
6-
context::{NameRefContext, PathKind},
7-
CompletionContext, CompletionItem, CompletionItemKind, Completions,
8+
context::NameRefContext, CompletionContext, CompletionItem, CompletionItemKind, Completions,
89
};
910

1011
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
11-
if matches!(ctx.nameref_ctx(), Some(NameRefContext { record_expr: Some(_), .. })) {
12-
cov_mark::hit!(no_keyword_completion_in_record_lit);
13-
return;
14-
}
15-
if ctx.is_non_trivial_path() {
16-
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
17-
return;
18-
}
19-
if ctx.pattern_ctx.is_some() {
20-
return;
21-
}
12+
let item = match ctx.nameref_ctx() {
13+
Some(NameRefContext { keyword: Some(item), record_expr: None, .. })
14+
if !ctx.is_non_trivial_path() =>
15+
{
16+
item
17+
}
18+
_ => return,
19+
};
2220

2321
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
2422

25-
if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
26-
return;
27-
}
28-
if ctx.has_unfinished_impl_or_trait_prev_sibling() {
29-
add_keyword("where", "where");
30-
if ctx.has_impl_prev_sibling() {
31-
add_keyword("for", "for");
23+
match item {
24+
Item::Impl(it) => {
25+
if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() {
26+
add_keyword("for", "for");
27+
}
28+
add_keyword("where", "where");
29+
}
30+
Item::Enum(_)
31+
| Item::Fn(_)
32+
| Item::Struct(_)
33+
| Item::Trait(_)
34+
| Item::TypeAlias(_)
35+
| Item::Union(_) => {
36+
add_keyword("where", "where");
3237
}
33-
return;
38+
_ => (),
3439
}
3540
}
3641

crates/ide-completion/src/context.rs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,8 @@ pub(super) struct NameRefContext {
185185
// FIXME: these fields are actually disjoint -> enum
186186
pub(super) dot_access: Option<DotAccess>,
187187
pub(super) path_ctx: Option<PathCompletionCtx>,
188+
/// Position where we are only interested in keyword completions
189+
pub(super) keyword: Option<ast::Item>,
188190
/// The record expression this nameref is a field of
189191
pub(super) record_expr: Option<(ast::RecordExpr, bool)>,
190192
}
@@ -343,21 +345,6 @@ impl<'a> CompletionContext<'a> {
343345
matches!(self.completion_location, Some(ImmediateLocation::RefExpr))
344346
}
345347

346-
/// Whether the cursor is right after a trait or impl header.
347-
/// trait Foo ident$0
348-
// FIXME: This probably shouldn't exist
349-
pub(crate) fn has_unfinished_impl_or_trait_prev_sibling(&self) -> bool {
350-
matches!(
351-
self.prev_sibling,
352-
Some(ImmediatePrevSibling::ImplDefType | ImmediatePrevSibling::TraitDefName)
353-
)
354-
}
355-
356-
// FIXME: This probably shouldn't exist
357-
pub(crate) fn has_impl_prev_sibling(&self) -> bool {
358-
matches!(self.prev_sibling, Some(ImmediatePrevSibling::ImplDefType))
359-
}
360-
361348
pub(crate) fn after_if(&self) -> bool {
362349
matches!(self.prev_sibling, Some(ImmediatePrevSibling::IfExpr))
363350
}
@@ -1092,8 +1079,13 @@ impl<'a> CompletionContext<'a> {
10921079
) -> (NameRefContext, Option<PatternContext>) {
10931080
let nameref = find_node_at_offset(&original_file, name_ref.syntax().text_range().start());
10941081

1095-
let mut nameref_ctx =
1096-
NameRefContext { dot_access: None, path_ctx: None, nameref, record_expr: None };
1082+
let mut nameref_ctx = NameRefContext {
1083+
dot_access: None,
1084+
path_ctx: None,
1085+
nameref,
1086+
record_expr: None,
1087+
keyword: None,
1088+
};
10971089

10981090
if let Some(record_field) = ast::RecordExprField::for_field_name(&name_ref) {
10991091
nameref_ctx.record_expr =
@@ -1190,7 +1182,7 @@ impl<'a> CompletionContext<'a> {
11901182
syntax::algo::non_trivia_sibling(node.into(), syntax::Direction::Prev)
11911183
{
11921184
if let Some(item) = ast::Item::cast(n) {
1193-
match item {
1185+
let is_inbetween = match &item {
11941186
ast::Item::Const(it) => it.body().is_none(),
11951187
ast::Item::Enum(it) => it.variant_list().is_none(),
11961188
ast::Item::ExternBlock(it) => it.extern_item_list().is_none(),
@@ -1203,13 +1195,13 @@ impl<'a> CompletionContext<'a> {
12031195
ast::Item::TypeAlias(it) => it.ty().is_none(),
12041196
ast::Item::Union(it) => it.record_field_list().is_none(),
12051197
_ => false,
1198+
};
1199+
if is_inbetween {
1200+
return Some(item);
12061201
}
1207-
} else {
1208-
false
12091202
}
1210-
} else {
1211-
false
12121203
}
1204+
None
12131205
};
12141206

12151207
let kind = path.syntax().ancestors().find_map(|it| {
@@ -1222,7 +1214,8 @@ impl<'a> CompletionContext<'a> {
12221214
ast::PathExpr(it) => {
12231215
if let Some(p) = it.syntax().parent() {
12241216
if ast::ExprStmt::can_cast(p.kind()) {
1225-
if inbetween_body_and_decl_check(p) {
1217+
if let Some(kind) = inbetween_body_and_decl_check(p) {
1218+
nameref_ctx.keyword = Some(kind);
12261219
return Some(None);
12271220
}
12281221
}
@@ -1250,7 +1243,8 @@ impl<'a> CompletionContext<'a> {
12501243
Some(PathKind::Pat)
12511244
},
12521245
ast::MacroCall(it) => {
1253-
if inbetween_body_and_decl_check(it.syntax().clone()) {
1246+
if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) {
1247+
nameref_ctx.keyword = Some(kind);
12541248
return Some(None);
12551249
}
12561250

crates/ide-completion/src/patterns.rs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ use crate::tests::check_pattern_is_applicable;
2121
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
2222
pub(crate) enum ImmediatePrevSibling {
2323
IfExpr,
24-
TraitDefName,
25-
ImplDefType,
2624
}
2725

2826
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -81,17 +79,6 @@ pub(crate) fn determine_prev_sibling(name_like: &ast::NameLike) -> Option<Immedi
8179
}
8280
}
8381
},
84-
ast::Trait(it) => if it.assoc_item_list().is_none() {
85-
ImmediatePrevSibling::TraitDefName
86-
} else {
87-
return None
88-
},
89-
ast::Impl(it) => if it.assoc_item_list().is_none()
90-
&& (it.for_token().is_none() || it.self_ty().is_some()) {
91-
ImmediatePrevSibling::ImplDefType
92-
} else {
93-
return None
94-
},
9582
_ => return None,
9683
}
9784
};
@@ -342,22 +329,6 @@ mod tests {
342329
check_location(r"fn my_fn() { let x = &m$0 foo; }", ImmediateLocation::RefExpr);
343330
}
344331

345-
#[test]
346-
fn test_impl_prev_sibling() {
347-
check_prev_sibling(r"impl A w$0 ", ImmediatePrevSibling::ImplDefType);
348-
check_prev_sibling(r"impl A w$0 {}", ImmediatePrevSibling::ImplDefType);
349-
check_prev_sibling(r"impl A for A w$0 ", ImmediatePrevSibling::ImplDefType);
350-
check_prev_sibling(r"impl A for A w$0 {}", ImmediatePrevSibling::ImplDefType);
351-
check_prev_sibling(r"impl A for w$0 {}", None);
352-
check_prev_sibling(r"impl A for w$0", None);
353-
}
354-
355-
#[test]
356-
fn test_trait_prev_sibling() {
357-
check_prev_sibling(r"trait A w$0 ", ImmediatePrevSibling::TraitDefName);
358-
check_prev_sibling(r"trait A w$0 {}", ImmediatePrevSibling::TraitDefName);
359-
}
360-
361332
#[test]
362333
fn test_if_expr_prev_sibling() {
363334
check_prev_sibling(r"fn foo() { if true {} w$0", ImmediatePrevSibling::IfExpr);

crates/ide-completion/src/tests/item.rs

Lines changed: 51 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,26 +76,66 @@ fn after_target_name_in_impl() {
7676
kw where
7777
"#]],
7878
);
79-
// FIXME: This should not emit `kw for`
8079
check(
81-
r"impl Trait for Type $0",
80+
r"impl Trait f$0",
8281
expect![[r#"
8382
kw for
8483
kw where
8584
"#]],
8685
);
86+
check(
87+
r"impl Trait for Type $0",
88+
expect![[r#"
89+
kw where
90+
"#]],
91+
);
8792
}
8893

8994
#[test]
90-
fn after_struct_name() {
91-
// FIXME: This should emit `kw where`
92-
check(r"struct Struct $0", expect![[r#""#]]);
93-
}
94-
95-
#[test]
96-
fn after_fn_name() {
97-
// FIXME: This should emit `kw where`
98-
check(r"fn func() $0", expect![[r#""#]]);
95+
fn completes_where() {
96+
check(
97+
r"struct Struct $0",
98+
expect![[r#"
99+
kw where
100+
"#]],
101+
);
102+
check(
103+
r"struct Struct $0 {}",
104+
expect![[r#"
105+
kw where
106+
"#]],
107+
);
108+
// FIXME: This shouldn't be completed here
109+
check(
110+
r"struct Struct $0 ()",
111+
expect![[r#"
112+
kw where
113+
"#]],
114+
);
115+
check(
116+
r"fn func() $0",
117+
expect![[r#"
118+
kw where
119+
"#]],
120+
);
121+
check(
122+
r"enum Enum $0",
123+
expect![[r#"
124+
kw where
125+
"#]],
126+
);
127+
check(
128+
r"enum Enum $0 {}",
129+
expect![[r#"
130+
kw where
131+
"#]],
132+
);
133+
check(
134+
r"trait Trait $0 {}",
135+
expect![[r#"
136+
kw where
137+
"#]],
138+
);
99139
}
100140

101141
#[test]

crates/ide-completion/src/tests/item_list.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ fn in_item_list_after_attr() {
108108

109109
#[test]
110110
fn in_qualified_path() {
111-
cov_mark::check!(no_keyword_completion_in_non_trivial_path);
112111
check(
113112
r#"crate::$0"#,
114113
expect![[r#"

crates/ide-completion/src/tests/record.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ fn check(ra_fixture: &str, expect: Expect) {
99

1010
#[test]
1111
fn without_default_impl() {
12-
cov_mark::check!(no_keyword_completion_in_record_lit);
1312
check(
1413
r#"
1514
struct Struct { foo: u32, bar: usize }

0 commit comments

Comments
 (0)