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

Commit d06d0f8

Browse files
committed
Auto merge of rust-lang#12459 - Veykril:completions, r=Veykril
internal: Clean up keyword completion handling rust-lang/rust-analyzer#12144
2 parents d0a7ad4 + 2a60b84 commit d06d0f8

File tree

15 files changed

+291
-396
lines changed

15 files changed

+291
-396
lines changed

crates/ide-completion/src/completions.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub(crate) mod attribute;
44
pub(crate) mod dot;
55
pub(crate) mod expr;
66
pub(crate) mod extern_abi;
7+
pub(crate) mod field;
78
pub(crate) mod flyimport;
89
pub(crate) mod fn_param;
910
pub(crate) mod format_string;
@@ -110,6 +111,26 @@ impl Completions {
110111
["self", "super", "crate"].into_iter().for_each(|kw| self.add_keyword(ctx, kw));
111112
}
112113

114+
pub(crate) fn add_keyword_snippet(&mut self, ctx: &CompletionContext, kw: &str, snippet: &str) {
115+
let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
116+
117+
match ctx.config.snippet_cap {
118+
Some(cap) => {
119+
if snippet.ends_with('}') && ctx.incomplete_let {
120+
// complete block expression snippets with a trailing semicolon, if inside an incomplete let
121+
cov_mark::hit!(let_semi);
122+
item.insert_snippet(cap, format!("{};", snippet));
123+
} else {
124+
item.insert_snippet(cap, snippet);
125+
}
126+
}
127+
None => {
128+
item.insert_text(if snippet.contains('$') { kw } else { snippet });
129+
}
130+
};
131+
item.add_to(self);
132+
}
133+
113134
pub(crate) fn add_crate_roots(&mut self, ctx: &CompletionContext) {
114135
ctx.process_all_names(&mut |name, res| match res {
115136
ScopeDef::ModuleDef(hir::ModuleDef::Module(m)) if m.is_crate_root(ctx.db) => {

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
1515
return;
1616
}
1717

18-
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update) =
18+
let (is_absolute_path, qualifier, in_block_expr, in_loop_body, is_func_update, after_if_expr) =
1919
match ctx.nameref_ctx() {
2020
Some(NameRefContext {
2121
path_ctx:
2222
Some(PathCompletionCtx {
23-
kind: PathKind::Expr { in_block_expr, in_loop_body },
23+
kind: PathKind::Expr { in_block_expr, in_loop_body, after_if_expr },
2424
is_absolute_path,
2525
qualifier,
2626
..
@@ -33,6 +33,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
3333
*in_block_expr,
3434
*in_loop_body,
3535
record_expr.as_ref().map_or(false, |&(_, it)| it),
36+
*after_if_expr,
3637
),
3738
_ => return,
3839
};
@@ -177,8 +178,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
177178
});
178179

179180
if !is_func_update {
180-
let mut add_keyword =
181-
|kw, snippet| super::keyword::add_keyword(acc, ctx, kw, snippet);
181+
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
182182

183183
if ctx.expects_expression() {
184184
if !in_block_expr {
@@ -202,7 +202,7 @@ pub(crate) fn complete_expr_path(acc: &mut Completions, ctx: &CompletionContext)
202202
add_keyword("let", "let");
203203
}
204204

205-
if ctx.after_if() {
205+
if after_if_expr {
206206
add_keyword("else", "else {\n $0\n}");
207207
add_keyword("else if", "else if $1 {\n $0\n}");
208208
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//! Completion of field list position.
2+
3+
use crate::{
4+
context::{IdentContext, NameContext, NameKind, NameRefContext, PathCompletionCtx, PathKind},
5+
CompletionContext, Completions,
6+
};
7+
8+
pub(crate) fn complete_field_list(acc: &mut Completions, ctx: &CompletionContext) {
9+
match &ctx.ident_ctx {
10+
IdentContext::Name(NameContext { kind: NameKind::RecordField, .. })
11+
| IdentContext::NameRef(NameRefContext {
12+
path_ctx:
13+
Some(PathCompletionCtx {
14+
has_macro_bang: false,
15+
is_absolute_path: false,
16+
qualifier: None,
17+
parent: None,
18+
kind: PathKind::Type { in_tuple_struct: true },
19+
has_type_args: false,
20+
..
21+
}),
22+
..
23+
}) => {
24+
if ctx.qualifier_ctx.vis_node.is_none() {
25+
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
26+
add_keyword("pub(crate)", "pub(crate)");
27+
add_keyword("pub(super)", "pub(super)");
28+
add_keyword("pub", "pub");
29+
}
30+
}
31+
_ => return,
32+
}
33+
}

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,8 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
110110
if !ctx.config.enable_imports_on_the_fly {
111111
return None;
112112
}
113-
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use))
113+
if matches!(ctx.path_kind(), Some(PathKind::Vis { .. } | PathKind::Use | PathKind::Item { .. }))
114114
|| ctx.is_path_disallowed()
115-
|| ctx.expects_item()
116-
|| ctx.expects_assoc_item()
117115
{
118116
return None;
119117
}
@@ -160,7 +158,10 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
160158
(_, ItemInNs::Types(hir::ModuleDef::Module(_))) => true,
161159
// and so are macros(except for attributes)
162160
(
163-
PathKind::Expr { .. } | PathKind::Type | PathKind::Item { .. } | PathKind::Pat,
161+
PathKind::Expr { .. }
162+
| PathKind::Type { .. }
163+
| PathKind::Item { .. }
164+
| PathKind::Pat,
164165
ItemInNs::Macros(mac),
165166
) => mac.is_fn_like(ctx.db),
166167
(PathKind::Item { .. }, _) => true,
@@ -170,14 +171,14 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
170171
(PathKind::Pat, ItemInNs::Types(_)) => true,
171172
(PathKind::Pat, ItemInNs::Values(def)) => matches!(def, hir::ModuleDef::Const(_)),
172173

173-
(PathKind::Type, ItemInNs::Types(ty)) => {
174+
(PathKind::Type { .. }, ItemInNs::Types(ty)) => {
174175
if matches!(ctx.completion_location, Some(ImmediateLocation::TypeBound)) {
175176
matches!(ty, ModuleDef::Trait(_))
176177
} else {
177178
true
178179
}
179180
}
180-
(PathKind::Type, ItemInNs::Values(_)) => false,
181+
(PathKind::Type { .. }, ItemInNs::Values(_)) => false,
181182

182183
(PathKind::Attr { .. }, ItemInNs::Macros(mac)) => mac.is_attr(ctx.db),
183184
(PathKind::Attr { .. }, _) => false,

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

Lines changed: 80 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,98 @@
22
33
use crate::{
44
completions::module_or_fn_macro,
5-
context::{PathCompletionCtx, PathKind, PathQualifierCtx},
5+
context::{ItemListKind, PathCompletionCtx, PathKind, PathQualifierCtx},
66
CompletionContext, Completions,
77
};
88

99
pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext) {
1010
let _p = profile::span("complete_item_list");
1111

12-
let (&is_absolute_path, path_qualifier, _kind) = match ctx.path_context() {
12+
let (&is_absolute_path, path_qualifier, kind) = match ctx.path_context() {
1313
Some(PathCompletionCtx {
1414
kind: PathKind::Item { kind },
1515
is_absolute_path,
1616
qualifier,
1717
..
18-
}) => (is_absolute_path, qualifier, kind),
18+
}) => (is_absolute_path, qualifier, Some(kind)),
19+
Some(PathCompletionCtx {
20+
kind: PathKind::Expr { in_block_expr: true, .. },
21+
is_absolute_path,
22+
qualifier,
23+
..
24+
}) => (is_absolute_path, qualifier, None),
1925
_ => return,
2026
};
27+
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
28+
29+
let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
30+
let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
31+
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
32+
let in_trait = matches!(kind, Some(ItemListKind::Trait));
33+
let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl));
34+
let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
35+
let no_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
36+
let in_block = matches!(kind, None);
37+
38+
'block: loop {
39+
if ctx.is_non_trivial_path() {
40+
break 'block;
41+
}
42+
if !in_trait_impl {
43+
if ctx.qualifier_ctx.unsafe_tok.is_some() {
44+
if in_item_list || in_assoc_non_trait_impl {
45+
add_keyword("fn", "fn $1($2) {\n $0\n}");
46+
}
47+
if in_item_list {
48+
add_keyword("trait", "trait $1 {\n $0\n}");
49+
if no_qualifiers {
50+
add_keyword("impl", "impl $1 {\n $0\n}");
51+
}
52+
}
53+
break 'block;
54+
}
55+
56+
if in_item_list {
57+
add_keyword("enum", "enum $1 {\n $0\n}");
58+
add_keyword("mod", "mod $0");
59+
add_keyword("static", "static $0");
60+
add_keyword("struct", "struct $0");
61+
add_keyword("trait", "trait $1 {\n $0\n}");
62+
add_keyword("union", "union $1 {\n $0\n}");
63+
add_keyword("use", "use $0");
64+
if no_qualifiers {
65+
add_keyword("impl", "impl $1 {\n $0\n}");
66+
}
67+
}
68+
69+
if !in_trait && !in_block && no_qualifiers {
70+
add_keyword("pub(crate)", "pub(crate)");
71+
add_keyword("pub(super)", "pub(super)");
72+
add_keyword("pub", "pub");
73+
}
74+
75+
if in_extern_block {
76+
add_keyword("fn", "fn $1($2);");
77+
} else {
78+
if !in_inherent_impl {
79+
if !in_trait {
80+
add_keyword("extern", "extern $0");
81+
}
82+
add_keyword("type", "type $0");
83+
}
84+
85+
add_keyword("fn", "fn $1($2) {\n $0\n}");
86+
add_keyword("unsafe", "unsafe");
87+
add_keyword("const", "const $0");
88+
}
89+
}
90+
break 'block;
91+
}
92+
93+
if kind.is_none() {
94+
// this is already handled by expression
95+
return;
96+
}
2197

2298
match path_qualifier {
2399
Some(PathQualifierCtx { resolution, is_super_chain, .. }) => {
@@ -33,9 +109,7 @@ pub(crate) fn complete_item_list(acc: &mut Completions, ctx: &CompletionContext)
33109
acc.add_keyword(ctx, "super::");
34110
}
35111
}
36-
None if is_absolute_path => {
37-
acc.add_crate_roots(ctx);
38-
}
112+
None if is_absolute_path => acc.add_crate_roots(ctx),
39113
None if ctx.qualifier_ctx.none() => {
40114
ctx.process_all_names(&mut |name, def| {
41115
if let Some(def) = module_or_fn_macro(ctx.db, def) {

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

Lines changed: 24 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -2,106 +2,39 @@
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::T;
5+
use syntax::ast::Item;
66

7-
use crate::{
8-
context::{NameRefContext, PathKind},
9-
CompletionContext, CompletionItem, CompletionItemKind, Completions,
10-
};
7+
use crate::{context::NameRefContext, CompletionContext, Completions};
118

129
pub(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
13-
if matches!(ctx.nameref_ctx(), Some(NameRefContext { record_expr: Some(_), .. })) {
14-
cov_mark::hit!(no_keyword_completion_in_record_lit);
15-
return;
16-
}
17-
if ctx.is_non_trivial_path() {
18-
cov_mark::hit!(no_keyword_completion_in_non_trivial_path);
19-
return;
20-
}
21-
if ctx.pattern_ctx.is_some() {
22-
return;
23-
}
24-
25-
let mut add_keyword = |kw, snippet| add_keyword(acc, ctx, kw, snippet);
26-
27-
let expects_assoc_item = ctx.expects_assoc_item();
28-
let has_block_expr_parent = ctx.has_block_expr_parent();
29-
let expects_item = ctx.expects_item();
30-
31-
if let Some(PathKind::Vis { .. }) = ctx.path_kind() {
32-
return;
33-
}
34-
if ctx.has_unfinished_impl_or_trait_prev_sibling() {
35-
add_keyword("where", "where");
36-
if ctx.has_impl_prev_sibling() {
37-
add_keyword("for", "for");
38-
}
39-
return;
40-
}
41-
if ctx.previous_token_is(T![unsafe]) {
42-
if expects_item || expects_assoc_item || has_block_expr_parent {
43-
add_keyword("fn", "fn $1($2) {\n $0\n}")
10+
let item = match ctx.nameref_ctx() {
11+
Some(NameRefContext { keyword: Some(item), record_expr: None, .. })
12+
if !ctx.is_non_trivial_path() =>
13+
{
14+
item
4415
}
16+
_ => return,
17+
};
4518

46-
if expects_item || has_block_expr_parent {
47-
add_keyword("trait", "trait $1 {\n $0\n}");
48-
add_keyword("impl", "impl $1 {\n $0\n}");
49-
}
50-
51-
return;
52-
}
53-
54-
if ctx.qualifier_ctx.vis_node.is_none()
55-
&& (expects_item || ctx.expects_non_trait_assoc_item() || ctx.expect_field())
56-
{
57-
add_keyword("pub(crate)", "pub(crate)");
58-
add_keyword("pub(super)", "pub(super)");
59-
add_keyword("pub", "pub");
60-
}
61-
62-
if expects_item || expects_assoc_item || has_block_expr_parent {
63-
add_keyword("unsafe", "unsafe");
64-
add_keyword("fn", "fn $1($2) {\n $0\n}");
65-
add_keyword("const", "const $0");
66-
add_keyword("type", "type $0");
67-
}
68-
69-
if expects_item || has_block_expr_parent {
70-
if ctx.qualifier_ctx.vis_node.is_none() {
71-
add_keyword("impl", "impl $1 {\n $0\n}");
72-
add_keyword("extern", "extern $0");
73-
}
74-
add_keyword("use", "use $0");
75-
add_keyword("trait", "trait $1 {\n $0\n}");
76-
add_keyword("static", "static $0");
77-
add_keyword("mod", "mod $0");
78-
}
79-
80-
if expects_item || has_block_expr_parent {
81-
add_keyword("enum", "enum $1 {\n $0\n}");
82-
add_keyword("struct", "struct $0");
83-
add_keyword("union", "union $1 {\n $0\n}");
84-
}
85-
}
86-
87-
pub(super) fn add_keyword(acc: &mut Completions, ctx: &CompletionContext, kw: &str, snippet: &str) {
88-
let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), kw);
19+
let mut add_keyword = |kw, snippet| acc.add_keyword_snippet(ctx, kw, snippet);
8920

90-
match ctx.config.snippet_cap {
91-
Some(cap) => {
92-
if snippet.ends_with('}') && ctx.incomplete_let {
93-
// complete block expression snippets with a trailing semicolon, if inside an incomplete let
94-
cov_mark::hit!(let_semi);
95-
item.insert_snippet(cap, format!("{};", snippet));
96-
} else {
97-
item.insert_snippet(cap, snippet);
21+
match item {
22+
Item::Impl(it) => {
23+
if it.for_token().is_none() && it.trait_().is_none() && it.self_ty().is_some() {
24+
add_keyword("for", "for");
9825
}
26+
add_keyword("where", "where");
9927
}
100-
None => {
101-
item.insert_text(if snippet.contains('$') { kw } else { snippet });
28+
Item::Enum(_)
29+
| Item::Fn(_)
30+
| Item::Struct(_)
31+
| Item::Trait(_)
32+
| Item::TypeAlias(_)
33+
| Item::Union(_) => {
34+
add_keyword("where", "where");
10235
}
103-
};
104-
item.add_to(acc);
36+
_ => (),
37+
}
10538
}
10639

10740
#[cfg(test)]

0 commit comments

Comments
 (0)