diff options
author | Florian Diebold <[email protected]> | 2018-12-25 14:15:40 +0000 |
---|---|---|
committer | Florian Diebold <[email protected]> | 2018-12-25 14:27:15 +0000 |
commit | ab0b63992be0cec4999810096a53b40f63f90349 (patch) | |
tree | 5e6dca70c4e842bc87e6e1287cca176031ecc92f /crates | |
parent | 0d724ea572a5dd26acbbf2eb4538eabe454fb894 (diff) |
Implement basic completion for fields
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_analysis/src/completion.rs | 8 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/complete_dot.rs | 98 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_context.rs | 34 | ||||
-rw-r--r-- | crates/ra_analysis/src/completion/completion_item.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/adt.rs | 14 | ||||
-rw-r--r-- | crates/ra_hir/src/lib.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 5 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/conv.rs | 1 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 4 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 |
10 files changed, 156 insertions, 12 deletions
diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index d742d6295..fe580700f 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs | |||
@@ -1,6 +1,7 @@ | |||
1 | mod completion_item; | 1 | mod completion_item; |
2 | mod completion_context; | 2 | mod completion_context; |
3 | 3 | ||
4 | mod complete_dot; | ||
4 | mod complete_fn_param; | 5 | mod complete_fn_param; |
5 | mod complete_keyword; | 6 | mod complete_keyword; |
6 | mod complete_snippet; | 7 | mod complete_snippet; |
@@ -20,13 +21,13 @@ use crate::{ | |||
20 | 21 | ||
21 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; | 22 | pub use crate::completion::completion_item::{CompletionItem, InsertText, CompletionItemKind}; |
22 | 23 | ||
23 | /// Main entry point for copmletion. We run comletion as a two-phase process. | 24 | /// Main entry point for completion. We run completion as a two-phase process. |
24 | /// | 25 | /// |
25 | /// First, we look at the position and collect a so-called `CompletionContext. | 26 | /// First, we look at the position and collect a so-called `CompletionContext. |
26 | /// This is a somewhat messy process, because, during completion, syntax tree is | 27 | /// This is a somewhat messy process, because, during completion, syntax tree is |
27 | /// incomplete and can look readlly weired. | 28 | /// incomplete and can look really weird. |
28 | /// | 29 | /// |
29 | /// Once the context is collected, we run a series of completion routines whihc | 30 | /// Once the context is collected, we run a series of completion routines which |
30 | /// look at the context and produce completion items. | 31 | /// look at the context and produce completion items. |
31 | pub(crate) fn completions( | 32 | pub(crate) fn completions( |
32 | db: &db::RootDatabase, | 33 | db: &db::RootDatabase, |
@@ -43,6 +44,7 @@ pub(crate) fn completions( | |||
43 | complete_snippet::complete_item_snippet(&mut acc, &ctx); | 44 | complete_snippet::complete_item_snippet(&mut acc, &ctx); |
44 | complete_path::complete_path(&mut acc, &ctx)?; | 45 | complete_path::complete_path(&mut acc, &ctx)?; |
45 | complete_scope::complete_scope(&mut acc, &ctx)?; | 46 | complete_scope::complete_scope(&mut acc, &ctx)?; |
47 | complete_dot::complete_dot(&mut acc, &ctx)?; | ||
46 | 48 | ||
47 | Ok(Some(acc)) | 49 | Ok(Some(acc)) |
48 | } | 50 | } |
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs new file mode 100644 index 000000000..fa62da210 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_dot.rs | |||
@@ -0,0 +1,98 @@ | |||
1 | use ra_syntax::ast::AstNode; | ||
2 | use hir::{Ty, Def}; | ||
3 | |||
4 | use crate::Cancelable; | ||
5 | use crate::completion::{CompletionContext, Completions, CompletionKind, CompletionItem, CompletionItemKind}; | ||
6 | |||
7 | /// Complete dot accesses, i.e. fields or methods (currently only fields). | ||
8 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | ||
9 | let module = if let Some(module) = &ctx.module { | ||
10 | module | ||
11 | } else { | ||
12 | return Ok(()); | ||
13 | }; | ||
14 | let function = if let Some(fn_def) = ctx.enclosing_fn { | ||
15 | hir::source_binder::function_from_module(ctx.db, module, fn_def) | ||
16 | } else { | ||
17 | return Ok(()); | ||
18 | }; | ||
19 | let receiver = if let Some(receiver) = ctx.dot_receiver { | ||
20 | receiver | ||
21 | } else { | ||
22 | return Ok(()); | ||
23 | }; | ||
24 | let infer_result = function.infer(ctx.db)?; | ||
25 | let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { | ||
26 | ty | ||
27 | } else { | ||
28 | return Ok(()); | ||
29 | }; | ||
30 | if !ctx.is_method_call { | ||
31 | complete_fields(acc, ctx, receiver_ty)?; | ||
32 | } | ||
33 | Ok(()) | ||
34 | } | ||
35 | |||
36 | fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, ty: Ty) -> Cancelable<()> { | ||
37 | // TODO: autoderef etc. | ||
38 | match ty { | ||
39 | Ty::Adt { def_id, .. } => { | ||
40 | match def_id.resolve(ctx.db)? { | ||
41 | Def::Struct(s) => { | ||
42 | let variant_data = s.variant_data(ctx.db)?; | ||
43 | for field in variant_data.fields() { | ||
44 | CompletionItem::new(CompletionKind::Reference, field.name().to_string()) | ||
45 | .kind(CompletionItemKind::Field) | ||
46 | .add_to(acc); | ||
47 | } | ||
48 | } | ||
49 | // TODO unions | ||
50 | _ => {} | ||
51 | } | ||
52 | } | ||
53 | Ty::Tuple(fields) => { | ||
54 | for (i, _ty) in fields.iter().enumerate() { | ||
55 | CompletionItem::new(CompletionKind::Reference, i.to_string()) | ||
56 | .kind(CompletionItemKind::Field) | ||
57 | .add_to(acc); | ||
58 | } | ||
59 | } | ||
60 | _ => {} | ||
61 | }; | ||
62 | Ok(()) | ||
63 | } | ||
64 | |||
65 | #[cfg(test)] | ||
66 | mod tests { | ||
67 | use crate::completion::*; | ||
68 | |||
69 | fn check_ref_completion(code: &str, expected_completions: &str) { | ||
70 | check_completion(code, expected_completions, CompletionKind::Reference); | ||
71 | } | ||
72 | |||
73 | #[test] | ||
74 | fn test_struct_field_completion() { | ||
75 | check_ref_completion( | ||
76 | r" | ||
77 | struct A { the_field: u32 } | ||
78 | fn foo(a: A) { | ||
79 | a.<|> | ||
80 | } | ||
81 | ", | ||
82 | r#"the_field"#, | ||
83 | ); | ||
84 | } | ||
85 | |||
86 | #[test] | ||
87 | fn test_no_struct_field_completion_for_method_call() { | ||
88 | check_ref_completion( | ||
89 | r" | ||
90 | struct A { the_field: u32 } | ||
91 | fn foo(a: A) { | ||
92 | a.<|>() | ||
93 | } | ||
94 | ", | ||
95 | r#""#, | ||
96 | ); | ||
97 | } | ||
98 | } | ||
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs index 064fbc6f7..12e98e870 100644 --- a/crates/ra_analysis/src/completion/completion_context.rs +++ b/crates/ra_analysis/src/completion/completion_context.rs | |||
@@ -31,6 +31,10 @@ pub(super) struct CompletionContext<'a> { | |||
31 | pub(super) is_stmt: bool, | 31 | pub(super) is_stmt: bool, |
32 | /// Something is typed at the "top" level, in module or impl/trait. | 32 | /// Something is typed at the "top" level, in module or impl/trait. |
33 | pub(super) is_new_item: bool, | 33 | pub(super) is_new_item: bool, |
34 | /// The receiver if this is a field or method access, i.e. writing something.<|> | ||
35 | pub(super) dot_receiver: Option<ast::Expr<'a>>, | ||
36 | /// If this is a method call in particular, i.e. the () are already there. | ||
37 | pub(super) is_method_call: bool, | ||
34 | } | 38 | } |
35 | 39 | ||
36 | impl<'a> CompletionContext<'a> { | 40 | impl<'a> CompletionContext<'a> { |
@@ -54,6 +58,8 @@ impl<'a> CompletionContext<'a> { | |||
54 | after_if: false, | 58 | after_if: false, |
55 | is_stmt: false, | 59 | is_stmt: false, |
56 | is_new_item: false, | 60 | is_new_item: false, |
61 | dot_receiver: None, | ||
62 | is_method_call: false, | ||
57 | }; | 63 | }; |
58 | ctx.fill(original_file, position.offset); | 64 | ctx.fill(original_file, position.offset); |
59 | Ok(Some(ctx)) | 65 | Ok(Some(ctx)) |
@@ -105,6 +111,12 @@ impl<'a> CompletionContext<'a> { | |||
105 | _ => (), | 111 | _ => (), |
106 | } | 112 | } |
107 | 113 | ||
114 | self.enclosing_fn = self | ||
115 | .leaf | ||
116 | .ancestors() | ||
117 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
118 | .find_map(ast::FnDef::cast); | ||
119 | |||
108 | let parent = match name_ref.syntax().parent() { | 120 | let parent = match name_ref.syntax().parent() { |
109 | Some(it) => it, | 121 | Some(it) => it, |
110 | None => return, | 122 | None => return, |
@@ -120,11 +132,6 @@ impl<'a> CompletionContext<'a> { | |||
120 | } | 132 | } |
121 | if path.qualifier().is_none() { | 133 | if path.qualifier().is_none() { |
122 | self.is_trivial_path = true; | 134 | self.is_trivial_path = true; |
123 | self.enclosing_fn = self | ||
124 | .leaf | ||
125 | .ancestors() | ||
126 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
127 | .find_map(ast::FnDef::cast); | ||
128 | 135 | ||
129 | self.is_stmt = match name_ref | 136 | self.is_stmt = match name_ref |
130 | .syntax() | 137 | .syntax() |
@@ -145,6 +152,23 @@ impl<'a> CompletionContext<'a> { | |||
145 | } | 152 | } |
146 | } | 153 | } |
147 | } | 154 | } |
155 | if let Some(_field_expr) = ast::FieldExpr::cast(parent) { | ||
156 | self.dot_receiver = self | ||
157 | .leaf | ||
158 | .ancestors() | ||
159 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
160 | .find_map(ast::FieldExpr::cast) | ||
161 | .and_then(ast::FieldExpr::expr); | ||
162 | } | ||
163 | if let Some(_method_call_expr) = ast::MethodCallExpr::cast(parent) { | ||
164 | self.dot_receiver = self | ||
165 | .leaf | ||
166 | .ancestors() | ||
167 | .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) | ||
168 | .find_map(ast::MethodCallExpr::cast) | ||
169 | .and_then(ast::MethodCallExpr::expr); | ||
170 | self.is_method_call = true; | ||
171 | } | ||
148 | } | 172 | } |
149 | } | 173 | } |
150 | 174 | ||
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index 6d466c8bd..c9f9f495d 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs | |||
@@ -30,6 +30,7 @@ pub enum CompletionItemKind { | |||
30 | Struct, | 30 | Struct, |
31 | Enum, | 31 | Enum, |
32 | Binding, | 32 | Binding, |
33 | Field, | ||
33 | } | 34 | } |
34 | 35 | ||
35 | #[derive(Debug, PartialEq, Eq)] | 36 | #[derive(Debug, PartialEq, Eq)] |
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 03770ed7d..e65f8deb8 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs | |||
@@ -124,6 +124,15 @@ pub struct StructField { | |||
124 | ty: Ty, | 124 | ty: Ty, |
125 | } | 125 | } |
126 | 126 | ||
127 | impl StructField { | ||
128 | pub fn name(&self) -> SmolStr { | ||
129 | self.name.clone() | ||
130 | } | ||
131 | pub fn ty(&self) -> Ty { | ||
132 | self.ty.clone() | ||
133 | } | ||
134 | } | ||
135 | |||
127 | /// Fields of an enum variant or struct | 136 | /// Fields of an enum variant or struct |
128 | #[derive(Debug, Clone, PartialEq, Eq)] | 137 | #[derive(Debug, Clone, PartialEq, Eq)] |
129 | pub enum VariantData { | 138 | pub enum VariantData { |
@@ -168,7 +177,10 @@ impl VariantData { | |||
168 | } | 177 | } |
169 | 178 | ||
170 | pub(crate) fn get_field_ty(&self, field_name: &str) -> Option<Ty> { | 179 | pub(crate) fn get_field_ty(&self, field_name: &str) -> Option<Ty> { |
171 | self.fields().iter().find(|f| f.name == field_name).map(|f| f.ty.clone()) | 180 | self.fields() |
181 | .iter() | ||
182 | .find(|f| f.name == field_name) | ||
183 | .map(|f| f.ty.clone()) | ||
172 | } | 184 | } |
173 | 185 | ||
174 | pub fn fields(&self) -> &[StructField] { | 186 | pub fn fields(&self) -> &[StructField] { |
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 796970d8a..68fdbb7ea 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs | |||
@@ -44,6 +44,7 @@ pub use self::{ | |||
44 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, | 44 | module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, |
45 | function::{Function, FnScopes}, | 45 | function::{Function, FnScopes}, |
46 | adt::{Struct, Enum}, | 46 | adt::{Struct, Enum}, |
47 | ty::Ty, | ||
47 | }; | 48 | }; |
48 | 49 | ||
49 | pub use self::function::FnSignatureInfo; | 50 | pub use self::function::FnSignatureInfo; |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index f931f3c87..83da13f1a 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -574,7 +574,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
574 | match receiver_ty { | 574 | match receiver_ty { |
575 | Ty::Tuple(fields) => { | 575 | Ty::Tuple(fields) => { |
576 | let i = text.parse::<usize>().ok(); | 576 | let i = text.parse::<usize>().ok(); |
577 | i.and_then(|i| fields.get(i).cloned()).unwrap_or(Ty::Unknown) | 577 | i.and_then(|i| fields.get(i).cloned()) |
578 | .unwrap_or(Ty::Unknown) | ||
578 | } | 579 | } |
579 | Ty::Adt { def_id, .. } => { | 580 | Ty::Adt { def_id, .. } => { |
580 | let field_ty = match def_id.resolve(self.db)? { | 581 | let field_ty = match def_id.resolve(self.db)? { |
@@ -589,7 +590,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { | |||
589 | } else { | 590 | } else { |
590 | Ty::Unknown | 591 | Ty::Unknown |
591 | } | 592 | } |
592 | }, | 593 | } |
593 | ast::Expr::TryExpr(e) => { | 594 | ast::Expr::TryExpr(e) => { |
594 | let _inner_ty = if let Some(e) = e.expr() { | 595 | let _inner_ty = if let Some(e) = e.expr() { |
595 | self.infer_expr(e)? | 596 | self.infer_expr(e)? |
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index af5289311..c0e4e3a36 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs | |||
@@ -58,6 +58,7 @@ impl Conv for CompletionItemKind { | |||
58 | CompletionItemKind::Struct => Struct, | 58 | CompletionItemKind::Struct => Struct, |
59 | CompletionItemKind::Enum => Enum, | 59 | CompletionItemKind::Enum => Enum, |
60 | CompletionItemKind::Binding => Variable, | 60 | CompletionItemKind::Binding => Variable, |
61 | CompletionItemKind::Field => Field, | ||
61 | } | 62 | } |
62 | } | 63 | } |
63 | } | 64 | } |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 4e0550487..6b2800a0e 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -2030,6 +2030,10 @@ impl<'a> MethodCallExpr<'a> { | |||
2030 | pub fn expr(self) -> Option<Expr<'a>> { | 2030 | pub fn expr(self) -> Option<Expr<'a>> { |
2031 | super::child_opt(self) | 2031 | super::child_opt(self) |
2032 | } | 2032 | } |
2033 | |||
2034 | pub fn name_ref(self) -> Option<NameRef<'a>> { | ||
2035 | super::child_opt(self) | ||
2036 | } | ||
2033 | } | 2037 | } |
2034 | 2038 | ||
2035 | // Module | 2039 | // Module |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 923da0324..dcde32923 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -403,7 +403,7 @@ Grammar( | |||
403 | ), | 403 | ), |
404 | "MethodCallExpr": ( | 404 | "MethodCallExpr": ( |
405 | traits: ["ArgListOwner"], | 405 | traits: ["ArgListOwner"], |
406 | options: [ "Expr" ], | 406 | options: [ "Expr", "NameRef" ], |
407 | ), | 407 | ), |
408 | "IndexExpr": (), | 408 | "IndexExpr": (), |
409 | "FieldExpr": (options: ["Expr", "NameRef"]), | 409 | "FieldExpr": (options: ["Expr", "NameRef"]), |