diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-09-14 11:22:20 +0100 |
---|---|---|
committer | GitHub <[email protected]> | 2020-09-14 11:22:20 +0100 |
commit | d134a810377d18e59508c41db839ef440ebde9fe (patch) | |
tree | 38f92d3234e9ebf23b7762794416fbc5c6d3b0a8 | |
parent | a61178d2186b8d9ca324231ab03368819be0ff6e (diff) | |
parent | 8ebf3596b78dc10541acd7e08d5c857967a9277c (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]>
-rw-r--r-- | crates/ide/src/completion/complete_trait_impl.rs | 175 |
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)] | ||
50 | enum ImplCompletionKind { | ||
51 | All, | ||
52 | Fn, | ||
53 | TypeAlias, | ||
54 | Const, | ||
55 | } | ||
56 | |||
49 | pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext) { | 57 | pub(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 | ||
107 | fn completion_match(ctx: &CompletionContext) -> Option<(SyntaxNode, Impl)> { | 80 | fn 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 | ||
121 | fn add_function_impl( | 121 | fn 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#" | ||
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 | } | ||
488 | } | 551 | } |