aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-04-26 09:54:08 +0100
committerAleksey Kladov <[email protected]>2020-04-26 09:54:08 +0100
commit05cdc87158ef99d1f59784372ce893596f8a5a80 (patch)
treec8090164eefa2074005b5d6e36a53b5f78aec1d7
parentfe99a29ad1226dd3f6801ea4bdb575506324be07 (diff)
Precompute expected type during completion
-rw-r--r--crates/ra_ide/src/completion/complete_unqualified_path.rs44
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs33
-rw-r--r--crates/ra_ide/src/completion/presentation.rs2
3 files changed, 44 insertions, 35 deletions
diff --git a/crates/ra_ide/src/completion/complete_unqualified_path.rs b/crates/ra_ide/src/completion/complete_unqualified_path.rs
index 56cd086c6..f559f2b97 100644
--- a/crates/ra_ide/src/completion/complete_unqualified_path.rs
+++ b/crates/ra_ide/src/completion/complete_unqualified_path.rs
@@ -4,7 +4,7 @@ use hir::ScopeDef;
4use test_utils::tested_by; 4use test_utils::tested_by;
5 5
6use crate::completion::{CompletionContext, Completions}; 6use crate::completion::{CompletionContext, Completions};
7use hir::{Adt, ModuleDef}; 7use hir::{Adt, ModuleDef, Type};
8use ra_syntax::AstNode; 8use ra_syntax::AstNode;
9 9
10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) { 10pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionContext) {
@@ -15,7 +15,9 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
15 return; 15 return;
16 } 16 }
17 17
18 complete_enum_variants(acc, ctx); 18 if let Some(ty) = &ctx.expected_type {
19 complete_enum_variants(acc, ctx, ty);
20 }
19 21
20 if ctx.is_pat_binding_or_const { 22 if ctx.is_pat_binding_or_const {
21 return; 23 return;
@@ -34,26 +36,24 @@ pub(super) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
34 }); 36 });
35} 37}
36 38
37fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext) { 39fn complete_enum_variants(acc: &mut Completions, ctx: &CompletionContext, ty: &Type) {
38 if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { 40 if let Some(Adt::Enum(enum_data)) = ty.as_adt() {
39 if let Some(Adt::Enum(enum_data)) = ty.as_adt() { 41 let variants = enum_data.variants(ctx.db);
40 let variants = enum_data.variants(ctx.db); 42
41 43 let module = if let Some(module) = ctx.scope().module() {
42 let module = if let Some(module) = ctx.scope().module() { 44 // Compute path from the completion site if available.
43 // Compute path from the completion site if available. 45 module
44 module 46 } else {
45 } else { 47 // Otherwise fall back to the enum's definition site.
46 // Otherwise fall back to the enum's definition site. 48 enum_data.module(ctx.db)
47 enum_data.module(ctx.db) 49 };
48 }; 50
49 51 for variant in variants {
50 for variant in variants { 52 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) {
51 if let Some(path) = module.find_use_path(ctx.db, ModuleDef::from(variant)) { 53 // Variants with trivial paths are already added by the existing completion logic,
52 // Variants with trivial paths are already added by the existing completion logic, 54 // so we should avoid adding these twice
53 // so we should avoid adding these twice 55 if path.segments.len() > 1 {
54 if path.segments.len() > 1 { 56 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
55 acc.add_enum_variant(ctx, variant, Some(path.to_string()));
56 }
57 } 57 }
58 } 58 }
59 } 59 }
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 5f2797e41..118fceb2e 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,7 +5,7 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, AstNode, 8 ast, match_ast, AstNode,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
@@ -26,6 +26,7 @@ pub(crate) struct CompletionContext<'a> {
26 /// The token before the cursor, in the macro-expanded file. 26 /// The token before the cursor, in the macro-expanded file.
27 pub(super) token: SyntaxToken, 27 pub(super) token: SyntaxToken,
28 pub(super) krate: Option<hir::Crate>, 28 pub(super) krate: Option<hir::Crate>,
29 pub(super) expected_type: Option<Type>,
29 pub(super) name_ref_syntax: Option<ast::NameRef>, 30 pub(super) name_ref_syntax: Option<ast::NameRef>,
30 pub(super) function_syntax: Option<ast::FnDef>, 31 pub(super) function_syntax: Option<ast::FnDef>,
31 pub(super) use_item_syntax: Option<ast::UseItem>, 32 pub(super) use_item_syntax: Option<ast::UseItem>,
@@ -93,6 +94,7 @@ impl<'a> CompletionContext<'a> {
93 token, 94 token,
94 offset: position.offset, 95 offset: position.offset,
95 krate, 96 krate,
97 expected_type: None,
96 name_ref_syntax: None, 98 name_ref_syntax: None,
97 function_syntax: None, 99 function_syntax: None,
98 use_item_syntax: None, 100 use_item_syntax: None,
@@ -175,23 +177,30 @@ impl<'a> CompletionContext<'a> {
175 self.sema.scope_at_offset(&self.token.parent(), self.offset) 177 self.sema.scope_at_offset(&self.token.parent(), self.offset)
176 } 178 }
177 179
178 pub(crate) fn expected_type_of(&self, node: &SyntaxNode) -> Option<Type> {
179 for ancestor in node.ancestors() {
180 if let Some(pat) = ast::Pat::cast(ancestor.clone()) {
181 return self.sema.type_of_pat(&pat);
182 } else if let Some(expr) = ast::Expr::cast(ancestor) {
183 return self.sema.type_of_expr(&expr);
184 }
185 }
186 None
187 }
188
189 fn fill( 180 fn fill(
190 &mut self, 181 &mut self,
191 original_file: &SyntaxNode, 182 original_file: &SyntaxNode,
192 file_with_fake_ident: SyntaxNode, 183 file_with_fake_ident: SyntaxNode,
193 offset: TextSize, 184 offset: TextSize,
194 ) { 185 ) {
186 // FIXME: this is wrong in at least two cases:
187 // * when there's no token `foo(<|>)`
188 // * when there is a token, but it happens to have type of it's own
189 self.expected_type = self
190 .token
191 .ancestors()
192 .find_map(|node| {
193 let ty = match_ast! {
194 match node {
195 ast::Pat(it) => self.sema.type_of_pat(&it),
196 ast::Expr(it) => self.sema.type_of_expr(&it),
197 _ => return None,
198 }
199 };
200 Some(ty)
201 })
202 .flatten();
203
195 // First, let's try to complete a reference to some declaration. 204 // First, let's try to complete a reference to some declaration.
196 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) { 205 if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(&file_with_fake_ident, offset) {
197 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. 206 // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`.
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index f5b074461..77d354376 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -351,7 +351,7 @@ impl Builder {
351 } 351 }
352 352
353 // Don't add parentheses if the expected type is some function reference. 353 // Don't add parentheses if the expected type is some function reference.
354 if let Some(ty) = ctx.expected_type_of(&ctx.token.parent()) { 354 if let Some(ty) = &ctx.expected_type {
355 if ty.is_fn() { 355 if ty.is_fn() {
356 return self; 356 return self;
357 } 357 }