aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-09-14 11:22:20 +0100
committerGitHub <[email protected]>2020-09-14 11:22:20 +0100
commitd134a810377d18e59508c41db839ef440ebde9fe (patch)
tree38f92d3234e9ebf23b7762794416fbc5c6d3b0a8 /crates/ide/src
parenta61178d2186b8d9ca324231ab03368819be0ff6e (diff)
parent8ebf3596b78dc10541acd7e08d5c857967a9277c (diff)
Merge #5976
5976: Complete trait impl immediately after type/const/fn r=jonas-schievink a=oxalica Currently, we can complete type/const/fn but only if we typed an identifier. That is, `impl .. { fn f<|> }` has completions with all trait fn including `f`, but `impl .. { fn <|> }` doesn't provide any suggestion (even if explicit trigger it). This PR tweak the logic of completion match to make it possible. However, we still need to explicit trigger suggestions (`Control + Space` by default) in vscode to show. Not sure if we can make it automatically triggered after typing the space after `fn`. Another question is that I cannot figure out why `BLOCK_EXPR` need to be checked. A block expr directly inside a impl block should be invalid, and nested items will failed to locate impl block in specific offset and skip the suggestion. Now I simply removed it and no tests are broken. https://github.com/rust-analyzer/rust-analyzer/blob/4f91478e50dc5c2a87235e9be8bd91e3f62de4b4/crates/ide/src/completion/complete_trait_impl.rs#L109 Co-authored-by: oxalica <[email protected]>
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/completion/complete_trait_impl.rs175
1 files changed, 119 insertions, 56 deletions
diff --git a/crates/ide/src/completion/complete_trait_impl.rs b/crates/ide/src/completion/complete_trait_impl.rs
index 26f268bd1..bff2603b8 100644
--- a/crates/ide/src/completion/complete_trait_impl.rs
+++ b/crates/ide/src/completion/complete_trait_impl.rs
@@ -46,76 +46,76 @@ use crate::{
46 display::function_declaration, 46 display::function_declaration,
47}; 47};
48 48
49#[derive(Debug, PartialEq, Eq)]
50enum ImplCompletionKind {
51 All,
52 Fn,
53 TypeAlias,
54 Const,
55}
56
49pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { 57pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) {
50 if let Some((trigger, impl_def)) = completion_match(ctx) { 58 if let Some((kind, trigger, impl_def)) = completion_match(ctx) {
51 match trigger.kind() { 59 get_missing_assoc_items(&ctx.sema, &impl_def).into_iter().for_each(|item| match item {
52 SyntaxKind::NAME_REF => get_missing_assoc_items(&ctx.sema, &impl_def) 60 hir::AssocItem::Function(fn_item)
53 .into_iter() 61 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Fn =>
54 .for_each(|item| match item { 62 {
55 hir::AssocItem::Function(fn_item) => { 63 add_function_impl(&trigger, acc, ctx, 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 }
76 } 64 }
77 65 hir::AssocItem::TypeAlias(type_item)
78 SyntaxKind::TYPE_ALIAS => { 66 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::TypeAlias =>
79 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 67 {
80 .into_iter() 68 add_type_alias_impl(&trigger, acc, ctx, type_item)
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 }
88 } 69 }
89 70 hir::AssocItem::Const(const_item)
90 SyntaxKind::CONST => { 71 if kind == ImplCompletionKind::All || kind == ImplCompletionKind::Const =>
91 for missing_fn in get_missing_assoc_items(&ctx.sema, &impl_def) 72 {
92 .into_iter() 73 add_const_impl(&trigger, acc, ctx, const_item)
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 }
100 } 74 }
101
102 _ => {} 75 _ => {}
103 } 76 });
104 } 77 }
105} 78}
106 79
107fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> { 80fn completion_match(ctx: &CompletionContext) -> Option<(ImplCompletionKind, SyntaxNode, Impl)> {
108 let (trigger, impl_def_offset) = ctx.token.ancestors().find_map(|p| match p.kind() { 81 let mut token = ctx.token.clone();
109 SyntaxKind::FN | SyntaxKind::TYPE_ALIAS | SyntaxKind::CONST | SyntaxKind::BLOCK_EXPR => { 82 // For keywork without name like `impl .. { fn <|> }`, the current position is inside
110 Some((p, 2)) 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))
111 } 96 }
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)),
113 _ => None, 112 _ => None,
114 })?; 113 })?;
114
115 let impl_def = (0..impl_def_offset - 1) 115 let impl_def = (0..impl_def_offset - 1)
116 .try_fold(trigger.parent()?, |t, _| t.parent()) 116 .try_fold(trigger.parent()?, |t, _| t.parent())
117 .and_then(ast::Impl::cast)?; 117 .and_then(ast::Impl::cast)?;
118 Some((trigger, impl_def)) 118 Some((kind, trigger, impl_def))
119} 119}
120 120
121fn add_function_impl( 121fn add_function_impl(
@@ -485,4 +485,67 @@ impl Test for () {
485", 485",
486 ); 486 );
487 } 487 }
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#"
501trait Test {{
502 type Foo;
503 const CONST: u16;
504 fn bar();
505}}
506struct T;
507
508impl Test for T {{
509 {}
510 {}
511}}
512"#,
513 hint, next_sibling
514 ),
515 &format!(
516 r#"
517trait Test {{
518 type Foo;
519 const CONST: u16;
520 fn bar();
521}}
522struct T;
523
524impl 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 }
488} 551}