Skip to content

Commit 8ebf359

Browse files
committed
Complete trait impl immediately after type/const/fn
1 parent 4f1167d commit 8ebf359

File tree

1 file changed

+119
-56
lines changed

1 file changed

+119
-56
lines changed

crates/ide/src/completion/complete_trait_impl.rs

Lines changed: 119 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -46,76 +46,76 @@ use crate::{
4646
display::function_declaration,
4747
};
4848

49+
#[derive(Debug, PartialEq, Eq)]
50+
enum ImplCompletionKind {
51+
All,
52+
Fn,
53+
TypeAlias,
54+
Const,
55+
}
56+
4957
pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50-
if let Some((trigger, impl_def)) = completion_match(ctx) {
51-
match trigger.kind() {
52-
SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def)
53-
.into_iter()
54-
.for_each(|item| match item {
55-
hir::AssocItem::Function(fn_item) => {
56-
add_function_impl(&trigger, acc, ctx, fn_item)
57-
}
58-
hir::AssocItem::TypeAlias(type_item) => {
59-
add_type_alias_impl(&trigger, acc, ctx, type_item)
60-
}
61-
hir::AssocItem::Const(const_item) => {
62-
add_const_impl(&trigger, acc, ctx, const_item)
63-
}
64-
}),
65-
66-
SyntaxKind::FN => {
67-
for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
68-
.into_iter()
69-
.filter_map(|item| match item {
70-
hir::AssocItem::Function(fn_item) => Some(fn_item),
71-
_ => None,
72-
})
73-
{
74-
add_function_impl(&trigger, acc, ctx, missing_fn);
75-
}
58+
if let Some((kind, trigger, impl_def)) = completion_match(ctx) {
59+
get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
60+
hir::AssocItem::Function(fn_item)
61+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
62+
{
63+
add_function_impl(&trigger, acc, ctx, fn_item)
7664
}
77-
78-
SyntaxKind::TYPE_ALIAS => {
79-
for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
80-
.into_iter()
81-
.filter_map(|item| match item {
82-
hir::AssocItem::TypeAlias(type_item) => Some(type_item),
83-
_ => None,
84-
})
85-
{
86-
add_type_alias_impl(&trigger, acc, ctx, missing_fn);
87-
}
65+
hir::AssocItem::TypeAlias(type_item)
66+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
67+
{
68+
add_type_alias_impl(&trigger, acc, ctx, type_item)
8869
}
89-
90-
SyntaxKind::CONST => {
91-
for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def)
92-
.into_iter()
93-
.filter_map(|item| match item {
94-
hir::AssocItem::Const(const_item) => Some(const_item),
95-
_ => None,
96-
})
97-
{
98-
add_const_impl(&trigger, acc, ctx, missing_fn);
99-
}
70+
hir::AssocItem::Const(const_item)
71+
if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
72+
{
73+
add_const_impl(&trigger, acc, ctx, const_item)
10074
}
101-
10275
_ => {}
103-
}
76+
});
10477
}
10578
}
10679

107-
fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> {
108-
let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() {
109-
SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => {
110-
Some((p, 2))
80+
fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
81+
let mut token = ctx.token.clone();
82+
// For keywork without name like `impl .. { fn <|> }`, the current position is inside
83+
// the whitespace token, which is outside `FN` syntax node.
84+
// We need to follow the previous token in this case.
85+
if token.kind() == SyntaxKind::WHITESPACE {
86+
token = token.prev_token()?;
87+
}
88+
89+
let (kind, trigger, impl_def_offset) = token.ancestors().find_map(|p| match p.kind() {
90+
// `const` can be a modifier of an item, so the `const` token may be inside another item syntax node.
91+
// Eg. `impl .. { const <|> fn bar() .. }`
92+
SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST
93+
if token.kind() == SyntaxKind::CONST_KW =>
94+
{
95+
Some((ImplCompletionKind::Const, p, 2))
11196
}
112-
SyntaxKind::NAME_REF => Some((p, 5)),
97+
SyntaxKind::FN => Some((ImplCompletionKind::Fn, p, 2)),
98+
SyntaxKind::TYPE_ALIAS => Some((ImplCompletionKind::TypeAlias, p, 2)),
99+
SyntaxKind::CONST => Some((ImplCompletionKind::Const, p, 2)),
100+
// `impl .. { const <|> }` is parsed as:
101+
// IMPL
102+
// ASSOC_ITEM_LIST
103+
// ERROR
104+
// CONST_KW <- token
105+
// WHITESPACE <- ctx.token
106+
SyntaxKind::ERROR
107+
if p.first_token().map_or(false, |t| t.kind() == SyntaxKind::CONST_KW) =>
108+
{
109+
Some((ImplCompletionKind::Const, p, 2))
110+
}
111+
SyntaxKind::NAME_REF => Some((ImplCompletionKind::All, p, 5)),
113112
_ => None,
114113
})?;
114+
115115
let impl_def = (0..impl_def_offset - 1)
116116
.try_fold(trigger.parent()?, |t, _| t.parent())
117117
.and_then(ast::Impl::cast)?;
118-
Some((trigger, impl_def))
118+
Some((kind, trigger, impl_def))
119119
}
120120

121121
fn add_function_impl(
@@ -485,4 +485,67 @@ impl Test for () {
485485
",
486486
);
487487
}
488+
489+
#[test]
490+
fn complete_without_name() {
491+
let test = |completion: &str, hint: &str, completed: &str, next_sibling: &str| {
492+
println!(
493+
"completion='{}', hint='{}', next_sibling='{}'",
494+
completion, hint, next_sibling
495+
);
496+
497+
check_edit(
498+
completion,
499+
&format!(
500+
r#"
501+
trait Test {{
502+
type Foo;
503+
const CONST: u16;
504+
fn bar();
505+
}}
506+
struct T;
507+
508+
impl Test for T {{
509+
{}
510+
{}
511+
}}
512+
"#,
513+
hint, next_sibling
514+
),
515+
&format!(
516+
r#"
517+
trait Test {{
518+
type Foo;
519+
const CONST: u16;
520+
fn bar();
521+
}}
522+
struct T;
523+
524+
impl Test for T {{
525+
{}
526+
{}
527+
}}
528+
"#,
529+
completed, next_sibling
530+
),
531+
)
532+
};
533+
534+
// Enumerate some possible next siblings.
535+
for next_sibling in &[
536+
"",
537+
"fn other_fn() {}", // `const <|> fn` -> `const fn`
538+
"type OtherType = i32;",
539+
"const OTHER_CONST: i32 = 0;",
540+
"async fn other_fn() {}",
541+
"unsafe fn other_fn() {}",
542+
"default fn other_fn() {}",
543+
"default type OtherType = i32;",
544+
"default const OTHER_CONST: i32 = 0;",
545+
] {
546+
test("bar", "fn <|>", "fn bar() {\n $0\n}", next_sibling);
547+
test("Foo", "type <|>", "type Foo = ", next_sibling);
548+
test("CONST", "const <|>", "const CONST: u16 = ", next_sibling);
549+
}
550+
}
488551
}

0 commit comments

Comments
 (0)